背景
Lambdaをgit管理して単体テストに通ったらデプロイってどうやるんだろうみたいなことができる。
概要
AWS SAMにはsam pipeline
というコマンドがあり、これを使うことでパイプラインを自動で作ってくれる。
出来上がるステージとしては
CodeCommit
-> CloudFormation (パイプライン自体の設定デプロイっぽい)
-> CodeBuild テスト (オプション)
-> CodeBuild デプロイ(sam deploy)
sam pipeline
ではmainブランチに対してはデプロイ環境が二つできてしまう(上記のsam deploy
を行うステージが2回ある)が、作るべきパイプラインの参考にはなる。
featureブランチに対しては上記のようにsam deploy
は1回のみ
手順参考
公式でしっかり説明してくれているのが以下くらいしか見当たらないが、これを参考にすればなんとかできる。
コンテナ Lambda の CI/CD パイプラインを SAM Pipeline で作ろう !
やってみる
環境
Cloud9
sam --version 1.71.0
パイプラインを使わずLambdaの単体テストをできるようにするまで
sam init
でHelloWorld関数のみを作成。python選択。
pytestというライブラリでテストができそうなディレクトリができるので、これを使ってテストする。
なお、pytestはtest_から始まるファイルを実行してしまうようで、今回はAPI Gatewayは使わないのでtests/integration/test_apigw.py
は消しておく。
pip install -r tests/requirements.txt
pytest tests # 後のCodeBuild上では失敗したためpython -m pytest testsにしたほうがいいかも
testsディレクトリ内にある初期のテストコードが実行される。
とりあえずここでsam deploy --guided
をしておくとデプロイできる。
sam pipeline 初期状態(単体テストなしでデプロイのみ)
sam pipeline bootstrap
を2回実行。
参考手順の通り、名前はdev, prodにする。質問はほとんどデフォルトで答えるとIAMロールも自動で作ってくれるので任せる。作成されたリソースは記事末の参考記事で説明がある。
2回実行するのは、そういうテンプレートしか今のところなさそうだから。。。
2回bootstrapしたらsam pipeline init
実行し、参考手順の記事を見ながら質問に答えていく。
ここで以下のコマンドでいよいよsam deploy
。codepipeline.yaml
はCodePipelineなどを定義したテンプレート。
sam deploy --guided \
--template codepipeline.yaml \
--stack-name php-lambda-app-pipeline-stack \
--capabilities=CAPABILITY_IAM \
--parameter-overrides="FeatureGitBranch=feature"
パイプライン自体のデプロイは完了する。完了後、featureブランチへのgit push
でLambdaをデプロイするためのパイプラインが開始される。
しかし以下のエラーでCodeBuildのデプロイステージが失敗していた。
Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: sam deploy --stack-name $(echo ${FEATURE_BRANCH_NAME} | tr -cd '[a-zA-Z0-9-]') --capabilities CAPABILITY_IAM --region ${TESTING_REGION} --s3-bucket ${TESTING_ARTIFACT_BUCKET} --no-fail-on-empty-changeset --role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}. Reason: exit status 1
Deploy this changeset? [y/N]:
の直後にエラーになっていることから、この質問にyesが押せていないだけに見える。
上記はbuildspec_feature.yml
で記述されて実行されているコマンドだが、ここのsam deploy
コマンドに-no-confirm-changeset
オプションを足してgit push
したらビルドできた。
これで単体テストなしで、git push
によりLambda関数がデプロイできるパイプラインができた。
なお、mainブランチにpushすると多分2つのステージでデプロイされる。codepipeline.yaml
を見ると、先ほどのFeatureGitBranchパラメータの値によって余分にリソースを作成しそうな箇所があった。
パイプラインに単体テストを追加する
codepipeline.yaml
をCodeBuildProjectUnitTestで文字列検索して、uncommentしろと書いてある箇所を全てコメントアウトする(3ヶ所)。
Uncomment and modify the following step for running the unit-tests
追加で作成されたCodeBuildのステージではbuildspec_unit_test.yml
をbuildspecとして使うので、このファイルにpytest
インストールとpytest
実行を追加する。
install:
runtime-versions:
python: 3.8
commands:
- pip install -r tests/requirements.txt
build:
commands:
# trigger the unit tests here
- echo 'Running unit tests'
- python -m pytest tests
なお、python -m pytest tests
じゃなくてローカルと同じようにpytest tests
とすると以下のエラーになった
_________________ ERROR collecting tests/unit/test_handler.py __________________
ImportError while importing test module '/codebuild/output/src000000000/src/tests/unit/test_handler.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/root/.pyenv/versions/3.8.13/lib/python3.8/importlib/__init__.py:127: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
tests/unit/test_handler.py:5: in <module>
from hello_world import app
E ModuleNotFoundError: No module named 'hello_world'
この記事のおかげで直った。
作成されたスタックを確認してみる
参考手順通りの命名になってしまっている。
Lambda自体のデプロイに使用されるfeatureスタック(Lambdaが作成される)の他は以下の3つ。
php-lambda-app-pipeline-stack
ここで作成されたartifactのS3バケットにはcodepipeline.yamlやassume-role.sh, hello_worldディレクトリなど必要な資材が一式入っているようだ。これはCodePipelineのUpdatePipelineステージでCloudFormation deployにより指定されていたので、パイプライン自体の設定変更があればこのスタックに反映されそう
aws-sam-cli-managed-dev-pipeline-resources
ここで作成されたartifactのS3バケットにはdev環境のlambdaのsam template, さらにその中で参照されているCodeUri(ただしこちらのコードはなぜか中身は壊れていて見えない
aws-sam-cli-managed-prod-pipeline-resources
ここで作成されたartifactのS3バケットはまだ空。prod環境へのデプロイを行なっていないからだと思われる