Amazon ECSの新ネットワーク機能”Service Connect”をAWS Copilot CLIで設定してみた #reinvent

2022.12.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

prismatixのとばち(@toda_kk)です。

AWS re:Invent 2022にて、Amazon ECS Service Connectという新たな機能が発表されました。

マイクロサービスアーキテクチャなどの構成において、ECSサービス間の通信を簡単に設定できるようにしたアップデートとなっています。詳細については、下記の記事などをご参照ください。

アップデートのアナウンスと共に公開されたAWS公式ブログによると、今回の新機能はCloudFormationやAWS CDKといったIaCツールでもサポートしているそうです。

ECS Service Connect is fully supported in AWS CloudFormation, AWS CDK, AWS Copilot, and AWS Proton for infrastructure provisioning, code deployments, and monitoring of your services.

CDKに関しては、既に試してみた記事が本ブログにて上がっています。

本記事では、AWS Copilotを用いてECS Service Connectを設定する手順を確認してみます。

AWS Copilotについて

AWS Copilotの概要については下記の記事で詳しく解説しているので、ぜひご参照ください。

Service Connectの発表後、AWS re:Invent 2022開催中にCopilotでも新たなバージョン(v1.24.0)がリリースされサポートが開始されました。

AWS CopilotでService Connectを設定してみる

Copilotでは、copilot svc init コマンドを実行した後、ECSサービスに関する設定を "manifest.yml" というファイルで簡単に管理することができます。

Service Connectを設定する際は、このファイルに下記の項目を追加するだけで済みます。

Service Connectの有効化

network:
  connect: true

例えば、他のECSサービスからの通信を受け付けたいバックエンドに対してService Connectを設定する際は、下記のような設定になるかと思います。

copilot/backend/manifest.yml

name: backend
type: Backend Service

image:
  port: 80
  build: backend/Dockerfile

cpu: 256
memory: 512
count: 2
exec: true

network:
  connect: true
  vpc:
    placement: 'private'

network.connect: true を設定する他に、 image.port でリクエストを受け付けるポート番号を指定しておく必要があります。

特にポートを外部に公開する必要のないクライアントとなるServiceに対して、バックエンドのエンドポイントを名前解決できるようにService Connectを有効化する際は、 image.port の項目なしで設定すればOKです。

copilot/client/manifest.yml

name: client
type: Backend Service

image:
  build: client/Dockerfile

cpu: 256
memory: 512
count: 2
exec: true

network:
  connect: true

上記のマニフェストファイルを元に copilot svc deploy でECSサービスをデプロイした後、 クライアントからバックエンドのECSサービスを名前解決できるか確認してみます。

copilot svc exec コマンドを使うと、ECS ExecによってECSサービスのタスクに対してコマンドを実行できます。

Service Connectの動作確認

$ copilot svc exec --name client --task-id xxxxxxx
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.

Starting session with SessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx

# curl http://backend:80
OK

# cat /etc/hosts
127.0.0.1 localhost
10.1.0.222 ip-10-1-0-222.ap-northeast-1.compute.internal
127.255.0.1 backend
2600:f0f0:0:0:0:0:0:1 backend

Copilot Service名(上の例では backend )がそのままECSサービスのエンドポイントとなっており、クライアントのECSサービスから名前解決および通信ができていることが確認できます。

Service Discoveryとの関連

ECSでは以前よりサービス間通信の仕組みとしてService Discovery(サービス検出)の機能が提供され、Copilotでも設定が可能でしたが、その場合もマニフェストファイルから image.port でポート番号を指定することで設定を有効化する手順となっていました。

実はCopilotでは、Serviceの設定と関係なくEnvironmentを作成した際にCloud Mapの名前空間リソースが作成されます。より具体的には、 copilot env init コマンドを実行した後、 copilot env deploy コマンドを実行したタイミングです。

作成される名前空間リソースは ${COPILOT_ENVIRONMENT_NAME}.${COPILOT_APPLICATION_NAME}.local といった命名規則となります。

そして、Copilotを利用してService DiscoveryおよびService Connectを設定した際は、同じApplication + Environmentであれば上記の名前空間を共有することになります。

Cloud Mapの名前空間には、エンドポイントとなるドメイン名とECSタスクを関連づけるための「サービス」というリソースが作成されるのですが、マニフェストファイルの設定内容によってはCopilot Serviceに応じて複数の「サービス」が作成されます。

  • image.port でポート番号が設定されている場合: Service Connectの「サービス」が作成される
    • 上記の例では、バックエンドのCopilot Serviceのみ作成されている
  • network.connect: true が設定されている場合: Service Discoveryの「サービス」が作成される
    • 上記の例では、クライアントとバックエンドのCopilot Serviceの両方で作成されている

この辺り、Copilotによって具体的なAWSリソースについては抽象化してくれるのですが、実際にどういったAWSリソースが作成されているか把握しておくと、トラブルシューティングの際などいざという時に混乱を防ぐことができるかと思います。

ちなみに、Service Connectではエンドポイントとなるドメイン名に関してエイリアスを設定することができます。

Copilotを用いてService Discoveryを既に設定している場合、ドメイン名は ${COPILOT_SERVICE_NAME}.${COPILOT_ENVIRONMENT_NAME}.${COPILOT_APPLICATION_NAME}.local といった命名規則によって決まるため、下記のように設定することでService Connectに簡単に移行することができます。

aliasの設定

network:
  connect:
    alias: ${COPILOT_SERVICE_NAME}.${COPILOT_ENVIRONMENT_NAME}.${COPILOT_APPLICATION_NAME}.local

ただし、上記の命名規則となったのはCopilot v1.9.0以降となっています。それ以前のバージョンを利用してService Discoveryを設定している場合には、命名規則が異なっておりますのでご注意ください。

詳細に関しては下記ページをご参照ください。

Copilotでは簡単にService Connectを設定できる

以上、Copilotによる設定手順と、作成されるリソースについて確認してみました。

マニフェストファイルに項目を数行追加するだけでService Connectを有効化できるので、既にCopilotを使ってECSサービスを構築されている方はぜひ試してみてください。

以上、prismatixのとばち(@toda_kk)でした。