CircleCI Orbsで ECR/ECS にデプロイ

CircleCI Orbsを使った自動デプロイ環境を試してみたので、一通りの手順（AWSの設定からCircleCIの設定まで）を残しておこうと思います。
AWSやCircleCIにあまり慣れていない方にも参考になるように、最後までやりきれば動く環境ができるはずです。

ちなみに私自身はあまりAWSやCircleCIに詳しくないため間違っている部分があるかもしれません。その際はご指摘いただけると嬉しいです。

## 1. プロジェクト作成

適当なディレクトリを作成し、Dockerfileを作成します。

```
mkdir circleci-obrs-sample
touch Dockerfile
```

内容は以下のサイトのサンプルをそのまま利用させていただきます。
[Docker Basics for Amazon ECS \- Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/docker-basics.html)

```
FROM ubuntu:16.04

# Install dependencies
RUN apt-get update
RUN apt-get -y install apache2

# Install apache and write hello world message
RUN echo 'Hello World!' > /var/www/html/index.html

# Configure apache
RUN echo '. /etc/apache2/envvars' > /root/run_apache.sh
RUN echo 'mkdir -p /var/run/apache2' >> /root/run_apache.sh
RUN echo 'mkdir -p /var/lock/apache2' >> /root/run_apache.sh
RUN echo '/usr/sbin/apache2 -D FOREGROUND' >> /root/run_apache.sh
RUN chmod 755 /root/run_apache.sh

EXPOSE 80

CMD /root/run_apache.sh
```

### ECRリポジトリ作成

AWSにログインしECRの画面で「リポジトリ作成」ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/eb14da43-021e-182a-8feb-7c36bae2dbfb.png)

リポジトリ名の入力が促されるので、任意の名前（ここでは、circleci-orbs-sample とする）を入力して保存します。

### ECRリポジトリにイメージをプッシュ

作成したリポジトリを開き「プッシュコマンドの表示」ボタンを押下します。

<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/e06dad6d-c993-3c08-fce1-7993c6864bec.png" width="800px"></kbd>


表示されたコマンドを一つづつ実行していけば問題ないはずです。
最初のコマンドは以下のようなログインコマンドを取得するコマンドです。

```
$(aws ecr get-login --no-include-email --region <region-name>)
```

上記のコマンドを実行するとECRログインのためのコマンド（`docker login -u AWS -p xxxxxxx`）が表示されるので、それをコピペして実行します。

ログインがSuccessしたらのこりのコマンドを実行していきます。

```
# Dockerイメージ構築
docker build -t circleci-orbs-sample .

# イメージにタグをつける
docker tag circleci-orbs-sample:latest <アカウントID>.dkr.ecr.<リージョン名>.amazonaws.com/circleci-orbs-sample:latest

# ECRにプッシュ
docker push <アカウントID>.dkr.ecr.<リージョン名>.amazonaws.com/circleci-orbs-sample:latest
```

一連のコマンドでECRリポジトリへのプッシュが完了します。
ECRリポジトリにアクセスすると 「latest」というタグが表示されると思います。

<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/cdc95fcf-83f9-7e70-dd81-a68d42a3199d.png" width="300px"></kbd>

### タスク定義

次にECSクラスターで利用するタスク定義を作成します。
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/46a86641-b754-7ff0-c4dc-ece421b27895.png" width="600px"></kbd>

新しいタスク定義の作成を押すと、FARGATEかEC2かをまず選びます。ここではEC2を選びます。
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/36f57e6b-a882-a791-2c90-9fa72bcfc85f.png" width="400px"></kbd>


最低限の設定として、タスク定義名とコンテナの定義を行います。
タスク定義名は任意の名前を入れてください（ここでは、「circleci-orbs-task」とします）
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/1a7311f9-143a-37d0-4909-2b4b595063d3.png" width="500px"></kbd>


「コンテナの追加」ボタンを押します。
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/a1f44d78-8bc5-cc94-1e25-587d6d814463.png" width="400px"></kbd>


コンテナの追加では、

- コンテナ名（ここでは、circleci-orbs-container）
- イメージ（ECRリポジトリに表示されているのをコピーする）
- メモリ制限（ここでは200）
- ポートマッピング「（ホスト80、コンテナ80）
- CPUユニット数（ここでは１）

を入力します。
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/a23d640b-bbac-76e0-31c0-270568c604fd.png" width="600px"></kbd>

<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/446b4588-1ed8-980a-dab2-d0627827ae5b.png" width="400px"></kbd>




これで右下の「作成」ボタンを押してタスク定義は完了です。

### クラスター作成
次にECSクラスターを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/bc065d24-dbca-da87-e5f6-16b7ff434d51.png)

ここでは「EC2 Linux + ネットワーキング」を選びます。

<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/d85f5c73-9ab3-dff9-89f4-3903a46fc845.png" width="400px"></kbd>


最低限の変更箇所として、
クラスター名（ここでは、circleci-orb-cluster）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/b9d9d27b-3aee-8206-2f67-ab08be3a977c.png)
EC2インスタンスタイプ（無料枠お使いであればt2.microに変えた方がよいかなと）、インスタンス数（ここでは２）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/6e576819-9d2e-48e7-6de9-beb9373ee09a.png)

VPC、サブネット、セキュリティグループは適宜既存のものなどを割り当ててください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/604a84ce-6839-2e7c-006f-eb88cc20e146.png)

右下の「作成」を押してクラスター作成は完了です。


### ALB作成
のちのサービス作成で必要になるので先に作成しておきます。
EC2ダッシュボードから、ロードバランサー作成を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/085a0e23-0174-4c30-635a-972214533bd9.png)
Application Load Blancer　を選択します。
<kbd><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/6886eb60-f4a2-45d0-59f0-9381ca2b9039.png" width="400px"></kbd>


設定画面では以下を設定します。

- 名前（ここでは、「alb-for-circleci-orbs-service」）
- ポート（デフォルトで、プロトコルがHTTP、ポートが80の設定があるはずなのでそのままでokです）
- VPC、アベイラビリティーゾーンは、クラスターと同じで。
- ターゲットグループ（ここでは、「tg-for-circleci-orbs」と命名。ターゲットグループへのインスタンス追加は不要です。）

### サービス作成
次にクラスター（circleci-orbs-cluster）のサービスを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/389b6086-f748-3c11-1503-f188f4e78e5c.png)

サービスの設定画面では以下を設定します。

- 起動タイプ（ここではEC2）
- タスク定義（ここでは「circleci-orbs-task」）
- クラスター（ここでは「circleci-orbs-cluster」）
- サービス名（ここでは「circleci-orbs-service」と命名）
- タスクの数（ここでは１。クラスターのインスタンスタイプより１少なくしておく。同じだと自動デプロイ時にサービスが自動でタスクを最新化してくれない(はず)）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/5c609578-6eba-a013-1c9d-0c1621111b4c.png)

ネットワーク構成の画面では

- ELBタイプ（ここでは、Application Load Blancer）
- ELB名（ここでは、alb-for-circleci-orbs-service）
- 負荷分散用のコンテナ（ここでは、circleci-orbs-container）


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/568ebadf-4248-6cca-5ecf-e373757769ef.png)

負荷分散用のコンテナの横の「ELBへの追加」ボタンを押すと以下のような画面が開くので、先ほどALB作成時に作成したターゲットグループ（ここでは、tg-for-circleci-orbs）を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/21fdbfd0-65f3-6a7f-3fe9-137fe5bb77ab.png)

次の AutoScalingのページはそのまま。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/7c06cd2f-89de-3cd5-ccdd-36a34c0a58b2.png)

これでサービスの作成は終わりです。

### Route53の設定

作成したALBに対してAレコードを作成します。
ここまでうまくできていれば、アクセスすると「Hello World!」と表示されるはずです。

### CircleCIの設定

ここからやっとCircleCIの設定です。

#### (CircleCI)プロジェクト追加
「ADD PROJECT」から一番はじめに作成したプロジェクトを追加してください。

#### (CircleCI)環境変数追加
Environment Variables から以下設定してください。
AWS_ACCESS_KEY_IDとSECRET_ACCESS_KEYには、IAMでCircleCI用にユーザーを作成してください。アクセス権限は`AmazonEC2ContainerRegistryFullAccess`と`AmazonEC2ContainerServiceFullAccess`を与えてください。


- AWS_ACCESS_KEY_ID・・・IAMユーザーより
- AWS_SECRET_ACCESS_KEY・・・IAMユーザーより
- AWS_ACCOUNT_ID・・・アカウントID（「<アカウントID>.dkr.ecr.<リージョン>.amazonaws.com」のアカウントID）
- AWS_ECR_ACCOUNT_URL・・・ECRアカウントURL（「<アカウントID>.dkr.ecr.<リージョン>.amazonaws.com」をすべて）
- AWS_REGION・・・リージョン（「<アカウントID>.dkr.ecr.<リージョン>.amazonaws.com」のリージョン）
- MY_APP_PREFIX・・・プレフィックス（ここでは、circleci-orbs）

### CircleCIの設定ファイル追加
```
mkdir .circleci
touch .circleci/config.yml
```

`.circleci/config.yml`の中身は以下です。
表題にある通り Orbs を利用しています。
利用しているOrbsの詳細ついては以下リファレンスをご確認ください。

CircleCI Orb Registry \- circleci/aws\-ecr
https://circleci.com/orbs/registry/orb/circleci/aws-ecr
CircleCI Orb Registry \- circleci/aws\-ecs
https://circleci.com/orbs/registry/orb/circleci/aws-ecs#commands-update-task-definition

```
version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@6.0.0
  aws-ecs: circleci/aws-ecs@0.0.8

workflows:
  build_and_push_image:
    jobs:
      - aws-ecr/build-and-push-image:
          region: AWS_REGION
          account-url: AWS_ECR_ACCOUNT_URL
          repo: '${MY_APP_PREFIX}-sample'
          tag: "${CIRCLE_SHA1}"
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          family: '${MY_APP_PREFIX}-task'
          cluster-name: '${MY_APP_PREFIX}-cluster'
          service-name: '${MY_APP_PREFIX}-service'
          container-image-name-updates: 'container=${MY_APP_PREFIX}-container,tag=${CIRCLE_SHA1}'
```

これでプッシュしてCircleCIがうまく通れば自動デプロイできているはずです。

### 動作確認
- Dockerfile内の「Hello World」を書き換えてプッシュして自動で反映されていれば想定通り動いています

- （下画像）サービスのタスク定義のリビジョン番号が増えていること（circleci-orbs-task:4 の　4）
- （下画像）「タスク」の「タスク定義」の列に新しいリビジョン番号のタスクができていること（一時的に新しい４と古い３が同居しますが古い方はそのうち消えるはず）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32875/3143a387-6855-08eb-665e-5b8abf4ce4e7.png)



以上で自動デプロイの設定は一通り終わりです。
CircleCIのOrbsが本当に便利！