はじめまして、西です。
入社 2 年目で、配属から現在まで、AWS 上に構築されたシステムの開発/運用を行なっています。 業務で触れる機会が多かったので、今回は AWS SAM を既存システムの CI/CD 環境へ組み込むまでの内容をご紹介します。 皆様の中に既存システムのサーバレス化などで AWS SAM の導入を検討されている方がおられましたら、参考になればと思います。
AWS SAM について
AWS SAM (以下 SAM) とは AWS Serverless Application Model のことで、AWS Lambda をベースとしたサーバレスアプリケーションを開発するためのフレームワークです。
Lambda だけでなく、API Gateway や Dynamo DB のようなサーバレスアプリケーションでよく利用されるリソース定義をシンプルに記述できること等が嬉しい点です。
SAM によって管理するサーバレスアプリケーションは、AWS SAM CLI (以下 SAM CLI) というツールでビルドからデプロイ、テスト用のローカル実行等が行えます。 Lambda が利用するアプリケーションファイルの保存場所には S3 と ECR が選択できますが、今回は template.yaml と併せて 1 箇所で管理できる S3 を採用した形を前提に話を進めます。
SAM 導入のユースケースと、実現すること
既存システムの一部機能などをサーバレス化する場合は、既に CI/CD 環境が整備されている所へ SAM 用のビルド/デプロイジョブを追加する形になるかと思います。 今回の記事ではそういったケースにフォーカスします。 CI/CD ツールには様々なものがありますが、今回は代表的なものの 1 つである Jenkins を想定した内容をご紹介できればと思います。
前提として、Jenkins にクレデンシャルを持たせてリソース作成/変更をデプロイする形になっていることを想定します。 今回実現する点として、Jenkins で SAM テンプレートをデプロイする場合は SAM 用の IAM ロールを利用させることを考えます。 最終的に目指す形は以下のようになります。図中の赤枠で囲っている範囲が今回追加する部分です。
では、必要な作業を順に考えていきます。
SAM 用の IAM Role を準備
Jenkins が利用しているクレデンシャルに SAM で作成/編集するリソースの操作権限を付与すればデプロイ自体はできます。
しかし、AWS には実行 Role という概念がありますので、適切に IAM 権限を管理するためにその仕組みを利用します。 SAM CLI でデプロイする際には実行 Role を指定するオプションが存在し、SAM 用に用意した IAM Role を指定することができます。 これにより、Jenkins に付与する権限を肥大化させることなくSAM に必要な権限だけを管理でき、SAM に関係しないリソースへの操作は制限することができます。
SAM は CloudFormation の拡張機能に相当するため、SAM についての操作を実行する主体は CloudFormation となります。 そのため、IAM Role は CloudFormation に渡す形になるのでポリシーの設定は以下のようになります。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "cloudformation.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
作成した Role に、SAM で作成/変更するリソースについての権限を付与
SAM 用 Role には、SAM のテンプレートで作成/変更したいリソースについての権限を付与する必要があります。 図中の赤枠の部分が該当する箇所です。
サーバレスアプリケーションとなるため、Lambda の他、API Gateway や Dynamo DB などの権限が主になり、以下がその例となります。
IAM ロールやポリシーを定義する場合はiam
の権限も別途必要になりますので、必要なリソースに応じて適宜権限の追加が必要です。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowSamDeploy", "Effect": "Allow", "Action": [ "apigateway:*", "cloudformation:*", "dynamodb:*", "iam:*", "lambda:*", "s3:*", ], "Resource": "*" } ] }
Jenkins のクレデンシャルに PassRole 権限と S3/CloudFormation 権限を付与
Jenkins で利用する IAM ユーザー (クレデンシャル) に、SAM テンプレートのデプロイに必要な権限を付与する必要があります。 図中の赤枠の部分が該当する箇所です。
はじめに、CloudFormation に対して SAM 用 IAM Role の権限を渡す (PassRole) ための権限を付与する必要があります。 全体図では省略しましたが、デプロイ処理の流れは以下のようになります。
そのため、template.yaml やアプリケーションファイルを S3 にアップロードする権限や、CloudFormation スタックを操作する権限は IAM ユーザー側にも必要となります。 こちらも併せて権限を付与します。
デプロイ処理で必要な権限を付与するために、Jenkinsの IAM ユーザーに以下のようなポリシーを追記します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutStackTemplate", "Action": [ "s3:CreateBucket", "s3:PutObject" ], "Effect": "Allow", "Resource": "*" }, { "Sid": "AllowStackOperation", "Action": [ "cloudformation:CreateChangeSet", "cloudformation:CreateStack", "cloudformation:DeleteChangeSet", "cloudformation:DeleteStack", "cloudformation:DescribeChangeSet", "cloudformation:DescribeStacks", "cloudformation:ExecuteChangeSet", "cloudformation:ValidateTemplate" ], "Effect": "Allow", "Resource": "*" }, { "Sid": "AllowPassRole", "Action": [ "iam:PassRole" ], "Effect": "Allow", "Resource": "arn:aws:iam::{AWSアカウントID}:role/{SAM用Role名}" } ] }
SAM のデプロイについて
ここまでで、Jenkins で SAM テンプレートをデプロイする上で必要な IAM 関連の準備を行いました。 ここからはデプロイ処理に関する点について考えていきます。
S3 バケットを作成し、samconfig.toml を設定
この状態でSAM テンプレートをデプロイすることも可能なのですが、その場合はアプリケーションや template.yaml をアップロードする先の S3 バケット が自動で作成されます。 利用に支障はありませんが、バケット名にデフォルト名用の文字列とランダム文字列が入ってしまいます。
私は名称を決めて予めバケットを用意しておきたい党なので、事前にバケットを作成する前提で進めます。
S3 バケット を事前に作成する場合は、作成後に SAM テンプレートのディレクトリに samconfig.toml を用意して S3 バケットについての情報を設定しましょう。 以下のファイルは S3 バケット nnc-sam-backend-devとnnc-sam-backend-prod を用意した際の例となります。
version = 0.1 [dev.deploy.parameters] stack_name = "nnc-y-nishi" s3_bucket = "nnc-sam-backend-dev" s3_prefix = "nnc-y-nishi-sam" region = "ap-northeast-1" confirm_changeset = false capabilities = "CAPABILITY_NAMED_IAM" [prod.deploy.parameters] stack_name = "nnc-y-nishi" s3_bucket = "nnc-sam-backend-prod" s3_prefix = "nnc-y-nishi-sam" region = "ap-northeast-1" confirm_changeset = true capabilities = "CAPABILITY_NAMED_IAM" parameter_overrides = "PARAM=\"HOGE\""
設定を dev/prod のように区別させることで、例えば以下のような環境毎の設定を 1 ファイル内で定義できます。
図はアカウント毎に設定を分ける例でしたが、同アカウント内で環境を分ける場合はstack_name
やs3_prefix
を別に設定することで実現できます。
また、以下のように template.yaml でParameters
を利用している場合は、必要に応じてparameter_overrides
で template.yaml に渡す値を上書きすることもできます。
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: > SAM tamplate for nnc-y-nishi. Globals: Function: Timeout: 3 Parameters: PARAM: Type: String Default: FUGA Resources: : :
regionは必ずしも samconfig.toml 内で設定しなくても問題ありませんが、Jenkins 上の環境変数等で設定をしていない場合は AWS のエンドポイントに接続できなくなるため、念のため設定しておくのが個人的にはお勧めです。
Jenkinsfile で定義すること
Jenkinsfile 上では、SAM CLI でデプロイするための一連の処理を定義します。
最終的にsam deploy
コマンドでのデプロイとなりますが、その際の実行 Role や、samconfig.toml で定義した設定を指定するには以下のオプションを利用します。
sam deploy --role-arn ${SAM用RoleのARN} --config-env dev
SAM CLI では sam pipeline コマンドで Jenkinsfile を自動生成させることもできるので、そちらを利用しても良いと思います。 また、SAMテンプレート をデプロイするにあたり、Terraform のように変更差分だけを確認したい場面もあるかと思います。 SAM の場合は以下のコマンドで、デプロイさせずに差分確認だけを行うことができます。
sam deploy --role-arn ${SAM用RoleのARN} --config-env dev --no-execute-changeset
差分確認をしてからデプロイさせるような運用をしたい場合には、こちらのオプションが活用できます。
(Jenkins ジョブ実行中に入力受付を可能にしている場合は、通常のオプションなしのsam deploy
コマンドをそのまま利用できるかと思います)
例えば以下のような Stage を構成することで、template.yaml の検証/ビルド/差分確認/デプロイ、という流れのジョブが実現できます。
steps { script { templates.each { template -> dir(template) { stage(template) { stage(‘Validate’) { sh ‘sam validate --template ./template.yaml’ } stage('Build') { sh “sam build --config-env ${ENV}” } stage('Confirm Changeset') { sh “sam deploy --config-env ${ENV} --role-arn ${SAM_ROLE} --no-execute-changeset” } stage('Deploy') { if (!autoApprove) { timeout(time:10, unit:'MINUTES') { input message: “Approve?” } } sh “sam deploy --config-env ${ENV} --role-arn ${SAM_ROLE} --no-confirm-changeset” } } } } } }
実行すると、Changeset が作成された後にそのままデプロイを進めるかどうかが選択できます。
おわりに
Jenkins で SAM をデプロイさせるまでの準備についてご紹介しました。 Jenkinsへの過剰な権限付与を避けるためにも、必要な権限を IAM Role に分割することが重要な点となります。 samconfig.toml の設定項目や Jenkins での処理で、それぞれの環境に合った CI/CD 環境が実現できるのではないかと思います。
また、以上の内容で挙げた他にも SAM CLI には様々な機能がありますので、それらも活用していければと思います。