🚴🏻‍♀️

Amazon ECS Service Connectがよく分からんので触っておく

2022/11/29に公開

概要

2022.11.29のAWS Updateで「Amazon ECS Service Connect」が発表されました。周りの方々は盛り上がっていたけど、自分があんまりコンテナ触らない為か盛り上がりが体感出来なかったので触っておきましょう。というアウトプット。

ざっくり

AWS Cloud Mapが提供する名前空間を使って論理名でサービスを参照・接続でき、ロードバランサーを導入・設定しなくてもECSタスク間で自動的にトラフィックを分散させることができる。

参考

https://aws.amazon.com/jp/blogs/aws/new-amazon-ecs-service-connect-enabling-easy-communication-between-microservices/

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

手順

■ecsクラスターの作成

--service-connect-defaultsでAWS Cloud Mapに追加するnamespaceの設定を行いつつクラスターを作成

cmd

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

result

result
{
    "cluster": {
        "clusterArn": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/tutorial",
        "clusterName": "tutorial",
        "status": "PROVISIONING",
        "registeredContainerInstancesCount": 0,
        "runningTasksCount": 0,
        "pendingTasksCount": 0,
        "activeServicesCount": 0,
        "statistics": [],
        "tags": [],
        "settings": [
            {
                "name": "containerInsights",
                "value": "enabled"
            }
        ],
        "capacityProviders": [],
        "defaultCapacityProviderStrategy": [],
        "attachments": [
            {
                "id": "f23637c8-dd3a-4c59-aefc-842f270a4ca0",
                "type": "sc",
                "status": "ATTACHING",
                "details": []
            }
        ],
        "attachmentsStatus": "UPDATE_IN_PROGRESS",
        "serviceConnectDefaults": {
            "namespace": "arn:aws:servicediscovery:ap-northeast-1:123456789012:namespace/ns-vdpucf3aztj23ecw"
        }
    }
}

result-image

クラスタが出来ました。名前空間というページが増えていますね

■AWS Cloud Mapに追加されていることを確認

service-connectという名前空間が追加されていることを確認

cmd

aws servicediscovery \
  list-namespaces \
  --region ap-northeast-1 \
  --filters Name="NAME",Values="service-connect",Condition="EQ"

result

result
{
    "Namespaces": [
        {
            "Id": "ns-vdpucf3aztj23ecw",
            "Arn": "arn:aws:servicediscovery:ap-northeast-1:123456789012:namespace/ns-vdpucf3aztj23ecw",
            "Name": "service-connect",
            "Type": "HTTP",
            "Properties": {
                "DnsProperties": {
                    "SOA": {}
                },
                "HttpProperties": {
                    "HttpName": "service-connect"
                }
            },
            "CreateDate": "2022-11-29T11:33:22.132000+00:00"
        }
    ]
}

result-image

■タスク定義ファイル作成

タスク定義を記載したjsonファイルを準備し、そのファイルを読み込みタスク定義を構築する
containerDefinitions[].nameがService Connectで追加された名前空間指定の箇所

cmd

cat << EOF > 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"
}
EOF
aws ecs register-task-definition \
  --cli-input-json file://service-connect-nginx.json

result

result
{
    "taskDefinition": {
        "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/service-connect-nginx:1",
        "containerDefinitions": [
            {
                "name": "webserver",
                "image": "public.ecr.aws/docker/library/nginx:latest",
                "cpu": 100,
                "portMappings": [
                    {
                        "containerPort": 80,
                        "hostPort": 80,
                        "protocol": "tcp",
                        "name": "nginx",
                        "appProtocol": "http"
                    }
                ],
                "essential": true,
                "environment": [],
                "mountPoints": [],
                "volumesFrom": [],
                "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                        "awslogs-group": "/ecs/service-connect-nginx",
                        "awslogs-region": "ap-northeast-1",
                        "awslogs-stream-prefix": "nginx"
                    }
                }
            }
        ],
        "family": "service-connect-nginx",
        "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
        "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
        "networkMode": "awsvpc",
        "revision": 1,
        "volumes": [],
        "status": "ACTIVE",
        "requiresAttributes": [
            {
                "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
            },
            {
                "name": "ecs.capability.execution-role-awslogs"
            },
            {
                "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
            },
            {
                "name": "com.amazonaws.ecs.capability.task-iam-role"
            },
            {
                "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
            },
            {
                "name": "ecs.capability.task-eni"
            }
        ],
        "placementConstraints": [],
        "compatibilities": [
            "EC2",
            "FARGATE"
        ],
        "cpu": "256",
        "memory": "512",
        "registeredAt": "2022-11-29T11:57:49.196000+00:00",
        "registeredBy": "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_AWSAdministratorAccess_bf8b32a74b14b58e/ShigeruOda"
    }
}

result-image

タスク定義が出来ました。

■サービス作成

  • サービス定義を記載したjsonファイルを準備し、そのファイルを読み込みサービスを構築する
  • セキュリティグループ、サブネットはデフォルトVPCを利用し、inboundのhttpポート開放
  • serviceConnectConfigurationがService Connectの為の設定となります

cmd

cat << EOF > 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-34692f46"
            ],
            "subnets": [
                "subnet-f9bb89a2",
                "subnet-9a4c8cd2",
                "subnet-725e9559"
            ]
           }
    },
    "platformVersion": "LATEST",
    "propagateTags": "SERVICE",
    "serviceName": "service-connect-nginx-service",
    "serviceConnectConfiguration": {
        "enabled": true,
        "namespace": 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"
}
EOF
aws ecs create-service \
  --cluster tutorial \
  --cli-input-json file://service-connect-nginx-service.json

result

result
{
    "service": {
        "serviceArn": "arn:aws:ecs:ap-northeast-1:123456789012:service/tutorial/service-connect-nginx-service",
        "serviceName": "service-connect-nginx-service",
        "clusterArn": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/tutorial",
        "loadBalancers": [],
        "serviceRegistries": [],
        "status": "ACTIVE",
        "desiredCount": 1,
        "runningCount": 0,
        "pendingCount": 0,
        "launchType": "FARGATE",
        "platformVersion": "LATEST",
        "platformFamily": "Linux",
        "taskDefinition": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/service-connect-nginx:1",
        "deploymentConfiguration": {
            "deploymentCircuitBreaker": {
                "enable": false,
                "rollback": false
            },
            "maximumPercent": 200,
            "minimumHealthyPercent": 0
        },
        "deployments": [
            {
                "id": "ecs-svc/2314065177896282440",
                "status": "PRIMARY",
                "taskDefinition": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/service-connect-nginx:1",
                "desiredCount": 1,
                "pendingCount": 0,
                "runningCount": 0,
                "failedTasks": 0,
                "createdAt": "2022-11-29T12:38:08.284000+00:00",
                "updatedAt": "2022-11-29T12:38:08.284000+00:00",
                "launchType": "FARGATE",
                "platformVersion": "1.4.0",
                "platformFamily": "Linux",
                "networkConfiguration": {
                    "awsvpcConfiguration": {
                        "subnets": [
                            "subnet-725e9559",
                            "subnet-9a4c8cd2",
                            "subnet-f9bb89a2"
                        ],
                        "securityGroups": [
                            "sg-34692f46"
                        ],
                        "assignPublicIp": "ENABLED"
                    }
                },
                "rolloutState": "IN_PROGRESS",
                "rolloutStateReason": "ECS deployment ecs-svc/2314065177896282440 in progress.",
                "serviceConnectConfiguration": {
                    "enabled": true,
                    "namespace": "arn:aws:servicediscovery:ap-northeast-1:123456789012:namespace/ns-vdpucf3aztj23ecw",
                    "services": [
                        {
                            "portName": "nginx",
                            "discoveryName": "nginx",
                            "clientAliases": [
                                {
                                    "port": 80,
                                    "dnsName": "nginx.service-connect"
                                }
                            ]
                        }
                    ],
                    "logConfiguration": {
                        "logDriver": "awslogs",
                        "options": {
                            "awslogs-group": "/ecs/service-connect-proxy",
                            "awslogs-region": "ap-northeast-1",
                            "awslogs-stream-prefix": "service-connect-proxy"
                        },
                        "secretOptions": []
                    }
                },
                "serviceConnectResources": [
                    {
                        "discoveryName": "nginx",
                        "discoveryArn": "arn:aws:servicediscovery:ap-northeast-1:123456789012:service/srv-tsw4bbsna23fxcjx"
                    }
                ]
            }
        ],
        "roleArn": "arn:aws:iam::123456789012:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
        "events": [],
        "createdAt": "2022-11-29T12:38:08.284000+00:00",
        "placementConstraints": [],
        "placementStrategy": [],
        "networkConfiguration": {
            "awsvpcConfiguration": {
                "subnets": [
                    "subnet-725e9559",
                    "subnet-9a4c8cd2",
                    "subnet-f9bb89a2"
                ],
                "securityGroups": [
                    "sg-34692f46"
                ],
                "assignPublicIp": "ENABLED"
            }
        },
        "schedulingStrategy": "REPLICA",
        "deploymentController": {
            "type": "ECS"
        },
        "createdBy": "arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/ap-northeast-1/AWSReservedSSO_AWSAdministratorAccess_bf8b32a74b14b58e",
        "enableECSManagedTags": true,
        "propagateTags": "SERVICE",
        "enableExecuteCommand": true
    }
}

result-image

サービスが起動しました

■接続確認

タスクを確認するとパブリック IPが確認できます。
またコンテナが2つ起動しています

  • webserver : nginxが起動しているコンテナ
  • ecs-service-connect-fphC624 : ecs-service-connectと繋げているコンテナ

パブリック IPでhttpアクセスするとnginxが確認できます。

ecs-service-connectのCloudWatchを見るとApp MeshENVOYが稼働していることを確認できます。

time="2022-11-29T12:49:57Z" level=info msg="App Mesh Environment Variables: [APPMESH_RESOURCE_ARN=arn:aws:ecs:ap-northeast-1:123456789012:task-set/tutorial/service-connect-nginx-service/ecs-svc/2314065177896282440 APPMESH_METRIC_EXTENSION_VERSION=1 APPMESH_XDS_ENDPOINT=unix:///var/run/ecs/appnet/relay/appnet_relay_listener.sock]"
time="2022-11-29T12:49:57Z" level=info msg="Envoy Environment Variables: [ENVOY_ENABLE_IAM_AUTH_FOR_XDS=0 ENVOY_ADMIN_MODE=UDS ENVOY_CONCURRENCY=2]"
time="2022-11-29T12:49:57Z" level=info msg="Agent Environment Variables: [APPNET_LISTENER_PORT_MAPPING={\"egress\":42084,\"ingress-nginx\":43727} APPNET_AGENT_ADMIN_UDS_PATH=/var/run/ecs/appnet/agent/appnet_admin.sock APPNET_ENVOY_RESTART_COUNT=3 APPNET_AGENT_ADMIN_MODE=UDS]"
time="2022-11-29T12:49:57Z" level=info msg="Disabling IAM Auth for xDS, xDS endpoint UDS path: /var/run/ecs/appnet/relay/appnet_relay_listener.sock"
[2022-11-29 12:49:57.559][1][info] [AppNet Agent] Server started, /var/run/ecs/appnet/agent/appnet_admin.sock

CloudMapを確認するとnginxがecsより追加されていることが確認できます

さらにCloudMapを詳細に確認するとIPアドレスやポートが想定通りであることが確認できます。

ECSの名前空間ページの方がCloud Mapより整理されていて分かりやすいね

感想

ELBやApp Meshを使わないでもCloud Mapで楽にマイクロサービスを実現できる
マイクロサービスをやりたいが、App Meshの運用までやりたくない場合にはコレで良いのでは無いだろうか。。。

Discussion