Come posso usare AWS SAM per creare una risorsa personalizzata supportata da Lambda in Java per CloudFormation?

6 minuti di lettura
0

Desidero usare Java per creare una risorsa personalizzata supportata da AWS Lambda da implementare in AWS CloudFormation.

Breve descrizione

La creazione di una risorsa personalizzata basata su Java in Lambda for CloudFormation è un processo complesso, soprattutto se si esegue questo processo manualmente. Per configurare l'ambiente manualmente, è necessario configurare il kit di sviluppo Java e il runtime prima di creare la risorsa. Quindi, devi impacchettare il codice e caricarlo su Amazon Simple Storage Service (Amazon S3) prima di creare finalmente la funzione Lambda.

Per semplificare questo processo, puoi utilizzare AWS Serverless Application Model (AWS SAM). AWS SAM è un framework open source che puoi utilizzare per creare applicazioni serverless su AWS. Usa questo servizio per creare la risorsa personalizzata Java, caricare i tuoi codici e implementare la risorsa personalizzata e la funzione Lambda.

Risoluzione

Crea un'istanza per lo sviluppo

  1. Crea un'istanza Amazon Elastic Compute Cloud (Amazon EC2) per sviluppare la tua risorsa. Puoi utilizzare qualsiasi ambiente per la tua istanza, ma è consigliabile creare la tua istanza in Amazon Linux 2 per questo caso d'uso.

  2. Crea e assegna una coppia di chiavi SSH Amazon EC2 alla tua istanza EC2.

  3. Configura un profilo di istanza con le autorizzazioni per implementare uno stack e una risorsa. In particolare, concedi le autorizzazioni per eseguire le seguenti azioni:

  • Creare una funzione Lambda
  • Aggiorna il codice della funzione
  • Richiama una funzione
  • Crea un gruppo di log che memorizzi il registro di Lambda
  1. Dopo aver avviato l'istanza, accedi con SSH. Usa questa istanza come ambiente di sviluppo nella sezione seguente.

Configura il tuo ambiente di sviluppo

Importante: Prima di iniziare, installa e configura l'interfaccia a riga di comando AWS (AWS CLI). Se ricevi errori durante l'esecuzione dei comandi dell'interfaccia a riga di comando di AWS, assicurati di utilizzare la versione più recente dell'interfaccia a riga di comando di AWS.

Installare java-corretto11

Per installare corretto11, esegui il seguente comando:

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

Verifica l'installazione con il seguente comando:

java -version

Per ulteriori informazioni, consulta Installare Amazon Corretto 11 su Linux basato su RPM.

Installa AWS SAM

Per installare AWS SAM, esegui il seguente comando:

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

Per ulteriori informazioni, consulta Installazione dell'interfaccia a riga di comando di AWS SAM.

Installa Gradle

Per installare Gradle, esegui il seguente comando:

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

Per ulteriori informazioni, vedere Installazione con un gestore di pacchetti sul sito Web di Gradle.

Nota: Potrebbe essere necessario esportare nuovamente il PATH dopo il riavvio. Per evitare questo passaggio, aggiungi la riga seguente al file ~/.bashrc:

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

Crea il progetto e i file

Per creare la cartella principale per il progetto SAM, esegui il seguente comando:

mkdir javaBasedCustomResource
cd javaBasedCustomResource/

Per creare le cartelle necessarie per il tuo progetto, esegui il seguente comando:

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

Crea i file di progetto tramite l'editor di testo Vim. Per ulteriori informazioni, visita il sito Web di Vim. Copia il seguente contenuto per eseguirlo in Vim:

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

La struttura del tuo progetto è simile al seguente albero strutturale:

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

Consulta le sezioni seguenti per i modelli di file per creare il tuo progetto.

template.yaml

Usa il seguente modello per lo stack. Questo definisce la funzione Lambda, il gruppo di log e 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

Usa il seguente modello di file per facilitare la creazione di java:

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 esegue il codice seguente. Questo imposta l'evento di input su una mappa da cui è possibile ottenere l'URL dell'endpoint necessario per restituire la risposta. Inserisci tutti i parametri necessari in una stringa JSON e invia una richiesta HTTP all'endpoint:

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");
    }
}

Implementa il progetto

  1. Dopo aver creato il tuo progetto AWS SAM, esegui il seguente comando nella cartella principale del progetto javaBasedCustomResource:
sam build
  1. Per implementare il progetto, esegui il comando seguente. Viene avviata una guida che richiede di specificare il nome dello stack e la regione AWS in cui si desidera creare le risorse:
sam deploy --guided

Questo implementa uno stack CloudFormation nel tuo account con il nome che hai specificato. Lo stack contiene una funzione Lambda che esegue il codice dei passaggi precedenti. CloudFormation crea anche il tipo di risorsa personalizzato AWS::CloudFormation::CustomResource nello stesso stack.

Quando CloudFormation crea AWS::CloudFormation::CustomResource, CloudFormation richiama anche la funzione Lambda precedente. In risposta, la funzione Lambda invia un segnale SUCCESS a CloudFormation. Questo segnale SUCCESS invia la risorsa a CreateComplete.

Quando vedi che la risorsa personalizzata nello stack viene creata correttamente, significa che anche Lambda funziona correttamente. Lambda restituisce il segnale allo stack e la risorsa personalizzata basata su java viene avviata correttamente.

Puoi controllare i log di Lambda per maggiori dettagli su metriche come l'evento di richiesta e l'evento di risposta. È possibile modificare il codice della funzione per specificare le proprie attività o creare risorse personalizzate.

AWS UFFICIALE
AWS UFFICIALEAggiornata un anno fa