CircleCI OrbsでECS Fargateのデプロイを自動化

# 内容
AWS Fargateを導入することになり、CircleCI Orbsを使ったデプロイが便利だったのでご紹介。
内容は、AWS Fargateが構築済であることを想定しており、AWS Fargateについては説明していません。
また、本番での本格的な運用は行っていないのであくまで参考程度でお願いします。

大まかな流れとしては、

* ECRにリポジトリを作成
* IAM User作成
* CircleCIの環境変数の設定
* .circleci/config.yamlの設定

となります。

# CircleCI Orbsとは
> CircleCI Orbs は、ジョブ、コマンド、Executor のような設定要素をまとめた共有可能なパッケージです。 CircleCI 独自の認証済み Orbs のほか、パートナー企業によるサードパーティ製 Orbs を用意しています。

引用元：https://circleci.com/docs/ja/2.0/orb-intro/


[こちら](https://circleci.com/orbs/registry/)からパッケージを検索できます。

例えば今回使用する`aws-ecr`のリポジトリは[こちら](https://circleci.com/orbs/registry/orb/circleci/aws-ecr)
今までだと、CircleCI上からECRにイメージをPushしようとするとAWS CLIのインストールとかやってた？かと思うんですが
orbsを使うことで決められたジョブに必要なパラメーターさえ渡せばよろしくやってくれます。
コード量を減らして可読性のあるconfig.yamlが作れる点がいいですね。

# 要件
今回の要件を以下に整理します。

* とあるブランチ(例としてstagingブランチ)にマージされたら特定のClusterのTask定義を更新し、自動でデプロイしたい。
* db migrationは不要（別処理ですでに実装されているため）

# 事前準備
* CircleCIとGithubのインテグレーションは設定しておく。

# 設定
## ECRにリポジトリを作成
まずはDockerイメージを保存するリポジトリを作成。

```shell
$ aws ecr create-repository --repository-name <ECRのリポジトリ名>
```

## IAM User作成
CircleCIからデプロイさせるために、IAM Userを作成し、以下のAWS 管理ポリシーを付与します。

* AmazonEC2ContainerRegistryFullAccess
* AmazonECS_FullAccess

アカウント作成後、アクセスキーを作成し、Credentials(Access key ID,Secret access key)を保存します。

## CircleCIの環境変数設定
必要なAWS関連の情報をCircleCIの環境変数に設定します。

| Name | Value |
----|---- 
| AWS_ACCESS_KEY_ID | 保存した Access key ID |
| AWS_SECRET_ACCESS_KEY | 保存した Secret access key |
| AWS_ECR_ACCOUNT_URL | AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com |
| AWS_REGION | AWS Region(今回はap-northeast-1） |

## .circleci/config.yaml の設定
以下のように設定しました。

```yaml

version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@1.0.0　　(1)使用するOrbsを設定
  aws-ecs: circleci/aws-ecs@0.0.6

executors:
  docker_build:
    machine:
      docker_layer_caching: true　　(2)キャッシュを有効化

workflows:
  build-and-deploy:
    jobs:
      - build
      - aws-ecr/build_and_push_image:
          executor: docker_build
          name: 'build-staging'
          account-url: AWS_ECR_ACCOUNT_URL
          region: AWS_REGION
          repo: '<ECRのリポジトリ名>'　　(3)作成したリポジトリ名を入れる
          tag: "${CIRCLE_SHA1}"　　(4)イメージのタグにコミットのハッシュ値付与
          path: ./
          dockerfile: Dockerfile-staging　　(5)ビルドに使用したいDockerFileを指定
          requires:
            - build
          filters:
            branches:
              only:
                - staging　(6)stagingブランチにpushがあった時のみ発火
      - aws-ecs/deploy-service-update:
          requires:
            - build-staging
          family: '<Fargateのタスク定義名>'
          service-name: '<Fargateのサービス名>'
          cluster-name: '<Fargateのクラスタ名>'
          container-image-name-updates: 'container=<タスク定義で設定したコンテナ名>,image-and-tag=${AWS_ECR_ACCOUNT_URL}/<ECRのリポジトリ名>:${CIRCLE_SHA1}'　　(7)イメージのPush
          filters:
            branches:
              only:
                - staging
```

ざっくり解説すると、

* `aws-ecs/aws-ecr/build_and_push_image` で指定したDockerfileでイメージのビルドとECRへのプッシュ
* `aws-ecs/deploy-service-update` でタスク定義を新たなリビジョンで作成し、Fargateのサービスで指定しているタスク定義のリビジョンを最新のものに更新

といった動きします。
**(7)** では、タスク定義で設定しているイメージのURL(`AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ECRのリポジトリ名:CIRCLE_SHA1で付与したタグ`)を最新のものに更新しているようです。

そうして新たに作成したタスク定義のリビジョンをFargateのサービスで設定することでデプロイ(今回はサービスにローリングアップデートを設定)が走るという仕組みです。

# 課題
今回はマイグレーションする必要がなかったですが、通常であればマイグレーションする処理を入れる必要があるかと思います。
やり方としては、マイグレーション用のタスク定義を用意しておいて、そのタスク定義を走らせる(`aws ecs run-task`)みたいなことができないかな？と考えています。
ロールバックは、今のところマネジメントコンソールからやる手動でやることを想定していますが、このあたりも要改善です。
他に良いやり方があれば教えてください。

# まとめ
Orbs便利。他にもいろんなOrbsがあるみたいなのでどんどん使っていこうと思います。

# 参考
http://kimh.github.io/blog/ecr-ecs-orbs/