Como usar o AWS SAM para criar um recurso personalizado baseado em Lambda em Java para o CloudFormation?

7 minuto de leitura
0

Quero usar o Java para criar um recurso personalizado baseado no AWS Lambda para implementar no AWS CloudFormation.

Breve descrição

Criar um recurso personalizado baseado em Java no Lambda para o CloudFormation é um processo complexo, especialmente se você fizer esse processo manualmente. Para configurar o ambiente manualmente, você precisa configurar o kit de desenvolvimento Java e o tempo de execução antes de criar o recurso. Em seguida, você precisa empacotar e enviar o código ao Amazon Simple Storage Service (Amazon S3) antes de finalmente criar a função do Lambda.

Para simplificar esse processo, você pode usar o AWS Serverless Application Model (AWS SAM). O AWS SAM é uma framework de código aberto que você pode usar para criar aplicações sem servidor na AWS. Use esse serviço para ajudar a criar o recurso personalizado Java, carregar seus códigos e implantar o recurso personalizado e a função do Lambda.

Resolução

Criar uma instância para desenvolvimento

1.    Crie uma instância do Amazon Elastic Compute Cloud (Amazon EC2) para desenvolver seu recurso. Você pode usar qualquer ambiente para sua instância, mas é uma prática recomendada criar sua instância no Amazon Linux 2 para esse caso de uso.

2.    Crie e atribua um par de chaves SSH do Amazon EC2 à sua instância do EC2.

3.    Configure um perfil de instância com permissões para implantar uma pilha e um recurso. Especificamente, conceda permissões para realizar as seguintes ações:

  • Criar uma função do Lambda
  • Atualizar o código da função
  • Invocar uma função
  • Criar um grupo de logs que armazene o log do Lambda

4.    Depois de iniciar sua instância, faça login com SSH. Use essa instância como seu ambiente de desenvolvimento na seção a seguir.

Configurar o ambiente de desenvolvimento

Importante: Antes de começar, instale e configure a AWS Command Line Interface (AWS CLI). Se você receber erros ao executar comandos da AWS CLI, certifique-se de estar utilizando a versão mais recente da AWS CLI.

Instalar java-corretto11

Para instalar corretto11, execute o seguinte 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

Verifique a instalação com o seguinte comando:

java -version

Para mais informações, consulte Instalar o Amazon Corretto 11 em um Linux baseado em RPM.

Instalar o AWS SAM

Para instalar o AWS SAM, execute o seguinte 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

Para mais informações, consulte Instalar a CLI do AWS SAM.

Instalar o Gradle

Para instalar o Gradle, execute o seguinte 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

Para mais informações, consulte Instalar com um gerenciador de pacotes no site do Gradle.

**Observação:**Talvez seja necessário exportar PATH novamente após a reinicialização. Para evitar essa etapa, acrescente a seguinte linha ao arquivo ~/.bashrc:

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

Criar o projeto e os arquivos

Para criar a pasta raiz do projeto SAM, execute o seguinte comando:

mkdir javaBasedCustomResource
cd javaBasedCustomResource/

Para criar as pastas necessárias para seu projeto, execute o seguinte comando:

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

Crie os arquivos do projeto por meio do editor de texto Vim. Para mais informações, consulte o site do Vim. Copie o seguinte conteúdo para executar no Vim:

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

A estrutura do seu projeto se assemelha à seguinte árvore estrutural:

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

Consulte as seções a seguir para ver os modelos de arquivo para criar seu projeto.

template.yaml

Use o modelo a seguir para a pilha. Isso define a função do Lambda, o grupo de logs e o 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

Use o seguinte modelo de arquivo para facilitar sua construção em 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

O Lambda executa o código a seguir. Isso define o evento de entrada como um mapa a partir do qual você pode obter a URL do endpoint necessária para enviar de volta a resposta. Coloque todos os parâmetros necessários em uma string JSON e envie uma solicitação HTTP ao 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");
    }
}

Implantar o projeto

1.    Depois de criar seu projeto do AWS SAM, execute o seguinte comando na pasta raiz do projeto javaBasedCustomResource:

sam build

2.    Para implantar o projeto, execute o comando a seguir. Isso inicia um guia que solicita que você especifique o nome da pilha e a região da AWS em que você deseja que os recursos sejam criados:

sam deploy --guided

Isso implanta uma pilha do CloudFormation na sua conta com o nome que você especificou. A pilha contém uma função do Lambda que executa o código das etapas anteriores. O CloudFormation também cria o tipo de recurso personalizado AWS::CloudFormation::CustomResource na mesma pilha.

Quando o CloudFormation cria AWS::CloudFormation::CustomResource, o CloudFormation também invoca a função do Lambda acima. Em resposta, a função do Lambda envia um sinal de SUCCESS de volta ao CloudFormation. Esse sinal de SUCCESS envia o recurso para CreateComplete.

Ao visualizar o recurso personalizado na pilha criado com êxito, isso significa que o Lambda também está sendo executado com êxito. O Lambda retorna o sinal para a pilha e o recurso personalizado baseado em java é iniciado com êxito.

Você pode verificar os logs do Lambda para obter mais detalhes sobre métricas, como o evento de solicitação e o evento de resposta. Você pode modificar o código da função para especificar suas próprias tarefas ou criar seus próprios recursos.

AWS OFICIAL
AWS OFICIALAtualizada há um ano