前回書いたメモ で AutoPublishAlias
のことを書きましたが、もう少し工夫すれば Serverless Framework でやれていたこととほぼ同等のことが実現できるのではないかという目途が立ったので、追加で書きます。前回同様、役に立つ記事とかそういうのはよそを当たってください…。
AutoPublishAlias で Function URL が使えなくなったワケ
前回
FunctionUrlConfig
AutoPublishAlias
DeploymentPreference
の 3 つを併用すると Function URL が (定義は残っているにもかかわらず) アクセスできなくなる
と書きましたが、これは自分の理解不足でした。
-
AutoPublishAlias
DeploymentPreference
の働きによって「古い Lambda バージョンを削除して、新しいバージョンを作成し、 Lambda のエイリアスに最新バージョンを登録する」という動きになる -
FunctionUrlConfig
の定義はあって Function URL も再発行されるものの、上記の手順の途中でおそらくパーミション (リソースベースポリシー)1 が消えてしまう
からであろうと推測しています。
で、おそらくこれは Function URL に限った話ではなく、 S3 や SNS のような「トリガーのための権限を Lambda のリソースベースポリシーで設定しなければならないサービス」ではみんな同じ話になるだろうと思います。(EventBridge ルールであればターゲットに identity ベースポリシーを割り当てられるので、ここの話は当てはまりません)
Serverless Framework と SAM の違い:エイリアスを意識する
Serverless Framework ではこの辺をキチンと面倒みてくれていたんでしょう。古いバージョンは消さずに新しいバージョンだけを作成し、各種トリガーは非修飾の $LATEST
に対するものとし、必要なパーミションは再設定もしてくれていた。
SAM で AutoPublishAlias
DeploymentPreference
を付けただけでは、新旧バージョンの交代は瞬時にできたとしても「旧バージョンの削除」という要らない動作まで付いてくるので、そこをなんとかしなければなりません。template.yaml で AWS::Serverless::Function
リソースに DeletionPolicy: Retain
をつければ旧バージョンの削除は抑制できますが、それをすると今度は FunctionUrlConfig
の設定が邪魔になります。
ポイントになるのは、Serverless Framework のときは気にしていなかった「エイリアス」を SAM では意識したほうがうまくいく、ということでしょうか。
バージョンがどれだけ変わろうと、呼び出したいのは AutoPublishAlias
で作ってもらったエイリアスが指しているバージョン。であるならば、 AWS::Serverless::Function
に FunctionUrlConfig
を付けるのではなくて、作成されるであろう「エイリアス」に対して明示的に AWS::Lambda::Url
リソースを作成すればよい。パーミションも「エイリアス」に対して付与すればよい。
あとは、デプロイすればするほど旧バージョンが (Serverless Framework の場合と同様) 増え続けるのを何とかすればよい。残念ながら serverless-prune-plugin のような便利ツールは SAM にはなさそうですが、なければ作ればよい。… Copilot くんに尋ねてデバッグさせて、ものの 10 分くらいで使えそうな bash スクリプトもできました。これなら GitHub Actions で自動化することもできそう。
というわけでこうなった
- 初回のデプロイの template.yaml (抜粋) - 手動で実行
Resources:
MyFunction:
Type: AWS::Serverless::Function
DeletionPolicy: Retain
Properties:
CodeUri: src/
Handler: index.handler
AutoPublishAlias: current
Metadata: # Manage esbuild properties
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: es2020
Sourcemap: true
EntryPoints:
- index.ts
External:
- '@aws-sdk'
- 2回目以降のデプロイの template.yaml (抜粋) - CI/CD で実行
Resources:
MyFunction:
Type: AWS::Serverless::Function
DeletionPolicy: Retain
Properties:
CodeUri: src/
Handler: index.handler
AutoPublishAlias: current
DeploymentPreference:
Type: AllAtOnce
Alarms:
- !Ref AliasErrorMetricGreaterThanZeroAlarm
- !Ref LatestVersionErrorMetricGreaterThanZeroAlarm
Metadata: # Manage esbuild properties
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: es2020
Sourcemap: true
EntryPoints:
- index.ts
External:
- '@aws-sdk'
MyFunctionCurrentUrlConfig:
Type: AWS::Lambda::Url
DependsOn: MyFunctionAliascurrent # 上記で生成される AWS::Lambda::Alias リソース
Properties:
AuthType: AWS_IAM
Cors:
AllowOrigins:
- '*'
Qualifier: current
TargetFunctionArn: !GetAtt MyFunction.Arn
MyFunctionPermission:
Type: AWS::Lambda::Permission
DependsOn: MyFunctionAliascurrent # 上記で生成される AWS::Lambda::Alias リソース
Properties:
Action: lambda:InvokeFunctionUrl
FunctionName: !GetAtt MyFunctionAliascurrent.AliasArn
FunctionUrlAuthType: AWS_IAM
# Function URL を CloudFront のオリジンとしてぶら下げようと思ってたので、こんな感じ
Principal: cloudfront.amazonaws.com
SourceArn: (CloudFront distribution の ARN)
- 古くなった Lambda バージョンを掃除する bash スクリプト
#!/bin/bash
# 設定
REGION="ap-northeast-1" # リージョンを指定
FUNCTION_NAME="YOUR_FUNCTION_NAME" # Lambda 関数名を指定
KEEP_VERSIONS=3 # 残すバージョンの数
# すべてのバージョンをリスト
versions=$(aws lambda list-versions-by-function --function-name ${FUNCTION_NAME} --region ${REGION} --query 'Versions[].[Version]' --output text | grep -v "\$LATEST" | sort -n)
# 古いバージョンを削除
count=0
total_versions=$(echo ${versions} | wc -w)
delete_count=$((${total_versions} - ${KEEP_VERSIONS}))
for version in ${versions}; do
if [ ${count} -lt ${delete_count} ]; then
echo "Deleting version ${version}"
aws lambda delete-function --function-name ${FUNCTION_NAME} --qualifier ${version} --region ${REGION}
count=$((count+1))
else
break
fi
done
echo "Cleanup completed!"
ここまで苦労するなら最初から CDK 使っちゃえば…
参考文献
- AWS SAMでLambdaのバージョン管理をしたい件 - Qiita
- AWS SAMの「AutoPublishAlias」の使い方を誤るとエイリアス消えちゃうよっていう話 - ヤサイブログ
- AWS公式ドキュメント
- AWS Lambdaのバージョン管理とエイリアスについて - Zenn
- 「お前らの AWS Lambda エイリアスは間違っている」と言われた気がしたので備忘録 - Qiita
-
追加・削除するときは
aws lambda add-permission
aws lambda remove-permission
なのに、今の状態を確認するときはaws lambda get-policy
... 名前に統一性がないのが残念というかなんというか。 ↩