前回まではCodeDeployを用いてEC2インスタンスへのデプロイを学習しました。
今回はデプロイ対象をlambdaでやってみたいと思います。CodeDeployのアプリケーション作成画面でlambdaを選択できるので同じ要領かと思っていましたが、SAMというやつを一緒に使うみたいです。
公式チュートリアルは普通に分かりやすいです。なので、今回はテンプレートyamlファイルの設定項目をここで指定していきましょう。
contents
- AWS SAM
- AWS CloudFormation
- 公式チュートリアル実施時の備忘録/トラブルシューティング
- template.ymlの解読(本題)
AWS SAM
AWS Serverless Application Model(AWS SAM)は、yaml形式で記述したテンプレートを元にして種々のAWSリソースを用いたアプリケーションを構築することができます。
ローカル上から利用するために、aws-sam-cli
を利用します。
AWS CloudFront
- テンプレート : アプリケーション全体の設計図(yamlベースもしくはjsonベース:コメントが書けるのでyaml推奨)
- スタック : テンプレートを元にCloudFormationが構築したリソースの集合全体(=環境自体)
CloudFormationで生成する環境は、スタック単位で管理されスタックの破棄とともに紐づいたリソースを全て削除することができる。
公式チュートリアルやってみた
SAM CLIのインストールorアップグレード
公式には以下のようにありますが多分pip
でいけると思います。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
$ pip install --upgrade awscli
$ pip install --upgrade aws-sam-cli
$ sam --version
awscli
を先に最新版にしたほうがいいです。botocore
のバージョンが一致しないと以下のようにエラーします。
ERROR: awscli 1.16.304 has requirement botocore==1.13.40, but you'll have botocore 1.13.44 which is incompatible.
Installing collected packages: botocore, boto3, aws-sam-translator, tomlkit, aws-sam-cli
Found existing installation: botocore 1.13.40
Uninstalling botocore-1.13.40:
ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: 'RECORD'
Consider using the `--user` option or check the permissions.
デプロイの仕組み
最終的にはディレクトリごとzipにするので、デプロイ用のディレクトリを作り、以下の構成にします。
$ tree
SAM-Tutorial
├── afterAllowTraffic.js
├── beforeAllowTraffic.js
├── myFunction.js
└── template.yml
ファイル | 説明 |
---|---|
myFunction.js | デプロイ対象のファイル |
template.yml | デプロイの流れを書いた仕様書(これが要) |
beforeAllowTraffic.js | AllowTraffic前に実行するテスト |
afterAllowTraffic.js | AllowTraffic後に実行するテスト |
template.ymlを元にパッケージを作成します。実行すると、SAM-Tutorial
ディレクトリ内に
package.ymlが生成し、S3バケットには
c2fe022d92156eeb459d45f02a766921`が上がっています。
$ sam package --template-file template.yml --output-template-file package.yml --s3-bucket YOUR_BUCKET
Uploading to c2fe022d92156eeb459d45f02a766921 4113 / 4113.0 (100.00%)
Successfully packaged artifacts and wrote output template to file package.yml.
続いてデプロイです。--capabilities CAPABILITY_IAM
はCloudFrontからロールを作成するのに必要な権限付与みたいな感じ必要です。
sam deploy --template-file package.yml --stack-name my-date-time-app2 --capabilities CAPABILITY_IAM
Deploying with following values
===============================
Stack name : my-date-time-app2
Region : None
Confirm changeset : False
Deployment s3 bucket : None
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
#以下略
当たり前なのですが、cliを実行しているIAMユーザに適切に権限を与えていないと途中でエラーします。SAMは、実質CloudFormationを利用しているので、IAMユーザにはCloudFormationFullAccess
を適応してください。(FullAccessでなくてもいいのかもしれないが面倒なので未調査)
なお、今回の検証用のユーザは以下のロール適用しました。
(多分不要なものも含んでます。CodeCommiteとか今回使ってないし)
- AWSCodeCommitFullAccess
- AWSLambdaFullAccess
- IAMFullAccess
- AmazonS3FullAccess
- AWSCodeDeployFullAccess
- AWSCloudFormationFullAccess
また、実行途中で失敗した場合、自動的にロールバックされ、ステータスがROLLBACK_COMPLETE
になります。この状態はから再度sam deploy
はできません。一度CloudFormationコンソールから削除します(cliでもできるはず)。
ステータスの見方についての公式ページ→https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-console-view-stack-data-resources.html
..../a417d920-239f-11ea-9fca-0ef015a9ecfc is in ROLLBACK_COMPLETE state and can not be updated.
デプロイ中はCodeDeployコンソールで進行状況が把握できる。
デプロイに成功したら、デプロイされたlambdaをコンソールから確認してみましょう。パッケージ名+関数名+英数字
となっているのでarnを取得してcliからinvokeしてみる。
$ aws lambda invoke --function arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:my-date-time-app2-myDateTimeFunction-1S9F2G73JFP32 --payload "{\"option\": \"date\", \"period\": \"today\"}" out.txt
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
$ SAM-Tutorial ls
afterAllowTraffic.js myDateTimeFunction.js package.yml
beforeAllowTraffic.js out.txt template.yml
$ SAM-Tutorial cat out.txt
{"statusCode":200,"headers":{"Content-type":"application/json"},"body":"{\"month\":12,\"day\":21,\"year\":2019}"}%
この後、ローカルでmyDateTimeFunction.jsファイルを更新した場合、上記と同じ手順でaws package
し、aws deploy
する。
なお、CodeDeployを通してデプロイしたlambdaでもコンソール上で直接変更はできる。ただこの方法で更新したものはversionとして変更履歴に保存されないので注意。
ここからが本題
template.ymlの解読
template.ymlのオプション指定を解読していきます。
Transform(必須)
使用するSAMのバージョンを指定し、使用できるSAM構文とCloudFormationがそれをどのように処理するかを定義します。AWS::Serverless-2016-10-31
を指定する。
Globals
グローバル領域。複数のリソースに対して共通の定義をできる。例えば全ての関数でランタイムはpython3.8
でメモリは256を指定したいといった場合、Globals
に書くことで以降のAWS::Serverless::Function
の各Functionの項目に書かなくても継承してくれる。
Grobals
で一括定義できるのは以下のみ。
- AWS::Serverless::Function
- AWS::Serverless::Api
- AWS::Serverless::SimpleTable
Grobals
をデフォルト値のようにしてAWS::Serverless::Function
側にも定義した場合その関数だけは指定した値に上書きできるとかあるのだろうか?
なお、それぞれ何を指定できるかは公式参照→https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-specification-template-anatomy-globals.html
Description
その名の通り説明。
Metadata
メタデータ。追加情報です。まだ使い道がわからない。。。
Parameters
テンプレートを実行するときに、ユーザーに入力させるパラメータを定義できます。
Mappings
連想配列/辞書を定義できます。
Conditions
条件を設定します。真の場合にのみリソースを生成するなどの条件分岐が可能です。
パラメータと組み合わせた例が以下。
Parameters:
EnvType:
Description: Environment type.
Default: test
Type: String
AllowedValues:
- prod
- test
Conditions:
CreateProdResources: !Equals [ !Ref EnvType, prod ]
Resources:
# CreateProdResourcesがfalseの場合にはインスタンスは作られない。
EC2Instance:
Type: "AWS::EC2::Instance"
Condition: CreateProdResources
Properties:
ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI]
Output
スタック構築後に、指定した情報を出力する。
Resources(必須)
準備するリソースを指定する。いくつかピックアップして調べてみる。
AutoPublishAlias
# Instructs your myDateTimeFunction is published to an alias named "live".
AutoPublishAlias: live
このAutoPublishAlias
はデプロイ対象の関数が変更されたときに検出し、テンプレート上で命名したエイリアスを使用してデプロイするようにフレームワークに指示します。この指定は必須です。
やってみるとわかるのですが、sam deploy
するとlambdaはバージョン管理されます。$LATEST
とは別にエイリアスをつけられます。
エイリアスとは、lambdaのバージョン番号に別名を付したものです。
DeploymentPreference Type
DeploymentPreference:
# Specifies the deployment configuration
Type: Linear10PercentEvery1Minute
ここでは新しいバージョンのlambdaへトラフィックを流す割合を定義しています。
Linear10PercentEvery1Minute
は1minのトラッフィクのうち10%を次バージョンへ回す設定です。
クラスメソッドの実際の実験がわかりやすいです。
https://dev.classmethod.jp/server-side/serverless/understanding-lambda-deploy-with-codedeploy-using-aws-sam/
また、このデプロイのタイプの設定はカナリアリリースなども選択可能であり同じく上記リンクに紹介があります。
DeploymentPreference Hooks
# Specifies Lambda functions for deployment lifecycle hooks
Hooks:
PreTraffic: !Ref beforeAllowTraffic
PostTraffic: !Ref afterAllowTraffic
Hooks
はCodeDeployでEC2へのデプロイ時の設定にもあった通り、各イベントの前後の適切なタイミングで指定したプログラムを実行するための設定です。lmabdaへのデプロイではtrafic allowの前後2つのタイミングを選択できます。公式には以下のように説明があります。(beforeAllowTraffic
とafterAllowTraffic
はCloudFrontでの指定方法であり、SAMではそれぞれPreTraffic
とPostTraffic
に対応しております。わかりにくい!)
AWS Lambda フックは、ライフサイクルイベントの名前の後の新しい行に文字列で指定された 1 つの Lambda 関数です。各フックはデプロイごとに 1 回実行されます。以下は、AppSpec ファイルに使用できるフックの説明です。
BeforeAllowTraffic – これを使用して、トラフィックがデプロイされた Lambda 関数のバージョンに移行する前にタスクを実行します。
AfterAllowTraffic – これを使用して、トラフィックがデプロイされた Lambda 関数のバージョンに移行した後でタスクを実行します。
用途としてはPreTraffic
の段階で単体テストし、成功すればデプロイ、 PostTraffic
で結合テストし、失敗すればロールバックさせるなど一連の流れを構築できそうです。
また、このRef!
という記法はCloudFrontの組み込み関数で、以下のように読み換えられます。
# パラメータ参照
!Ref {パラメーター名}
# リソースの返り値を取得
!Ref {リソース名}
同じく、 !Sub
という表現も次に出てきます。
これは文字列とパラメータを組み合わせた値を作成する際に使用します。
参考) https://qiita.com/ryurock/items/766154e0afb8fdb629e2
関数ポリシー
# Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus
Statement:
- Effect: "Allow"
Action:
- "codedeploy:PutLifecycleEventHookExecutionStatus"
Resource:
!Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
- Version: "2012-10-17"
デプロイ前後のテスト用関数から対象の関数をinvokeするにはアクセス許可をする必要があります。(正直lambdaFullアクセスを与えてしまってもいいと思ってます。)
lambda環境変数の指定
Environment:
Variables:
NewVersion: !Ref myDateTimeFunction.Version