Lambda Layerだけを更新したとき、Lambdaが参照するLayerのバージョンが追従しないのでAutoPublishAliasを止めてみた話
AWS SAMを使ってLambdaとLambda Layerの管理をしています。LambdaではAutoPublishAlias
でaliasを付与しています。
このとき、Lambda Layerのコードだけを修正してデプロイしても、Lambdaが参照するLayerのバージョンは変更になりませんでした。
本記事では、状況を確認したあと、AutoPublishAlias
を未使用に変更し、Lambda Layerのコードのみを変更するとLambdaが参照するLayerのバージョンも追従することを確認します。
なお、AutoPublishAlias
が未使用の場合は、$LATEST
を参照するため大丈夫でした。
GitHubにIssuesがある
AWS SAMのGitHubには下記のIssuesがあります。どうやら不具合みたいです。
- Feature Request : When 'AWS::Serverless::LayerVersion' is updated then to publish a new version of the Lambda function · Issue #1777 · aws/serverless-application-model
- Change in Ref'd Parameter Values do not trigger Function Alias update via AutoPublishAlias · Issue #1640 · aws/serverless-application-model
最初にまとめ
本記の執筆時点において、「Lambda Layerだけを更新したとき、Lambdaが参照するLayerのバージョンを追従したい」ならば、下記いずれかとなりそうです。
AutoPublishAlias
を止めるAutoPublishAlias
を使い、関連するLambdaも何らかの修正を行う(Lambda自体もデプロイの対象とする)AutoPublishCodeSha256
を使って、「Lambdaa Layerのコード+Lambdaのコード」をzip化したハッシュ値をParametersでLambda毎に渡す- これをするぐらいなら、Lambda Layerの利用を止めて、デプロイパッケージにまとめたほうがシンプルだと思います
おすすめの方
AutoPublishAlias
とLambda LayerとLambdaのデプロイについて知りたい方
環境
項目 | バージョン |
---|---|
AWS SAM CLI | version 1.21.1 |
まずは再現確認をする
動作確認用のLambdaをデプロイする
sam init
sam init \ --runtime python3.8 \ --name lambda-layer-deploy-test \ --app-template hello-world \ --package-type Zip
SAMテンプレート
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: lambda-layer-deploy-test Resources: MessageLayer: Type: AWS::Serverless::LayerVersion Metadata: BuildMethod: python3.8 Properties: LayerName: my-layer ContentUri: src/layer CompatibleRuntimes: - python3.8 HelloWorldFunction: Type: AWS::Serverless::Function Properties: FunctionName: lambda-layer-deploy-test-function CodeUri: src/hello_world/ Handler: app.lambda_handler Runtime: python3.8 AutoPublishAlias: dev Layers: - !Ref MessageLayer Events: HelloWorld: Type: Api Properties: Path: /hello Method: get HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} Outputs: HelloWorldApi: Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
Lambdaのコード
Lambda LayerのAPIを叩いて、メッセージをReturnしています。
import json import my_layer def lambda_handler(event, context): return { 'statusCode': 200, 'body': json.dumps({ 'message': my_layer.hello(), }), }
Lambda Layerのコード
メッセージを返しています。
def hello(): return 'hello'
デプロイ
sam build --use-container sam deploy \ --stack-name Lambda-Layer-Deploy-Test-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIのエンドポイントを取得します。
$ aws cloudformation describe-stacks \ --stack-name Lambda-Layer-Deploy-Test-Stack \ --query 'Stacks[].Outputs' [ [ { "OutputKey": "HelloWorldApi", "OutputValue": "https://jbp01lz74i.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/" } ] ]
APIを叩くと、期待通りのResponseが得られます。
$ url https://jbp01lz74i.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello"}
API GatewayとLambdaの関係を確認する
作成したAPIのリソース情報を取得します。jbp01lz74i
部分は、API GatewayのIDです(URLの先頭部分)。そして、レスポンスのGET /hello
のリソースID(hm0sge
)をメモしておきます。
$ aws apigateway get-resources \ --rest-api-id jbp01lz74i \ --query items [ { "id": "gaaohbff76", "path": "/" }, { "id": "hm0sge", "parentId": "gaaohbff76", "pathPart": "hello", "path": "/hello", "resourceMethods": { "GET": {} } } ]
API GatewayのMethod情報を取得し、どのLambdaが呼ばれているのかを確認します。alias:dev
付きでlambda-layer-deploy-test-function:dev
が呼ばれています。
$ aws apigateway get-method \ --rest-api-id jbp01lz74i \ --resource-id hm0sge \ --http-method GET \ --query methodIntegration.[uri] [ "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function:dev/invocations" ]
Lambdaのエイリアスとバージョンを確認する
lambda-layer-deploy-test-function
には、$LATEST
と1
の2つのバージョンがあります。それぞれmy-layer:1
を参照しています。
$ aws lambda list-functions \ --function-version ALL \ --query Functions[?FunctionName==\'lambda-layer-deploy-test-function\'].[Version,Layers[*].Arn] [ [ "$LATEST", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1" ] ], [ "1", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1" ] ] ]
alias:dev
は、Lambda Version:1
であり、my-layer:1
を参照しています。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function:dev \ --query Configuration.[Version,Layers] [ "1", [ { "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1", } ] ]
Lambda Layerは、version:1
だけ存在しています。
$ aws lambda list-layer-versions \ --layer-name my-layer \ --query LayerVersions[*].[Version] [ [ 1 ] ]
絵で整理すると下記です。
Lambda Layerのコードだけ変更する
Lambda Layerのコード
hello!
からhello world!!
に変更しました。
def hello(): return 'hello world!!'
デプロイ
sam build --use-container sam deploy \ --stack-name Lambda-Layer-Deploy-Test-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIを叩くと、変更前のLambda Layerの内容が取得できました。Lambda Layerを変更しても反映されませんでした。
$ url https://jbp01lz74i.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello"}
API GatewayとLambdaの関係を確認する
API GatewayのMethod情報を取得し、どのLambdaが呼ばれているのかを確認します。alias:dev
付きでlambda-layer-deploy-test-function:dev
が呼ばれています。
$ aws apigateway get-method \ --rest-api-id jbp01lz74i \ --resource-id hm0sge \ --http-method GET \ --query methodIntegration.[uri] [ "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function:dev/invocations" ]
Lambdaのエイリアスとバージョンを確認する
layer-deploy-test-function
には、$LATEST
と1
の2つのバージョンがあります。$LATEST
は、my-layer:2
の参照に変わりました。
$ aws lambda list-functions \ --function-version ALL \ --query Functions[?FunctionName==\'lambda-layer-deploy-test-function\'].[Version,Layers[*].Arn] [ [ "$LATEST", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:2" ] ], [ "1", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1" ] ] ]
alias:dev
は、Lambda Version:1
であり、my-layer:1
を参照しています。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function:dev \ --query Configuration.[Version,Layers] [ "1", [ { "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1", } ] ]
Lambda Layerは、version:1
とversion:2
が存在しています。
$ aws lambda list-layer-versions \ --layer-name my-layer \ --query LayerVersions[*].[Version] [ [ 2 ], [ 1 ] ]
絵で整理すると下記です。$LATEST
が参照しているLayerバージョンのみが更新されています。
Lambda Layerのみを変更してもデプロイしたい
まずは AutoPublishAlias をやめる
SAMテンプレート
AutoPublishAlias
をコメントアウトしています。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: lambda-layer-deploy-test Resources: MessageLayer: Type: AWS::Serverless::LayerVersion Metadata: BuildMethod: python3.8 Properties: LayerName: my-layer ContentUri: src/layer CompatibleRuntimes: - python3.8 HelloWorldFunction: Type: AWS::Serverless::Function Properties: FunctionName: lambda-layer-deploy-test-function CodeUri: src/hello_world/ Handler: app.lambda_handler Runtime: python3.8 # AutoPublishAlias: dev Layers: - !Ref MessageLayer Events: HelloWorld: Type: Api Properties: Path: /hello Method: get HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} Outputs: HelloWorldApi: Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
デプロイ
sam build --use-container sam deploy \ --stack-name Lambda-Layer-Deploy-Test-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIを叩くと、最新のLambda Layerの内容が取得できました。
$ url https://jbp01lz74i.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello world!!"}
API GatewayとLambdaの関係を確認する
API GatewayのMethod情報を取得し、どのLambdaが呼ばれているのかを確認します。Aliasは無く、lambda-layer-deploy-test-function
が呼ばれています。
$ aws apigateway get-method \ --rest-api-id jbp01lz74i \ --resource-id hm0sge \ --http-method GET \ --query methodIntegration.[uri] [ "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function/invocations" ]
Lambdaのエイリアスとバージョンを確認する
layer-deploy-test-function
には、$LATEST
と1
の2つのバージョンがあります。
$ aws lambda list-functions \ --function-version ALL \ --query Functions[?FunctionName==\'lambda-layer-deploy-test-function\'].[Version,Layers[*].Arn] [ [ "$LATEST", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:2" ] ], [ "1", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1" ] ] ]
alias:dev
は無くなったので、ResourceNotFoundException
が発生しました。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function:dev \ --query Configuration.[Version,Layers] An error occurred (ResourceNotFoundException) when calling the GetFunction operation: Function not found: arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function:dev
Lambda関数名からalias:dev
を無くすと、$LATEST
であることが分かります。$LATEST
は、my-layer:2
を参照しています。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function \ --query Configuration.[Version,Layers] [ "$LATEST", [ { "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:2", } ] ]
Lambda Layerは、version:1
とversion:2
が存在しています。
$ aws lambda list-layer-versions \ --layer-name my-layer \ --query LayerVersions[*].[Version] [ [ 2 ], [ 1 ] ]
絵で整理すると下記です。
Lambda Layerのコードだけ変更する
Lambda Layerのコード
hello world!!
からhello??
に変更しました。
def hello(): return 'hello??'
デプロイ
sam build --use-container sam deploy \ --stack-name Lambda-Layer-Deploy-Test-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
動作確認
APIを叩くと、変更後のLambda Layerの内容が取得できました!
$ url https://jbp01lz74i.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ {"message": "hello??"}
API GatewayとLambdaの関係を確認する
API GatewayのMethod情報を取得し、どのLambdaが呼ばれているのかを確認します。Aliasは無く、lambda-layer-deploy-test-function
が呼ばれています。
$ aws apigateway get-method \ --rest-api-id jbp01lz74i \ --resource-id hm0sge \ --http-method GET \ --query methodIntegration.[uri] [ "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function/invocations" ]
Lambdaのエイリアスとバージョンを確認する
layer-deploy-test-function
には、$LATEST
と1
の2つのバージョンがあります。$LATEST
は、my-layer:3
の参照に変わりました。
$ aws lambda list-functions \ --function-version ALL \ --query Functions[?FunctionName==\'lambda-layer-deploy-test-function\'].[Version,Layers[*].Arn] [ [ "$LATEST", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:3" ] ], [ "1", [ "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:1" ] ] ]
alias:dev
は無いので、ResourceNotFoundException
が発生します。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function:dev \ --query Configuration.[Version,Layers] An error occurred (ResourceNotFoundException) when calling the GetFunction operation: Function not found: arn:aws:lambda:ap-northeast-1:123456789012:function:lambda-layer-deploy-test-function:dev
Lambda関数名からalias:dev
を無くすと、$LATEST
であることが分かります。$LATEST
は、my-layer:3
を参照しています。
$ aws lambda get-function \ --function-name lambda-layer-deploy-test-function \ --query Configuration.[Version,Layers] [ "$LATEST", [ { "Arn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer:3", } ] ]
Lambda Layerは、version:1
とversion:2
とversion:3
が存在しています。
$ aws lambda list-layer-versions \ --layer-name my-layer \ --query LayerVersions[*].[Version] [ [ 3 ], [ 2 ], [ 1 ] ]
絵で整理すると下記です。$LATEST
が参照しているLayerバージョンが更新されました。
さいごに
かなりハマりました。AutoPublishAlias
とLambda Layerの組み合わせにご注意ください。
参考
- sam build - AWS Serverless Application Model
- レイヤーのビルド - AWS Serverless Application Model
- serverless-application-model/safe_lambda_deployments.rst at master · aws/serverless-application-model
- Feature Request : When 'AWS::Serverless::LayerVersion' is updated then to publish a new version of the Lambda function · Issue #1777 · aws/serverless-application-model
- Change in Ref'd Parameter Values do not trigger Function Alias update via AutoPublishAlias · Issue #1640 · aws/serverless-application-model