コンテナ Lambda の CI/CD パイプラインを SAM Pipeline で作ろう !
~コンテナ利用者に捧げる AWS Lambda の新しい開発方式 ! ~ 第 6 回~
Author : 下川 賢介
こんにちは、サーバーレス スペシャリストソリューションアーキテクトの下川 (@_kensh) です。
「第 1 回 コンテナ Lambda の ”いろは”、AWS CLI でのデプロイに挑戦 !」では、AWS CLI を使ってコンテナ Lambda 関数を実際に AWS Lambda サービスにデプロイして動作確認をしてみました。
「第 2 回 コンテナ Lambda を開発、まずは RIC と RIE を使ってみよう !」では、開発者のローカル環境でコンテナ Lambda 関数の動作確認をする方法を紹介しました。
「第 3 回 コンテナ Lambda をカスタマイズして、自分好みの PHP イメージを作ろう !」では、コンテナイメージサポート Lambda 関数のカスタムイメージ作成方法について紹介しました。
「第 4 回コンテナ Lambda を AWS SAM でデプロイしよう !」では、コンテナ Lambda の Infrastructure as Code の考え方について紹介しました。
「第 5 回コンテナ Lambda の CI/CD パイプラインの考え方」では、サーバーレスの CI/CD パイプラインの代表的な構築方法と選択の仕方をご紹介しました。
今回は AWS SAM で Infrastructure as Code 管理された、コンテナ Lambda アプリケーションの CI/CDパイプラインを作ってデプロイを試していきたいと思います。
この連載記事のその他の記事はこちら
- 選択
- 第 1 回 コンテナ Lambda の”いろは”、AWS CLI でのデプロイに挑戦 !
- 第 2 回 コンテナ Lambda を開発、まずは RIC と RIE を使ってみよう !
- 第 3 回 コンテナ Lambda をカスタマイズして、自分好みの PHP イメージを作ろう !
- 第 4 回 コンテナ Lambda を AWS SAM でデプロイしよう !
- 第 5 回 コンテナ Lambda の CI/CD パイプラインの考え方
- 第 6 回 コンテナ Lambda の CI/CD パイプラインを SAM Pipeline で作ろう !
- 第 7 回 コンテナLambdaでサイドカーパターンは実現可能なの ?
ご注意
本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。
このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »
毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。
準備
- AWS CLIをインストール してください。
- AWS SAM CLI のバージョンを最新化 してください。(本稿では AWS SAM CLI 1.27.2 を使用しています。)
- Git をインストール してください。(認証情報の設定 も実施してください)
コードをリポジトリ管理する
CI/CD での管理の始まりはコードからです。パイプラインの起点となるコードをどのように管理するかが大切になります。SAM Pipeline では AWS CodeCommit、GitHub をコードリポジトリとして選択することができます。
今回は CodeCommit をリポジトリとして採用し、コード管理をしていきます。
まずは、リポジトリを作成します。
$ aws codecommit create-repository \
--repository-name php-lambda-app-repo \
--repository-description "php container lambda demonstration repository"
実行結果の cloneUrlHttp をメモしておきます。後ほど リモートリポジトリの追加 の際にリモートのオリジンとして利用します。
リポジトリ名称は任意でよいですが、それぞれ以降のシナリオで適切に読み替えてください。
さて、ローカルの SAM ワークディレクトリを決定したいと思います。新規にワークディレクトリを作る場合は任意のディレクトリで、sam init コマンドを実行して、SAM のワークディレクトリを生成しそのディレクトリ内をワークディレクトリとします。
$ sam init
$ cd <work dir>
今回は、前回までに作っておいた phplambda の SAM ワークディレクトリをそのまま利用することにします。
このようなディレクトリの内容でした。(有効な SAM アプリケーションであれば、どのような構成でも大丈夫ですのでご自身の SAM 環境でもぜひ試してみてください。)
phplambda
├── event
├── src
│ ├── Dockerfile
│ ├── lambda
│ │ └── app.php
│ └── runtime
│ └── bootstrap
└── template.yaml
カレントディレクトリ (ワークディレクトリ) を phplambda にして話を進めて行きます。
このワークディレクトリをリポジトリに登録しておきます。
git remote add コマンドでオリジンを追加していますが、この追加 URL は先ほどメモしておいた cloneUrlHttp になります。
$ pwd
/xxx/phplambda
$ git init
$ git add .
$ git commit -m "first commit"
$ git branch -M main
$ git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/php-lambda-app-repo
$ git push -u origin main
また、今回は feature ブランチの更新をトリガーとして開発環境の AWS クラウドへのデプロイを行いたいので、あらかじめローカルブランチとして feature ブランチ を起こして、checkout してカレントブランチを切り替えておきます。(ここではまだリモートへのプッシュはしていません。)
$ git branch feature
$ git checkout feature
bootstrap コマンドで、CI/CD に必要なリソースのスタックを生成
bootstrap コマンドを使用してパイプラインに必要なアーティファクトリソース (ECR リポジトリや S3 Bucket) と CloudFormation の実行ロールをプロビジョニングしてアーティファクトリソースの管理や権限管理を設定していきますが、これらのリソースがすでに用意されている場合は、既存リソースを参照するように設定することもできます。
$ pwd
/xxx/phplambda
$ sam pipeline bootstrap
bootstrap コマンドでインタラクティブに質問されるので、受け答えして行きます。
まず、ステージの名前を決めます。開発向けに利用するので dev という名前にしておきましょう。
[1] Stage definition
Enter a name for this stage. This will be referenced later when you use the sam pipeline init command:
Stage name: dev
次に、AWS のクレデンシャル情報を選択します。予め 名前付きプロファイル の設定をしている場合は、自動的に選択肢として列挙されます。また、環境変数 に設定して参照させたい場合も 1 番目の選択肢により指示することができます。(事前に環境変数か名前付きプロファイルの設定が必要です。)
[2] Account details
The following AWS credential sources are available to use:
To know more about configuration AWS credentials, visit the link below:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html
1 - Environment variables (not available)
2 - xxx (named profile)
3 - yyy (named profile)
4 - zzz (named profile)
q - Quit and configure AWS credentials
Select a credential source to associate with this stage: 2
Associated account 012345678912 with stage dev.
パイプライン (今回は CodePipeline) と、CloudFormation の実行ロールを設定します。指定がある場合は参照することもできますし、自動生成するためにブランクにしておくこともできます。今回は自動生成します。
今回は、Zip 形式の Lambda 用のアーティファクト管理の S3 バケットだけでなく、ECR リポジトリも生成することにします。そのため、Does your application contain any IMAGE type Lambda functions? という質問に YES で回答しています。 ECR リポジトリも指定するか自動生成が選べます。ここでは自動生成することにします。
[3] Reference application build resources
Enter the pipeline execution role ARN if you have previously created one, or we will create one for you []:
Enter the CloudFormation execution role ARN if you have previously created one, or we will create one for you []:
Please enter the artifact bucket ARN for your Lambda function. If you do not have a bucket, we will create one for you []:
Does your application contain any IMAGE type Lambda functions? [y/N]: y
Please enter the ECR image repository ARN(s) for your Image type function(s).If you do not yet have a repository, we will create one for you []:
ここまで実行したあとに、生成されるローカルのファイル、ディレクトリを確認してみましょう。
pipelineconfig.toml は、マルチステージのアーティファクトリソースと CloudFormation の実行ロールの ARN を記録しておく toml ファイルになります。これを sam pipeline init 時に参照します。
マルチアカウント 、マルチリージョンでパイプラインを生成する場合には、もう一つステージを作る必要があります。例えば、開発用のステージと商用のステージなどです。必要に応じて、商用のプロファイルを利用して bootstrap コマンドを再度実行してください。
init コマンドで、パイプラインを構築
それでは、パイプライン構築用のテンプレートファイルを生成していきましょう。sam pipeline init コマンドでインタラクティブに実行していきます。
$ sam pipeline init
CI/CD のパイプラインを受け持つシステムを選択します。ここでは CodePipeline を選択しています。
CI/CD system
1 - Jenkins
2 - GitLab CI/CD
3 - GitHub Actions
4 - AWS CodePipeline
Choice: 4
CI/CD の構築にマルチステージのプロファイルを要求されますが、さきほど bootstrap 時に作成していた情報が pipelineconfig.toml ファイルから読み込まれます。
You are using the 2-stage pipeline template.
_________ _________
| | | |
| Stage 1 |->| Stage 2 |
|_________| |_________|
コードリポジトリのプロバイダも選択できるので、CodeCommit を選択します。
What is the Git provider?
1 - Bitbucket
2 - CodeCommit
3 - GitHub
4 - GitHubEnterpriseServer
Choice []: 2
その他の情報も質問されますが、適宜、環境にあった情報を選択してください。
pipelineconfig.toml ファイルから読み出された情報が表示されています。bootstrap では dev と prod のステージを作成していたので、どちらが Stage 1 でどちらが Stage 2 かを指定して行きます。
Here are the stage names detected in .aws-sam/pipeline/pipelineconfig.toml:
1 - dev
2 - prod
What is the name of stage 1 (as provided during the bootstrapping)?
Select an index or enter the stage name: 1
What is the sam application stack name for stage 1? [sam-app]: php-lambda-app-dev-stack
Stage 1 configured successfully, configuring stage 2.
ここまで選択した内容でビルド構成情報、テスト構成情報などのパイプラインに必要なテンプレートが生成されます。
ここまでで、ワークディレクトリの生成されたファイルはこのようになっているはずです。
codepipeline.yaml は AWS SAM のテンプレートになります。しかしアプリケーション用ではなく、パイプライン構築用のテンプレートで内容は、CodePipeline や EventBridge ルールになっています。
本当に正しい SAM テンプレート形式になっているか validate コマンドで確認してみましょう。
$ sam validate --template codepipeline.yaml
2021-08-13 03:56:44 Loading policies from IAM...
2021-08-13 03:56:51 Finished loading policies from IAM.
/xxx/phplambda/codepipeline.yaml is a valid SAM Template
valid (有効な) テンプレートと判断されました。
パイプラインのデプロイ
ここから、CodePipeline とトリガーイベント用の EventBridge ルールの構築をしていきます。アプリケーションのテンプレートではなく、パイプラインのテンプレートファイルを指定していることに注意ください。(5 行目)
$ sam deploy --guided \
--template codepipeline.yaml \
--stack-name php-lambda-app-pipeline-stack \
--capabilities=CAPABILITY_IAM \
--parameter-overrides="FeatureGitBranch=feature"
FeatureGitBranch=feature を指定していますが、これにより EventBridge ルールでどのブランチのプッシュイベントを受けてパイプラインを動かすかを指示しています。
実際にデプロイが終わった後、CodePipeline のパイプラインリソースとそれをトリガーする EventBridge ルール が登録されているはずです。
EventBridge ルール
{
"detail-type": [
"CodeCommit Repository State Change"
],
"resources": [
"arn:aws:codecommit:ap-northeast-1:012345678912:php-lambda-app-repo"
],
"source": [
"aws.codecommit"
],
"detail": {
"referenceType": [
"branch"
],
"event": [
"referenceCreated",
"referenceUpdated"
],
"referenceName": [
"feature"
]
}
}
ルールをみると、event 通知の内容が referenceCreated と referenceUpdated になっており、対象のブランチ名は feature になっています。feature ブランチにプッシュやマージが発生すると Event が送信され、この ルールに合致する イベントペイロードであれば、EventBridge はターゲットとして設定されている CodePipeline リソースを起動します。
ここまでで、パイプラインの構築は全て終了です。ここからは実際にパイプラインが動作するか確認して行きましょう。
feature ブランチにプッシュしてパイプラインの動きを確認する
git push からパイプラインが起動して、SAM アプリケーションのデプロイができるか見てみます。
今までのように、ローカルで sam build や sam deploy を使ってアプリケーションスタックのデプロイは行いません。すべてパイプラインにビルド、デプロイ作業を委譲しています。
ローカルのカレントブランチは feature で、リモートの origin ブランチを feature にしてプッシュします。(2 回目以降は --set-upstream は不要です)
$ git push --set-upstream origin feature
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/php-lambda-app-repo
* [new branch] feature -> feature
Branch 'feature' set up to track remote branch 'feature' from 'origin'.
CodePipeline の実行結果を確認します。どうやら無事にデプロイまで終了しているようです。
クリックすると拡大します
デプロイされた Lambda 関数を実行してみましょう。
$ aws lambda invoke --function-name feature-phplambda-OuEEk1cUhX6I output
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
成功しました。うまくデプロイできているようです。
Tips
1) 実際にアプリケーションをデプロイしている sam deploy コマンドはパイプラインの最後のステージで実行され、内容は buildspec_feature.yml に記述されています。ビルドやデプロイ内容を変えたい場合はこれを編集します。
buildspec_feature.yml
(snip)
build:
commands:
- sam build --use-container --template ${SAM_TEMPLATE}
- . ./assume-role.sh ${TESTING_PIPELINE_EXECUTION_ROLE} feature-deploy
- 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}
--image-repository ${TESTING_IMAGE_REPOSITORY}
--no-fail-on-empty-changeset
--role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}
2) 次の Tips です。ローカルで sam deploy --guided コマンドを使ってデプロイすると、deploy 時の構成選択を記録する samconfig.toml が生成されます。このファイルをリモートリポジトリにプッシュしてしまうと、パイプライン時にローカルの構成が反映されることがあるので、通常は .gitignore に指定してリポジトリ管理から外します。仮にチームで toml ファイルを共有する必要がありリポジトリで管理したい場合は、buildspec_feature.yml の sam deploy コマンドパラメータとして、--no-confirm-changeset を指定して、デプロイ確認のプロント表示をオフにしないと、デプロイが失敗します。また、他の理由でアプリケーションの deploy が失敗することもあるので、--debug を指定しておくとデバッグのための情報がコンソール出力されます。
3) もう一つの Tips としては、Git リポジトリは SAM Pipeline では自動生成されないため予め作っておく必要があることも説明しましたが、開発の初期段階からパイプラインを構成するのは (プラクティスとしては正しいですが) feature ブランチにパイプラインまで必要かどうかは開発の当初はわからなかったりするものです。たとえば、マージ対象のブランチがあれば featureブランチにはパイプラインが不要だったり、Git リポジトリとローカル開発環境だけである程度まで開発やローカルテストを進めることもあるでしょうし、AWS SAM CLI で手動デプロイをすることも開発の初期段階ではあることでしょう。そういう開発スタイルでも AWS SAM を利用していれば後付けで簡単にパイプラインを起こせるので、気軽にローカル手動開発を進めることができます。これも SAM Pipeline のメリットと言えます。
商用リリース向けのパイプラインを作成する
feature ブランチへのプッシュから feature パイプラインを起動できることは確認できたので、ここで商用リリース向けのパイプラインについても見てみましょう。リポジトリブランチの作成方法や、スタックのリソース構成についてはほとんど同じなので、feature パイプラインとの違いについて着目して説明します。
商用のパイプラインを作成するには以下のコマンドを実行します。
$ sam deploy --guided \
--template codepipeline.yaml \
--stack-name php-lambda-app-pipeline-release-stack \
--capabilities=CAPABILITY_IAM
feature パイプラインの生成コマンドとの違いとして、もちろんスタック名称は違いますが、着目すべきは FeatureGitBranch=feature を指定していないというところになります。これにより、bootstrap で作成した 2 つの (dev と prod) の情報を読み取り、それぞれのアカウント、リージョンにアプリケーションをデプロイするような商用パイプラインが作成されます。もちろんシングルアカウント、シングルリージョンの構成も可能です。
商用リリース向けのパイプラインができたら、main ブランチにプッシュしてみましょう。(実際の開発フローではチームメンバー間で Pull Request や Merge Request することが多いでしょう)
$ git checkout main
$ git merge feature
$ git push
CodePipeline の実行結果を確認します。どうやら無事にデプロイまで終了しているようです。
クリックすると拡大します
DeployTest ステージが bootstrapでプロファイルを設定した Stage 1 になります。そして、DeployProd ステージが Stage 2 になります。
Tips
4) 商用パイプラインの二つのステージ (DeployTest と DeployProd) 間は、前ステージの成功を条件に次ステージに移行するようになっています。もしマニュアルの承認が必要な場合は、間に承認用のステージを追加し 承認アクション を実行するように、codepipeline.yaml を修正して再度パイプラインスタックの更新をデプロイしてください。また、簡易的に DeployProd への移行を止めるには、「移行を無効にする」ボタンを押下することで自動移行を停止できます。
5) feature スタックはローカル開発用のスタックであるため、そこで生成されたコンテナイメージは商用リリースのアーティファクトとして利用しません。main ブランチへのプッシュをトリガーとする商用パイプラインでビルドして商用のリポジトリ登録を行います。商用のパイプラインには DeployTest と DeployProd の二つのステージがありますが、それぞれ異なる ECR リポジトリを参照してデプロイしています。しかし前段の BuildAndPackage ステージでのアクションとして実行される buildspec_build_package.yml をみると、sam build でローカルイメージを生成した後、同じイメージを DeployTest と DeployProd のそれぞれの ECR リポジトリにプッシュ (sam package) しています。つまりステージ間でイメージの不変性を担保したパイプライン構成になっています。
buildspec_build_package.yml
(snip)
build:
commands:
- sam build --use-container --template ${SAM_TEMPLATE}
- . ./assume-role.sh ${TESTING_PIPELINE_EXECUTION_ROLE} test-package
- sam package --s3-bucket ${TESTING_ARTIFACT_BUCKET}
--image-repository ${TESTING_IMAGE_REPOSITORY}
--region ${TESTING_REGION}
--output-template-file packaged-test.yaml
- . ./assume-role.sh ${PROD_PIPELINE_EXECUTION_ROLE} prod-package
- sam package --s3-bucket ${PROD_ARTIFACT_BUCKET}
--image-repository ${PROD_IMAGE_REPOSITORY}
--region ${PROD_REGION}
--output-template-file packaged-prod.yaml
6) SAM Pipeline を使った CLI のナビゲーションでは現在 (2021/8/14 時点) では、各ステージごとのアーティファクト管理のために ECR リポジトリと S3 バケットがそれぞれ一つずつ作られます。 Zip 形式の Lambda 関数の場合、デプロイアーティファクトは単一の S3 バケットをシェアすることができます。しかし、コンテナ形式の Lambda 関数の場合は、一つのコンテナイメージが一つの ECR リポジトリに対応するため、複数のコンテナ Lambda を アプリケーション側の SAM テンプレートで管理する場合には、ECR リポジトリを別途作成しターゲットリポジトリに設定する必要があります。そのため、コード規模が大きくなく共通部分が多いコンテナ Lambda であれば、同じコンテナイメージでエントリーポイント を切り替えた Lambda 関数としてデプロイすることも可能です。ただしこの方法を取る場合はモノリシックデザイン にならないように気をつけてください。
まとめ
コンテナ Lambda のデプロイに SAM Pipeline を使用して DevOps に必要な CI/CD 環境を構築しました。 マルチアカウント 、マルチリージョンでも適用できマルチステージで様々なユースケースに対応できることも説明しました。今回は CodePipeline による構築を選択しましたが、開発プロジェクトに応じた CI/CD システムをご利用いただくこともできます。
プロジェクトやチームの慣れた CI/CD 環境を AWS SAM Pipeline で素早く構築して、サーバーレスアプリケーションのデリバリーやイテレーションを加速させてみてください !
次回以降ではさらに、コンテナサポート Lambda 関数の特徴や利点を追っていきたいと思います。
この連載記事のその他の記事はこちら
- 選択
- 第 1 回 コンテナ Lambda の”いろは”、AWS CLI でのデプロイに挑戦 !
- 第 2 回 コンテナ Lambda を開発、まずは RIC と RIE を使ってみよう !
- 第 3 回 コンテナ Lambda をカスタマイズして、自分好みの PHP イメージを作ろう !
- 第 4 回 コンテナ Lambda を AWS SAM でデプロイしよう !
- 第 5 回 コンテナ Lambda の CI/CD パイプラインの考え方
- 第 6 回 コンテナ Lambda の CI/CD パイプラインを SAM Pipeline で作ろう !
- 第 7 回 コンテナLambdaでサイドカーパターンは実現可能なの ?
筆者プロフィール
下川 賢介 (@_kensh)
アマゾン ウェブ サービス ジャパン合同会社
シニア サーバーレススペシャリスト ソリューションアーキテクト
Serverless Specialist Solutions Architect として AWS Japan に勤務。
Serverless の大好きな特徴は、ビジネスロジックに集中できるところ。
ビジネスオーナーにとってインフラの管理やサービスの冗長化などは、ビジネスのタイプに関わらず必ず必要になってくる事柄です。
でもどのサービス、どのビジネスにでも必要ということは、逆にビジネスの色はそこには乗って来ないということ。
フルマネージドなサービスを使って関数までそぎ落とされたロジックレベルの管理だけでオリジナルのサービスを構築できるという Serverless の特徴は技術者だけでなく、ビジネスに多大な影響を与えています。
このような Serverless の嬉しい特徴をデベロッパーやビジネスオーナーと一緒に体験し、面白いビジネスの実現を支えるために日々活動しています。
AWS を無料でお試しいただけます