Wie verwende ich AWS SAM, um eine von Lambda unterstützte, benutzerdefinierte Ressource in Java für CloudFormation zu erstellen?

Lesedauer: 6 Minute
0

Ich möchte Java verwenden, um eine von AWS Lambda unterstützte, benutzerdefinierte Ressource für die Implementierung in AWS CloudFormation zu erstellen.

Kurze Beschreibung

Das Erstellen einer Java-basierten benutzerdefinierten Ressource in Lambda for CloudFormation ist ein komplexer Prozess, insbesondere wenn Sie diesen Vorgang manuell durchführen. Um die Umgebung manuell einzurichten, müssen Sie das Java Development Kit und die Laufzeit einrichten, bevor Sie die Ressource erstellen. Anschließend müssen Sie den Code verpacken und auf Amazon Simple Storage Service (Amazon S3) hochladen, bevor Sie schließlich die Lambda-Funktion erstellen.

Um diesen Prozess zu vereinfachen, können Sie das AWS Serverless Application Model (AWS SAM) verwenden. AWS SAM ist ein Open-Source-Framework, mit dem Sie serverlose Anwendungen auf AWS erstellen können. Verwenden Sie diesen Dienst, um die benutzerdefinierte Java-Ressource zu erstellen, Ihre Codes hochzuladen und die benutzerdefinierte Ressource und die Lambda-Funktion bereitzustellen.

Lösung

Eine Instance für die Entwicklung erstellen

1.    Erstellen Sie eine Amazon Elastic Compute Cloud (Amazon EC2)-Instance, um Ihre Ressource zu entwickeln. Sie können jede Umgebung für Ihre Instance verwenden, es hat sich jedoch bewährt, Ihre Instance für diesen Anwendungsfall in Amazon Linux 2 zu erstellen.

2.    Erstellen Sie ein Amazon EC2-SSH-Schlüsselpaar und weisen Sie es Ihrer EC2-Instance zu.

3.    Richten Sie ein Instance-Profil mit Berechtigungen zur Bereitstellung eines Stacks und einer Ressource ein. Weisen Sie insbesondere Berechtigungen zur Ausführung der folgenden Aktionen zu:

  • Lambda-Funktion erstellen
  • Funktionscode aktualisieren
  • Funktion aufrufen
  • Protokollgruppe zur Speicherung des Lambda-Protokolls erstellen

4.    Nachdem Sie Ihre Instance gestartet haben, melden Sie sich mit SSH an. Verwenden Sie diese Instance im folgenden Abschnitt als Ihre Entwicklungsumgebung.

Richten Sie Ihre Entwicklungsumgebung ein

**Wichtig:**Bevor Sie beginnen, installieren und konfigurieren Sie das AWS Command Line Interface (AWS CLI). Wenn Sie beim Ausführen von AWS-CLI-Befehlen Fehler erhalten, stellen Sie sicher, dass Sie die neueste Version der AWS-CLI verwenden.

java-corretto11 installieren

Führen Sie den folgenden Befehl aus, um corretto11 zu installieren:

sudo rpm --import https://yum.corretto.aws/corretto.key   
sudo curl -L -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo  
sudo yum install -y java-11-amazon-corretto-devel

Überprüfen Sie die Installation mit dem folgenden Befehl:

java -version

Weitere Informationen finden Sie unter Installieren von Amazon Corretto 11 auf RPM-basiertem Linux.

AWS SAM installieren

Führen Sie den folgenden Befehl aus, um AWS SAM zu installieren:

wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
sudo ./sam-installation/install
sam --version

Weitere Informationen finden Sie unter Installation der AWS SAM CLI.

Gradle installieren

Führen Sie den folgenden Befehl aus, um Gradle zu installieren:

wget https://services.gradle.org/distributions/gradle-7.6-bin.zip
sudo mkdir /opt/gradle
sudo unzip -d /opt/gradle gradle-7.6-bin.zip
export PATH=$PATH:/opt/gradle/gradle-7.6/bin
gradle -v

Weitere Informationen finden Sie unter Installation mit einem Paketmanager auf der Gradle-Website.

**Hinweis:**Möglicherweise müssen Sie den PFAD nach dem Neustart erneut exportieren. Um diesen Schritt zu vermeiden, fügen Sie die folgende Zeile an die Datei ~/.bashrc an:

export PATH=$PATH:/opt/gradle/gradle-7.6/bin

Projekt und Dateien erstellen

Führen Sie den folgenden Befehl aus, um den Stammordner für das SAM-Projekt zu erstellen:

mkdir javaBasedCustomResource
cd javaBasedCustomResource/

Führen Sie den folgenden Befehl aus, um die erforderlichen Ordner für Ihr Projekt zu erstellen:

mkdir -p src/Function/src/main/java/

Erstellen Sie die Projektdateien mit dem Vim-Texteditor. Weitere Informationen finden Sie auf der Vim-Website. Kopieren Sie den folgenden Inhalt, um ihn in Vim auszuführen:

vim template.yaml
vim ./src/Function/build.gradle
vim ./src/Function/src/main/java/Handler.java

Die Struktur Ihres Projekts ähnelt dem folgenden Strukturbaum:

.
└── javaBasedCustomResource
    ├── src
    │   └── Function
    │       ├── build.gradle
    │       └── src
    │           └── main
    │               └── java
    │                   └── Handler.java
    └── template.yaml

In den folgenden Abschnitten finden Sie die Dateivorlagen zum Erstellen Ihres Projekts.

template.yaml

Verwenden Sie die folgende Vorlage für den Stack. Diese definiert die Lambda-Funktion, die Protokollgruppe und die CustomResource:

Transform: AWS::Serverless-2016-10-31
Resources:
  CustomResourceJavaBased:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !Join ["", [ !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:", !Ref CustomResourceLambdaInJava]]
      DummyKey: DummyValue

  CustomResourceLambdaInJava:
    Type: AWS::Serverless::Function
    Properties:
      Description: !Sub
        - Stack ${AWS::StackName} Function ${ResourceName}
        - ResourceName: CustomResourceLambdaInJava
      CodeUri: src/Function
      Handler: Handler::handleRequest
      Runtime: java11
      MemorySize: 3008
      Timeout: 30
      Tracing: Active
  CustomResourceLambdaInJavaLogGroup:
    Type: AWS::Logs::LogGroup
    DeletionPolicy: Retain
    Properties:
      LogGroupName: !Sub /aws/lambda/${CustomResourceLambdaInJava}

build.gradle

Verwenden Sie die folgende Dateivorlage, um Ihren Java-Build zu vereinfachen:

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
    implementation 'com.amazonaws:aws-lambda-java-events:3.11.0'
    implementation 'com.google.code.gson:gson:2.10'

}

task buildZip(type: Zip) {
    from compileJava
    from processResources
    into('lib') {
        from configurations.compileClasspath
    }
}

build.dependsOn buildZip

Handler.java

Lambda führt den folgenden Code aus. Dadurch wird das Eingabeereignis auf eine Map gesetzt, von der Sie die erforderliche Endpunkt-URL abrufen können, um die Antwort zurückzusenden. Fügen Sie alle benötigten Parameter in eine JSON-Zeichenfolge ein und senden Sie eine HTTP-Anfrage an den Endpunkt:

import com.google.gson.Gson;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import java.util.*;
import java.io.*;

public class Handler {
    public String handleRequest(Object event) {
    /* Customer workload */
    System.out.println("Dummy CustomResource Job");

    /* sending signal back to CFN stack */
    try {
        sendResponse(event);
    }
    catch (Exception e){
        System.out.println("Got Exception!!");
        e.printStackTrace(System.out);
    }

        return "JobFinished";
    }


    public void sendResponse(Object event) throws IOException, InterruptedException {
    System.out.println("start sending signal");

    Gson gson = new Gson();
    String eventJson = gson.toJson(event);
    Map map = gson.fromJson(eventJson, Map.class);

    System.out.println("Request event: " + eventJson);

    /* Generate response  parameters */
    String putEndpoint = (String)map.get("ResponseURL");

    String Status = "SUCCESS"; // "SUCCESS" or "FAILED"

    String Reason = "Dummy reason for Java based Custom Resource";

    String PhysicalResourceId = "CustomResourcePhysicalID";

    String StackId = (String)map.get("StackId");

    String RequestId = (String)map.get("RequestId");

    String LogicalResourceId = (String)map.get("LogicalResourceId");

    /* Building response */
        String responseJson = "{\"Status\":\"" + Status + "\",\"Reason\":\"" + Reason + "\",\"PhysicalResourceId\":\"" + PhysicalResourceId + "\",\"StackId\":\"" + StackId + "\",\"RequestId\":\"" + RequestId + "\",\"LogicalResourceId\":\"" + LogicalResourceId + "\",\"NoEcho\":false,\"Data\":{\"Key\":\"Value\"}}";

    System.out.println("Response event: " + responseJson);

    var request = HttpRequest.newBuilder()
            .uri(URI.create(putEndpoint))
            .header("Content-Type", "application/json")
            .PUT(HttpRequest.BodyPublishers.ofString(responseJson))
            .build();

        var client = HttpClient.newHttpClient();

    /* Sending Response */
    System.out.println("Sending Response to stack, response code: ");

        var response = client.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println(response.statusCode());
        System.out.println(response.body());

    System.out.println("Finish sending signal");
    }
}

Projekt bereitstellen

1.    Nachdem Sie Ihr AWS-SAM-Projekt erstellt haben, führen Sie den folgenden Befehl im Stammprojektordner javaBasedCustomResource aus:

sam build

2.    Führen Sie den folgenden Befehl aus, um das Projekt bereitzustellen. Daraufhin wird ein Leitfaden gestartet, in dem Sie aufgefordert werden, den Stack-Namen und die AWS-Region anzugeben, in der die Ressourcen erstellt werden sollen:

sam deploy --guided

Dadurch wird in Ihrem Konto ein CloudFormation-Stack mit dem von Ihnen angegebenen Namen bereitgestellt. Der Stack enthält eine Lambda-Funktion, die den Code aus den vorherigen Schritten ausführt. CloudFormation erstellt auch den benutzerdefinierten Ressourcentyp AWS::CloudFormation::CustomResource im selben Stack.

Wenn CloudFormation AWS::CloudFormation::CustomResource erstellt, ruft CloudFormation auch die obige Lambda-Funktion auf. Als Antwort sendet die Lambda-Funktion ein SUCCESS-Signal zurück an CloudFormation. Dieses SUCCESS-Signal sendet die Ressource an CreateComplete.

Wenn Sie sehen, dass die benutzerdefinierte Ressource im Stack erfolgreich erstellt wurde, bedeutet dies, dass Lambda ebenfalls erfolgreich ausgeführt wird. Lambda gibt das Signal an den Stack zurück und die Java-basierte benutzerdefinierte Ressource wird erfolgreich initiiert.

Weitere Informationen zu Metriken wie dem Anforderungsereignis und dem Antwortereignis finden Sie in den Lambda-Protokollen. Sie können den Funktionscode ändern, um Ihre eigenen Aufgaben zu spezifizieren oder Ihre eigenen Ressourcen zu erstellen.

AWS OFFICIAL
AWS OFFICIALAktualisiert vor einem Jahr