ecs-cli でECSのサービスディスカバリを行う

# はじめに
案件でEC2で運用していたサービスのコンテナ化と同時に、Route53のAuto Namingを使用したECSのディスカバリを行ったのでその時の手順を書きます。

# 動作環境
* ecs-cli v1.12.0
* docker-compose

# 対応イメージ

プライベートサブネット内で稼働しているCLB+EC2の構成を、
ECSサービスディスカバリーを利用したアクセスに変更します。
対応の概念などは
[ECSのサービスディスカバリーが東京にやってきて、コンテナ間通信の実装が簡単になりました！](https://dev.classmethod.jp/cloud/aws/ecs-service-discovery/)
を参考にさせていただきました。

* 対応前  
![スクリーンショット 2018-12-03 6.22.27.png](https://qiita-image-store.s3.amazonaws.com/0/63155/1b0234d2-30c7-a3fe-b60e-e24ab41232cf.png)
* 対応後  
![スクリーンショット 2018-12-03 6.23.24.png](https://qiita-image-store.s3.amazonaws.com/0/63155/3b30656c-e46d-3abc-a5a2-d5c98b5f0682.png)

# 手順
公式ドキュメントにチュートリアルがあったので、ほぼその通りに行いました。特別なことはしていません。
[Tutorial: Creating an Amazon ECS Service That Uses Service Discovery Using the Amazon ECS CLI](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-cli-tutorial-servicediscovery.html)
ecs-cliのgithubのREADMEにも同様のことが書かれています。
[using-route53-service-discovery](https://github.com/aws/amazon-ecs-cli#using-route53-service-discovery)

## ECSのタスクの準備
アプリケーションはnginx+javaアプリというよくある構成です。
`ecs-cli`を使用するのでdocker-composeの形式で記述します。

まずは基本となる共通のyamlです。

```docker-compose.common.yml
version: '3'
services:
  sample-api-app:
    image: 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/sample-api-app:latest
    user: quartette
    ulimits:
      nproc: 65536
      nofile:
        soft: 65536
        hard: 65536
    environment:
      - JAVA_OPTS=-Xms1024m -Xmx2048m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -Duser.country=JP -Duser.language=ja -Duser.timezone=Asia/Tokyo -Djava.awt.headless=true -XX:-OmitStackTraceInFastThrow
    command:
      /bin/sh -c
        "java -jar -Dspring.profiles.active=$$APP_ENV /var/app/quartette/app.jar"
  sample-api-nginx:
    image: 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/sample-api-nginx:latest
    ulimits:
      nproc: 65536
      nofile:
        soft: 65536
        hard: 65536
    command:
      /bin/sh -c
        "nginx -g 'daemon off;'"
    ports:
      - "80:80"
```

次に環境ごとの設定を記述するyamlです

```docker-compose.dev.yml
version: '3'
services:
  sample-api-app:
    environment:
      - APP_ENV=dev
    labels:
      - Env=dev
    logging:
      driver: awslogs
      options:
        awslogs-group: "quartette-app"
        awslogs-region: "ap-northeast-1"
        awslogs-stream-prefix: "dev"
  sample-api-nginx:
    environment:
      - APP_ENV=dev
    labels:
      - Env=dev
    logging:
      driver: awslogs
      options:
        awslogs-group: "quartette-nginx"
        awslogs-region: "ap-northeast-1"
        awslogs-stream-prefix: "dev"
```

最後にECS用のパラメーターの設定ファイルを用意します。

```ecs-params-dev.yml
version: 1
task_definition:
  ecs_network_mode: awsvpc
  # Amazon ECS タスク実行 IAM ロール
  # https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html
  task_execution_role: ecsTaskExecutionRole
  task_size:
    cpu_limit: 2048
    mem_limit: 4096
  services:
    sample-api-app:
      cpu_shares: 2048
      mem_limit: 3GB
      mem_reservation: 3GB
      essential: true
    sample-api-nginx:
      mem_limit: 128M
      mem_reservation: 128M
      
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        # private subnet
        - subnet-xxxxxxxx
        - subnet-zzzzzzzz
      security_groups:
        - sg-yyyyyyyy
      assign_public_ip: DISABLED
```

## ECSクラスターの作成（まだ作っていない場合）

ECSクラスターを作成していない場合はまず作成します。

```
% ecs-cli up --cluster quartette-cluster --vpc vpc-aaaaaaaa --subnets subnet-xxxxxxxx,subnet-zzzzzzzz --security-group sg-yyyyyyyy --launch-type FARGATE --region ap-northeast-1 --ecs-profile quartette
INFO[0005] Created cluster                               cluster=quartette-cluster region=ap-northeast-1
INFO[0006] Waiting for your cluster resources to be created...
INFO[0006] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
Cluster creation succeeded.
```

![スクリーンショット 2018-12-03 5.42.41.png](https://qiita-image-store.s3.amazonaws.com/0/63155/adad562e-6911-942c-c7ab-a6d4c029e90e.png)

## Service Discoveryを使ったECS　Serviceの作成

下記のパラメーターが必要になります。

* `--private-dns-namespace`：プライベートDNSで設定されるドメインのネームスペース
* `--enable-service-discovery`：ECSのサービスディスカバリを有効にするオプション

```
% ecs-cli compose --project-name dev -f compose/docker-compose.common.yml -f compose/docker-compose.dev.yml --ecs-params compose/ecs-params/ecs-params-dev.yml --cluster quartette-cluster service up --private-dns-namespace quartette.com --vpc vpc-aaaaaaaa --create-log-groups --launch-type FARGATE --enable-service-discovery
INFO[0000] Using ECS task definition                     TaskDefinition="dev:2"
WARN[0000] Failed to create log group quartette-app in ap-northeast-1: The specified log group already exists
WARN[0000] Failed to create log group quartette-app in ap-northeast-1: The specified log group already exists
INFO[0006] Waiting for the private DNS namespace to be created...
INFO[0006] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0066] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
WARN[0102] Defaulting DNS Type to A because network mode was awsvpc
INFO[0102] Waiting for the Service Discovery Service to be created...
INFO[0102] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0134] Created an ECS service                        service=dev taskDefinition="dev:2"
INFO[0134] Updated ECS service successfully              desiredCount=1 serviceName=dev
INFO[0149] (service dev) has started 1 tasks: (task 66666666-cccc-ffff-b000-bbbbbbbbbbbb).  timestamp="2018-12-02 20:50:00 +0000 UTC"
INFO[0211] Service status                                desiredCount=1 runningCount=1 serviceName=dev
INFO[0211] (service dev) has reached a steady state.     timestamp="2018-12-02 20:50:57 +0000 UTC"
INFO[0211] ECS Service has reached a stable state        desiredCount=1 runningCount=1 serviceName=dev
```

作成するとこのような感じにサービスの検出の項目が設定されます。
この設定では、VPC内からは `dev.quartette.com` でアクセスできるようになります。

![スクリーンショット 2018-12-03 5.51.04.png](https://qiita-image-store.s3.amazonaws.com/0/63155/2716ebfd-5ed2-5383-2d27-18618bdb8486.png)

DNSにはこのように設定されています。

![スクリーンショット 2018-12-03 5.52.20.png](https://qiita-image-store.s3.amazonaws.com/0/63155/130e9f62-1df3-5365-dce4-b86290f1d2e8.png)

いい感じですね。

ためしにアクセスしてみます。
アプリケーションからはjsonが返ってくる想定です。

```
$ curl http://dev.quartette.com/api/
{"message":null,"result": …
```

## タスクのスケール

次にタスクをスケールしてみます。

```
% ecs-cli compose --verbose --project-name dev -f compose/docker-compose.common.yml -f compose/docker-compose.dev.yml --ecs-params compose/ecs-params/ecs-params-dev.yml --cluster quartette-cluster service scale 4
DEBU[0000] Parsing the compose yaml...
DEBU[0000] Docker Compose version found: 3
DEBU[0000] Docker Compose version found: 3
DEBU[0000] Parsing v3 project...
DEBU[0000] Parsing the ecs-params yaml...
DEBU[0000] Parsing the ecs-registry-creds yaml...
DEBU[0000] Transforming yaml to task definition...
DEBU[0000] Updated ECS service                           count=4 service=dev
INFO[0000] Updated ECS service successfully              desiredCount=4 serviceName=dev
INFO[0001] Service status                                desiredCount=4 runningCount=1 serviceName=dev
INFO[0016] (service dev) has started 3 tasks: (task 11111111-1111-1111-1111-111111111111) (task 22222222-2222-2222-2222-222222222222) (task 33333333-3333-3333-3333-333333333333).  timestamp="2018-12-02 20:55:28 +0000 UTC"
INFO[0061] Service status                                desiredCount=4 runningCount=2 serviceName=dev
INFO[0076] Service status                                desiredCount=4 runningCount=4 serviceName=dev
INFO[0076] ECS Service has reached a stable state        desiredCount=4 runningCount=4 serviceName=dev
```

スケールされました。

![スクリーンショット 2018-12-03 5.56.59.png](https://qiita-image-store.s3.amazonaws.com/0/63155/9f6a8c85-fdc1-1d15-af05-4977b9ef96e7.png)

DNSも確認してみます。

![スクリーンショット 2018-12-03 5.57.36.png](https://qiita-image-store.s3.amazonaws.com/0/63155/6f74b5b1-b5ec-8a08-6df4-5122e25d46b7.png)

ちゃんとレコードが増えていますね。

## サービスの更新

サービスの更新はこれまでの使い方とほとんど変わりませんが、 `--private-dns-namespace` パラメーターが増えています。

```
ecs-cli compose --project-name dev -f compose/docker-compose.common.yml -f compose/docker-compose.dev.yml --ecs-params compose/ecs-params/ecs-params-dev.yml --cluster quartette-cluster service up --private-dns-namespace quartette.com --vpc vpc-aaaaaaaa --create-log-groups --launch-type FARGATE --deployment-max-percent 200 --deployment-min-healthy-percent 50 --healthcheck-custom-config-failure-threshold 2
```

## サービスの削除

最後にサービスを削除します。private dnsの設定も削除するには、 `--delete-namespace` を追加してあげる必要があります。
このパラメーターを追加せずに手動でRoute53から削除しようとするとエラーになります。

```
% ecs-cli compose --project-name dev -f compose/docker-compose.common.yml -f compose/docker-compose.dev.yml --cluster quartette-cluster service rm --delete-namespace
INFO[0000] Updated ECS service successfully              desiredCount=0 serviceName=dev
INFO[0000] Service status                                desiredCount=0 runningCount=1 serviceName=dev
INFO[0015] Service status                                desiredCount=0 runningCount=0 serviceName=dev
INFO[0015] (service dev) has stopped 1 running tasks: (task 11111111-1111-1111-1111-1111111111111).  timestamp="2018-12-02 21:07:00 +0000 UTC"
INFO[0015] ECS Service has reached a stable state        desiredCount=0 runningCount=0 serviceName=dev
INFO[0015] Deleted ECS service                           service=dev
INFO[0015] ECS Service has reached a stable state        desiredCount=0 runningCount=0 serviceName=dev
INFO[0015] Trying to delete any Service Discovery Resources that were created by the ECS CLI...
INFO[0016] Waiting for your Service Discovery Service resource to be deleted...
INFO[0016] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
INFO[0047] Waiting for your Private DNS Namespace resource to be deleted...
INFO[0047] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
INFO[0107] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
```

# 最後に
ECSの運用を始めてから１年半ほどになりますが、Fargateの登場以降どんどん便利になっていきますね。
現状はECS Fargateばかりで運用していますが、世の中的にはkubernetesの事例のほうが圧倒的に多いのでEKSあたりもどんどん触っていこうと思います。