ECS Service Connectで別クラスターのECSサービスに接続できるか試してみた

ECS Service Connectで別クラスターのECSサービスに接続できるか試してみた

Clock Icon2025.07.21

別のECSクラスターにあるECSサービスにECS Service Connectで接続できるかを調査してみました。

結論

可能です。

ECSサービスが別のECSクラスターに所属していても、同リージョンで同一のCloud Map名前空間を選択すれば、Service Connect経由で通信できます。

名前空間内のサービスは、同じ AWS アカウント内の同じ AWS リージョン内の異なる Amazon ECS クラスターに分散させることができます。

Use Service Connect to connect Amazon ECS services with short names - Amazon Elastic Container Service

Clusterが異なるVPCにある場合は、VPC間の相互通信のためにVPCピアリングなど疎通性の確保は必要になります。

やってみた

実際に以下の構成をCDKで作成して、疎通を試してみました。

ECS Service Connect (1).png

CDKでリソースをデプロイする

以下のコードを用意します。

ServerとClientt用のECSサービスをそれぞれ別クラスターにデプロイします。

ServerはNginxコンテナを起動します。

Client側では、NginxコンテナにService Connect経由で疎通確認を行う処理を入れています。

bin/ecs-service-connect.ts
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { EcsServiceConnectStack } from '../lib/ecs-service-connect-stack';

const app = new cdk.App();
new EcsServiceConnectStack(app, 'EcsServiceConnectStack', {});
lib/ecs-service-connect-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as servicediscovery from 'aws-cdk-lib/aws-servicediscovery';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class EcsServiceConnectStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC作成
    const vpc = new ec2.Vpc(this, 'ServiceConnectVpc', {
      maxAzs: 2,
      natGateways: 1,
    });

    // Service Connect用のCloud Mapネームスペース作成
    const namespace = new servicediscovery.PrivateDnsNamespace(this, 'ServiceConnectNamespace', {
      name: 'service-connect-test',
      vpc
    });

    // Server用のECSクラスター
    const serverCluster = new ecs.Cluster(this, 'ServerCluster', {
      vpc,
      clusterName: 'server',
    });

    // Client用のECSクラスター
    const clientCluster = new ecs.Cluster(this, 'ClientCluster', {
      vpc,
      clusterName: 'client',
    });

    // サーバータスク定義
    const serverTaskDefinition = new ecs.FargateTaskDefinition(this, 'ServerTaskDef', {
      memoryLimitMiB: 512,
      cpu: 256,
    });

    const serverContainer = serverTaskDefinition.addContainer('ServerContainer', {
      image: ecs.ContainerImage.fromRegistry('nginx:latest'),
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'server',
        logGroup: new logs.LogGroup(this, 'ServerLogGroup', {
          logGroupName: '/aws/ecs/server-service-connect',
          retention: logs.RetentionDays.ONE_WEEK,
          removalPolicy: cdk.RemovalPolicy.DESTROY,
        }),
      }),
    });

    serverContainer.addPortMappings({
      containerPort: 80,
      protocol: ecs.Protocol.TCP,
      name: 'web',
    });

    // クライアントタスク定義
    const clientTaskDefinition = new ecs.FargateTaskDefinition(this, 'ClientTaskDef', {
      memoryLimitMiB: 512,
      cpu: 256,
    });

    const clientContainer = clientTaskDefinition.addContainer('ClientContainer', {
      image: ecs.ContainerImage.fromRegistry('curlimages/curl:latest'),
      command: ['sh', '-c', 'while true; do curl -s http://server:80 && sleep 30; done'],
      logging: ecs.LogDrivers.awsLogs({
        streamPrefix: 'client',
        logGroup: new logs.LogGroup(this, 'ClientLogGroup', {
          logGroupName: '/aws/ecs/client-service-connect',
          retention: logs.RetentionDays.ONE_WEEK,
          removalPolicy: cdk.RemovalPolicy.DESTROY,
        }),
      }),
    });

    // Server用のSecurity Group
    const serverSecurityGroup = new ec2.SecurityGroup(this, 'ServerSecurityGroup', {
      vpc,
      description: 'Security group for Server service',
      allowAllOutbound: true,
    });

    // Client用のSecurity Group
    const clientSecurityGroup = new ec2.SecurityGroup(this, 'ClientSecurityGroup', {
      vpc,
      description: 'Security group for Client service',
      allowAllOutbound: true,
    });

    // Server Security GroupでClientからのアクセスを許可
    serverSecurityGroup.addIngressRule(
      clientSecurityGroup,
      ec2.Port.tcp(80),
      'Allow HTTP access from Client'
    );

    // サーバーサービス(Service Connect有効)
    const serverService = new ecs.FargateService(this, 'ServerService', {
      cluster: serverCluster,
      serviceName: 'server',
      taskDefinition: serverTaskDefinition,
      desiredCount: 1,
      enableExecuteCommand: true,
      securityGroups: [serverSecurityGroup],
      serviceConnectConfiguration: {
        namespace: namespace.namespaceArn,
        services: [
          {
            portMappingName: 'web',
            discoveryName: 'server',
            dnsName: 'server',
            port: 80,
          },
        ],
      },
    });

    // クライアントサービス(Service Connect有効、クライアントとして設定)
    const clientService = new ecs.FargateService(this, 'ClientService', {
      cluster: clientCluster,
      serviceName: 'client',
      taskDefinition: clientTaskDefinition,
      desiredCount: 1,
      enableExecuteCommand: true,
      securityGroups: [clientSecurityGroup],
      serviceConnectConfiguration: {
        namespace: namespace.namespaceArn,
        // 空の配列でクライアント専用モードに設定
        services: [],
      },
    });

    // サーバーサービスデプロイ後にクライアントサービスを起動
    clientService.node.addDependency(serverService);

    // ECS Execのための権限をタスクロールに追加
    const ecsExecPolicy = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      actions: [
        'ssmmessages:CreateControlChannel',
        'ssmmessages:CreateDataChannel',
        'ssmmessages:OpenControlChannel',
        'ssmmessages:OpenDataChannel',
      ],
      resources: ['*'],
    });

    serverTaskDefinition.taskRole.addToPrincipalPolicy(ecsExecPolicy);
    clientTaskDefinition.taskRole.addToPrincipalPolicy(ecsExecPolicy);
  }
}

AWS認証情報をコンソールにセットして、cdk deployを実行します。

npx cdk deploy

動作確認

CDKデプロイ完了後に動作確認を行います。

ECS Service clientのログを確認します。

以下のようにcurlのレスポンスとして、NginxのWelcomeページが返ってきていることが分かります。

サービスログ___Elastic_Container_Service___ap-northeast-1.png

ECS Execでclientコンテナに入って確認してみます。

# タスクIDを取得
CLIENT_TASK_ID=$(aws ecs list-tasks --cluster client --query 'taskArns[0]' --output text)

# Consumerコンテナに接続
aws ecs execute-command \
  --cluster consumer \
  --task $CLIENT_TASK_ID \
  --container ClientContainer \
  --interactive \
  --command "/bin/sh"

/etc/hostsにproducerサービスのエントリがあることを確認できます。

cat /etc/hosts
出力例
127.0.0.1 localhost
10.0.196.20 ip-10-0-196-20.ap-northeast-1.compute.internal
127.255.0.1 server
2600:f0f0:0:0:0:0:0:1 server

serverに対してcurlしてレスポンスが返ってくることも確認できました。

curl server
出力例
<!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>

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.