AWS SAMのGo言語Lambdaアプリケーションのランタイムを更新してみた
しばたです。
先月よりAWSから「AWS LambdaのGo 1.xランタイムのサポートが2023年12月31日で終了する」旨の通知が案内されており、既に多くの方がランタイムの移行を試されているかと思います。
この通知に対してしばらく他人事でいたのですが、以前Go言語で簡単なAWS SAMアプリケーションを作っていたことを思い出したため、改めて現状を整理しランタイムの移行を試みました。
Go言語のLambdaランタイムおさらい
Go言語Lambdaの概要と移行手順についてはAWS Compute Blogの以下の記事が分かりやすいです。
Go言語のLambdaは最初にAWSが提供するAmazon Linux 1ベースのgo1.x
が提供されており、多くの方はこのランタイムを使っていると思います。
Amazon Linux 1ベースのため2023年12月31日でサポートが切れ非推奨化フェーズ 1になります。
その後Lambdaがカスタムランタイムをサポートする様になり、Amazon Linux 1ベースのprovided
およびAmazon Linux 2ベースのprovided.al2
上でGo言語アプリケーションを動作させるパターンが増え、さらにLambdaがコンテナイメージをサポートしたことで任意のイメージ上でGo言語アプリケーションを動作させることも可能になりました。
現時点においてgo1.x
の後継となるランタイムは発表されなかったため、go1.x
の利用者はカスタムランタイムprovided.al2
か独自のコンテナイメージを使う様にアプリケーションを移行する必要があります。
aws-lambda-go モジュール
Go言語でLambda関数を実装する場合aws-lambda-goを使用します。
このモジュールがLambda内部のAPI(Runtime API)をよしなに扱うことで開発者はアプリケーションの実装に注力できる形になっています。
前述のAWS Compute Blogを読むまで知らなかったのですが、go1.x
ランタイムではランタイム内部にRuntime APIを扱う層(Runtime Client)が用意されておりaws-lambda-goはこのRuntime ClientとRPC通信をする実装になっています。
(Migrating AWS Lambda functions from the Go1.x runtime to the custom runtime on Amazon Linux 2より引用)
対してカスタムランタイムの場合は開発者が直接Runtime APIを扱う必要があり、aws-lambda-goにおいてはVer.1.18からカスタムランタイムに対応しています。
(Migrating AWS Lambda functions from the Go1.x runtime to the custom runtime on Amazon Linux 2より引用)
内部実装としてはRPCを使うパターン、Runtime APIを直接実行するパターンの二種類の実装を実行環境に応じて使い分ける様になっていました。
_LAMBDA_SERVER_PORT
環境変数が定義されている場合はRPCを使い、AWS_LAMBDA_RUNTIME_API
環境変数が定義されている場合はRuntime APIを直接利用します。
また、Goアプリケーションのビルド時にlambda.norpc
タグを付けてやることでRPCに依存する処理を除外しバイナリサイズを減らすことも可能です。
# lambda.norpc タグを付けたビルド例
GOARCH=amd64 GOOS=linux go build -tags lambda.norpc -o bootstrap main.go
ざっくりまとめ
ここまでの内容をざっくりまとめると下表のとおりとなります。
ランタイム | ベースOS | aws-lambda-go | 内部通信 | サポート期限 |
---|---|---|---|---|
go1.x | Amazon Linux 1 | 任意のバージョン | RPC→Runtime API | 2023年12月31日 |
カスタムランタイム (provided) | Amazon Linux 1 | Ver.1.18以降 | Runtime API | 2023年12月31日 |
カスタムランタイム (provided.al2) | Amazon Linux 2 | Ver.1.18以降 | Runtime API | 未定 |
コンテナイメージ | イメージ次第 | Ver.1.18以降 | Runtime API | イメージ次第 |
provided.al2
のサポート期限は現時点で決まっていませんが、Amazon Linux 2のサポート期限が2025年6月30日までなので概ね同時期にLambdaのサポートも切れることでしょう。
AWS SAMの場合
AWS SAMではVer.0.26からGoランタイムgo1.x
のビルドをサポートしており、AWS::Serverless::FunctionリソースのRuntime
プロパティを指定してやればよしなにビルドしてくれます。
# AWS SAMでの記述例
Resources:
# Lambda function
OjichatFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: cloudwatch-ojichat-widget
CodeUri: function/
Handler: main
Runtime: go1.x
具体的なビルド処理はAWS Lambda Buildersに記述されています。
Go言語の場合はざっくり以下の様なパラメーター指定でビルドする様になっています。
export GOOS=linux
export GOARCH=Architecturesで指定されたアーキテクチャ
# 条件により -trimpath パラメーターが付くこともある
# デバッグ時は -gcflags all=-N -l パラメーターが付く
go build -o "Handlerパラメーターで指定された値" "CodeUriで指定されたパス"
残念ながらlambda.norpc
タグには対応してない様でした。
そしてgo1.x
ランタイムをprovided.al2
に変える場合はこんな感じにしてやります。
# provided.al2 で Go言語Lambdaをビルドする例
Resources:
# Lambda function
OjichatFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: cloudwatch-ojichat-widget
CodeUri: function/
Handler: bootstrap
Runtime: provided.al2
Metadata:
BuildMethod: go1.x
AWS Lambda Builders Ver.1.21.0 (AWS SAM Ver.1.62.0)よりメタデータのBuildMethodでgo1.x
の指定が可能となっており、カスタムランタイムを使いつつ前述のオプションでGoアプリケーションのビルドが可能になっています。
加えてカスタムランタイムではバイナリファイルの名前をbootstrap
にする必要があるため、Handlerプロパティの値をbootstrap
にしてやります。
これだけでOKです。
移行してみた
ここまでの内容をふまえて実際に移行したアプリケーションを紹介します。
CloudWatch custom widget用に非常にシンプルなGo言語のLambda関数を実装しています。
今回のために予め開発環境やモジュールバージョンを刷新し、
- Go 1.21.1
- AWS SAM Ver.1.97.0
- aws-lambda-go v1.41.0
で動作する様にしています。
移行前の状態はこんな感じでgo1.x
ランタイムで動作していました。
# 一部抜粋
Resources:
# Lambda function
OjichatFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: cloudwatch-ojichat-widget
CodeUri: function/
Handler: main
Runtime: go1.x
Tracing: PassThrough
前節の内容に倣いランタイムをprovided.al2
に更新し、ついでにアーキテクチャも単価の安いarm64
に変更しておきました。
# 一部抜粋
Resources:
# Lambda function
OjichatFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: cloudwatch-ojichat-widget
CodeUri: function/
Handler: bootstrap
Runtime: provided.al2
Architectures: [arm64]
Tracing: PassThrough
Metadata:
BuildMethod: go1.x
この状態でsam build
およびsam deploy
を実行し再デプロイしてやれば移行は完了です。
シンプルなアプリケーションなのでこれだけで特に問題無く動作しています。
実運用されているアプリケーションの場合はもう少し事前検証が必要になると思いますが、手順としては大差ないでしょう。
補足 : Makefileビルドする場合
AWS SAMのバージョンが古かったりGoのビルド時にlambda.norpc
タグを付けたい場合はMakefileを使ったビルドをしないといけない様です。
こちらの記事を参考にするとざっくり以下の様な記述で動作するはずです。
Resources:
# Lambda function
OjichatFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: cloudwatch-ojichat-widget
CodeUri: function/
Handler: bootstrap
Runtime: provided.al2
Architectures: [arm64]
Tracing: PassThrough
Metadata:
BuildMethod: makefile
build-OjichatFunction:
GOOS=linux GOARCH=arm64 go build -tags lambda.norpc -o bootstrap
cp ./bootstrap $(ARTIFACTS_DIR)/.
Makefileの内容は環境依存になるため本記事では厳密に動作検証していませんのでご了承ください。
Ubuntu環境で上記内容に対しsam build
が通ることまでは確認しています。
ちなみにビルドしたバイナリファイルは以下のサイズ感でした。
条件 | ファイルサイズ |
---|---|
通常のビルド(arm64, lambda.norpcタグ無し) | 21.6MB |
Makefileビルド(arm64, lambda.norpcタグ有り) | 19.9MB |
最後に
以上となります。
簡単なアプリケーションだったので手順としてもあっさり目の内容になりました。
本記事の内容が皆さんの役に立てば幸いです。