【Github Actions】Amazon ECS にデプロイする

GithubActionsを利用して、新しいコンテナイメージをビルドして Amazon ECR にプッシュ、Amazon ECS にデプロイする手順をまとめました。
所々で学習中の Rails7 + MySQL の名前を使っています。適宜ご自身のケースに置き換えてください。

※やってみた的な要素が強いですので、初歩的な部分も含めて記載しています。おかしい部分がありましたらご指摘いただけるとありがたいです。

## 概要
大まかなフローと概要は以下のような感じです。
次のステップからさっそく手を動かしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/ca838ce4-48d3-1af8-4e09-b974afce233f.png)

## IAMユーザー
ECRリポジトリにプッシュするため、リポジトリ更新権限を持つユーザーが必要です。
なければ以下の手順で作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/bc5977b3-ab7a-de43-37fc-828d77e11a89.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/21f55f47-8c8c-9856-c479-41427b5c99af.png)
許可のオプション：ポリシーを直接アタッチ
許可ポリシー：AmazonElasticContainerRegistryPublicFullAccess
　　　　　　　※更新権限があればよいので PowerUser でもOK

## アクセスキー作成
Github Actions 内でAWS接続を行うため、認証情報を作成する

ユーザーの詳細 → セキュリティ認証情報
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/9d089bc8-7d11-f0d9-7f4f-f857feb142e5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/df9ded8d-5318-4dfa-f33c-8e340bc09a60.png)
ユースケース：いったんその他を選択
タグ：分かればなんでもOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/a4ab8b3e-b9df-bfc2-80dd-510c17d0b787.png)
作成されたアクセスキー・シークレットアクセスキーをコピーして保存
※後述のGithub Actionsでの作業時に使用します

## ECRリポジトリ作成
https://ap-northeast-1.console.aws.amazon.com/ecr/repositories?region=ap-northeast-1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/1f332af7-bb07-a95e-5e86-823b8613ba6f.png)
可視性：プライベート
リポジトリ名：任意の名前（Githubのリポジトリ名が無難？
タグのイミュータビリティ：<font color="darkorange">**有効**</font>
※同一タグ名で上書きされなくなる  ※latest運用しない


## クラスター作成
※事前にVPCとサブネットを設定しておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/1c29e180-0bc9-cc66-8641-36fb810b64d1.png)
クラスター名：ecs-rails7-mysql-cluster
VPC：事前に作成しておいたもの
サブネット：事前に作成しておいたもの
インフラストラクチャ：Fargete

<font color="darkorange">※以下のようなエラーが発生してクラスター作成が失敗する場合
すこし時間をおいてから再度実行</font>
Resource handler returned message: "Error occurred during operation 'CreateCluster SDK error: Service Unavailable. Please try again later


## タスク定義の作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/fd55acf7-4b95-a1df-1ea5-effe0bbffce2.png)

**ステップ１**
タスク定義ファミリー：識別できればなんでもOK
コンテナ名：Githubリポジトリ名など
イメージURI：ECRにあるイメージを指定する必要がるので、事前になにかイメージ(latestなど)をアップしておく
イメージプッシュについての記事：https://qiita.com/sakuchi/items/90886663f7adf3941472
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/3be24ac9-d226-7b17-30de-8e48b9253e4e.png)

**ステップ２**
アプリケーション環境：Fargate
タスクサイズ：<font color="darkorange">**CPU- .5vCPU  メモリ1GB**</font>
※学習用なので最小スペックを選択。
　デフォルトは最小値になっていないので必ず変更する
ログ収集の使用：学習用なので使用しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/d024187b-07fb-d9e6-def8-298aa5cde347.png)

## task-definition.json作成
前述で作成したタスク定義のjson表記をコピー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/f4fe1ea9-d32d-aee7-2bf2-6483a87b6903.png)

Githubのルートに配置。
後述の Actions で作業する際に使用します。
ビルドしたイメージを動的にタスク定義に紐づけるため、imageに環境変数を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/6e0a3587-903e-dc82-6750-9fafa6d8da45.png)

```json
"image": "${{ env.ECR_REGISTRY }}/rails7-mysql:${{ env.IMAGE_TAG }}",
```

## サービスの作成
超重要：常時このコンテナ（タスク）を起動しておくための設定。
サービスは<font color="darkorange">**常に指定した数のタスクを実行することを保証**</font>し、タスクが異常終了した場合には自動的に新しいタスクが起動される。つまりコンソール上でタスクを停止しても、タスク数が０になっていない限りサービスによって自動で起動される。料金に大きく影響するので注意。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/e6b97d86-f7c4-8398-e0d8-64debef278f2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/99144b5a-c4ae-667e-b3f9-ab33d5247428.png)
**環境**
コンピューティング設定：起動タイプ
起動タイプ：Fargate

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/ed59a5a3-6801-dab7-268f-893dfadff101.png)
**デプロイ設定**
アプリケーションタイプ：サービス
ファミリー：前述で作成したタスク定義
サービス名：なんでもOK
サービスタイプ：レプリカ
必要なタスク：１
※ここで設定したタスク数にAWSが<font color="darkorange">**自動で調整する。**</font>

**デプロイオプション**
デフォルトのまま

**ネットワーキング**
VPC：今回作成したもの
サブネット：：今回作成したもの
セキュリティグループ：既存のセキュリティグループを使用
パブリックIP：オン
ロードバランシング：なし
Auto Scaling：使用しない
タグ：デフォルト


## Github Actions でビルド・プッシュ設定
**Github Actions でビルド・プッシュ設定**
Github側でIAMユーザーのアクセスキーとシークレットアクセスキーを設定
※後述のワークフロー内で使用する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/5908278e-b67e-3947-dd51-f23f4ed6986e.png)

**ワークフローファイルの作成**
今回は mainブランチに対して行います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/8ec8a8d8-64ce-2444-5887-16ceb7919f4c.png)

Deploy to Amazon ECS を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/95811b35-70d3-c588-aaca-8a7264dd3729.png)

ymlの記述を以下に変更

```yaml:aws.yml
name: Deploy to Amazon ECS

on:
  push:
    branches: [main]

env:
  AWS_REGION: ap-northeast-1                   # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: rails7-mysql                 # set this to your Amazon ECR repository name
  ECS_SERVICE: svc-rails7-mysql                # set this to your Amazon ECS service name
  ECS_CLUSTER: ecs-rails7-mysql-cluster        # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: task-definition.json    # set this to the path to your Amazon ECS task definition
                                               # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: rails7-mysql                 # set this to the name of the container in the
                                               # containerDefinitions section of your task definition

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    
    steps:
      # ワークフローファイル内で利用できるように、リポジトリをチェックアウト
      - name: Checkout repository
        uses: actions/checkout@v3

      # 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: ${{ env.AWS_REGION }}

      # ECR にログイン
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      # リポジトリ内の Dockerfile を使用して、イメージのビルド、タグ付けをして ECR にプッシュ
      - name: Build, tag, and push docker image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          REPOSITORY: rails7-mysql
          #IMAGE_TAG: latest
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          #イメージのURLを保持
          echo "IMAGE_URL=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_ENV
          
      - name: Use IMAGE_URL
        run: |
          echo ${{ env.IMAGE_URL }}
          
      # ECSタスク定義のイメージID を新しくビルドしたイメージに更新。これにより新しいイメージがデプロイされる。
      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ env.ECS_TASK_DEFINITION }}
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ env.IMAGE_URL }}

      # 新しいタスク定義でデプロイ。指定したタスク定義とクラスタ、サービスを使用して、既存のタスクを更新。
      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true
```
**環境変数について**
AWS_REGION: ECR,ECSのリージョン（例：ap-northeast-1
  ECR_REPOSITORY: ECRリポジトリ名 （例：rails7-mysql
  ECS_SERVICE: ECSサービス名 （例：svc-rails7-mysql
  ECS_CLUSTER: ECSクラスター名 （例：ecs-rails7-mysql-cluster
  ECS_TASK_DEFINITION: task-definition.json （例：task-definition.json
  CONTAINER_NAME: タスク定義で指定したコンテナ名   （例：rails7-mysql


## ※注意点など
構築の流れは一通り以上です。
これで mainブランチにpushが発生するとワークフローファイルをもとにビルド・プッシュ・デプロイが行われるようになります。
学習用の場合、最後に必ずサービスの「必要なタスク」を <font color="darkorange">**0**</font> に設定するか、サービス自体を<font color="darkorange">**削除**</font>するようにしてください。<font color="darkorange">**タスクが起動している間は料金が発生**</font>しますのでご注意ください。
サービスが存在していてもタスクが起動していなければ料金は発生しません。