はじめに
業務でAWSにおいてCI/CD環境を構築する必要があるため、そもそもCICDに関する理解が浅いと感じたため本記事にてアウトプットをいたします。
今回はAWSのCodePipelineとCodeBuildに絞っていきたいと思います。
そもそもCI/CDとは
CICDとはContinuous Integration
(継続的インテグレーション), Continous Deliversy
(継続的デリバリー)の略称です。
Continuous Integration
とはAWS公式の言葉を引用すると
ソフトウェアのリリースプロセスにおけるビルド段階と単体テスト段階のことを指します。リビジョンがコミットされるたびに、自動化されたビルドとテストが開始されます。
つまりどういうことか?
EC2上でDockerのイメージを作っていく工程を例に解説します。
- Dockerfileを作る
- Dockerfileを基にDocker Hubからイメージを取得し、buildを実行する
- build実行後、Dockerコンテナを起動し、アプリケーションのテストを実行する
- Dockerイメージが出来上がり、テストも完了後、そのイメージをECRにPushする (正確にはECRにPushできるようにタグを編集後、Pushを実行する)
この作業、手作業で一つ一つの工程を実施することはもちろんできます。
が、より効率的にやろうと思った場合、この一連の流れをあることをトリガーにその後の作業を自動でやってくれると便利だと思いませんか?
はい、これらの作業をAWSのCodeBuildを使うことで(厳密にはCodeBuildだけではありませんが…)GitHubへのSourcePush
をトリガーに、一連の流れを自動実行できるように仕組み化することが可能です。
CodeBuildとは
CloudFormationテンプレートの内容を抜粋しながら説明します。
全体のコードについてはGitHubにありますのでそちらを参照ください。
実際にデプロイしたいという方はs3-cicd/s3-pipeline-codestar.yml
を先にデプロイし、その後にcodebuild/codebuild.yml
をデプロイすることで動作確認することが可能かと思います。
s3-cicd/s3-pipeline-codestar.yml
の方は資材バックアップ用のものです。
GitHubにPushを実行するとPipeliineが起動し、CodeBuild
が起動し、GitHubにPushした資材がS3にバックアップとして保存されます。
イメージは以下です。
GitHubに必要な資材をPushすると、Pipeline
が動き出し、CodeBuild
でaws S3 sync
コマンドが実行され、GitHubにある資材がS3に同期されます。
s3-pipeline-codestar.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to build and push Docker images to S3 from GitHub.
Parameters:
ConnectionArn:
Description: The CodeConnections ARN for your source repository.
Type: String
FullRepositoryId:
Description: The full repository ID to use with your CodeConnections connection.
Type: String
Default: "Kyrieee-ops/s3-cicd"
BranchName:
Description: The branch name to use with your CodeConnections connection.
Type: String
Default: main
CodePipelineName:
Description: The CodePipeline pipeline name that will deploy to your CloudFormation stack.
Type: String
Default: s3-codepipeline
BucketNameBackup:
Type: String
Default: s3-cicd-backup
Resources:
# ------------------------------------------------------------#
# CodeBuild
# buildspecを実行するプロジェクトを定義する
# GitHubに保存されているソースコードのビルドを行いArtifact
# ------------------------------------------------------------#
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: github-to-s3-build
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
phases:
build:
commands:
- echo aws s3 sync....
- aws s3 sync . s3://${BucketNameBackup}-${AWS::AccountId}/template --delete
Environment:
ComputeType: BUILD_GENERAL1_SMALL # ビルドする際のリソースを指定する(AWS公式に環境環境タイプの容量一覧があるので参考にする)
Image: aws/codebuild/standard:5.0
Type: LINUX_CONTAINER # ビルドの実行タイプをLinuxベースのコンテナを使用する
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: CODEPIPELINE # build成果物が不要な場合はNO_ARTIFACTSを指定する
# ------------------------------------------------------------#
# CodeBuildが使用するロールの定義
# ------------------------------------------------------------#
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: codebuild-role-for-s3
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action: sts:AssumeRole
# ------------------------------------------------------------#
# CodeBuildRoleにアタッチするポリシー
# カスタマーインラインポリシー
# ------------------------------------------------------------#
CodeBuildPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource:
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${CodeBuildProject}:*"
- Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- s3:PutObject
- s3:GetObject
- s3:ListBucket
- s3:DeleteObject
Effect: Allow
Resource:
- !GetAtt CodeBuildProject.Arn
# - !ImportValue s3bucketbackupArn
- !GetAtt s3bucketbackup.Arn # s3バケットリソースの論理名でs3バケットのARNを取得する
- !Join
- '/'
# - - !ImportValue s3bucketbackupArn
- - !GetAtt s3bucketbackup.Arn
- '*'
PolicyName: CodeBuildPolicy
Roles:
- !Ref CodeBuildRole
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref s3bucketbackup
Type: S3
# ExecutionMode: PARALLEL # Code Pipelineが実行中であっても並列実行を制御するためにキューに入る設定
Name: !Ref CodePipelineName
# PipelineType: V2
RoleArn: !GetAtt CodePipelineRole.Arn
Stages:
- Name: Source
Actions:
- Name: CodeConnections
ActionTypeId: # Sourceコードの取得を行うフェーズを定義
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection # CodeStarSourceConnectionを使用してGitHubと連携をする
Version: '1'
Configuration:
ConnectionArn: !Ref ConnectionArn # 事前にCodeConnectionArnを作成しておく必要があるSource
FullRepositoryId: !Ref FullRepositoryId
BranchName: !Ref BranchName
# PollForSourceChanges: false # 自動トリガーを無効化 -> ここをfalseに設定しておかないとCloudFormation deploy時にCodePipelineが起動してしまうため
OutputArtifacts:
- Name: SourceOutput # ソースステージの出力 -> 指定されたリポジトリからコードを取得しそれをSourceOutputという名前で出力アーティファクトとして生成する
RoleArn: !GetAtt CodePipelineSourceActionRole.Arn
RunOrder: 1
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
InputArtifacts:
- Name: SourceOutput
Configuration:
ProjectName: !Ref CodeBuildProject
OutputArtifacts:
- Name: BuildOutput
DependsOn:
- CodePipelineRoleDefaultPolicy
# ------------------------------------------------------------#
# CodePipelineが使用するロールを指定する
# codepipelineに対してAssumeRole -> 要するにCodePipelineが権限を引き受けることができる
# CloudFormationでロール名を指定しない場合、スタック名 + 論理ID + ランダムな文字列で生成する
# ------------------------------------------------------------#
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
RoleName: codepipeline-role-for-s3
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Version: '2012-10-17'
# ------------------------------------------------------------#
# CodePipelineRoleにアタッチするポリシー
# CodePipelineが指定のS3バケットに対する操作を行うことができる
# ------------------------------------------------------------#
CodePipelineRoleDefaultPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action: # CodePipelineが指定のS3バケットに対して操作を行えるようにしている
- s3:Abort*
- s3:DeleteObject*
- s3:GetBucket*
- s3:GetObject*
- s3:List*
- s3:PutObject
- s3:PutObjectLegalHold
- s3:PutObjectRetention
- s3:PutObjectTagging
- s3:PutObjectVersionTagging
- s3:PutObjectAcl
- s3:PutObjectVersionAcl
Effect: Allow
# 以下ではS3バケットレベルの操作とオブジェクトレベルの操作を許可するために指定している
# `Join`でS3バケットに`/*`のように結合し、`arn:aws:s3:::my-bucket/*`を指定
# つまり対象のS3バケットの全てのオブジェクトに対して許可をする、ということを示している
Resource:
# - !ImportValue s3bucketbackupArn
- !GetAtt s3bucketbackup.Arn # s3バケットリソースの論理名でs3バケットのARNを取得する
- !Join
- '/'
# - - !ImportValue s3bucketbackupArn
- - !GetAtt s3bucketbackup.Arn
- '*'
- Action: # CodePipelineに対してCodeBuildの操作権限を付与する
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- codebuild:BatchGetProjects
Effect: Allow
Resource: !GetAtt CodeBuildProject.Arn
- Action: sts:AssumeRole # あくまでCodePipelineがCodeBuildプロジェクトのロールを引き受ける許可
Effect: Allow
Resource:
- !GetAtt CodeBuildRole.Arn
- !GetAtt CodePipelineSourceActionRole.Arn
Version: '2012-10-17'
PolicyName: CodePipelineRoleDefaultPolicy
Roles:
- !Ref CodePipelineRole
# ------------------------------------------------------------#
# CodePipelineのソースアクションに必要なロールを定義
# GitHubからコードを取得するアクション
# ------------------------------------------------------------#
CodePipelineSourceActionRole:
Type: AWS::IAM::Role
Properties:
RoleName: codepipeline-source-from-github-role
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root # 使用しているAWSアカウントに対して権限を引き受けることができる
Version: '2012-10-17'
# ------------------------------------------------------------#
# ソースアクション(コードを取得するアクション)が必要とする権限を定義する
# ------------------------------------------------------------#
CodePipelineSourceActionRoleDefaultPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action: codestar-connections:UseConnection # CodePipelineがGitHubのソースプロバイダーに接続し、GitHubからソースを取得する
Effect: Allow
Resource: !Ref ConnectionArn # AWS ⇔ GitHub接続名のARNを参照し、GitHubとの接続を使用してコードを取得が可能となる
# CodePipelineがGitHubから取得したファイルをS3へアップロードするために必要なActionの操作権限を定義
- Action:
- s3:Abort*
- s3:DeleteObject*
- s3:PutObject
- s3:PutObjectLegalHold # 必要に応じてオブジェクトの保持ポリシーを設定する場合に必要
- s3:PutObjectRetention
- s3:PutObjectTagging
- s3:PutObjectVersionTagging # アップロードしたファイルにタグを付与する場合に必要
Effect: Allow
Resource:
# - !ImportValue s3bucketbackupArn
- !GetAtt s3bucketbackup.Arn
- !Join
- '/'
# - - !ImportValue s3bucketbackupArn
- - !GetAtt s3bucketbackup.Arn
- '*'
- Action:
- s3:PutObjectAcl
- s3:PutObjectVersionAcl
Effect: Allow
Resource: !Join
- /
# - - !ImportValue s3bucketbackupArn
- - !GetAtt s3bucketbackup.Arn
- '*'
Version: '2012-10-17'
PolicyName: CodePipelineSourceActionRoleDefaultPolicy
Roles:
- !Ref CodePipelineSourceActionRole
# ------------------------------------------------------------#
# S3 backup
# ------------------------------------------------------------#
s3bucketbackup:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketName: !Sub ${BucketNameBackup}-${AWS::AccountId}
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Suspended
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketKeyEnabled: true
Tags:
- Key: Name
Value: !Sub ${BucketNameBackup}-${AWS::AccountId}
s3BucketPolicyBackup:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref s3bucketbackup
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "s3:GetObject"
Effect: Allow
Resource: !Sub "arn:${AWS::Partition}:s3:::${BucketNameBackup}-${AWS::AccountId}/*"
Principal:
AWS: !Sub arn:${AWS::Partition}:iam::674078804300:user/Administrator
Outputs:
s3bucketbackup:
Value: !Ref s3bucketbackup
Export:
Name: s3bucketbackup
s3BucketArn:
Value: !Sub arn:aws:s3:::${s3bucketbackup}
Export:
Name: s3bucketbackupArn
codebuild.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to build and push Docker images to ECR from GitHub.
Parameters:
ECRRepositoryName:
Description: The name of the ECR repository to push the Docker image.
Type: String
Default: amazonlinux-repository
CodeBuildRoleName:
Description: The CodeBuild role name
Type: String
Default: codebuild-role-for-ecr
CodeBuildProjectName:
Description: The CodeBuild name
Type: String
Default: github-to-ecr-build
GitHubConnectionArn:
Description: The ARN of the GitHub connection for CodePipeline.
Type: String
FullRepositoryId:
Description: The full repository ID to use with your CodeConnections connection.
Type: String
Default: "Kyrieee-ops/codebuild"
BranchName:
Description: The branch name to use with your GitHub repository.
Type: String
Default: master
CodePipelineName:
Description: The CodePipeline name that will deploy to your CloudFormation stack.
Type: String
Default: codebuild-codepipeline
CodePipelineSourceActionRoleName:
Description: The CodePipeline role name for ecr
Type: String
Default: codepipeline-source-from-github-role-for-ecr
CodePipelineRoleName:
Description: The CodePipeline role name for codebuild
Type: String
Default: codepipeline-role-for-codebuild
Resources:
# ECRリポジトリの作成
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Sub ${ECRRepositoryName}
Tags:
- Key: Name
Value: !Sub ${ECRRepositoryName}
# ------------------------------------------------------------#
# CodeBuildがECRにDockerImageをPushするために必要なロール
# codebuild-role-for-ecr
# ------------------------------------------------------------#
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${CodeBuildRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
Resource: !Join
- /
- - !ImportValue s3bucketbackupArn
- '*'
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- Effect: Allow
Action:
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
- ecr:BatchCheckLayerAvailability
- ecr:DescribeRepositories
- ecr:GetAuthorizationToken
Resource:
- "*"
# ------------------------------------------------------------#
# CodeBuild
# buildspecを実行するプロジェクトを定義する
# GitHubに保存されているソースコードのビルドを行いArtifact
# envはphasesの外で書かないとエラーになる
# ------------------------------------------------------------#
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${CodeBuildProjectName}
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
env:
variables:
IMAGE_REPO_NAME: ${ECRRepositoryName}
IMAGE_TAG: dev
AWS_ACCOUNT_ID: ${AWS::AccountId}
AWS_DEFAULT_REGION: ${AWS::Region}
phases:
pre_build:
commands:
- echo Logging in to $AWS_DEFAULT_REGION/$AWS_ACCOUNT_ID Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker image ls
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker image ls
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image to ECR...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:5.0
Type: LINUX_CONTAINER
PrivilegedMode: true # Dockerビルドのために必要
ServiceRole: !Ref CodeBuildRole
Artifacts:
Type: CODEPIPELINE
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub ${CodePipelineName}
RoleArn: !GetAtt CodePipelineRole.Arn
ArtifactStore:
Type: S3 # アーティファクトの保存場所をS3に指定
Location: !ImportValue s3bucketbackup # 事前に作成したS3バケットのインポート
Stages:
- Name: Source
Actions:
- Name: GitHubSource
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: '1'
Configuration:
ConnectionArn: !Sub ${GitHubConnectionArn}
FullRepositoryId: !Sub ${FullRepositoryId}
BranchName: !Sub ${BranchName}
OutputArtifacts:
- Name: SourceOutput
RoleArn: !GetAtt CodePipelineRole.Arn
RunOrder: 1 # 実行順序
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: '1'
InputArtifacts:
- Name: SourceOutput
Configuration:
ProjectName: !Sub ${CodeBuildProject}
# OutputArtifacts:
# - Name: BuildOutput
# ------------------------------------------------------------#
# CodePipelineRole
# CodeBuildを実行する用途
# ECRに保存されたイメージを使用する
# ------------------------------------------------------------#
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${CodePipelineRoleName}
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: CodePipelinePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- ecr:GetAuthorizationToken
- codestar-connections:UseConnection # GitHub接続を使用するための権限を追加
Resource: "*"
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:DeleteObject
- s3:ListBucket
# `arn:aws:s3:::${s3bucketbackup}`と`arn:aws:s3:::${s3bucketbackup}/*`イコール
Resource:
- !ImportValue s3bucketbackupArn
- !Join
- '/'
- - !ImportValue s3bucketbackupArn
- '*'
# ------------------------------------------------------------#
# CodePipelineのソースアクションに必要なロールを定義
# GitHubからコードを取得するアクション
# ECRにイメージをPushする用途
#------------------------------------------------------------#
CodePipelineSourceActionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${CodePipelineSourceActionRoleName}
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root # 使用しているAWSアカウントに対して権限を引き受けることができる
Version: '2012-10-17'
Policies:
- PolicyName: CodePipelineSourceActionRoleDefaultPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- codestar-connections:UseConnection
Resource: !Sub ${GitHubConnectionArn}
- Effect: Allow
Action:
- s3:Abort*
- s3:DeleteObject*
- s3:PutObject
- s3:PutObjectLegalHold
- s3:PutObjectRetention
- s3:PutObjectTagging
- s3:PutObjectVersionTagging # アップロードしたファイルにタグを付与する場合に必要
Resource:
- !ImportValue s3bucketbackupArn
- !Join
- '/'
- - !ImportValue s3bucketbackupArn
- '*'
- Effect: Allow
Action:
- s3:PutObjectAcl
- s3:PutObjectVersionAcl
Resource: !Join
- /
- - !ImportValue s3bucketbackupArn
- '*'
Outputs:
ECRRepositoryUri:
Description: "URI of the ECR repository"
Value: !GetAtt ECRRepository.RepositoryUri
GitHubとの接続
GitHubのPushをトリガーにCodePipeline
を動かすためには、前提としてGitHubとAWSを連携させる必要があります。
ConnectionArn:
Description: The CodeConnections ARN for your source repository.
Type: String
FullRepositoryId:
Description: The full repository ID to use with your CodeConnections connection.
Type: String
Default: "Kyrieee-ops/s3-cicd"
BranchName:
Description: The branch name to use with your CodeConnections connection.
Type: String
Default: main
yamlの中ではParameter
セクションで定義している以下の部分です。
-
ConnectionArn
: GitHubと接続する際に使用するプロバイダーARNです。
ConnectionARNを発行するにはPipelineの画面から左ペインの[接続]から[接続を作成]ボタンから簡単に作成することが可能です。
この値は外部に公開したくない値となるため、.gitignore
でシークレットにしておくことをおすすめします。
FullRepositoryId
: GitHubのリポジトリの名前です。
BranchName
: GitHubのブランチです。今回はmain
としています。
CodeBuild (S3 Sync)
CodeBuildでは、ソースの定義とビルドする環境を定義しています。
コードの一部を再掲します。
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
phases:
build:
commands:
- echo aws s3 sync....
- aws s3 sync . s3://${BucketNameBackup}-${AWS::AccountId}/template --delete
Environment:
ComputeType: BUILD_GENERAL1_SMALL # ビルドする際のリソースを指定する(AWS公式に環境環境タイプの容量一覧があるので参考にする)
Image: aws/codebuild/standard:5.0
Type: LINUX_CONTAINER # ビルドの実行タイプをLinuxベースのコンテナを使用する
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: CODEPIPELINE # build成果物が不要な場合はNO_ARTIFACTSを指定する
Source:
GitHubへのPushをトリガーにCodePipelineが起動し、Source:
以下に記述されているBuildSepcが実行されます。
phases.build
から以下コマンドが実行されます。
Source
今回のコードはaws cli
コマンドを使用して、GitHubにPushされたymlをS3バケットに同期させています。
Environment:
Environment
ではBuildを実行する環境を定義しています。
今回のコードの場合、AWS公式の情報から
実行するコンテナ環境はLinuxであり、
メモリ: 3GB, vCPU: 2vCPUのスペックであることがわかります。
イメージはUbuntu 20.04
のディストリビューションを使用したイメージであることがわかります。
コンソール上からはわからないですが、実際に内部ではLinuxが建てられ、Dockerイメージを使用して実行環境が作られているというのがわかりますね。
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:5.0
Type: LINUX_CONTAINER
今回のphases
はbuild
が指定されていますが、このphases
にはいくつか種類があります。
- install: 必要なソフトウェアのインストールを実行するフェーズ。
- pre_build: ビルド準備に必要な処理を実行するフェーズ(例: 環境変数設定や認証) -> ECRにPushするにはECRのプライベートリポジトリに対して、Dockerクライアントからプライベートリポジトリに対して認証を得る必要がありますが、その認証を実行するフェーズです。
- build: ソースコードのコンパイルやテストの実行など、ビルドプロセスの主要な部分を担当するフェーズ -> もう少し具体的に言うとDockerイメージをbuildし、ECRにPushする前までのプロセスをここで実行するフェーズです。
- post_build: ビルド後の処理(例: 成果物のアップロードや通知)を実行するフェーズ -> buildフェーズでできあがったDockerイメージをECRにPushするフェーズです。
s3-pipeline-codestar.yml
ではGitHubにymlなどのファイルをPushするとS3に格納されるymlのため、特にビルドに必要なものやビルド後の処理などが必要ないため、build
のみとしています。
CodePipeline
CodePipelineでは、「何を起因にしてトリガーを引くか?」を定義する必要があります。
今回の場合はGitHubのPushがトリガーですね。
コードの一部を再掲します。
Stages:
- Name: Source
Actions:
- Name: CodeConnections
ActionTypeId: # Sourceコードの取得を行うフェーズを定義
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection # CodeStarSourceConnectionを使用してGitHubと連携をする
Version: '1'
Configuration:
ConnectionArn: !Sub ${ConnectionArn} # 事前にCodeConnectionArnを作成しておく必要があるSource
FullRepositoryId: !Sub ${FullRepositoryId}
BranchName: !Sub ${BranchName}
# PollForSourceChanges: false # 自動トリガーを無効化 -> ここをfalseに設定しておかないとCloudFormation deploy時にCodePipelineが起動してしまうため
OutputArtifacts:
- Name: SourceOutput # ソースステージの出力 -> 指定されたリポジトリからコードを取得しそれをSourceOutputという名前で出力アーティファクトとして生成する
RoleArn: !GetAtt CodePipelineSourceActionRole.Arn
RunOrder: 1
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
InputArtifacts:
- Name: SourceOutput
Configuration:
ProjectName: !Ref CodeBuildProject
OutputArtifacts:
- Name: BuildOutput
Stages:
Stages
: セクションとはソースコードを取得するための内容を定義しています。
ActionTypeId
以下では
-
Category
: このアクションのカテゴリを定義します (Source
のためソースコード取得のフェーズを意味する) -
Owner
: アクションプロバイダーの所有者はAWS
です。 -
Provider
:CodeStarSourceConnection
プロバイダーにCodeStarSourceConnection
を使用しますが、CodeStarSourceConnection
とは接続先プロバイダーをGitHubにしたい場合に選択します。
Stages:
- Name: Source
Actions:
- Name: CodeConnections
ActionTypeId: # Sourceコードの取得を行うフェーズを定義
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection # CodeStarSourceConnectionを使用してGitHubと連携をする
Version: '1'
Configuration
ソースコードを取得する内容をStages
で定義した後は、Configuration
で具体的な値を定義します。
- AWSと連携しているGitHubは?
- GitHubのリポジトリ名は?
- ブランチ名は?
を定義しています。
Configuration:
ConnectionArn: !Sub ${ConnectionArn}
FullRepositoryId: !Sub ${FullRepositoryId}
BranchName: !Sub ${BranchName}
OutputArtifacts
OutputArtifacts
でStages
で取得した成果物をSourceOutput
として格納します。
OutputArtifacts:
- Name: SourceOutput
ではその成果物はどこに保存されるのか?は
ArtifactStore
に記載されているS3ということになります。
ArtifactStore:
Location: !Ref s3bucketbackup
Type: S3
検証
では実際に検証してみましょう。
実際に適当なtest.txt
をGitHubにCommit
し、Pushしてみましょう。
1.git commitします。
$ git commit -m 'test commit'
[main xxxxx] test commit
2 files changed, 26 insertions(+), a12 deletions(-)
create mode 100644 test.txt
2.git pushします
$ git push
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 748 bytes | 748.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:Kyrieee-ops/s3-cicd.git
xxxxxx..xxxxxx main -> main
GitPushをトリガーにCodePipelineが起動します。
CodeBuildのログを確認すると、CloudFormationのBuildSpecで記述したaws cli
のs3 sync
コマンドが実行され、ローカルのGitリポジトリの内容がS3に同期されているのがわかります。
最終的にS3にtest.txtが追加されているのがわかります。
s3-pipeline-codestar.yml
の流れはこれで理解できたかと思います。
次にcodebuild.yml
の方ですが、こちらはDockerイメージをBuildを実行し、Pythonのユニットテストを実行しECRにPushされるまでの流れです。
CodeBuild (ECR Push)
DockerイメージをECRにPushする流れを今一度確認してみましょう。
構成図を再掲します。
GitHubにPushを実行すると、CodePipelineが起動し、その後CodeBuildが起動されます。
CodeBuildで実行されている内容は
-
Dockerfile
に基づいて、docker image build
が実行されimage
がPull
される -
Pull
したimage
を基にDockerfile
の内容に基づいてbuild
を実行 -
build
が完了し、コンテナが起動 - 最終的に、
build
が完了したDockerイメージをECR
にPush
する
上記の流れです。
この内容を整理しておくと、CodeBuildのBuildSpec
に何を書けば良いかが明確になるかと思います。
以下コードを掲載します。
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${CodeBuildProjectName}
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
env:
variables:
IMAGE_REPO_NAME: ${ECRRepositoryName}
IMAGE_TAG: dev
AWS_ACCOUNT_ID: ${AWS::AccountId}
AWS_DEFAULT_REGION: ${AWS::Region}
phases:
pre_build:
commands:
- echo Logging in to $AWS_DEFAULT_REGION/$AWS_ACCOUNT_ID Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker image ls
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker image ls
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image to ECR...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
variables
BuildSpec以下で先ほど定義したdocker image build
からECR Push
の内容を定義しています。
valiablesでは環境変数を定義します。
-
IMAGE_REPO_NAME: ${ECRRepositoryName}
: ECRのリポジトリ名 -
IMAGE_TAG: dev
: ECRにPushするDockerイメージのタグ名 -
AWS_ACCOUNT_ID: ${AWS::AccountId}
とAWS_DEFAULT_REGION: ${AWS::Region}
はCloudFormationの組み込み関数
BuildSpec: !Sub |
version: 0.2
env:
variables:
IMAGE_REPO_NAME: ${ECRRepositoryName}
IMAGE_TAG: dev
AWS_ACCOUNT_ID: ${AWS::AccountId}
AWS_DEFAULT_REGION: ${AWS::Region}
pre_build
phases
以下からbuildの内容が実行されます。
ここではECRリポジトリにdockerコマンドを使用してログインするコマンドを実行しています。
phases:
pre_build:
commands:
- echo Logging in to $AWS_DEFAULT_REGION/$AWS_ACCOUNT_ID Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build
Dockerfileを基にbuild
を実行しています。
以下のDockerfileのbuildを実行をします。
このDockerfileではPythonをインストールし、build実行の中でUnit Test
を実行後にECRにPushする一連の流れが実行されます。
# Amazon Linux 2023をベースにする
FROM amazonlinux:2023.6.20241111.0
# 必要なパッケージをインストール
RUN dnf install -y python3 python3-pip && \
dnf clean all
# 作業ディレクトリを設定
WORKDIR /app
# アプリケーションコードとテストコードをコピー
COPY app/ /app
# テスト用のPythonライブラリをインストール
RUN pip3 install --no-cache-dir pytest \
&& chmod +x /app/entrypoint.sh
CMD ["/bin/bash", "/app/entrypoint.sh"]
CMDでentrypoint.sh
が実行され、
#!/bin/bash
echo "Running tests..."
pytest /app --maxfail=5 --disable-warnings --verbose
if [ $? -ne 0 ]; then
echo "Tests failed. Exiting."
exit 1
fi
echo "Tests passed. Starting application..."
python3 app.py
pytest
の部分でtest_app.py
が実行され、
import pytest
import app
def test_add():
assert app.add(1, 2) == 3
assert app.add(-1, 1) == 0
assert app.add(0, 0) == 0
その後、python3 app.py
が実行され、アプリケーションファイルapp.py
が動作し、アプリケーションが実行されるという流れになります。
def add(a, b):
return a + b
yamlの話に戻り、タグをECRにPushできる状態のタグに編集をします。
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker image ls
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker image ls
post_build
post_build
でECRリポジトリにPushを実行しています。
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image to ECR...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
検証
では実際に動きを確認してみましょう。
CodePipelineの[変更をリリースする]からリリースをしてみます。
CodeBuildが実行されるとCloudFormationで定義していたBuildSpecの内容が実行されていることがCodeBuildのログから確認することができます。
[Container] 2025/01/26 06:16:49.113511 Running on CodeBuild On-demand
[Container] 2025/01/26 06:16:49.113527 Waiting for agent ping
[Container] 2025/01/26 06:16:49.315256 Waiting for DOWNLOAD_SOURCE
[Container] 2025/01/26 06:16:50.758677 Phase is DOWNLOAD_SOURCE
[Container] 2025/01/26 06:16:50.799082 CODEBUILD_SRC_DIR=/codebuild/output/src2468269984/src
[Container] 2025/01/26 06:16:50.799650 YAML location is /codebuild/readonly/buildspec.yml
[Container] 2025/01/26 06:16:50.802038 Setting HTTP client timeout to higher timeout for S3 source
[Container] 2025/01/26 06:16:50.802443 Processing environment variables
[Container] 2025/01/26 06:16:50.851902 No runtime version selected in buildspec.
[Container] 2025/01/26 06:16:50.869552 Moving to directory /codebuild/output/src2468269984/src
[Container] 2025/01/26 06:16:50.871222 Unable to initialize cache download: no paths specified to be cached
[Container] 2025/01/26 06:16:50.900340 Configuring ssm agent with target id: codebuild:70d347c0-7e64-41c1-a132-bb5a9d0e3fae
[Container] 2025/01/26 06:16:50.901217 Successfully updated ssm agent configuration
[Container] 2025/01/26 06:16:50.901738 Registering with agent
[Container] 2025/01/26 06:16:50.940839 Phases found in YAML: 3
[Container] 2025/01/26 06:16:50.940863 BUILD: 6 commands
[Container] 2025/01/26 06:16:50.940868 POST_BUILD: 3 commands
[Container] 2025/01/26 06:16:50.940872 PRE_BUILD: 2 commands
[Container] 2025/01/26 06:16:50.941301 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED
[Container] 2025/01/26 06:16:50.941391 Phase context status code: Message:
[Container] 2025/01/26 06:16:51.011590 Entering phase INSTALL
[Container] 2025/01/26 06:16:51.052717 Phase complete: INSTALL State: SUCCEEDED
[Container] 2025/01/26 06:16:51.052740 Phase context status code: Message:
[Container] 2025/01/26 06:16:51.086807 Entering phase PRE_BUILD
[Container] 2025/01/26 06:16:51.123739 Running command echo Logging in to $AWS_DEFAULT_REGION/$AWS_ACCOUNT_ID Amazon ECR...
Logging in to ap-northeast-1/アカウントID Amazon ECR...
[Container] 2025/01/26 06:16:51.127508 Running command aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[Container] 2025/01/26 06:17:06.298072 Phase complete: PRE_BUILD State: SUCCEEDED
[Container] 2025/01/26 06:17:06.298089 Phase context status code: Message:
[Container] 2025/01/26 06:17:06.347421 Entering phase BUILD
[Container] 2025/01/26 06:17:06.348824 Running command echo Build started on `date`
Build started on Sun Jan 26 06:17:06 UTC 2025
[Container] 2025/01/26 06:17:06.393993 Running command echo Building the Docker image...
Building the Docker image...
[Container] 2025/01/26 06:17:06.397646 Running command docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
問題なく実行されると、CodePipelineとCodeBuildが成功ステータスが表示されています。
最後に
今回CodePipelineとCodeBuildについてまとめてみました。
ECRへのPushまでの流れについてまとめてみました。
EKSやECSに実際にデプロイする部分についても今後はまとめて良ければとおもっておr
参考