AWS SAM を使用して、Lambda ベースのカスタムリソースを Java で CloudFormation 用に構築する方法を教えてください。

所要時間4分
0

Java を使用して AWS Lambda ベースのカスタムリソースを構築し、AWS CloudFormation に実装したいと考えています。

簡単な説明

Lambda for CloudFormation で Java ベースのカスタムリソースを構築することは、特にこのプロセスを手動で行う場合では複雑なプロセスとなります。環境を手動で設定するには、リソースをビルドする前に Java 開発キットとランタイムをセットアップする必要があります。次に、最終的に Lambda 関数を作成する前に、コードをパッケージ化して Amazon Simple Storage Service (Amazon S3) にアップロードする必要があります。

このプロセスを簡略化するには、AWS Serverless Application Model (AWS SAM)を使用できます。AWS SAM は、AWS でサーバーレスアプリケーションを構築するために使用できるオープンソースのフレームワークです。このサービスは、Java カスタムリソースの構築、コードのアップロード、カスタムリソースと Lambda 関数のデプロイに役立てることができます。

解決方法

開発用インスタンスを作成する

1.    Amazon Elastic Compute Cloud (Amazon EC2) インスタンスを作成してリソースを開発してください。インスタンスにはどのような環境でも使用できますが、このユースケースでは Amazon Linux 2 でインスタンスを作成するのがベストプラクティスです。

2.    Amazon EC2 SSH キーペアを作成して EC2 インスタンスに割り当てます

3.    スタックとリソースをデプロイする権限を持つインスタンスプロファイル を設定します。具体的には、次のアクションを実行する権限を付与してください。

  • Lambda 関数を作成する
  • 機能コードを更新する
  • 関数を呼び出す
  • Lambda のログを格納するロググループを作成する

4.    インスタンスを起動したら、SSH でログインします。次のセクションでは、このインスタンスを開発環境として使用します。

開発環境のセットアップ

重要: 始める前に、AWS コマンドラインインターフェイス (AWS CLI) を インストールして、設定します。AWS CLI コマンドの実行中にエラーが発生した場合は、最新バージョンの AWS CLI を使用していることを確認してください

java-corretto11 をインストールする

corretto11 をインストールするには、次のコマンドを実行します。

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

以下のコマンドでインストールを確認します。

java -version

詳細については、RPM ベースの Linux に Amazon Corretto 11 をインストールするを参照してください。

AWS SAM をインストールする

AWS SAM をインストールするには、次のコマンドを実行します。

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

詳細については、AWS SAM CLI のインストールを参照してください。

Gradle をインストールする

Gradle をインストールするには、次のコマンドを実行します。

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

詳細については、Gradle Web サイトのパッケージマネージャーによるインストールを参照してください。

注記:再起動後に PATH を再度エクスポートする必要がある場合があります。この手順を回避するには、~/.bashrc ファイルに次の行を追加してください。

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

プロジェクトとファイルを作成する

SAM プロジェクトのルートフォルダを作成するには、以下のコマンドを実行します。

mkdir javaBasedCustomResource
cd javaBasedCustomResource/

プロジェクトに必要なフォルダを作成するには、以下のコマンドを実行します。

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

Vim のテキストエディタでプロジェクトファイルを作成します。詳細については、Vim の Web サイトを参照してください。次の内容をコピーして Vim で実行します。

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

プロジェクトの構造は次の構造ツリーに似ています。

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

プロジェクトを構築するためのファイルテンプレートについては、以下のセクションを参照してください。

template.yaml

スタックには次のテンプレートを使用してください。これにより、Lambda 関数、ロググループ、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

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 は次のコードを実行します。これにより、入力イベントが Map に設定され、そこからレスポンスを送り返すために必要なエンドポイント URL を取得できます。必要なパラメータをすべて JSON 文字列に入力し、エンドポイントに HTTP リクエストを送信します。

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

プロジェクトをデプロイする

1.    AWS SAM プロジェクトを作成したら、ルートプロジェクトフォルダ JavaBasedCustomResourceで次のコマンドを実行します。

sam build

2.    プロジェクトをデプロイするには、次のコマンドを実行します。すると、リソースを作成するスタック名と AWS リージョンの指定を求めるガイドが表示されます。

sam deploy --guided

これにより、指定した名前の CloudFormation スタックがアカウントにデプロイされます。スタックには、前のステップのコードを実行する Lambda 関数が含まれています。また、CloudFormation は同じスタックにカスタムリソースタイプ AWS:: CloudFormation:: CustomResource を作成します。

CloudFormation が AWS:: CloudFormation:: CustomResource を作成すると、CloudFormation は上記の Lambda 関数も呼び出します。これに応答して、Lambda 関数は SYCEESS シグナルを CloudFormation に送り返します。この SUCCESS シグナルは、リソースを CreateCompleteに送信します。

スタック内のカスタムリソースが正常に作成されると、Lambda も正常に実行されていることになります。Lambda はシグナルをスタックに返し、Java ベースのカスタムリソースは正常に起動します。

リクエストイベントやレスポンスイベントなどのメトリクスの詳細については、Lambda ログで確認できます。関数コードを変更して独自のタスクを指定したり、独自のリソースを作成したりできます。

AWS公式
AWS公式更新しました 1年前
コメントはありません

関連するコンテンツ