ECSの自動デプロイをGitHubActionsで実装してみた

今回は、GitHub ActionsでECSに自動デプロイする方法を紹介します。

ECSにデプロイするまでの話も記事にしています。

https://qiita.com/hiiragiya/items/7df1af73b6f3e34b63ab


今回は、GitHubActionsの細かい話しません。ECRとECSにデプロイする流れを説明して、コードを紹介します。

## GitHubActionsとは？
GitHub Actionsは、GitHub上でCI/CDを簡単に実行できるサービスです。この機能を使うことで、コードをプッシュしたり、プルリクエストをマージした際に、テストやビルド、デプロイなどの一連のタスクを自動で行えます。

Circle CIやAWSのcodeシリーズなど、同じようなサービスは複数ありますが、GitHubActionsがコスト（工数、費用）が少いのでおすすめです。
privateレポジトリでもある程度は無料で使えます。

【参照】

https://chariosan.com/2020/07/19/comparison_ci_tool_cost/


## 実装について
今回は、ECSに対してローリングアップデートで実装します。
ローリングアップデートかBlue/Greenデプロイのいずれかを採用することが多いと思います。
一長一短ですが、シンプルで安価に実装できるローリングデプロイとしています。


【参照】

https://dev.classmethod.jp/articles/ecs-deploytype/

### 全体の流れ
下記のような感じです。非常にシンプルですね。
ECRにDocker imageをpushして、ECS Serviceを更新するような流れです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258821/fce25667-77ae-d0f7-70f0-85d95ddf815a.png)

ECSのサービス定義で、必要なタスク２、最小実行タスク100%、最大実行タスク200%の設定を前提としています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258821/a3acbd3b-e2d2-d01a-8450-f0118ae92b44.png)

### ポイント
ECRにimageをpushする際に、タグ付けができます。
基本的には、LATESTタグを付与して、ECSでLATESTタグのイメージを使えば、常に最新版で運用できるのでいいかと思います。
ただ、問題発生時にどのイメージで問題が起こっているのか追跡できるようにするために、今回はLATESTタグに加えて、gitコミットハッシュ値を持たせておきます。

ECRのイメージにコミットハッシュをタグとしてつけることで、追跡性、一意性、ロールバックの容易さなど、多くの利点を享受できます。

そもそもLATEST運用がアンチパターンという意見もあるみたいなので、
プロダクトによってご検討ください。

https://dev.classmethod.jp/articles/ecs-deploy-all/#toc-7

では、実装を見ていきます。
「# 変更してください」という記述の部分は、環境変数的なところなので、
ご自身で設定してください。

### ディレクトリ構成
cd-workflow.ymlで実装していきます。
``` 
── .github
│   ├── pull_request_template.md
│   └── workflows
│       ├── cd-workflow.yml
``` 

###　流れ
1. マシーンを起動、ソースコードを取得
1. OIDCでAWS認証をする
1. docker build, tagを付与し、ECRにプッシュする
1. ECSを更新する
1. おしまい

### 実際のコード
下記ですね。細かい説明は後述します。
```bash:cd-workflow.yml
name: push image to ECR and deploy to ECS
on:
  pull_request:
    branches:
      - main
    types: [closed]

jobs:
  push:
    name: push image to ECR and deploy to ECS
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v3

      # - name: Set AWS CLI Debug Output (if needed for debugging)
      #   run: |
      #     echo "AWS_DEFAULT_OUTPUT=text" >> $GITHUB_ENV
      #     echo "AWS_PAGER=" >> $GITHUB_ENV
      #     # echo "AWS_DEBUG=true" >> $GITHUB_ENV

      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: 'ap-northeast-1' #変更してください リージョン名
          role-to-assume: 'arn:aws:iam::hogehogehoge:role/oidc-role' #変更してください ロール名

      - uses: aws-actions/amazon-ecr-login@v1
        id: login-ecr

      - name: Build, tag, and push docker image to ECR
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: "twitter-clone" #変更してください ECRのリポジトリ名
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build . --tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
          docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
          docker tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }} ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:latest
          docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:latest

      - name: Force new deployment and wait for services stability in ECS
        env:
          CLUSTER_NAME: "ProjectA-ecs-cluster" #変更してください クラスター名
          SERVICE_NAME: "ProjectA-esc-service4" #変更してください サービス名
        run: |
          aws ecs update-service --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }} --force-new-deployment
          aws ecs wait services-stable --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }}
          aws ecs describe-services --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }}
```

#### 1.マシーンを起動、ソースコードを取得
この辺りは、GithubActionsを触ったことがある人ならすぐにわかると思いますが、GitHubActionsでgithub上のソースコードを使うためのお約束的なのです。

- mainブランチへのマージをトリガーにしています。
- マシーンは、ubuntu-latest
- timeout-minutesは、バグった時に、GithubActionsの無駄使いしないように設定しています。
- permissionsは、後述のOIDC認証した時に、AWSリソースを扱うために必要な記述です。
- uses: actions/checkout@v3で、ソースコードを取得できます。

```bash:cd-workflow.yml
name: push image to ECR and deploy to ECS
on:
  pull_request:
    branches:
      - main
    types: [closed]

jobs:
  push:
    name: push image to ECR and deploy to ECS
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v3
```

#### 2.OIDCでAWS認証をする
AWSの認証**: AWSのリソースにアクセスするためには、適切な権限を持ったIAMロールをGitHub Actionsに付与する必要があります。これを簡単に行うためのアクションがaws-actions/configure-aws-credentials@v1です。

設定方法など、下記の記事がめっちゃわかりやすいので、見てください。

【参照】

https://zenn.dev/kou_pg_0131/articles/gh-actions-ecr-push-image

```bash:cd-workflow.yml
- uses: aws-actions/configure-aws-credentials@v1
  with:
    aws-region: 'ap-northeast-1'
    role-to-assume: 'arn:aws:iam::925173743335:role/oidc-role'
```


#### 3.docker build, tagを付与し、ECRにプッシュする
まず、ECRへのログインが必要です。これはaws-actions/amazon-ecr-login@v1を使用して簡単に行えます。
ockerイメージのビルド、タグ付け、プッシュ: リポジトリに変更があった場合、Dockerイメージをビルドし、それをECRにプッシュします。
タグには、gitコミットのハッシュ値と、最新のイメージとしてlatestタグを追加します。

```bash:cd-workflow.yml
      - uses: aws-actions/amazon-ecr-login@v1
        id: login-ecr

      - name: Build, tag, and push docker image to ECR
        env:
          REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: "twitter-clone"
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build . --tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
          docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
          docker tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }} ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:latest
          docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:latest
```



#### 4.ECSを更新する
1. ECSのサービスを更新して新しいタスクを起動します。
1. ECSのサービスが安定するまで待機します。
1. そして、最後にログを出力してくれます。

2,3はおまけみたいなもので処理に結構時間がかかるので、1だけでもOKです。

```bash:cd-workflow.yml
      - name: Force new deployment and wait for services stability in ECS
        env:
          CLUSTER_NAME: "ProjectA-ecs-cluster"
          SERVICE_NAME: "ProjectA-esc-service4"
        run: |
          aws ecs update-service --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }} --force-new-deployment
          aws ecs wait services-stable --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }}
          aws ecs describe-services --cluster ${{ env.CLUSTER_NAME }} --service ${{ env.SERVICE_NAME }}
```

## まとめ
GitHub Actionsを使用することで、ソースコードの変更を簡単にAWSのECSサービスにデプロイすることができました。これにより、開発のサイクルが速くなり、リリースの頻度を上げることが可能となります。

お楽しみに！


下記、関連記事も書いているので、よかったら見てください。

https://qiita.com/hiiragiya/items/7df1af73b6f3e34b63ab

https://qiita.com/hiiragiya/items/00a41f2c340b7d176274