背景

フロントエンドからバックエンドへの通信をECSタスク間で実施したいです。

当初はフロントエンドコンテナ(Reactで実装)とバックエンドコンテナ(golangで実装)を1つのタスクにのせようと考えていました。

ですが、今後の拡張性を考えたり、バックエンドをAPI-GWに載せ替えたりすることを考えると別のタスクに配置したほうが良いと考えました。

また、デプロイについて、同一タスクだとフロントエンドだけ変更したとしても、バックエンドもリリースしなおすことになります。それは非効率的だと考えました。

調べたこと

以下の記事がとても参考になりました。

改めてECSサービス間通信を整理する - NRIネットコムBlog

記事には、こんな比較表がありました。

方式 通信の可観測性 通信の信頼性 通信の柔軟性 設定の容易さ 追加コンピュートリソース 追加インフラ 特記事項
ELB 不要 ELBの機能を使用可能。BGデプロイ選択可能
Service Discovery 不要 不要 同一VPCから接続可能
App Mesh 不要 App Meshの管理が必要
Service Connect 不要 他クラスターや他VPCのECSサービスから接続可能

まずは、ELB方式。インフラを追加で構築するのは避けたいです。追加でALB作るのは気が重いです。また、APIに対して常にヘルスチェックが必要です。ヘルスチェックはどのパスにするんだろう。専用のパスを作るのもなんだかって考えると面倒。ということで、ELB方式は外します。

そして、SeriviceConnect方式。比較的新しいサービスの「Service Connect」を使いたいと思ってました。が、サイドカーでコンテナが必要です。サイドカーを使用すると、タスク定義も書き直す必要がある。気が重い。

AppMesh方式もAppMeshの管理が必要で気が乗らない。

ということで、Service Discoveryで構築することにしました。

実装

terraformでやります。

まずは、通信先となるタスクの名前を決めます。この例だと、backend.localがドメインっぽい雰囲気で定義しています。

resource "aws_service_discovery_private_dns_namespace" "this" {
  name        = "backend.local"  
  description = "Private DNS namespace for ECS services"
  vpc         = var.vpc_id
}
terraform

次にディスカバリーサービスを定義。

resource "aws_service_discovery_service" "service" {
  name         = "my-ecs-service"
  
  dns_config {
  namespace_id = aws_service_discovery_private_dns_namespace.this.id

    dns_records {
      type = "A"
      ttl  = 10
    }
    # MULTIVALUE ルーティングの場合、複数タスクの IP を DNS レコードに登録します。
    routing_policy = "MULTIVALUE"
  }
}
terraform

ECSサービスの定義にservice_registriesを追加します。

resource "aws_ecs_service" "this" {
  name    = "${var.name_prefix}-service"
  cluster = aws_ecs_cluster.backend.id
  desired_count          = var.backend_desired_count
  launch_type            = "FARGATE"
  scheduling_strategy    = "REPLICA"
  enable_execute_command = true
  force_new_deployment   = true

〜省略〜

  service_registries {
    registry_arn = aws_service_discovery_service.service.arn
  }
}
terraform

通信先

上記のaws_service_discovery_private_dns_namespaceaws_service_discovery_serviceを組み合わせた以下の名前で通信できます。

my-ecs-service.backend.local
fallback

もちろん、名前解決してくれます。とりあえず、サービス間での通信をさっと作りたいという要件にはピッタリだと思います。