何をしたいか
この記事とほぼ一緒ですが、今のUIとは少し違っていたので、その覚え書き。
以下のような構成のファイルがCodeCommitにあるときに、自動的に全部のLambdaを更新したい。
.
├── buildspec.yml
├── cloudformation-template-init.json
├── cloudformation-template.json
├── lambda1
│ └── lambda_function.py
├── lambda2
│ └── lambda_function.py
├── requirements_lambda1.txt
└── requirements_lambda2.txt
ファイル説明
- lambda1/lambda_function.py
- lambda2/lambda_function.py
デプロイするlambda、とりあえず最低限のもの
import json
import numpy as np
def lambda_handler(event, context):
arr = np.asarray([1,2,3])
return {
'statusCode': 200,
'body': json.dumps(str(arr))
}
- requirements_lambda1.txt
- requirements_lambda2.txt
lambdaが使うパッケージ、これも最低限のもの
numpy==1.15.1
- buildspec.yml
- cloudformation-template-init.json
- cloudformation-template.json
それぞれ「CodeBuild」「CloudFormationの初期設定」「CodePipelineのCloudFormationのテンプレート」で使うファイル
必要なタイミングで説明。
やること
作業内容は
- CodePipelineで指定するCloudFormationのスタックを作る
- CodeBuildを作ってlambdaの実行に必要なライブラリをpip installする
- CodePipelineでデプロイまでつなげる
というような流れでやります。
CodePipelineで指定するCloudFormationのスタックを作る
CodePipelineでパイプラインを作る前に、CloudFormationのスタックを作っておく必要があります。
本来は不要で(だと思いま)すが、ブラウザでCodePipelineの設定を行い、デプロイにCloudformationを選んだ場合、既存のスタックの選択が必須になっているので事前に作っておきます。→ 詳しくは余談へ記載
ここでは一応それっぽいスタック(2個のlambdaを作るスタック)を作っておきます。
(多分ですが、CodePipelineでは、スタックさえ選択できればいいので、ゴミみたいな内容のスタックでも問題無いと思います。)
とりあえず以下を使って、Cloudformationで2つのLambdaを管理するスタックを作ります。
どうせCodePipelineでデプロイするときに上書きされるので、Lambdaには意味の無いコードをデプロイしています。
cloudformation-template-init.json
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "hoge",
"Resources" : {
"SampleLambda1": {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code": {
"ZipFile": { "Fn::Join": ["", [
"nothing"
]]}
},
"FunctionName": "SampleLambda1",
"Runtime": "python3.6",
"Role": "****YOUR LAMBDA ROLE****",
"Handler": "lambda_function.lambda_handler",
"Description": "",
"Timeout": 3,
"MemorySize": 128
}
},
"SampleLambda2": {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code": {
"ZipFile": { "Fn::Join": ["", [
"nothing"
]]}
},
"FunctionName": "SampleLambda2",
"Runtime": "python3.6",
"Role": "****YOUR LAMBDA ROLE****",
"Handler": "lambda_function.lambda_handler",
"Description": "",
"Timeout": 3,
"MemorySize": 128
}
}
}
}
スタックの名前を適当につけて、何も変更せず作りきってしまいます。
作成後にLambdaを確認すると、2つのLambdaが作られていることが確認できます。↓
CodeBuildを作ってlambdaの実行に必要なライブラリをpip installする
CodeBuildのビルド設定
以下のようなビルド設定にします。
pipで必要なライブラリをインストールして、artifact1とartifact2と言う名前の成果物を作ります。
CodePipelineとつなげるとき、この「artifact1」「artifact2」と言う名前が重要になってくるので覚えておきましょう。
buildspec.yml
version: 0.2
phases:
install:
commands:
- echo 'install phase'
- pip install -r requirements_lambda1.txt -t ./lambda1
- pip install -r requirements_lambda2.txt -t ./lambda2
build:
commands:
- echo 'build phase'
- echo 'todo test'
artifacts:
secondary-artifacts:
artifact1:
base-directory: 'lambda1'
discard-paths: no
files:
- '**/*'
artifact2:
base-directory: 'lambda2'
discard-paths: no
files:
- '**/*'
CodeBuild作成
CodeBuildでビルドを作ります。
- buildspec.ymlファイルを正しく選ぶ(今回はリポジトリ直下にあるのでデフォルトにする)こと
- artifactの受け渡しはCodePipeline側でやるのでNo artifactsにすること※
※今回のbuildspec.ymlにはprimary artifactを設定してないので失敗します。
ビルド成果物を確認したいときはbuildspec.ymlにprimary artifactを設定しましょう。
あたりが注意ポイントです。
・buildspec.ymlを実行するためのイメージを選ぶ(pip installするためにpython)
・buildspec.ymlを選ぶ(ソース直下にあるのでデフォルトのまま)
ここで一度ビルドを実行してみて、pip installできるか確認しても良いです。
S3にartifactを保存するように設定している場合は、そこに目的のものが出来上がっているかで確認できます。
CodePipelineでデプロイまでつなげる
以下のように作っていきます。
完成したパイプラインを選択して「編集する」から、ビルドステージのアクションを編集します。
次のデプロイステージにビルド成果物を引き渡すために、出力アーティファクトの項目にbuildspec.ymlで指定したartifact1とartifact2を同じ名前で設定します。(参考)
パイプラインの JSON ファイルで指定されている出力成果物の名前は、buildspec ファイルで定義されているセカンダリアーティファクトの名前と一致していなければなりません。
そして、ステージの追加から、Deployステージを追加し、CloudFormationを選び、デプロイの設定を行います。
入力アーティファクトはビルドから受け取るartifact1とartifact2、cloudformation-template.jsonを使うためにSourceArtifactを指定。
「スタックを作成または更新」を選び、既存のスタックとして、上記で作成済みのスタックを選択。
テンプレートは、SourceArtifactの直下にあるcloudformation-template.jsonを指定。
Lambdaのデプロイが行えるロールを選択(高度な設定のパラメータの上書きについては後述)
ここで指定している
「cloudformation-template.json」と「高度-パラメータの上書き」は以下のようになっています。
詳しくはこの記事をご覧ください。
※cloudformation-template.jsonに書いてあるlambdaの設定(メモリとかタイムアウトとか名前とか)は、ドキュメントを見ながら書くより、ブラウザで自分が作りたいlambdaを作って、関数のエクスポートからSAMファイルを取ったり
aws lambda get-function-configuration --function-name ○○
で設定すべきパラメータを確認するのが楽だと思います。
- cloudformation-template.json
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "hoge",
"Parameters": {
"LatestS3BucketLambda1": {
"Type": "String"
},
"LatestS3KeyLambda1": {
"Type": "String"
},
"LatestS3BucketLambda2": {
"Type": "String"
},
"LatestS3KeyLambda2": {
"Type": "String"
}
},
"Resources" : {
"SampleLambda1": {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code": {
"S3Bucket": {
"Ref": "LatestS3BucketLambda1"
},
"S3Key": {
"Ref": "LatestS3KeyLambda1"
}
},
"FunctionName": "SampleLambda1",
"Runtime": "python3.6",
"Role": "****YOUR LAMBDA ROLE****",
"Handler": "lambda_function.lambda_handler",
"Description": "",
"Timeout": 3,
"MemorySize": 128
}
},
"SampleLambda2": {
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code": {
"S3Bucket": {
"Ref": "LatestS3BucketLambda2"
},
"S3Key": {
"Ref": "LatestS3KeyLambda2"
}
},
"FunctionName": "SampleLambda2",
"Runtime": "python3.6",
"Role": "****YOUR LAMBDA ROLE****",
"Handler": "lambda_function.lambda_handler",
"Description": "",
"Timeout": 3,
"MemorySize": 128
}
}
}
- 高度-パラメータの上書き
{
"LatestS3BucketLambda1": {
"Fn::GetArtifactAtt": [
"artifact1",
"BucketName"
]
},
"LatestS3KeyLambda1": {
"Fn::GetArtifactAtt": [
"artifact1",
"ObjectKey"
]
},
"LatestS3BucketLambda2": {
"Fn::GetArtifactAtt": [
"artifact2",
"BucketName"
]
},
"LatestS3KeyLambda2": {
"Fn::GetArtifactAtt": [
"artifact2",
"ObjectKey"
]
}
}
確認
CodePipelineの「変更をリリース」を押し、成功したことを確認し、適当にLambdaを実行します。
Sample-Lambda-Stackに所属していることと、ソースがデプロイされていること、numpyが使えてるっぽいことが確認できるはずです。
失敗している場合は、CodeBuildのビルドログと、Cloudformationのイベントログ・テンプレート・パラメータが意図したものになっているか確認してみましょう。
もしかしたらIAMで何かしらの権限が必要だったりするかもしれないです。
余談
本来は不要で(だと思いま)すが、ブラウザでCodePipelineの設定を行い、デプロイにCloudformationを選んだ場合、既存のスタックの選択が必須になっているので事前に作っておきます。
について
例えば最後まで作った状態で、CloudFormationから「Sample-Lambda-Stack」を削除したとします。
(スタックを消したので「SampleLambda1」と「SampleLambda2」も一緒に消えます)
この状態でCodePipelineを見ると、「Sample-Lambda-Stackが無いから、このPipelineはおかしい」と言われます。
その警告を無視して、CodePipelineを実行すると「Sample-Lambda-Stack」の名前でスタックを作ってくれます。
(結果として「SampleLambda1」と「SampleLambda2」も作られます。)
この「スタックがなければ自動で作る」という動きについては、CodePipelineのCloudFormationの設定で「スタックの作成または更新」と設定しているので、正しいように思います。
そもそも「スタックを作成する」のであれば、既存のスタックなんて無いはずなので、CodePipelineの設定で、既存のスタックを必須で選択させること自体が、なにかおかしい気がします。
それかそういう設定はCLIでやれって意味なのかもしれないです。
参考
CodePipeLineを使ってLambdaへの自動デプロイ
CodePipeline と複数の入力ソースおよび出力アーティファクトサンプルとの CodeBuild の統合
CodePipeline パイプラインでのパラメーターオーバーライド関数の使用