🏷️

GitHub Actionsでタグを指定してリリースできるようにする

2023/03/16に公開

はじめに

この記事では弊プロダクト「えんさがそっ♪」の Laravelアプリケーションを GitHub Actions を利用してタグ(バージョン)を指定してECS にデプロイする方法についてまとめます。

前提

  • イメージのビルド・プッシュするアクションは定義済み
    • この記事で扱うデプロイとは別のフローになっている
  • GitHub の Release 機能を利用して tag をバージョン(リリースの単位)としている
  • task-definition の説明は端折る
  • task-definition.json をGit管理に含めたい

全体の流れ

  1. GitHub Actionの手動実行時に、タグ(バージョン)を入力する
  2. aws-actions/amazon-ecs-render-task-definition アクションで、タグ(バージョン)を指定したイメージのタスク定義(のJSON文字列)を生成する
  3. aws cli で、2. で生成したタスク定義で ECS サービスを更新する

task-definition.json を用意する

イメージ(タグ)の指定をするにはどうするかというと、タスク定義でイメージのパスを指定する必要があります。そのためタスク定義を構成する task-definition.json を利用してイメージの指定を実現します。

もし他にもやり方があったらコメント等で教えて頂けますと嬉しいです🙇

どうやって用意するか

やり方はいろいろあるみたいなのですが、task-definition.json をGit管理に含めたいというところもあり今回は愚直に「ダウンロードしてきて必要なものを整理する」という方法を採用しました。

主にこちらの記事を参考にさせていただいてので詳しくはこちらを見ていただければと思います。

GitHub Actions の実行時にタグを指定する

指定の仕方をどうするか検討する中で、タグをデータにしたセレクターみたいなものを表示できたらいいよなーとか思いましたがパッとできそうではなかった(?)ので今回はシンプルなテキスト入力を設けてそこにリリースしたいバージョン(タグ)を入力する形式としました。

yaml を更新します。

production-deploy.yml
on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Enter the version (tag) to deploy'
        required: true

inputs を追加しました。

これで、GitHub Actions の「Run workflow」の UI にタグを指定する入力欄ができました。
こんなかんじ。

タグの指定を受け取ってデプロイする

aws-actions/amazon-ecs-render-task-definition アクションを利用して、指定されたバージョンを task-definition.json にフィルします。

指定されたバージョンは、inputs.tag で受け取ることができますので image のパラメータに前のステップの返り値の outputs.registry 、環境変数に入れてる ECR_APP_REPOSITORY を連結して image のパスを定義します。

production-deploy.yml
    - name: "Fill in the image ID in the Amazon ECS task definition"
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: ${{ env.APP_TASK_DEFINITION_PATH }}
        container-name: ${{ env.APP_CONTAINER_NAME }}
        image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_APP_REPOSITORY }}:${{ inputs.tag }}

前のステップの返り値の outputs.task-definition で作成された json のパスが返ってくるので jqtaskDefinitionArn を取り出して aws ecs register-task-definition に渡して新しい task-definition を登録します。

よく例でもある aws-actions/amazon-ecs-deploy-task-definition を使えばよりシンプルにタスクの登録、サービスの更新までできるみたいでしたが、desired-count の指定ができなかったため aws-cli でサービスの更新をするようにしました。

production-deploy.yml
    - name: "Register task definition & Update ECS service"
      env:
        IMAGE_ID_FILLED_APP_TASK_DEFINITION: ${{ steps.task-def.outputs.task-definition }}
      run: |
        set -x
        NEW_TASK_DEFINITION_ARN=$(aws ecs register-task-definition \
          --family $ECS_APP_TASK_DEFINITION_FAMILY \
          --cli-input-json "file://${IMAGE_ID_FILLED_APP_TASK_DEFINITION}" | jq -r '.taskDefinition.taskDefinitionArn')

        aws ecs update-service \
          --task-definition $NEW_TASK_DEFINITION_ARN \
          --force-new-deployment \
          --cluster $ECS_CLUSTER \
          --service $ECS_APP_SERVICE \
          --desired-count 2

指定されたタグのイメージがないときはエラーにする

正直なところ、デプロイができた時点で「ヨシっ!」となってしまっていたのですが想定外なところがありました。存在しないタグが指定された際にもワークフローや、タスク定義が正常終了してしまうということです。

その対策のため、ECR にイメージが存在するかの確認をするステップをタスクを更新する前に追加しました。

production-deploy.yml
    - name: "Check if image exists in ECR"
      run: |
        IMAGES=$(aws ecr list-images --repository-name $ECR_APP_REPOSITORY --query "imageIds[?imageTag=='${{ inputs.tag }}']" --output text)
        if [ -z "$IMAGES" ]; then
          echo "Image does not exist."
          exit 1
        fi

aws cli の ecr list-images コマンドを利用して ECR に指定されたイメージが存在するか確認します。--output text を指定して文字列で出力するようしています。存在しなければここで exit 1 してワークフローを終了します。

これで存在しないタグが指定されても正常終了せずエラーを発生させることができました。

さいごに

今回の作業の中で masterブランチ以外の workflow ってどうやって実行するんだ?というところで GitHub CLI を使ってみたりしたのでそのあたりもまた記事できればと考えています。

最後までお読みいただきありがとうございました。

参考にさせていただいた記事

BABYJOB テックブログ

Discussion