CodePipelineを使ってCodeCommitプッシュをトリガーにしてECRにイメージをプッシュする
CodePipelineを使用することによって、CI/CD構成を構築することができます。
今回はCodeCommitにコードをプッシュした際に、CodeBuildでDockerイメージをビルドし、ECRにプッシュするパイプラインを構成すること目指します。
構築する環境
CodePipelineを構成し、2つのリソースを連携させます。
ソースとビルドの2つのステージを定義します。
1つ目はCodeCommitです。
Gitリポジトリとして使用します。
2つ目はCodeBuildです。
CodeCommitにプッシュされたコードから、Dockerイメージをビルドします。
ビルドしたイメージをECRにプッシュします。
S3バケットを作成します。
CodePipelineで生成されるアーティファクトを格納するために使用します。
SSMパラメータストアにDockerHubアカウント情報を保存します。
DockerBuildでイメージを生成する際に、DockerHubにサインインした上でベースイメージをプルするために、これらを使用します。
CodePipelineが開始されるきっかけですが、CodeCommitへのプッシュを条件とします。
具体的には、EventBridgeに上記を満たすルールを用意します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレート等を配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/075
テンプレートファイルのポイント解説
S3バケット
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref Prefix
AccessControl: Private
Code language: YAML (yaml)
CodePipelineで生成されるアーティファクトを格納するバケットです。
特別な設定は不要です。
SSMパラメータストア
Resources:
SSMParameterDockerHubPassword:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub "${Prefix}-DockerHubPassword"
Type: String
Value: !Ref DockerHubPassword
SSMParameterDockerHubUsername:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub "${Prefix}-DockerHubUsername"
Type: String
Value: !Ref DockerHubUsername
Code language: YAML (yaml)
DockerHubにサインインするためのアカウント情報です。
ユーザ名・パスワードをパラメータとして登録します。
ECR
Resources:
ECRRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: !Ref Prefix
Code language: YAML (yaml)
自作イメージを保存するリポジトリです。
特別な設定は不要です。
CodeCommit
Resources:
CodeCommitRepository:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName: !Ref Prefix
Code language: YAML (yaml)
コードリポジトリです。
特別な設定は不要です。
CodeBuild
Resources:
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Cache:
Type: NO_CACHE
Environment:
ComputeType: !Ref ProjectEnvironmentComputeType
EnvironmentVariables:
- Name: DOCKERHUB_PASSWORD
Type: PARAMETER_STORE
Value: !Ref SSMParameterDockerHubPassword
- Name: DOCKERHUB_USERNAME
Type: PARAMETER_STORE
Value: !Ref SSMParameterDockerHubUsername
Image: !Ref ProjectEnvironmentImage
ImagePullCredentialsType: CODEBUILD
Type: !Ref ProjectEnvironmentType
PrivilegedMode: true
LogsConfig:
CloudWatchLogs:
Status: DISABLED
S3Logs:
Status: DISABLED
Name: !Ref Prefix
ServiceRole: !GetAtt CodeBuildRole.Arn
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws --version
- aws ecr get-login-password --region ${AWS::Region} | docker login --username AWS --password-stdin ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com
- REPOSITORY_URI=${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryName}
- COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- IMAGE_TAG=${!COMMIT_HASH:=latest}
- echo Logging in to Docker Hub...
- echo $DOCKERHUB_PASSWORD | docker login -u $DOCKERHUB_USERNAME --password-stdin
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $REPOSITORY_URI:latest .
- docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker images...
- docker push $REPOSITORY_URI:latest
- docker push $REPOSITORY_URI:$IMAGE_TAG
Visibility: PRIVATE
Code language: YAML (yaml)
ポイントとなるパラメータを取り上げます。
Artifactsプロパティで、CodeBuildで生成されるアーティファクトに関する設定を行います。
内部のTypeプロパティでアーティファクトのタイプを設定します。
今回はCodeBuildを後述のCodePipeline内で実行するため、「CODEPIPELINE」を指定します。
Environmentプロパティでビルド環境を定義します。
ComputeTypeプロパティで、ビルド環境に割り当てるリソースを設定します。今回は最小環境を意味する「BUILD_GENERAL1_SMALL」を指定し、メモリ3GB、2vCPUのリソースを確保します。
EnvironmentVariablesプロパティで、ビルド環境用の環境変数を定義できます。今回は先述の2つのSSMパラメータストアの値を環境変数として設定します。Nameプロパティで変数名を指定できます。Typeプロパティに「PARAMETER_STORE」を指定することで、SSMパラメータストアの値を参照できます。Valueプロパティに参照するSSMパラメータストアのパラメータ名を指定します。
Imageプロパティで、ビルド環境として使用するDockerイメージを設定します。今回は「aws/codebuild/amazonlinux2-aarch64-standard:2.0」を指定し、ARM版Amazon Linux 2のイメージを使用します。
ImagePullCredentialsTypeプロパティで、ビルド環境用イメージをプルするために使用するクリデンシャルタイプを設定します。先述のCodeBuild用イメージを使用する場合は、「CODEBUILD」を指定します。
PrivilegedModeプロパティはtrueを設定します。Dockerコンテナ内でDockerデーモンを実行できるようにするためです。これはDockerイメージをビルドするために必要です。
Typeプロパティでビルド環境のタイプを設定します。今回は「ARM_CONTAINER」を指定し、ARM版コンテナを使用します。
Sourceプロパティでビルドで使用するソースコードや、ビルド仕様ファイル(buildspec.yml)に関する設定です。
Typeプロパティでソースコードのリポジトリのタイプを設定します。今回はCodePipelineの中でCodeBuildを使用しますので、「CODEPIPELINE」を指定します。
BuildSpecプロパティはbuildspec.ymlに関するパラメータです。今回は本プロパティにbuildspec.ymlの内容を直接記載します。内容はAWS公式ページを参考にして記載しました。
1つ変更点があります。イメージをビルドする時に、DockerHubからベースイメージをプルしますが、DockerHubにサインインする処理を加えた点です。サインインなしでDockerHubからイメージをプルしようとすると、プルに失敗する場合があるためです。以下のページを参考にして記載しました。
https://dev.classmethod.jp/articles/codebuild-has-to-use-dockerhub-login-to-avoid-ip-gacha/
CodeBuild用のIAMロールを確認します。
Resources:
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
Policies:
- PolicyName: PipelineExecutionPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ssm:GetParameters
Resource:
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SSMParameterDockerHubPassword}"
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SSMParameterDockerHubUsername}"
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
Resource:
- !Sub "arn:aws:s3:::${BucketName}"
- !Sub "arn:aws:s3:::${BucketName}/*"
Code language: YAML (yaml)
ECRにイメージをプッシュするためにAWS管理ポリシーAmazonEC2ContainerRegistryPowerUserをアタッチします。
環境変数を設定する際に、SSMパラメータストアからパラメータを取得しますので、これを許可します。
イメージをビルドする際に、S3バケットに格納されたCodePipelineアーティファクトを取得しますので、これを許可します。
CodePipeline
Resources:
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref BucketName
Type: S3
Name: !Ref Prefix
RoleArn: !GetAtt CodePipelineRole.Arn
Stages:
- Actions:
- ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
BranchName: !Ref BranchName
OutputArtifactFormat: CODE_ZIP
PollForSourceChanges: false
RepositoryName: !GetAtt CodeCommitRepository.Name
Name: SourceAction
OutputArtifacts:
- Name: !Ref PipelineSourceArtifact
Region: !Ref AWS::Region
RunOrder: 1
Name: Source
- Actions:
- ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: !Ref PipelineSourceArtifact
Name: Build
OutputArtifacts:
- Name: !Ref PipelineBuildArtifact
Region: !Ref AWS::Region
RunOrder: 1
Name: Build
Code language: YAML (yaml)
ポイントとなるパラメータを取り上げます。
ArtifactStoreプロパティで、パイプラインを実行中に生成されるアーティファクトを格納するS3バケットを指定します。
Stagesプロパティで、パイプラインを構成するステージを定義します。
今回は2つのステージを定義します。
1つ目のステージはソースステージです。
以下のAWS公式ページを参考にして設定します。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/action-reference-CodeCommit.html
ActionTypeIdプロパティで、CodeCommitがパイプラインのソースとなるように設定します。
Configurationプロパティで、ソースとしてCodeCommitを扱う上でのパラメータを設定します。RepositoryNameおよびBranchNameプロパティで、参照するCodeCommitリポジトリ名およびブランチ名を指定します。OutputArtifactFormatプロパティはデフォルトの「CODE_ZIP」を指定します。PollForSourceChangesプロパティはパイプラインを開始するきっかけに関するパラメータです。これをtrueとすると、CodePipelineからCodeCommitへポーリングすることで、パイプラインを開始すべきアクティビティを検出します。今回は後述のEventBridgeを使用してアクティビティを検出するため、本パラメータはfalseとします。
OutputArtifactsプロパティはソースステージで生成したアーティファクト名に関するパラメータです。
2つ目のステージはビルドステージです。
以下のAWS公式ページを参考にして設定します。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/action-reference-CodeBuild.html
ActionTypeIdプロパティで、パイプラインにおいてCodeBuildがビルドを実行するように設定します。
Configurationプロパティは、CodeBuildを使ってビルドするためのパラメータです。ProjectNameプロパティでパイプラインに関連づけるCodeBuildを指定します。
InputArtifactsプロパティで、ビルドする上で使用するアーティファクトの名前を指定します。ポイントはソースステージで指定したOutputArtifactsプロパティの値と同一にする必要があるという点です。ソースステージで生成されたアーティファクトを使ってビルドするためです。
OutputArtifactsプロパティはビルドステージで生成したアーティファクト名に関するパラメータです。
CodePipeline用のIAMロールを確認します。
Resources:
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: PipelinePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- codecommit:CancelUploadArchive
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:GetRepository
- codecommit:GetUploadArchiveStatus
- codecommit:UploadArchive
Resource:
- !GetAtt CodeCommitRepository.Arn
- Effect: Allow
Action:
- codebuild:BatchGetBuilds
- codebuild:StartBuild
- codebuild:BatchGetBuildBatches
- codebuild:StartBuildBatch
Resource:
- !GetAtt CodeBuildProject.Arn
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
Resource:
- !Sub "arn:aws:s3:::${BucketName}"
- !Sub "arn:aws:s3:::${BucketName}/*"
Code language: YAML (yaml)
ソースステージでCodeCommitからソースコードをプルするため、これを可能にする権限を付与します。
ビルドステージでCodeBuildを使用してビルドを行うため、ビルドを実行する権限を付与します。
パイプライン実行中に、S3バケット上のアーティファクトを取得・保存しますので、これを許可します。
EventBridge
Resources:
EventsRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- CodeCommit Repository State Change
resources:
- !GetAtt CodeCommitRepository.Arn
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- !Ref BranchName
Name: !Ref Prefix
Targets:
- Arn: !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}"
Id: !Sub "${Prefix}-CodePipeline-CodeCommit"
RoleArn: !GetAtt EventsRuleRole.Arn
Code language: YAML (yaml)
CodePipelineが開始するきっかけとして、EventBridgeを使用します。
以下のAWS公式ページを参考にして設定します。
EventPatternプロパティで検知するイベントを指定します。今回はソースであるCodeCommitにブランチが作成、または更新されたことを検知するように設定します。
Targetsプロパティで通知先のリソースを指定します。今回はCodePipelineを指定し、パイプラインを開始させます。
EventBridge用のIAMロールを確認します。
Resources:
EventsRuleRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: PipelineExecutionPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- codepipeline:StartPipelineExecution
Resource:
- !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}"
Code language: YAML (yaml)
CodePipelineのパイプライン実行を許可する設定を付与します。
アプリコンテナ
Dockerfile
FROM amazonlinux
RUN yum update -y && yum install python3 python3-pip -y
RUN pip3 install bottle
COPY main.py ./
CMD ["python3", "main.py"]
EXPOSE 8080
Code language: Dockerfile (dockerfile)
アプリコンテナ用のイメージは、Amazon Linux 2をベースとして作成します。
Python製WebフレームワークのBottleを使用します。
ですからPythonおよびpipをインストール後、これをインストールします。
アプリロジックを記載したPythonスクリプト(main.py)をコピーし、これを実行するように設定します。
先述の通り、アプリはtcp/8080でHTTPリクエストを待ち受けますので、このポートを公開します。
main.py
from bottle import route, run
@route('/')
def hello():
return 'Hello CodePipeline!'
if __name__ == '__main__':
run(host='0.0.0.0', port=8080)
Code language: Python (python)
Bottleを使用して、簡易的なWebサーバを構築します。
8080/tcpでHTTPリクエストを待ち受け、「Hello CodePipeline!」を返すという単純な構成です。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ECR:fa-075
- CodeCommit:fa-075
- CodeBuildプロジェクト:fa-075
- CodePipeline:fa-075
- SSMパラメータのパラメータ名1:fa-075-DcokerHubUsername
- SSMパラメータのパラメータ名2:fa-075-DcokerHubPassword
作成されたリソースをAWS Management Consoleから確認します。
ECRを確認します。
空です。
パイプラインが実行されることによって、ここにイメージがプッシュされることになります。
CodeCommitを確認します。
こちらも空です。
ここにコードをプッシュすることによって、パイプラインが実行されることになります。
CodeBuildを確認します。
CloudFormationテンプレートで指定した通りに、CodeBuildが作成されています。
CodePipelineを確認します。
パイプラインの実行に失敗しています。
これはCloudFormationスタック作成時に、CodeCommitおよびその内部にブランチが作成されたことがきっかけとして、パイプラインが実行されたためです。
現時点ではCodeCommitにコードをプッシュしていないため、パイプライン実行の過程でエラーが発生しました。
動作確認
準備が整いましたので、CodeCommitにコードをプッシュします。
まずCodeCommitをプルします。
$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/fa-075
Cloning into 'fa-075'...
warning: You appear to have cloned an empty repository.
Code language: Bash (bash)
空のリポジトリがプルされました。
リポジトリにDockerfileおよびmain.pyを加えます。
$ ls -al fa-075
total 8
drwxrwxr-x 3 ec2-user ec2-user 51 Aug 11 23:53 .
drwxrwxr-x 3 ec2-user ec2-user 20 Aug 11 23:53 ..
-rw-rw-r-- 1 ec2-user ec2-user 186 Aug 11 07:02 Dockerfile
drwxrwxr-x 7 ec2-user ec2-user 119 Aug 11 23:52 .git
-rw-rw-r-- 1 ec2-user ec2-user 630 Aug 11 07:42 main.py
Code language: Bash (bash)
2ファイルをCodeCommitにプッシュします。
$ git add .
$ git commit -m "first commit"
[master (root-commit) 2596718] first commit
...
2 files changed, 37 insertions(+)
create mode 100644 Dockerfile
create mode 100644 main.py
$ git push
...
* [new branch] master -> master
Code language: JavaScript (javascript)
正常にプッシュできました。
改めてCodeCommitを確認します。
確かに2ファイルがプッシュされています。
CodePipelineが実行が開始されました。
しばらく待機します。
パイプラインの実行が正常に完了しました。
CodeBuildの実行履歴を確認します。
確かに正常にビルドが実行されています。
ECRを確認します。
イメージがプッシュされています。
CodeBuildによってビルドされたイメージが、ECRにプッシュされたということです。
このようにCodePipelineでパイプラインを構成することで、CodeCommitにプッシュしたコードをソースとして、CodeBuildでDockerイメージをビルドし、ECRにイメージをプッシュすることができます。
まとめ
CodePipelineを使って、CodeCommitにコードをプッシュした際に、CodeBuildでDockerイメージをビルドし、ECRにプッシュするパイプラインを構成する方法を確認しました。