Komunikacja mikroserwisów w AWS ECS bez Load Balancera dzięki DNS Service Discovery¶
Pewnego dnia zamarzyłem o prostej architekturze na AWS. Kilka microserwisów z FastAPI, prosta baza SQLLite, bo RDS był dla mnie za drogi, na etapie nauki AWS... Nie wspominając o kosztach ALB (Application Load Balancer).
Chciałem tym razem poeksperymentować z AWS ECS, zależało mi szybkiej komunikacji pomiędzy serwisami, niezawodnej i skalowalnej. Standardowe rozwiązanie to Application Load Balancer pomiędzy mikroserwisami.
ALE Chciałem to zbudować bez użycia Load Balancera, wykorzystując AWS Cloud Map i **DNS-based Service Discovery"
1. Problem: Komunikacja bez Load Balancera¶
Jak to wygląda zazwyczaj?¶
W klasycznych projektach na AWS ECS, komunikacja między mikroserwisami odbywa się przez Load Balancer. Przykładowo, jeśli mamy user-service
, który potrzebuje komunikować się z auth-service
, zwykle wygląda to tak:
- Tworzymy Application Load Balancer (ALB).
- Rejestrujemy
auth-service
jako target group. - Używamy adresu ALB do komunikacji, np.:
http://auth-alb.mycompany.internal/login
Problemy z Load Balancerem:¶
- Koszt – Każdy ALB to dodatkowy, stały koszt. Przy większej liczbie usług koszty rosną znacząco. Kosztuje gdy ruch jest niewielki, a dla samego POC, był poprostu zbędny.Poczułem strach w oczach i chciałem się poddać, malutki projekt, ale nie poddałem się i nie polecialem po Siano do Bociana.
- Złożoność – Trzeba zarządzać routingiem, target groups, portami i health checkami.
- Opóźnienia – Zapytania są kierowane przez warstwę ALB, co niepotrzebnie wydłuża ścieżkę, zwłaszcza w obrębie tej samej sieci VPC.
A gdyby tak… bez Load Balancera?¶
W prostych i średnich architekturach mikroserwisowych można całkowicie zrezygnować z Load Balancera. AWS ECS umożliwia użycie DNS Service Discovery, co pozwala usługom komunikować się bezpośrednio przez nazwę DNS — prosto, tanio i szybko.
2. Rozwiązanie: DNS-based Service Discovery z AWS Cloud Map¶
Co to jest Cloud Map?¶
AWS Cloud Map to usługa umożliwiająca tworzenie nazw usług (service names), które dynamicznie wskazują na działające instancje ECS (lub inne). ECS automatycznie rejestruje uruchomione kontenery w Cloud Map.
Dzięki temu, inny mikroserwis może po prostu zapytać:
stock-service.trading.platform
…i Cloud Map przekaże prawidłowy adres IP kontenera.
3. Konfiguracja w Terraform¶
Poniższy kod tworzy wpis w Cloud Map z obsługą rekordów A
i SRV
oraz strategią MULTIVALUE
.
resource "aws_service_discovery_service" "service_discovery" {
name = var.service_name
dns_config {
namespace_id = var.namespace_id
dns_records {
type = "A"
ttl = 60
}
dns_records {
type = "SRV"
ttl = 60
}
routing_policy = "MULTIVALUE"
}
}
To był najtrudniejszy dla mnie fragment wpisów w DNS records, a w zasadzie routingu.
- A - klastyczny record DNS z adresem IP kontenera
- SRV - zawiera IP, port, wage i protokół
- MULTIVALUE - DNS może zwrócić wiele IP
Co robi MULTIVALUE?
Gdy inne mikroserwisy zapytają DNS o stock-service.trading-platform.local
, to:
Cloud Map (z routing policy = MULTIVALUE) zwróci wszystkie aktywne IP (i porty jeśli używasz SRV) zarejestrowane przez ECS dla tego serwisu. To znaczy, że klient dostaje 3 rekordy IP, z których może wybrać dowolny — lub po kolei próbować wszystkich, jeśli jeden nie działa.
Zwycięstwo!
Albo i nie?
A więc tworzymy DNS
module "dns_namespace" {
source = "./modules/core/dns_namespace"
namespace_name = "trading-platform.local"
vpc_id = var.vpc_id # Set this variable in root variables
}
Tworzymy service discovery:
module "stock_data_service_discovery" {
source = "./modules/core/service_discovery"
service_name = "stock-service"
namespace_id = module.dns_namespace.namespace_id
}
module "signal_processing_service_discovery" {
source = "./modules/core/service_discovery"
service_name = "signal-processing-service"
namespace_id = module.dns_namespace.namespace_id
}
Utworzenie nowej usługi w ECS:
resource "aws_ecs_service" "signal_processing_service" {
name = "signal-processing-service"
cluster = var.ecs_cluster_id
task_definition = aws_ecs_task_definition.task_definition.arn
desired_count = 1
launch_type = "FARGATE"
service_registries {
registry_arn = aws_service_discovery_service.signal_processing_service_discovery.arn
port = "80"
}
network_configuration {
subnets = var.subnets
security_groups = var.security_groups
}
depends_on = [aws_service_discovery_service.signal_processing_service_discovery]
}
Tutaj koniecznie trzeba było podpiąć nasz Sevice Discovery
service_registries {
registry_arn = aws_service_discovery_service.signal_processing_service_discovery.arn
port = "80"
}
czyli rejestrujemy naszą usługe w AWS Cloud Map - czyli mamy nasz DNS-based service discovery!
i wszystko razem jako serwis:
module "stock_data_service_ecs" {
source = "./modules/services/stock_data_service"
subnets = var.subnets
security_groups = var.security_groups
ecs_cluster_id = aws_ecs_cluster.ecs_cluster.id
efs_file_system_id = module.efs.file_system_id
ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn
ecr_repository_url = module.stock_data_ecr.repository_url
namespace_id = module.dns_namespace.id
}
module "signal_processing_service_ecs" {
source = "./modules/services/signal_processing_service"
subnets = var.subnets
security_groups = var.security_groups
ecs_cluster_id = aws_ecs_cluster.ecs_cluster.id
efs_file_system_id = module.efs.file_system_id
ecs_task_execution_role_arn = module.iam.ecs_task_execution_role_arn
ecr_repository_url = module.signal_processing_ecr.repository_url
namespace_id = module.dns_namespace.id
}
4. Praktyka: Jak mikroserwisy odnajdują się nawzajem?¶
Przykład serwis: stock-service.trading.platform' dostępny z
signal-service.
Czyli mamy serwis który odpowiada za CRUD operacje związanie z notowaniami -
stock-service', a drugi serwis signal-service
chce pobrać dane i dokonaćp pewnej operacji.
curl http://stock-service.trading.platform/api/v1/data/TICKER_1
Gdy mamy 3 repliki tasków na ECS to otrzymujemy takie odpowiedzi z DNS'a:
$ dig stock-service.trading-platform.local
;; ANSWER SECTION:
stock-service.trading.local. 60 IN A 10.0.1.101
stock-service.trading.local. 60 IN A 10.0.1.102
stock-service.trading.local. 60 IN A 10.0.1.103
5. Wnioski i czego się nauczyłem¶
Czasami dobrze mieć mały budżet na swoją naukę :) Przy pomocy AWS Cloud Map i DNS Service Discovery udało się zbudować lekką, elastyczną i tanią komunikację mimikroserwisów bez użycia klasyczenego Load Balancera. Idealne dla środowisk testowych, ,architektur, gdzie ważna jest prostota i niskie koszty!
6. O mnie¶
Jestem początkującym pasjonatem chmury, architektury systemów rozproszonych i prostych, eleganckich (?) rozwiązań. Buduję własną platformę opartą o mikroserwisy i dzielę się tym, czego uczę się po drodze!!
Dzięki!