GitHubActionsでDockerをビルドしてECSにデプロイする

CI/CDにはJenkinsやCircleCIもありますが、
GitHubActionsというものを知り、GitHubを使っているならソースコードとの整合性も保てて便利じゃないかと思いまして、早速導入することにしました。
今回は、GitHubActionsでDockerをビルドしてECSクラスタにさくっとデプロイしてみます。

### 1.ECSクラスターを作成する
ECSクラスターを作ってください。
※このなかではクラスタ名は、「development」としています。

### 2.ECRを作成する
DockerのImageを登録するためのECRを作成してください。
このなかではECRのリポジトリ名は、「development」としています。

### 3.Dockerfileを作成する
例えば、下記のようなPHPを動作させるコンテナをDockerfileを作ります。
/deploy/development/docker/php/Dockerfile

``` Dockerfile
FROM php:7.4-fpm-alpine

# JSTに変更
RUN apk add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# PHP などインストール
RUN set -eux \
 && apk add --update --no-cache git autoconf g++ libtool make libzip-dev libpng-dev libjpeg-turbo-dev freetype-dev oniguruma-dev libxml2-dev sed nginx redis \
 && pecl install redis \
 && docker-php-ext-configure gd --with-jpeg=/usr/include/ --with-freetype=/usr/include/ \
 && docker-php-ext-configure opcache --enable-opcache \
 && docker-php-ext-install opcache pdo_mysql gd zip mbstring xml \
 && docker-php-ext-enable redis \
 && apk del autoconf g++ libtool make \
 && rm -rf /tmp/*

#フォルダコピー
COPY ./ /var/www/html

# 作業ディレクトリを変更
WORKDIR /var/www/html

#パーミッション変更
RUN chown -R www-data:www-data /var/www/html

#コンテナ起動時にphp-fpmとnginx実行
CMD php-fpm -D && nginx -g 'daemon off;'
```

### 4.AWSでデプロイ用IAMを作成する
AWSで、ECSにデプロイさせるためにIAMユーザーを作ります。
下記の２つのパーミッションポリシーが必要です。
・AmazonEC2ContainerRegistryFullAccess
・AmazonEC2ContainerServiceFullAccess
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561175/6846da34-8702-7c2a-9b87-1c59a1bd90b3.png)

### 5.GitHubのActions secretsにAWSのデプロイ用IAMのアクセスキー・シークレットキーを設定する
GitHubのレポジトリー→「Settings」→「Secrets」でAWSのデプロイ用アクセスキーを「AWS_ACCESS_KEY_ID」・シークレットキーを「AWS_SECRET_ACCESS_KEY」として登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561175/e0326d31-78cd-5be0-742c-6139a8ea9385.png)



### 6.ECSクラスタのタスク定義jsonを作成する
ECSのタスク定義は、jsonファイルを作成してawscliでアップしておきましょう。


``` deploy/development/ecs/ecs-task-definition.json
{
    "requiresCompatibilities": [
        "EC2"
    ],
    "inferenceAccelerators": [],
    "containerDefinitions": [
        {
            "name": "development",
            "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/development:latest",
            "memoryReservation": 350,
            "resourceRequirements": [],
            "essential": true,
            "portMappings": [
                {
                    "hostPort": 0,
                    "containerPort": 80,
                    "protocol": "tcp"
                }
            ],
            "environment": [],
            "environmentFiles": [],
            "secrets": [],
            "mountPoints": [],
            "volumesFrom": [],
            "extraHosts": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/development",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "ecs"
                }
            },
            "ulimits": [],
            "dockerLabels": {},
            "dependsOn": []
        }
    ],
    "volumes": [],
    "networkMode": "bridge",
    "placementConstraints": [],
    "family": "development",
    "taskRoleArn": "arn:aws:iam::xxxxxxxxxxxx:role/ecsTaskExecutionRole"
}
```

次のコマンドで、awscliでECSクラスタにタスクをアップしておきます。

``` 
aws ecs register-task-definition --cli-input-json file://./deploy/development/ecs/ecs-task-definition.json  --profile hogehogeplofile
```
※hogehogeplofileはawscliのプロファイル。ご自分のプロファイルをお使いください。


### 7.ECSクラスタのサービスを作る
6のタスク定義をつかったサービスを作ってください。
jsonファイルを作成してawscliでアップでも良いですし、AWSコンソールからでも良いです。


### 8.GitHubActionsのWorkflow構成YAMLファイルを書く

``` /.github/workflows/development-build-deploy.yml
on:
  push:
    branches:
      - development

name: Deploy to AmazonECS(development) from development branch

jobs:
  deploy:
    name: Deploy app
    runs-on: ubuntu-latest
    steps:

    - name: Checkout code
      uses: actions/checkout@v2

    - name: Configure AWS Credentials # AWSアクセス権限設定
      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 # ECRログイン処理
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push image to Amazon ECR # ECRイメージPush
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: development
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f ./deploy/development/docker/php/Dockerfile .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

    - name: Render Amazon ECS task definition # ECSタスク定義ファイルレンダリング
      id: render-container
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: deploy/development/ecs/ecs-task-definition.json # レポジトリ以下のタスク定義ファイルがあるPath
        container-name: development
        image: ${{ steps.build-image.outputs.image }}

    - name: Deploy to Amazon ECS service # ECSサービスデプロイ
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.render-container.outputs.task-definition }}
        service: development-web
        cluster: development-cluster
```
このコードがGitHubActionsの設定ファイルです。

条件：developmentにPushされたときに下記内容を順次実行します。
①AWSアクセス権限設定
②ECRログイン処理
③DockerfileよりビルドしてDockerImageを作成してECRにPush
④ECSタスク定義を再修正
⑤ECSにタスク定義を使ってデプロイ



### 9.ソースをCommitしてGitHubにPushしてPullRequestを出します。

それでは用意ができたので、Gitで別ブランチでコミットしてください。
そして、GitHubにPushしてPullRequest出しておきましよう。

### 10.PullRequestでマージして、GitHubActions実行
別ブランチ→developmentブランチにマージした瞬間にGitHubActionsが実行されます。
GitHubリポジトリの「Actions」タブを開くと実行状況が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561175/ee39b2c7-0fb5-ba37-3d85-28f60fa045bb.png)

### 11.ECSのローリングアップデートが終わるまで待つ
GitHubActionsで成功したら、ECSクラスタでローリングアップデートが始まりますので、新しいコンテナになるまで待ちましょう。