ecs-cliでCloud Map

AWS Cloud Mapを調べる機会があったので、ついでにecs-cliで実際に構成してみた際のメモ。

### やりたいこと
- AWS Cloud Mapを理解する
- ECS Fargateクラスターで実際にCloud Mapを使ってみる

### AWS Cloud Mapって何

コンテナでサービスディスカバリーをするためのサービス、ぐらいのフワッとした概念しかなかったので、[公式](https://aws.amazon.com/jp/cloud-map/faqs/)で調べてみる。
結果、わかったこと。

- バージョン変化等に伴うマイクロサービスのエンドポイント変化に追従しつつ、サービスの「場所」を返すことができるサービス。
- DNSっぽいなと思ったら、Route 53 Auto Namingの上位互換だった。
- DNSクエリーに対してIPを返す、またはHTTP APIコールに対してURLやARNを返す。
- Route 53のように、ヘルスチェックを行って異常系を検出結果から除外できる。
- ECSとインテグレーションされている。
- ecs-cliとecs-params.ymlで簡単に有効化できそう。

ということで、[以前試した方法](https://qiita.com/itsuki3/items/430dd908d4af363cb372)を拡張して、ecs-cliでハンズオンしてみることにする。

### Step by Step

#### 1. ecs-params.ymlの修正

[これ](https://aws.amazon.com/jp/blogs/aws/aws-cloud-map-easily-create-and-maintain-custom-maps-of-your-applications/)や[これ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-cli-tutorial-servicediscovery.html)を見ると、コマンドラインでも比較的簡単に設定出来そうだが、面倒なのでecsparams.ymlに記載できないか試してみる。

前回作成したecsparams.yamlはこちら。

```yaml:ecsparams.yml（旧）
version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet-XXXXXXXX"
        - "subnet-YYYYYYYY"
      security_groups:
        - "sg-ZZZZZZZZ"
      assign_public_ip: ENABLED
```

そして、[パラメータ一覧](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/cmd-ecs-cli-compose-ecsparams.html)を眺めつつ、`service_discovery`セクションを追加したecsparams.ymlがこちら。

```yaml:ecsparams.yml（新）
version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet-XXXXXXXX"
        - "subnet-YYYYYYYY"
      security_groups:
        - "sg-ZZZZZZZZ"
      assign_public_ip: ENABLED
  service_discovery:
    private_dns_namespace:
      vpc: "vpc-AAAABBBB"
      name: "myDiscovery"
```

VPC IDとCloud Mapの名前空間名だけ追加すればよい模様。

#### 2. ECSクラスターの生存確認

`ecs-cli compose`でサービスを追加する際に有効化するようなので、大元となるFargateクラスターをまずは生存確認。

![スクリーンショット 2020-06-18 23.49.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/e4159b8f-e195-659f-445d-b9c5bb57cc4a.png)

無事生きてた。

#### 3. Cloud Map（サービスディスカバリー）を有効化したFargateサービスの作成

先程作成したecsparams.ymlを使って、Fargateサービスを作成する。
この際に、`service_discovery`セクションの記載を用いてCloud Mapを有効化することになる。

```
% ecs-cli compose --project-name SvcDiscoveryTest service up --enable-service-discovery
INFO[0000] Using ECS task definition                     TaskDefinition="SvcDiscoveryTest:1"
INFO[0006] Waiting for the private DNS namespace to be created...
INFO[0006] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
WARN[0066] Defaulting DNS Type to A because network mode was awsvpc
INFO[0071] Waiting for the Service Discovery Service to be created...
INFO[0072] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0102] Auto-enabling ECS Managed Tags
INFO[0108] (service SvcDiscoveryTest) has started 1 tasks: (task 9bac8a5967b24ca0891fc94eac0424b1).  timestamp="2020-06-18 15:21:52 +0000 UTC"
INFO[0153] Service status                                desiredCount=1 runningCount=1 serviceName=SvcDiscoveryTest
INFO[0153] ECS Service has reached a stable state        desiredCount=1 runningCount=1 serviceName=SvcDiscoveryTest
INFO[0153] Created an ECS service                        service=SvcDiscoveryTest taskDefinition="SvcDiscoveryTest:1"
```

できた。
Cloud Map側から見るとこのような感じ。
![スクリーンショット 2020-06-19 00.28.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/640b3671-ddbb-0fb7-3361-3f21c55dd56c.png)
![スクリーンショット 2020-06-19 00.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/5344af6d-01b3-8977-1db3-157086cc64b6.png)
![スクリーンショット 2020-06-19 00.30.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/fc59c118-6002-db17-3c94-5234163d927e.png)

同じものをFargate側から見ると、以下のようになっている。

<img width="1738" alt="スクリーンショット 2020-06-19 00.33.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/0711c3e7-42d4-5718-e7fc-79b2c4991663.png">

![スクリーンショット 2020-06-19 00.34.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/5e093a49-0c82-43eb-24d8-74d440569ecb.png)
`SvcDiscoveryTest.myDiscovery`

#### 4. 名前解決テスト

今回は特定のVPCからのみアクセスできるプライベート名前空間として作成したので、同じサブネット上のEC2から`SvcDiscoveryTest.myDiscovery`を解決してみる。

まずDNS。

```
$ nslookup SvcDiscoveryTest.myDiscovery
Server:         10.100.0.2
Address:        10.100.0.2#53

Non-authoritative answer:
Name:   SvcDiscoveryTest.myDiscovery
Address: 10.100.10.33
```

プライベートIPが返ってきた。
次いでAPI。

```
% aws servicediscovery discover-instances --namespace-name myDiscovery --service-name SvcDiscoveryTest
{
    "Instances": [
        {
            "InstanceId": "9ba（中略）4b1",
            "NamespaceName": "myDiscovery",
            "ServiceName": "SvcDiscoveryTest",
            "HealthStatus": "HEALTHY",
            "Attributes": {
                "AVAILABILITY_ZONE": "ap-northeast-1a",
                "AWS_INIT_HEALTH_STATUS": "HEALTHY",
                "AWS_INSTANCE_IPV4": "10.100.10.33",
                "ECS_CLUSTER_NAME": "myFargate",
                "ECS_SERVICE_NAME": "SvcDiscoveryTest",
                "ECS_TASK_DEFINITION_FAMILY": "SvcDiscoveryTest",
                "REGION": "ap-northeast-1"
            }
        }
    ]
}
```

最後にcurlも試してみる。

```
$ curl SvcDiscoveryTest.myDiscovery
<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Simple PHP App</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="assets/css/bootstrap.min.css" rel="stylesheet">
        <style>body {margin-top: 40px; background-color: #333;}</style>
        <link href="assets/css/bootstrap-responsive.min.css" rel="stylesheet">
        <!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
    </head>

    <body>
        <div class="container">
            <div class="hero-unit">
                <h1>Simple PHP App</h1>
                <h2>Congratulations</h2>
                <p>Your PHP application is now running on a container in Amazon ECS.</p>
                <p>The container is running PHP version 5.4.16.</p>
```

以上、ecs-cliから試してみたCloud Mapでした。