継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

『AWS Copilot』で ECS の CI/CD パイプラインをサクッと作る

タダです.

AWS Copilot」のコマンドでcopilot pipelineからはじまるコマンドがあります.このコマンドでは,GitHub にコードをプッシュした時に CodeBuild,CodePipeline によるリリースパイプラインを作ることができます.この記事では,copilot pipelineコマンドを使ってパイプラインの作成とパイプラインでの ECS アプリケーションのデプロイをやってみます.なお,バージョンはv0.2.0で動作確認してます.

» copilot version
version: v0.2.0, built for darwin

パイプラインの構造

AWS Copilot」で作られるパイプラインの構造は3つあります.

  1. デフォルトですと, master ブランチへのプッシュをトリガーにして GitHub からソースコードを取得するステージ
  2. コンテナイメージがビルドされ,ECR リポジトリにイメージを格納するステージ
  3. ECR アプリケーションのデプロイステージ

パイプラインに関するドキュメント github.com

なお,GitHub と CodePipeline の連携は GitHub のアクセストークンを介して行います.発行していない場合は下記ドキュメントを参考にrepoおよびadmin:repo_hookの権限を与えてアクセストークンを準備しておいてください.

docs.aws.amazon.com

ローカルでのセットアップ

まずは,ローカルでパイプライン作成に必要なセットアップを行います.予めデプロイしたいサービスは準備しているディレクトリまで移動し,copilot pipeline initコマンドを実行します.いくつかの質問にウィザードに沿って回答します.この工程では①アプリケーションをデプロイするステージの指定,②CodePipeline と連携する GitHub リポジトリの指定,③GitHub のアクセストークンを Secret Manager へ保存,④パイプライン構成のための各種ファイル生成が行われています.

» copilot pipeline init                                          
Would you like to add an environment to your pipeline? Yes
Which environment would you like to add to your pipeline? test
Which GitHub repository would you like to use for your service? git@github.com:tasogare0919/copilot-sandbox
Please enter your GitHub Personal Access Token for your repository copilot-sandbox: 
✔ Created the secret github-token-sample-app-copilot-sandbox for pipeline source stage!
✔ Wrote the pipeline manifest for copilot-sandbox at 'copilot/pipeline.yml'
The manifest contains configurations for your CodePipeline resources, such as your pipeline stages and build steps.
✔ Wrote the buildspec for the pipeline's build stage at 'copilot/buildspec.yml'
The buildspec contains the commands to build and push your container images to your ECR repositories.

Recommended follow-up actions:
- Commit and push the generated buildspec and manifest file.
- Update the build phase of your buildspec to unit test your services before pushing the images.
- Update your pipeline manifest to add additional stages.
- Run `copilot pipeline update` to deploy your pipeline for the repository.

copilot pipeline initコマンド実行によりcopilotディレクトリ配下にbuildspec.ymlpipeline.ymlファイルが生成されます.

» tree                
.
├── copilot
│   ├── buildspec.yml
│   └── pipeline.yml

各ファイルの中身をみてみます.pipeline.ymlはパイプラインのためのマニフェストファイルです.マニフェストファイルでは大きく3つのセクションがあり,パイプライン名を指定している箇所・リピジトリに関する情報の箇所・デプロイステージの箇所です.ファイルの中身は変更できますが,更新する場合はcopilot pipeline updateを行います.

# This YAML file defines the relationship and deployment ordering of your environments.
# The name of the pipeline
name: pipeline-sample-app-tasogare0919-copilot-sandbox

# The version of the schema used in this template
version: 1

# This section defines the source artifacts.
source:
  # The name of the provider that is used to store the source artifacts.
  provider: GitHub
  # Additional properties that further specifies the exact location
  # the artifacts should be sourced from. For example, the GitHub provider
  # has the following properties: repository, branch.
  properties:
    access_token_secret: github-token-sample-app-copilot-sandbox
    branch: master
    repository: https://github.com/tasogare0919/copilot-sandbox

# The deployment section defines the order the pipeline will deploy
# to your environments.
stages:
    - # The name of the environment to deploy to.
      name: test
      # Optional: use test commands to validate this stage of your build.
      # test_commands: [echo 'running tests', make test]

buildspec.ymlは CodeBuild でのビルド実行に関する定義ファイルです.ECR へのコンテナイメージビルドと格納を行なっています.

# Buildspec runs in the build stage of your pipeline.
version: 0.2
phases:
  install:
    runtime-versions:
      docker: 18
      ruby: 2.6
    commands:
      - echo "cd into $CODEBUILD_SRC_DIR"
      - cd $CODEBUILD_SRC_DIR
      # Download the copilot linux binary.
      - wget https://ecs-cli-v2-release.s3.amazonaws.com/copilot-linux-v0.2.0
      - mv ./copilot-linux-v0.2.0 ./copilot-linux
      - chmod +x ./copilot-linux
  build:
    commands:
      - echo "Run your tests"
      # - make test
  post_build:
    commands:
      - ls -l
      - export COLOR="false"
      # Find all the local services in the workspace.
      - svcs=$(./copilot-linux svc ls --local --json | jq '.services[].name' | sed 's/"//g')
      # Find all the environments.
      - envs=$(./copilot-linux env ls --json | jq '.environments[].name' | sed 's/"//g')
      # Generate the cloudformation templates.
      # The tag is the build ID but we replaced the colon ':' with a dash '-'.
      - tag=$(sed 's/:/-/g' <<<"$CODEBUILD_BUILD_ID")
      - >
        for env in $envs; do
          for svc in $svcs; do
          ./copilot-linux svc package -n $svc -e $env --output-dir './infrastructure' --tag $tag;
          done;
        done;
      - ls -lah ./infrastructure
      # If addons exists, upload addons templates to each S3 bucket and write template URL to template config files.
      - |
        for svc in $svcs; do
          ADDONSFILE=./infrastructure/$svc.addons.stack.yml
          if [ -f "$ADDONSFILE" ]; then
            tmp=$(mktemp)
            timestamp=$(date +%s)
            aws s3 cp "$ADDONSFILE" "s3://xxxx/manual/$timestamp/$svc.addons.stack.yml";
            jq --arg a "https://xxxxs3-ap-northeast-1.amazonaws.com/manual/$timestamp/$svc.addons.stack.yml" '.Parameters.AddonsTemplateURL = $a' ./infrastructure/$svc-test.params.json > "$tmp" && mv "$tmp" ./infrastructure/$svc-test.params.json
          fi
        done;
      # Build images
      # - For each manifest file:
      #   - Read the path to the Dockerfile by translating the YAML file into JSON.
      #   - Run docker build.
      #   - For each environment:
      #     - Retrieve the ECR repository.
      #     - Login and push the image.
      - >
        for svc in $svcs; do
          for df_rel_path in $(cat $CODEBUILD_SRC_DIR/copilot/$svc/manifest.yml | ruby -ryaml -rjson -e 'puts JSON.pretty_generate(YAML.load(ARGF))' | jq '.image.build' | sed 's/"//g'); do
          df_path=$CODEBUILD_SRC_DIR/$df_rel_path
          df_dir_path=$(dirname "$df_path")
          docker build -t $svc:$tag -f $df_path $df_dir_path;
          image_id=$(docker images -q $svc:$tag);
            for env in $envs; do
            repo=$(cat $CODEBUILD_SRC_DIR/infrastructure/$svc-$env.params.json | jq '.Parameters.ContainerImage' | sed 's/"//g');
            region=$(echo $repo | cut -d'.' -f4);
            $(aws ecr get-login --no-include-email --region $region);
            docker tag $image_id $repo;
            docker push $repo;
            done;
          done;
        done;
artifacts:
  files:
    - "infrastructure/*"

ローカルでのセットアップが完了なので次はパイプラインの作成に移るのですが,予めこれらのファイルを GitHub リポジトリにを上げておきます.

AWS 環境にパイプラインの作成及び実行

パイプラインの作成及び更新にはcopilot pipeline updateで行います.コマンド実行後,CloudFormation スタックが作成されて CodePipeline と CodeBuild が作成されます.

» copilot pipeline update
✔ Successfully added pipeline resources to your application: sample-app
✔ Successfully created a new pipeline: pipeline-sample-app-tasogare0919-copilot-sandbox

f:id:sadayoshi_tada:20200807075903p:plain

スタックが作成された後に,パイプラインが動作します.僕はcopillot pipeline update実行前にコードを GitHub リポジトリにプッシュし忘れていたので一度処理が失敗しましたが,コードが上がっていればパイプラインは全ての処理をパスしました.

f:id:sadayoshi_tada:20200807080041p:plain

なお,AWS マネジメントコンソールでも確認する以外にもcopilot pipeline showcopilot pipeline statusでもパイプラインの実行状況を確認できます.

» copilot pipeline show  
About

  Name              pipeline-sample-app-tasogare0919-copilot-sandbox
  Region            ap-northeast-1
  AccountID         XXXXXXXXXXXX
  Created At        40 minutes ago
  Updated At        40 minutes ago

Stages

  Name              Category            Provider            Details
  ----              ----                ----                ----
  Source            Source              GitHub              Repository: tasogare0919/copilot-sandbox
  Build             Build               CodeBuild           BuildProject: pipeline-sample-app-tasogare0919-copilot-sandbox-BuildProject
  DeployTo-test     Deploy              CloudFormation      StackName: sample-app-test-front-end

» copilot pipeline status
Pipeline Status

Stage                              Transition          Status
-----                              ----------          ------
Source                             ENABLED             Succeeded
└── SourceCodeFor-sample-app                           Succeeded
Build                              ENABLED             Succeeded
└── Build                                              Succeeded
DeployTo-test                      ENABLED             Succeeded
└── CreateOrUpdate-front-end-test                      Succeeded

Last Deployment

  Updated At    38 minutes ago

自動テストステージの追加

パイプラインでは,デプロイした ECS アプリケーションに対してテストを入れていくことも考えられます.「AWS Copilot」でも自動テストのステージを追加できます.以下の定義では,make testコマンドを実行するだけのものですが,独自のテストコマンドを定義できます.この状態でcopilot pipeline updateを実行すると,テスト用の CodeBuild プロジェクトとデプロイパイプラインにテスト処理が追加されます.

# This YAML file defines the relationship and deployment ordering of your environments.

# The name of the pipeline
name: pipeline-sample-app-tasogare0919-copilot-sandbox

# The version of the schema used in this template
version: 1

# This section defines the source artifacts.
source:
  # The name of the provider that is used to store the source artifacts.
  provider: GitHub
  # Additional properties that further specifies the exact location
  # the artifacts should be sourced from. For example, the GitHub provider
  # has the following properties: repository, branch.
  properties:
    access_token_secret: github-token-sample-app-copilot-sandbox
    branch: master
    repository: https://github.com/tasogare0919/copilot-sandbox

# The deployment section defines the order the pipeline will deploy
# to your environments.
stages:
    - # The name of the environment to deploy to.
      name: test
      # Optional: use test commands to validate this stage of your build.
      test_commands: <= 更新
        - echo 'running tests' <= 更新
        - make test <= 更新
        - echo 'tests passed' <= 更新

AWS 環境のパイプラインを削除

作成したパイプラインの削除するにはcopilot pipeline deleteで行います.Secret Manager の情報も消えます.

» copilot pipeline delete
Are you sure you want to delete pipeline pipeline-sample-app-tasogare0919-copilot-sandbox from application sample-app? Yes
Are you sure you want to delete the source secret github-token-sample-app-copilot-sandbox associated with pipeline pipeline-sample-app-tasogare0919-copilot-sandbox? Yes
✔ Deleted secret github-token-sample-app-copilot-sandbox.
✔ Deleted pipeline pipeline-sample-app-tasogare0919-copilot-sandbox from application sample-app.

まとめ

AWS Copilot」での CI/CD パイプラインを行うコマンドとどのようにデプロイをしていくかを見てきました.ドキュメントにも記載があったのですが,リリースプロセスの構成をできるだけ簡単にしたいというメッセージの通りで体感としてもパイプラインの作成とデプロイがコマンド数回で実現できてしまうため簡単でした.リリースの戦略や方針にもよると思いますが,気になる方は試しに使ってみて検討してみてほしいです!

関連記事

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com