起動中のECS FargateにCI/CD (GithubActions)

起動中のECS Fargateへ簡単にGithubActionsでCI/CDできたので、その際の備忘録です。
メモ書き程度なので、内容薄ですがご了承下さい🙏

## CI/CD用のIAMユーザーを作成
IAMポリシーには以下のポリシーをアタッチします。
- AmazonEC2ContainerRegistryPowerUser
- AmazonECS_FullAccess

作成したIAMユーザーのアクセスキーとシークレットアクセスキーをメモっておきます。
なお、最小権限でのポリシーはこちらの記事を参考にしました。

https://dev.classmethod.jp/articles/github-actions-ecs-ecr-minimum-iam-policy/

GithubのActions secrets and variablesから先程メモしたキーを設定します。
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/a88133a5-ceff-44e9-53fb-a796e19b70fd.png)

# ワークフローファイルを作成
公開されている雛形があるのでそれを使用します。
- **Actions**タブから**Deploy to Amazon ECS**をダウンロードしてコミット。
- ローカルにpullします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/43c702c8-d324-ee20-6c9c-ece6e963152f.png)

ローカルのディレクトリ構造はこんな状態。
>├── .git
├── .github
│   └── workflows
│   　　　└── aws.yml
├── Dockerfile
├── src
│   └── <アプリケーションファイルたち>
└── task-definition.json #タスク定義ファイルを作成

ワークフローファイルを適宜修正します。雛形にガイドも書かれているのでにっこり😊
```yaml:aws.yml
# このワークフローは、新しいコンテナイメージをビルドしてAmazon ECRにプッシュします、
# その後、"main"ブランチにプッシュされると、新しいタスク定義をAmazon ECSにデプロイします。
#
# このワークフローを使用するには、以下のセットアップステップを完了する必要があります：
#
# 1. イメージを保存するためのECRリポジトリを作成します。
# 例: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
# 以下のワークフローの環境変数 `ECR_REPOSITORY` の値を、リポジトリの名前に置き換えます。
# 以下のワークフローの環境変数 `AWS_REGION` の値をリポジトリの地域に置き換えてください。
#
# 2. ECSタスク定義、ECSクラスタ、およびECSサービスを作成します。
# 例えば、ECSコンソールのGetting Startedガイドに従います：
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
# 以下のワークフローの環境変数 `ECS_SERVICE` の値を、Amazon ECS サービスに設定した名前に置き換える。
# 以下のワークフローの `ECS_CLUSTER` 環境変数の値を、クラスタに設定した名前に置き換えてください。
#
# 3. ECSタスクの定義をJSONファイルとしてリポジトリに保存します。
# フォーマットは `aws ecs register-task-definition --generate-cli-skeleton` の出力と同じにする必要があります。
# 以下のワークフローの環境変数 `ECS_TASK_DEFINITION` の値を、JSONファイルへのパスに置き換えることで、JSONファイルを作成します。
# 以下のワークフローの環境変数 `CONTAINER_NAME` の値をコンテナ名で置き換え。
# タスク定義の `containerDefinitions` セクションにあるコンテナの名前に置き換え。
#
# 4. IAM ユーザーのアクセスキーを GitHub Actions secret の `AWS_ACCESS_KEY_ID` と `AWS_SECRET_ACCESS_KEY` という名前で保存します。
# この IAM ユーザーに推奨される IAM ポリシーについては、以下で使用する各アクションのドキュメントを参照してください、
# このIAMユーザーに推奨されるIAMポリシー、およびアクセスキー認証情報の取り扱いに関するベストプラクティスについては、以下の各アクションのドキュメントを参照してください。

name: Deploy to Amazon ECS

on:
  push:
    branches: [ "main" ]

env:
  AWS_REGION: ap-northeast-1 # AWSリージョン
  ECR_REPOSITORY: <ECR repository name> # ECRのリポジトリ名
  ECS_SERVICE: <ECS service name> # ECSのサービス名
  ECS_CLUSTER: <ECS cluster name> # ECSのクラスター名
  ECS_TASK_DEFINITION: task-definition.json # ECSタスク定義ファイルのパス
                                            
  CONTAINER_NAME: <Container name> # タスク定義で設定したコンテナ名

permissions:
  contents: read

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - 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 }}

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

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}
      run: |
        # Build a docker container and
        # push it to ECR so that it can be deployed to ECS.
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

    - 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: ${{ steps.build-image.outputs.image }}

    - 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
```
次にタスク定義ファイルを作成します！
ポータル画面のタスク定義 > JSONから各プロパティを確認しながら記述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/581e242b-efbe-76f8-7a4f-4ac3a6e89e85.png)

```json:task-definition.json
{
    "family": "<Task Family Name>",
    "networkMode": "awsvpc",
    "containerDefinitions": [
      {
        "name": "<Container Name>",
        "image": "ワークフロー実行時に置き換わるため仮でOK",
        "portMappings": [
          {
            "containerPort": 80,
            "hostPort": 80,
            "protocol": "tcp"
          }
        ],
        "essential": true,
        "environment":[
            {
                "name":"<Environment-name>",
                "value":"<value>"
            }
        ],
        "secrets":[
          {
            "name": "<Secret Name>",
            "valueFrom": "<SecretManagerArn>:<KEY Name>::"
          }
        ],
        "logConfiguration":{
          "logDriver": "awslogs",
          "options": {
            "awslogs-create-group": "true",
            "awslogs-group": "/ecs/****",
            "awslogs-region": "ap-northeast-1",
            "awslogs-stream-prefix": "ecs"
          }
        }
      }
    ],
    "runtimePlatform": {
      "cpuArchitecture": "X86_64",
      "operatingSystemFamily": "LINUX"
    },
    "requiresCompatibilities": [
      "FARGATE"
    ],
    "taskRoleArn": "<ECS Task Role ARN>",
    "executionRoleArn": "<ECS Task Role ARN>",
    "cpu": "****",
    "memory": "****"
}
```
上記の例は[**SeacretManager**](https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/intro.html)にあるシークレット情報を追加しています。
書き方には注意が必要で、`シークレットのARN:キー名::`としないと参照できませんでした。

また、ECSタスク実行ロールに`SeacretManager`と`KMS`のアクセス権限を与えておく必要があります。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html#task-execution-secrets

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:secretsmanager:<region>:<aws_account_id>:secret:<secret_name>",
        "arn:aws:kms:<region>:<aws_account_id>:key/<key_id>"
      ]
    }
  ]
}
```
Pushするとワークフローが走り出し、正常にCI/CDが完了しました！
わーい簡単！🙌
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821082/c6323962-f2a8-5ae0-3ced-67ce657bb575.png)

### 参考

https://dev.classmethod.jp/articles/githubactions-ecs-fargate-cicd-beginner/#toc-4

https://zenn.dev/takuyanagai0213/articles/4299244bb35565