AWS ECS Service Connect について理解する

# 概要

今回は、AWS Service Connect のチュートリアルを試してみたいと思います。

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/create-service-connect.html

なお、ECS の基本的な事については、理解している前提で進めていきます。

# ECS Service Connect とは

マイクロサービスアーキテクチャなどの構成において、ECSサービス間の通信を簡単に設定できるようにしたアップデートです。

![スクリーンショット 2022-12-13 0.53.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/4e6e6232-67b6-0476-19f0-b1d878810a38.png)

今までも、ECSサービス間の通信を接続する方法として、以下の方法がありました。

- ELB
- ECS Service Discovery
- AWS App Mesh

但し、これらのサービスはいくつかの課題もありました。そのあたりの課題を上手くやってくれるのが、AWS Service Connect です。

ELB、ECS Service Discovery、AWS App Mesh、AWS Service Connect の違いなどについては下記の資料が大変参考になりましたので、是非確認してみてください。

https://speakerdeck.com/iselegant/a-new-way-to-connect-services-with-ecs-service-connect

# やってみる

## 1.Amazon ECS クラスターの作成

クラスター`tutorial`、名前空間(Cloud Map)`service-connect`を作成します。

```
aws ecs create-cluster --cluster-name tutorial --service-connect-defaults namespace=service-connect
```

今回は理解を深めるために、コンソールから作成されたことを確認しておきます。
![スクリーンショット 2022-12-11 16.44.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/46498c6f-1b1f-8fe0-6d7f-842523b8b773.png)
![スクリーンショット 2022-12-11 16.44.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/abc3c12c-b3c1-a1fb-ce75-edca2e5f8874.png)

## 2.Amazon ECS サービスの作成

### 2-1.タスク定義の作成

タスク定義を作成します。
　
`taskRoleArn` と `executionRoleArn` は、とりあえず、**AmazonECSTaskExecutionRolePolicy** をアタッチした IAMロールで良いかと面います。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html

`portName` は次のサービスの Service Connect の設定で「クライアントとサーバー」指定するため必須です。

```json:service-connect-nginx.json
{
    "family": "service-connect-nginx",
    "taskRoleArn": arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
    "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "containerDefinitions": [
        {
        "name": "webserver",
        "image": "public.ecr.aws/docker/library/nginx:latest",
        "cpu": 100,
        "portMappings": [
            {
                "name": "nginx",
                "containerPort": 80,
                "protocol": "tcp", 
                "appProtocol": "http"
            }
        ],
        "essential": true,
        "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
                "awslogs-group": "/ecs/service-connect-nginx",
                "awslogs-region": "ap-northeast-1", 
                "awslogs-stream-prefix": "nginx"
            }
        }
        }
    ],
    "cpu": "256",
    "memory": "512"
}                       
```

タスク定義を登録します。

```
aws ecs register-task-definition --cli-input-json file://service-connect-nginx.json
```

### 2-2.サービスの作成

サービスを作成します。

`securityGroups` と `subnets` は、ご自身の環境のものに合わせてセキュリティグループとサブネットに変更してください。

```json:service-connect-nginx-service.json
{
    "cluster": "tutorial",
    "deploymentConfiguration": {
        "maximumPercent": 200,
        "minimumHealthyPercent": 0
    },
    "deploymentController": {
        "type": "ECS"
    },
    "desiredCount": 1,
    "enableECSManagedTags": true,
    "enableExecuteCommand": true,
    "launchType": "FARGATE",
    "networkConfiguration": {
        "awsvpcConfiguration": {
            "assignPublicIp": "ENABLED",
            "securityGroups": [
                "sg-EXAMPLE"
            ],
            "subnets": [
                "subnet-EXAMPLE",
                "subnet-EXAMPLE",
                "subnet-EXAMPLE"
            ]
           }
    },
    "platformVersion": "LATEST",
    "propagateTags": "SERVICE",
    "serviceName": "service-connect-nginx-service",
    "serviceConnectConfiguration": {
        "enabled": true,
        "services": [
            {
                "portName": "nginx",
                "clientAliases": [
                    {
                        "port": 80
                    }
                ]
            }
        ],
        "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
                "awslogs-group": "/ecs/service-connect-proxy",
                "awslogs-region": "ap-northeast-1",
                "awslogs-stream-prefix": "service-connect-proxy"
            }
        }
    },
    "taskDefinition": "service-connect-nginx"
}                    
```

サービスを登録します。

```
aws ecs create-service --cluster tutorial --cli-input-json file://service-connect-nginx-service.json
```

コンソールを見ながらいくつか設定項目を確認してみます。

- Service Connect の設定
    - **クライアント側のみ** : Service Connect を通じてエンドポイントに対してリクエストするクライアントとなるECSサービス。
    - **クライアントとサーバー** : エンドポイントを提供しクライアントからリクエストを受け付けるサーバーとなるECSサービス。

- 名前空間
    - 「1.Amazon ECS クラスターの作成」で作成した名前空間(Cloud Map)
 
- Service Connect および検出名の設定
    - サービスの `serviceConnectConfiguration` の部分です。
    - 名前空間で実行されている ECS のクライアントアプリケーションは、`portName` および `port` を使用して、このサービスに接続します。(例)http://nginx.service-connect:80
    - Amazon ECS で実行されていない、または同じ名前空間にない外部アプリケーションは、タスクの IP アドレスとタスク定義のポート番号を使用して、Service Connect プロキシ経由でこのアプリケーションに到達できます。(例)http://10.0.0.100:80

![service_connect1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/60e56dc9-3580-f575-04b6-5486e221ed2c.png)

Service Connect の仕組みとして、ECSタスクの中で、タスク定義で指定したコンテナとは別に Service Connect agent と呼ばれるコンテナが立ち上がります。これは Envoy を含んだ Sidecar コンテナとなっており、Service Connect を有効化した ECS サービスは Envoy をプロキシとして通信が制御されます。

![service_connect2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/6a607abf-11ae-ace6-44fd-7def301b854a.png)

次の通り、**webserver** と **ecs-serviceconnect-～**　のコンテナが起動しました。
![スクリーンショット 2022-12-11 19.28.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/5c23c60a-758a-b653-9dd8-d5545ca06b56.png)

Cloud Map のコンソールから名前空間の詳細を確認してみると、サービスインスタンスとしてECSタスクが自動で登録されていることがわかります。
![スクリーンショット 2022-12-11 19.53.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/cbfc87ba-5d19-ac9e-4268-a84131cf762e.png)
![スクリーンショット 2022-12-11 19.53.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/402cb653-0865-181c-b6ce-b8caed6a748c.png)


## 3.接続確認

今回はドキュメント通り、Amazon ECS で実行されていない、または同じ名前空間にない外部アプリケーションから実行します。
ですので、タスクの IP アドレスとタスク定義のポート番号を使用して、Service Connect プロキシ経由でアプリケーションに接続してみます。
下記はサブネットに EC2 を起動し、プライベートIPに対して curl を実行しています。

```
[ec2-user@ip-10-0-0-186 ~]$ curl -v http://10.0.2.34:80
*   Trying 10.0.2.34:80...
* Connected to 10.0.2.34 (10.0.2.34) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.0.2.34
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< server: envoy
< date: Sun, 11 Dec 2022 11:00:05 GMT
< content-type: text/html
< content-length: 615
< last-modified: Wed, 19 Oct 2022 07:56:21 GMT
< etag: "634fada5-267"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 0
< 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 10.0.2.34 left intact
```

アプリケーションは nginx ですが、`server: envoy` ヘッダーには Service Connect プロキシが使用されていることが示されています。

最後に、CloudWatch Logs から動作していることを確認できました。
![スクリーンショット 2022-12-13 1.07.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/2eb93e74-1972-3cf9-15bf-14525ba441f0.png)