はじめに

以前、以下の記事で go1.x ランタイムのコンテナ Lambda を CDK で作成する例を紹介しました。

モノレポ構造の CodeCommit でパイプラインの実行を制御する Lambda 関数を Go 言語で書いてみた

その後、Lambda の go1.x ランタイムのサポートが 2023/12/31 に終了する旨の案内があり、上の記事においてもカスタムランタイム (provided.al2) へ移行する必要が出てきました。今回はこちらの内容に沿って、ランタイムの移行をしてみました。

コード修正

CDK は TypeScript で書いており、こんなコードでした。

// Create lambda function for pipeline handler
const pipelineHandler_ = new lambda.Function(this, "PipelineHandler", {
  functionName: `pipeline-handler`,
  description: "Receives codecommit code change events and starts pipelines for specific directories.",
  code: lambda.Code.fromAsset("src/lambda/pipeline-trigger", {
    bundling: {
      image: lambda.Runtime.GO_1_X.bundlingImage,
      command: [
        "bash",
        "-c",
        [
          "export GOCACHE=/tmp/go-cache",
          "export GOPATH=/tmp/go-path",
          "GOOS=linux go build -o /asset-output/main main.go",
        ].join(" && "),
      ],
    },
  }),
  handler: "main",
  architecture: lambda.Architecture.X86_64,
  runtime: lambda.Runtime.GO_1_X,
  role: pipelineHandlerRole,
  logRetention: logs.RetentionDays.THREE_DAYS,
  environment: {
    PIPELINES: JSON.stringify(pipelines),
  },
  currentVersionOptions: {
    removalPolicy: RemovalPolicy.RETAIN,
  },
  deadLetterQueueEnabled: true,
  deadLetterQueue: deadLetterQueue,
  reservedConcurrentExecutions: 1,
  retryAttempts: 2,
});

これを以下のように修正します。

// Create lambda function for pipeline handler
const pipelineHandler_ = new lambda.Function(this, "PipelineHandler", {
  functionName: `pipeline-handler`,
  description: "Receives codecommit code change events and starts pipelines for specific directories.",
  code: lambda.Code.fromAsset("src/lambda/pipeline-trigger", {
    bundling: {
      //image: lambda.Runtime.GO_1_X.bundlingImage,
      image: DockerImage.fromRegistry("golang:1.21.0"),
      command: [
        "bash",
        "-c",
        [
          "export GOCACHE=/tmp/go-cache",
          "export GOPATH=/tmp/go-path",
          //"GOOS=linux go build -o /asset-output/main main.go",
          "CGO_ENABLED=0 GOOS=linux go build -tags lambda.norpc -o /asset-output/bootstrap main.go",
        ].join(" && "),
      ],
    },
  }),
  //handler: "main",
  handler: "bootstrap",
  architecture: lambda.Architecture.X86_64,
  //runtime: lambda.Runtime.GO_1_X,
  runtime: lambda.Runtime.PROVIDED_AL2,
  role: pipelineHandlerRole,
  logRetention: logs.RetentionDays.THREE_DAYS,
  environment: {
    PIPELINES: JSON.stringify(pipelines),
  },
  currentVersionOptions: {
    removalPolicy: RemovalPolicy.RETAIN,
  },
  deadLetterQueueEnabled: true,
  deadLetterQueue: deadLetterQueue,
  reservedConcurrentExecutions: 1,
  retryAttempts: 2,
});

以下が修正点です。

  1. runtime には lambda.Runtime.PROVIDED_AL2 を設定
  2. 実行バイナリ名、ハンドラ名は bootstrap
  3. code.bundling.image では、DockerImage.fromRegistry() を使って DockerHub から golang のイメージを取ってくる (つい先日リリースされた 1.21 系にしている)
  4. code.bundling.command を仕様に合わせて修正 (後述)

補足

修正点 2 についてですが、公式に以下のように記載がありました。実行バイナリ名が bootstrap 固定、ハンドラの名前はそれに合わせておくのが推奨のようです。

In order to run a compiled Go application on Lambda, you must compile your code for Linux. While the go1.x runtime allows you to use any executable name, the provided.al2 runtime requires you to use bootstrap as the executable name.


コンパイル済みのGoアプリケーションをLambda上で実行するには、コードをLinux用にコンパイルする必要があります。go1.xランタイムでは任意の実行ファイル名を使用できますが、provided.al2ランタイムではbootstrapを実行ファイル名として使用する必要があります。

Note: The handler value is not used by the provided.al2 runtime, nor the aws-lambda-go library, and may be set to any value. We recommend the setting the value to bootstrap to help with migrating between go1.x and provided.al2.


注意: handlerの値はprovided.al2ランタイムやaws-lambda-goライブラリでは使用されません。go1.xとprovided.al2間の移行を容易にするため、bootstrapに設定することを推奨します。

4 のバンドリング時ビルドコマンドの修正ですが、概ね以下のような意図となります。

  • -o /asset-output/bootstrap: 前述の通り、バイナリを所定の名前で出力するように修正
  • -tags lambda.norpc: 不要な RPC コンポーネントを削除するように修正 (以下引用を参照)

Removing the additional process and RPC hop streamlines the function execution path, resulting in faster invokes. You can also remove the RPC component from the aws-lambda-go package, giving a smaller binary size and faster code loading during cold starts.


追加のプロセスとRPCホップを削除することで、関数の実行パスが効率化され、呼び出しが高速化されます。また、aws-lambda-goパッケージからRPCコンポーネントを削除することで、バイナリサイズが小さくなり、コールドスタート時のコードロードが高速になります。

  • CGO_ENABLED=0: cgo を無効化 (以下エラーが発生したことの対策)

/var/task/bootstrap: /lib64/libc.so.6: version `GLIBC_2.34′ not found (required by /var/task/bootstrap)

おわりに

ほぼほぼ公式に沿った内容となりましたが、TypeScript の CDK で定義した go1.x のコンテナ Lambda を移行する際の修正ポイントをまとめておくという意味では有用かと思います。