Github ActionsでのECSへの手動デプロイ

こんにちは。 エキサイト株式会社の三浦です。

アプリケーションのデプロイ方法は、エンジニアなら誰もが頭を悩ませるものでしょう。 コマンドラインAWS CodePipeline、Jenkinsなど、様々な方法が考えられます。

今回は、ECSへのデプロイ方法としてGithub Actionsを選択したときの方法と注意点について説明していきます。

Github Actions

Github Actionsは、Github上で任意の処理を実行できる機能です。 公式には以下のように説明されています。

Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you'd like, including CI/CD, and combine actions in a completely customized workflow.

手動実行はもちろん、指定時間に実行したり、ブランチやプルリクエストに何かしらのアクションがあったタイミングで自動実行させることもできます。 例えば

  • デフォルトブランチにブランチがマージされたら、自動的にタグを切る
  • プルリクエストが新しく作られたら、ユニットテストを実行する

などが可能です。

今回やりたいのは、「手動実行」でECSにデプロイする方法となります。

ECSへのデプロイ

Github Actionsは、 .github/workflows ディレクトリ配下に実行処理を書いたYAMLファイルを置くことで設定することができます。 Github Actionsの実行単位は workflow と呼ばれ、このディレクトリ配下に置かれる1ファイルごとに1workflowが設定されることになります。

ですが、ファイル作成前にまずは下準備をします。

AWS環境

今回はECSへのデプロイということで、関連する以下のようなAWSの環境を用意しておく必要があります。

  • ECR
  • ECS
  • コード上からAWSへアクセスするためのIAMユーザ

Secretsの設定

AWSへのアクセス用のキーやSlack通知用のWebhookなどは、セキュリティ性の高いものであるため、Githubのバージョン管理に含めるべきではありません。 Github Actionsでは、Githubの「Setting -> Secrets」にて、そういった文字列を外部に見られないように保存することができます。

保存した文字列は、Github Actions実行時に ${{ secrets.保存したキー }} の形式で参照することができます。

今回は、

を作成します。

なおSlack通知はデプロイに必須ではありませんが、これがあるとデプロイの運用が容易になるため今回は含めています。

Task Definition

ECSに使用するTask Definition用のJSONファイルも用意しておく必要があります。 イメージ名は後から上書きするので適当で大丈夫ですが、それ以外については適切なファイルを作っておきます。

設定ファイル

ここまで準備できたら、ようやくGithub Actionsの設定ファイルを作成します。

name: Deploy

# 同じ文字列がconcurrencyに指定されているworkflowは、二重で実行できなくなります
concurrency: deploy

# 手動実行をさせるため、workflow_dispatchを指定してください
on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      ECR_REPOSITORY: sample-ecr
      CLUSTER_NAME: sample-cluster
      SERVICE_NAME: sample-service
      TASK_DEFINITION: task-definition.json
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      # AWS環境へデプロイするため、認証を行います
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      # Dockerイメージをビルドし、ECRにpushします。また、outputでイメージ名を出力します
      # イメージのタグ名にはコミットSHAを使用します
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          IMAGE_NAME: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $IMAGE_NAME:$IMAGE_TAG -t $IMAGE_NAME:latest .
          docker push $IMAGE_NAME:$IMAGE_TAG
          docker push $IMAGE_NAME:latest
          echo "::set-output name=image::$IMAGE_NAME:latest"

      # 出力されたイメージ名で、 あらかじめ作成しておいたTask DefinitionのJSONファイルのイメージ名を上書きします
      - name: Fill the Amazon ECS task definition with image ID
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ env.TASK_DEFINITION }}
          container-name: sample
          image: ${{ steps.build-image.outputs.image }}

      # 作成したTaskDefinitionを元にECSへのデプロイを行います
      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          cluster: ${{ env.CLUSTER_NAME }}
          service: ${{ env.SERVICE_NAME }}
          wait-for-service-stability: true

      # Slackへ通知します
      - name: Slack Notification
        if: always()
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_CHANNEL: '#sample'
          SLACK_COLOR: ${{ (job.status == 'success' && 'good') || 'danger' }}
          SLACK_TITLE: "[${{ github.repository }}] にデプロイしました"
          SLACK_MESSAGE: "デプロイ結果:${{ job.status }}"
          SLACK_USERNAME: github actions
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          MSG_MINIMAL: ref,actions url

これで、準備は完了です。

あとは、Githubの「Actions」から手動で実行すればデプロイが可能です。

注意点

Github Actionsで手動実行する際、いくつか注意点があります。

ブランチしか選択できない(解決済み!)

かつては手動実行する際に選択できるのはブランチのみでしたが、最近になってタグを選択できるようになりました! 特にデプロイではタグによって制御している場合も多いと思うので、これは非常に嬉しいアップデートです!

デフォルトブランチにマージされないと実行できない

手動実行する場合は、デフォルトブランチにYAMLファイルがマージされていないと項目が出てきません。 最初は詰まりやすいところだと思いますので、注意しましょう。

デプロイをしたブランチ / タグがどれだか分かりづらい

ブランチやプルリクエストのアクションで自動実行されるものはGithub Actionsの一覧画面で実行元のブランチやタグが表示されていますが、手動実行のものは表示されないようです。 少し手間ですが、実行ログの「Checkout -> Checking out the ref」から確認できます。

uses で指定しているライブラリではできないことがある

例えば aws-actions/amazon-ecs-deploy-task-definition@v1 では、2021/12/13現在、ECSでのデプロイ時に platform-version の指定ができないようです。 指定したい場合は、自分でAWS CLIを使うようにするなど、方法を変えると良いでしょう。

最後に

Github Actionsは、

  • Githubをバージョン管理に使用している場合、リポジトリと密接につながっているため、エンジニアが見る場所がバラけづらい
  • 完全に独立した環境のため、共用デプロイサーバのように同時実行時のCPUやメモリの心配をする必要がない
  • Github Actions用の様々なライブラリが存在するため、やりたいことを簡単に設定できる

などの利点があります。 みなさんもぜひ使ってみてはいかがでしょうか?