Amazon Web Services ブログ
AWS CodePipelineを利用したネストされたAWS CloudFormationスタックの継続的デリバリー
CodePipeline の更新 – CloudFormation スタックの継続的デリバリーワークフローの構築で、 Jeff BarrはInfrastructure as Codeについてと、AWS CodePipelineを継続的デリバリーに使用する方法について説明しています。 本ブログ記事では、ソースリポジトリとしてAWS CodeCommitを、ビルドおよびテストツールとしてAWS CodeBuildを使用した、AWS CodePipelineを使ったネストされたCloudFormationスタックの継続的デリバリーについて説明します。手動承認プロセスに従ってCloudFormationチェンジセットを使用してスタックをデプロイします。
AWS CodePipelineでは、次の4つのステージでパイプラインを作成します。
- Source (AWS CodeCommit)
- Build and Test (AWS CodeBuild および AWS CloudFormation)
- Staging (AWS CloudFormation および 手動承認)
- Production (AWS CloudFormation および 手動承認)
次の図に、パイプラインのステージと、各ステージのアクション、およびステージ間の遷移を示します。
CloudFormationテンプレート、テストスクリプト、およびビルドスペックファイルは、AWS CodeCommitリポジトリに格納されています。これらのファイルは、AWS CodePipelineのパイプラインのSourceステージで使用されます。
AWS::CloudFormation::Stackリソースタイプは、親スタックから子スタックを作成するために使用されます。 CloudFormationスタックリソースでは、S3バケットに格納される子スタックのテンプレートを必要とします。テンプレートファイルの場所は、リソース定義のPropertiesセクションにURLとして指定されます。
次のテンプレートは、3つの子スタックを作成します。
- Security (IAM, セキュリティグループ)
- Database (RDSインスタンス)
- Web stacks (Auto ScalingグループのEC2インスタンス, ELB)
Description: Master stack which creates all required nested stacks
Parameters:
TemplatePath:
Type: String
Description: S3Bucket Path where the templates are stored
VPCID:
Type: "AWS::EC2::VPC::Id"
Description: Enter a valid VPC Id
PrivateSubnet1:
Type: "AWS::EC2::Subnet::Id"
Description: Enter a valid SubnetId of private subnet in AZ1
PrivateSubnet2:
Type: "AWS::EC2::Subnet::Id"
Description: Enter a valid SubnetId of private subnet in AZ2
PublicSubnet1:
Type: "AWS::EC2::Subnet::Id"
Description: Enter a valid SubnetId of public subnet in AZ1
PublicSubnet2:
Type: "AWS::EC2::Subnet::Id"
Description: Enter a valid SubnetId of public subnet in AZ2
S3BucketName:
Type: String
Description: Name of the S3 bucket to allow access to the Web Server IAM Role.
KeyPair:
Type: "AWS::EC2::KeyPair::KeyName"
Description: Enter a valid KeyPair Name
AMIId:
Type: "AWS::EC2::Image::Id"
Description: Enter a valid AMI ID to launch the instance
WebInstanceType:
Type: String
Description: Enter one of the possible instance type for web server
AllowedValues:
- t2.large
- m4.large
- m4.xlarge
- c4.large
WebMinSize:
Type: String
Description: Minimum number of instances in auto scaling group
WebMaxSize:
Type: String
Description: Maximum number of instances in auto scaling group
DBSubnetGroup:
Type: String
Description: Enter a valid DB Subnet Group
DBUsername:
Type: String
Description: Enter a valid Database master username
MinLength: 1
MaxLength: 16
AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
DBPassword:
Type: String
Description: Enter a valid Database master password
NoEcho: true
MinLength: 1
MaxLength: 41
AllowedPattern: "[a-zA-Z0-9]*"
DBInstanceType:
Type: String
Description: Enter one of the possible instance type for database
AllowedValues:
- db.t2.micro
- db.t2.small
- db.t2.medium
- db.t2.large
Environment:
Type: String
Description: Select the appropriate environment
AllowedValues:
- dev
- test
- uat
- prod
Resources:
SecurityStack:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL:
Fn::Sub: "https://s3.amazonaws.com/${TemplatePath}/security-stack.yml"
Parameters:
S3BucketName:
Ref: S3BucketName
VPCID:
Ref: VPCID
Environment:
Ref: Environment
Tags:
- Key: Name
Value: SecurityStack
DatabaseStack:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL:
Fn::Sub: "https://s3.amazonaws.com/${TemplatePath}/database-stack.yml"
Parameters:
DBSubnetGroup:
Ref: DBSubnetGroup
DBUsername:
Ref: DBUsername
DBPassword:
Ref: DBPassword
DBServerSecurityGroup:
Fn::GetAtt: SecurityStack.Outputs.DBServerSG
DBInstanceType:
Ref: DBInstanceType
Environment:
Ref: Environment
Tags:
- Key: Name
Value: DatabaseStack
ServerStack:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL:
Fn::Sub: "https://s3.amazonaws.com/${TemplatePath}/server-stack.yml"
Parameters:
VPCID:
Ref: VPCID
PrivateSubnet1:
Ref: PrivateSubnet1
PrivateSubnet2:
Ref: PrivateSubnet2
PublicSubnet1:
Ref: PublicSubnet1
PublicSubnet2:
Ref: PublicSubnet2
KeyPair:
Ref: KeyPair
AMIId:
Ref: AMIId
WebSG:
Fn::GetAtt: SecurityStack.Outputs.WebSG
ELBSG:
Fn::GetAtt: SecurityStack.Outputs.ELBSG
DBClientSG:
Fn::GetAtt: SecurityStack.Outputs.DBClientSG
WebIAMProfile:
Fn::GetAtt: SecurityStack.Outputs.WebIAMProfile
WebInstanceType:
Ref: WebInstanceType
WebMinSize:
Ref: WebMinSize
WebMaxSize:
Ref: WebMaxSize
Environment:
Ref: Environment
Tags:
- Key: Name
Value: ServerStack
Outputs:
WebELBURL:
Description: "URL endpoint of web ELB"
Value:
Fn::GetAtt: ServerStack.Outputs.WebELBURL
Validateステージでは、AWS CodeBuildはAWS CodeCommitソースリポジトリの変更をチェックします。 ValidateTemplate APIを使用してCloudFormationテンプレートを検証し、子テンプレートと設定ファイルをS3バケットの適切な場所にコピーします。
次のAWS CodeBuildビルドスペックでは、TEMPLATE_FILES環境変数にリストされているCloudFormationテンプレートが検証され、AWS CodeBuildプロジェクトの環境変数TEMPLATE_BUCKETに指定されたS3バケットにコピーされます。 オプションとして、TEMPLATE_PREFIX環境変数を使用して、バケット内のパスを指定することもできます。これにより、構成ファイルの子テンプレートファイルの場所が更新されます。テンプレートファイルの場所は、親スタックのパラメータとして渡されます。
version: 0.1
environment_variables:
plaintext:
CHILD_TEMPLATES: |
security-stack.yml
server-stack.yml
database-stack.yml
TEMPLATE_FILES: |
master-stack.yml
security-stack.yml
server-stack.yml
database-stack.yml
CONFIG_FILES: |
config-prod.json
config-test.json
config-uat.json
phases:
install:
commands:
npm install jsonlint -g
pre_build:
commands:
- echo "Validating CFN templates"
- |
for cfn_template in $TEMPLATE_FILES; do
echo "Validating CloudFormation template file $cfn_template"
aws cloudformation validate-template --template-body file://$cfn_template
done
- |
for conf in $CONFIG_FILES; do
echo "Validating CFN parameters config file $conf"
jsonlint -q $conf
done
build:
commands:
- echo "Copying child stack templates to S3"
- |
for child_template in $CHILD_TEMPLATES; do
if [ "X$TEMPLATE_PREFIX" = "X" ]; then
aws s3 cp "$child_template" "s3://$TEMPLATE_BUCKET/$child_template"
else
aws s3 cp "$child_template" "s3://$TEMPLATE_BUCKET/$TEMPLATE_PREFIX/$child_template"
fi
done
- echo "Updating template configurtion files to use the appropriate values"
- |
for conf in $CONFIG_FILES; do
if [ "X$TEMPLATE_PREFIX" = "X" ]; then
echo "Replacing \"TEMPLATE_PATH_PLACEHOLDER\" for \"$TEMPLATE_BUCKET\" in $conf"
sed -i -e "s/TEMPLATE_PATH_PLACEHOLDER/$TEMPLATE_BUCKET/" $conf
else
echo "Replacing \"TEMPLATE_PATH_PLACEHOLDER\" for \"$TEMPLATE_BUCKET/$TEMPLATE_PREFIX\" in $conf"
sed -i -e "s/TEMPLATE_PATH_PLACEHOLDER/$TEMPLATE_BUCKET\/$TEMPLATE_PREFIX/" $conf
fi
done
artifacts:
files:
- master-stack.yml
- config-*.json
テンプレートファイルがS3にコピーされると、CloudFormationはテストスタックを作成し、テストアクションとしてAWS CodeBuildをトリガーします。
AWS CodeBuildのビルドスペックでは、ネストされたCloudFormationスタックを使用して作成されたリソースが、CONFIG_FILEで指定された仕様に準拠しているかどうかをチェックするためのPythonスクリプト、validate-env.py
が実行されます。
version: 0.1
environment_variables:
plaintext:
CONFIG_FILE: env-details.yml
phases:
install:
commands:
- pip install --upgrade pip
- pip install boto3 --upgrade
- pip install pyyaml --upgrade
- pip install yamllint --upgrade
pre_build:
commands:
- echo "Validating config file $CONFIG_FILE"
- yamllint $CONFIG_FILE
build:
commands:
- echo "Validating resources..."
- python validate-env.py
- exit $?
テストアクションが正常に完了すると、CloudFormationはテストスタックを削除し、パイプラインのUAT(訳者注:User Acceptance Test/ユーザ受け入れテスト)ステージに進みます。
このステージでは、CloudFormationはUATスタックに対して変更セットを作成し、変更セットを実行します。これにより、UAT環境が更新され、受け入れテストが可能になります。プロセスは手動承認アクションに進みます。 QAチームがUAT環境を検証して承認をおこなった後、プロセスはパイプラインのProductionステージに移行します。
このステージでは、CloudFormationはネストされた本番スタックの変更セットを作成し、プロセスは手動承認ステップに進みます。(通常は指定されたエグゼクティブによって)承認されると、変更セットが実行され、Productionデプロイメントが完了します。
継続的デリバリーパイプラインの設定
CloudFormationテンプレートを使用して、継続的デリバリーパイプラインを設定します。 GitHubから入手できるcodepipeline-cfn-codebuild.ymlテンプレートは、フル機能のパイプラインを設定します。
このテンプレートを使用してパイプラインを作成するときは、次の項目を指定します。
- AWS CodeCommitリポジトリ
- 承認通知を送信するSNSトピック
- アーティファクトが格納されるS3バケット名
CFNTemplateRepoNameには、CloudFormationテンプレート、設定ファイル、およびビルドスペックファイルが格納されているAWS CodeCommitリポジトリを指定します。
私のリポジトリには以下のファイルが含まれています:
継続的デリバリーパイプラインは、Create Stackをクリックするとすぐに配備されます。作成後、パイプラインは各ステージを実行します。 UATおよびProductionステージの手動承認により、パイプラインは継続的デリバリーを可能にします。
ネストされたスタックの変更の実装
ネストしたスタック内の子スタックを変更するには(たとえば、パラメータ値を更新する、またはリソースを追加または変更するなど)、親スタックを更新します。変更は適切なテンプレートファイルまたは設定ファイルで行い、AWS CodeCommitリポジトリにチェックインする必要があります。これにより、次のデプロイプロセスがトリガーされます:
まとめ
この記事では、AWS CodePipeline、AWS CloudFormation、AWS CodeBuild、および手動承認プロセスを使用して、Infrastructure as Codeとアプリケーションデプロイメントの両方で継続的デリバリーパイプラインを作成する方法を示しました。
AWS CodePipelineの詳細については、AWS CodePipelineのドキュメントを参照してください。数回のクリックで始めることができます。 すべてのCloudFormationテンプレート、AWS CodeBuildビルドスペックファイル、および検証を実行するPythonスクリプトは、codepipeline-nested-cfn GitHubリポジトリに公開しています。
著者紹介
Prakash PalanisamyはAmazon Web ServicesのSolutions Architectです。 Serverless、DevOps、Alexaを担当する他、Project Eulerでの問題解決をおこなっています。彼はまた、教育ドキュメンタリーを見て楽しんでいます。
(翻訳はSA千葉が担当しました。原文はこちら)