Serverless Frameworkの有料化に伴い、AWS SAMを使う機会が増えたためメモする。
前提知識
環境構築
公式ドキュメントのInstallationに従い、AWS CLIとSAM CLIを入れておく
AWS Serverless Application Model (AWS SAM) Documentation
入力補完される環境を作るのが良さげ
VS Codeを使う場合、CloudFormationの入力補完をするExtensionを入れると楽。
例えば、AWS CloudFormation Snippets - Visual Studio Marketplace
コマンド
sam init
sam build && sam deploy
sam delete
samconfig.toml
confirm_changeset = false
にしておくとデプロイ時にいちいち確認されずに済む
[default.deploy.parameters]
confirm_changeset = false
ポイントは、AWS::Serverless::Api
と AWS::Serverless::Function
に加えて
AWS::ApiGateway::ApiKey
AWS::ApiGateway::UsagePlan
AWS::ApiGateway::UsagePlanKey
も必要になるということ。
正直、長くて覚えていられないのでここにメモしておく。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
ApiKeyRequired: true
EndpointConfiguration:
Type: REGIONAL
MyApiKey:
Type: AWS::ApiGateway::ApiKey
DependsOn:
- MyApiProdStage
Properties:
Enabled: true
StageKeys:
- RestApiId: !Ref MyApi
StageName: Prod
MyApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
DependsOn:
- MyApiKey
Properties:
ApiStages:
- ApiId: !Ref MyApi
Stage: Prod
MyApiUsagePlanKey:
Type: AWS::ApiGateway::UsagePlanKey
DependsOn:
- MyApiUsagePlan
Properties:
KeyId: !Ref MyApiKey
KeyType: API_KEY
UsagePlanId: !Ref MyApiUsagePlan
MyFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-MyFunction
PackageType: Image
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
RestApiId: !Ref MyApi
Metadata:
Dockerfile: Dockerfile
DockerContext: ./src
DockerTag: python3.12-v1
MyFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${MyFunction}
RetentionInDays: 14
Outputs:
MyAPi:
Description: "API Gateway endpoint URL"
Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
TIPS
LogGroupの指定
上記のtemplate.ymlの例にはLogGroupの設定も含めてある。AWS::Logs::LogGroup
についての記述はなくても動くが、LogGroupの設定もしっかり行うことが望ましい。
LogGroupについて指定しないとデプロイ時に自動で生成されるが、SAMのStackの中にLogGroupが含まれないので、あとで SAMのStackを削除したあともLogGroupだけ残り続けてしまう (参考)。
また、RetentionInDays(ログの保持期間)も指定しないとデフォルトだと一生ログをため続けて、 使わないログのために費用を支払うことになるので、そういう意味でもLogGroupは指定して、ついでにRetentionInDaysも指定したほうがいい
FunctionNameの指定
FunctionNameは省略可能なのですが、デフォルトだと
[Stack名]-[Functionのセクションに使った名前]-[ランダムに生成された文字列]
という名前になります。そのままでも問題ないというか、むしろランダム生成した文字列は名前被りを防いでくれて合理的ではあるものの、人間にとっての扱いやすさを考えると
FunctionName: !Sub ${AWS::StackName}-MyFunction
など明示的に指定してあげたほうがよい気がしています。
詰まりやすいポイント
リソース作成順序の管理
上記のymlにも書いたが、「このリソースを作るにはこのリソースが存在することが前提」のような依存関係があることもあり、AWS側でうまく管理してくれるわけではない様子
「〇〇 not exists」みたいなエラーが出てデプロイに失敗したらこの問題が起きているので、Logical ID(下記の例だとMyApiProdStage
)を推測してDependsOnに書いてやる必要がある
(前略)
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
(中略)
MyApiKey:
Type: AWS::ApiGateway::ApiKey
DependsOn:
- MyApiProdStage
(後略)
ROLLBACK_COMPLETE state でデプロイできない
時折、以下のようなエラーが出ることがある
Error: Failed to create changeset for the stack: (あなたのAPI名), An error occurred (ValidationError) when calling the CreateChangeSet operation: Stack:(あなたの関数のStack ID) is in ROLLBACK_COMPLETE state and can not be updated.
初回のデプロイに失敗した場合、それ以降更新できなくなるらしい。こうなるとStackを削除する必要がある。
方法は2つ
- AWSマネジメントコンソールからCloudFormationのページに移動して手動でStackを削除する
sam delete
コマンドを打つ
※Stackを削除すると、その後作られるAPIのURLも変わるので注意