はじめに
今までコンソール画面でCI/CDパイプラインをぽちぽちと構築していたのですが、プロジェクトが増えるたびにデプロイフローが増えるので、「またぽちぽちしないといけんのか…正直めんどい。そうか、CloudFormationでテンプレート化してしおう」と思い立ち、CloudFormationを利用して簡単に構築できるようにしました。
結果、構築する工数が減ったってのと、基本的に同じような構成になるので、デプロイ周りの治安を改善することができました。
デプロイフローはこんな感じ
登場人物
サービス名 | ざっくり説明 |
---|---|
AWS CloudFormation | 今回の主人公 |
AWS CodePipeLine | CI/CDパイプライン |
AWS CodeCommit | ソースコードのリポジトリ |
AWS CodeBuild | テストを実行して、完了したらS3にアーカイブする |
AWS CodeDeploy | DockerImageをCreateしてECRにPushする |
Amazon ECR | DockerImageのリポジトリ |
AWS ElasticBeanstalk | アプリケーションが実行されている環境 |
CloudFormationとは?
あるテンプレートファイルを元にStackというタスクを作成し、AWSのリソースをデプロイ、プロビジョニングしてくれるサービスです。
詳細は公式ドキュメントを呼んでください。
前提条件
- AWSCLIインストール済み / Credential設定済み
- (必要ならば)DockerイメージをCreateしてPushしてくれる、CodeDeployAgentがインストールされているEC2
→ こちらを参考にCodeDeployAgent
をインストール、そのあとDockerをインストールしてください。
以上。
テンプレートファイル
CloudFormationのテンプレートファイルはこんな感じ
AWSTemplateFormatVersion: 2010-09-09
# 各種パラメータ
Parameters:
AccountID:
Type: String
Default: ""
# 各種リソース
Resources:
# ECRのRepositoryを作成
# リポジトリポリシーに特定のアカウントからのアクセスのみを許可するように設定
# rootにしていますが特定のuserだけに絞りたいときは user/hoge のように書き換えてください
Repository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: "sample_repository"
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
-
Sid: cfnSample
Effect: "Allow"
Principal:
AWS:
- !Join ["", ["arn:aws:iam::", !Ref AccountID, ":root"]]
Action:
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
- "ecr:BatchCheckLayerAvailability"
# CodeBuildに適用するIAMRole
# 必要に応じて内容を変更してください
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Resource: "*"
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- dynamodb:*
- ses:*
- Resource: !Sub arn:aws:s3:::${ArtifactBucket}/*
Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
- s3:GetObjectVersion
# CodePipelineに適用するIAMRole
# 必要に応じて内容を変更してください
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Resource:
- !Sub arn:aws:s3:::${ArtifactBucket}/*
Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketVersioning
- Resource: "*"
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:StopBuild
- codebuild:BatchGet*
- codebuild:Get*
- codebuild:List*
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:GetRepository
- codecommit:ListBranches
- codecommit:GetUploadArchiveStatus
- codecommit:UploadArchive
- codecommit:CancelUploadArchive
- codedeploy:CreateDeployment
- codedeploy:GetApplicationRevision
- codedeploy:GetDeployment
- codedeploy:GetDeploymentConfig
- codedeploy:RegisterApplicationRevision
- s3:GetBucketLocation
- s3:ListAllMyBuckets
- iam:PassRole
- Resource: "arn:aws:ssm:*:*:parameter/CodeBuild/*"
Effect: Allow
Action:
- ssm:PutParameter
# CodeDeployに適用するIAMRole
# 必要に応じて内容を変更してください
# すでにAWS側が用意してくれているPolicyを指定
CodeDeployServiceRole:
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: codedeploy.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole
# S3Bucket
ArtifactBucket:
Type: AWS::S3::Bucket
# CodeBuild
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Source:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/nodejs:6.3.1
Type: LINUX_CONTAINER
Name: !Ref AWS::StackName
ServiceRole: !Ref CodeBuildServiceRole
# CodeDeploy
CodeDeployAplication:
Type: AWS::CodeDeploy::Application
Properties:
ApplicationName: "build-test-app"
CodeDeployGroup:
Type: AWS::CodeDeploy::DeploymentGroup
Properties:
ApplicationName: !Ref CodeDeployAplication
DeploymentGroupName: "build-test-group"
DeploymentConfigName: CodeDeployDefault.OneAtATime
Ec2TagFilters:
- Type: KEY_AND_VALUE
Key: "Name"
Value: "docker-ue1"
ServiceRoleArn: !GetAtt [CodeDeployServiceRole, Arn]
# CodePipeLine
# 内容は CodeCommit → CodeBuild → CodeDeploy の順で処理をして、最終的にECRにDockerイメージをPushする
# DockerイメージをPushするのはCodeDeploy
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt CodePipelineServiceRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: 1
Provider: CodeCommit
Configuration:
RepositoryName: IoT.kyoto
BranchName: build-test
RunOrder: 1
OutputArtifacts:
- Name: App
- Name: Build
Actions:
- Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Version: 1
Provider: CodeBuild
Configuration:
ProjectName: !Ref CodeBuildProject
RunOrder: 1
InputArtifacts:
- Name: App
OutputArtifacts:
- Name: MyApp
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Version: 1
Provider: CodeDeploy
Configuration:
ApplicationName: !Ref CodeDeployAplication
DeploymentGroupName: !Ref CodeDeployGroup
RunOrder: 1
InputArtifacts:
- Name: MyApp
Stackを作成する
パラメータ AccountID
は、 eb deploy
する先のアカウントIDです。
--stack-name
でStackの名前を指定します。
--region
でCloudFormationのStackを作成するリージョンを設定する
$ aws cloudformation create-stack --template-body file://codepipeline-template.yml \
--stack-name cfn-sample --region us-east-1 \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=AccountID,ParameterValue=1234567890123
Stackが正常に作成されると、CodePipeLineでデプロイ処理開始されます。
Stackを削除する
--stack-name
でStackの名前を指定します。
--region
で削除するCloudFormationのStackのリージョンを設定する
$ aws cloudformation delete-stack --stack-name cfn-sample --region us-east-1
[注意] 削除対象のECRとS3Bucketが空でなければdelete-stackは失敗しますので、ECRとBucketの中身を綺麗にしてからDeleteするようにしてください。
さいごに
こうしてCI/CDパイプライン構築をテンプレート化することができました。美しいですね。
標準化することができたこともあって、構築するときに全くわけのわからない構築をされる心配が減り、治安が改善されたと思います。
真面目な話をすると、今回CloudFormationを初めて触ったのですが、普段コンソール画面でぽちぽちしている裏でどんな設定がされているのかという部分の一面を見ることができました。
また今回構築した各サービスの理解も深まり良かったと思います。
今後もテンプレート化できそうな構成があれば、どんどんCloudFormationを活用していこうと思います!
ではまた!