背景
フロントエンドからバックエンドへの通信を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
}
次にディスカバリーサービスを定義。
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"
}
}
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
}
}
通信先
上記のaws_service_discovery_private_dns_namespace
とaws_service_discovery_service
を組み合わせた以下の名前で通信できます。
my-ecs-service.backend.local
もちろん、名前解決してくれます。とりあえず、サービス間での通信をさっと作りたいという要件にはピッタリだと思います。