AWS

CodePipelineを使ってCodeCommitプッシュをトリガーにしてECRにイメージをプッシュする

スポンサーリンク
CodePipelineを使ってCodeCommitプッシュをトリガーにしてECRにイメージをプッシュする AWS
スポンサーリンク
スポンサーリンク

CodePipelineを使ってCodeCommitプッシュをトリガーにしてECRにイメージをプッシュする

CodePipelineを使用することによって、CI/CD構成を構築することができます。

今回はCodeCommitにコードをプッシュした際に、CodeBuildでDockerイメージをビルドし、ECRにプッシュするパイプラインを構成すること目指します。

構築する環境

Diagram of use CodePipeline to trigger CodeCommit pushes to push images to 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テンプレート等を配置しています。

awstut-fa/075 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

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公式ページを参考にして記載しました。

チュートリアル: CodePipeline を使用した Amazon ECS 標準デプロイ - AWS CodePipeline
このチュートリアルでは、CodePipeline を使用して Amazon ECS で完全なエンドツーエンドの継続的デプロイメント (CD) パイプラインを作成する方法を説明します。

1つ変更点があります。イメージをビルドする時に、DockerHubからベースイメージをプルしますが、DockerHubにサインインする処理を加えた点です。サインインなしでDockerHubからイメージをプルしようとすると、プルに失敗する場合があるためです。以下のページを参考にして記載しました。

“Too Many Requests.” でビルドが失敗する…。AWS CodeBuild で IP ガチャを回避するために Docker Hub ログインしよう!という話 | DevelopersIO
Docker Hubのダウンロード回数制限、もうはじまってたんですね…。

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公式ページを参考にして設定します。

CodeCommit - AWS CodePipeline
で設定されたで新しいコミットが行われたときに、パイプラインを開始します。 CodeCommit リポジトリとブランチ。

ActionTypeIdプロパティで、CodeCommitがパイプラインのソースとなるように設定します。
Configurationプロパティで、ソースとしてCodeCommitを扱う上でのパラメータを設定します。RepositoryNameおよびBranchNameプロパティで、参照するCodeCommitリポジトリ名およびブランチ名を指定します。OutputArtifactFormatプロパティはデフォルトの「CODE_ZIP」を指定します。PollForSourceChangesプロパティはパイプラインを開始するきっかけに関するパラメータです。これをtrueとすると、CodePipelineからCodeCommitへポーリングすることで、パイプラインを開始すべきアクティビティを検出します。今回は後述のEventBridgeを使用してアクティビティを検出するため、本パラメータはfalseとします。
OutputArtifactsプロパティはソースステージで生成したアーティファクト名に関するパラメータです。

2つ目のステージはビルドステージです。
以下のAWS公式ページを参考にして設定します。

AWS CodeBuild - AWS CodePipeline
パイプラインの一部としてビルドとテストを実行できます。を走らせた時 CodeBuild ビルドまたはテストアクション、ビルド仕様で指定されたコマンドは CodeBuild コンテナ。への入力アーティファクトとして指定されたすべてのアーティファクト CodeBuild アクションは、コマンドを実行するコンテナ内で使用でき...

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公式ページを参考にして設定します。

の作成 CloudWatch のイベントルール CodeCommit ソース (AWS CloudFormationテンプレート) - AWS CodePipeline
テンプレートの Resources で、 AWS::IAM::Role AWS CloudFormation リソースを使用して、イベントにパイプラインの開始を許可する IAM ロールを設定します。このエントリによって、2 つのポリシーを使用するロールが作成されます。 最初のポリシーでは、ロールを引き受けることを許可し...

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を確認します。

Detail of ECR 1.

空です。
パイプラインが実行されることによって、ここにイメージがプッシュされることになります。

CodeCommitを確認します。

Detail of CodeCommit 1.

こちらも空です。
ここにコードをプッシュすることによって、パイプラインが実行されることになります。

CodeBuildを確認します。

Detail of CodeBuild Settings 1.
Detail of CodeBuild Settings 2.

CloudFormationテンプレートで指定した通りに、CodeBuildが作成されています。

CodePipelineを確認します。

Result of CodePipeline 1.

パイプラインの実行に失敗しています。
これは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を確認します。

Detail of CodeCommit 2.

確かに2ファイルがプッシュされています。

CodePipelineが実行が開始されました。
しばらく待機します。

Result of CodePipeline 2.

パイプラインの実行が正常に完了しました。

CodeBuildの実行履歴を確認します。

Result of CodeBuild execution.

確かに正常にビルドが実行されています。

ECRを確認します。

Detail of ECR 2.

イメージがプッシュされています。
CodeBuildによってビルドされたイメージが、ECRにプッシュされたということです。

このようにCodePipelineでパイプラインを構成することで、CodeCommitにプッシュしたコードをソースとして、CodeBuildでDockerイメージをビルドし、ECRにイメージをプッシュすることができます。

まとめ

CodePipelineを使って、CodeCommitにコードをプッシュした際に、CodeBuildでDockerイメージをビルドし、ECRにプッシュするパイプラインを構成する方法を確認しました。

タイトルとURLをコピーしました