Lab 33 — Descoberta e malha: Service Connect, VPC Lattice ou nada
O problema, e a empresa que o tem
A Cadência extraiu o serviço de estoque do monólito no L31, e no L32 decidiu chamar "estoque" de forma SÍNCRONA: "pedidos" não pode confirmar um pedido sem saber, na hora, se a loja tem o produto — aceitar e descobrir depois que faltou é o tipo de erro que vira devolução e reclamação. A decisão de negócio estava certa. A forma de "pedidos" achar "estoque" na rede, não.
Para "pedidos" encontrar "estoque", alguém rodou describe-tasks, copiou o IP privado de uma task que estava rodando e colou em ESTOQUE_HOST, uma variável de ambiente da definição da task. Funcionou no primeiro deploy, e continuou funcionando — até o próximo deploy de "estoque", quando a task antiga morreu, uma nova subiu com outro endereço (modo awsvpc atribui interface de rede por TASK, não por serviço), e "pedidos" passou a chamar um IP que não responde mais.
O sintoma não chega como erro de negócio: chega como demora. Toda confirmação de pedido espera o timeout inteiro do cliente HTTP antes de falhar, porque não existe nada do outro lado da conexão para recusar rápido — é silêncio de rede, não uma resposta. E como cada deploy de "estoque" muda o IP, o defeito não é intermitente: é determinístico. Só que ninguém liga "publiquei o estoque hoje de manhã" a "pedidos parou de confirmar às 14h" — os dois times nem sabem que compartilham essa fragilidade.
O que este laboratório NÃO é
Não é a decisão entre chamada síncrona e assíncrona — essa é o L32, e este módulo assume que a chamada HTTP síncrona já foi escolhida por um motivo de negócio real. Também não é AWS App Mesh: a AWS descontinuou o serviço para clientes novos em setembro de 2024, com desligamento anunciado para 30 de setembro de 2026, e aponta Service Connect e VPC Lattice como sucessores — por isso App Mesh nem entra como alternativa viável adiante.
O que você vai conseguir fazer
Objetivos verificáveis: cada um se prova com um comando na seção de implantação, não com a sensação de ter entendido.
- Explicar por que gravar IP de task em variável de ambiente ou SSM Parameter quebra a cada implantação, a partir de como o modo awsvpc atribui endereço por task.
- Nomear as três formas de descoberta de serviço na AWS deste laboratório e a pergunta específica que cada uma responde.
- Distinguir isolamento de rede de autenticação de identidade de serviço, e explicar por que "a rede é privada" não impede um chamador se passar por outro.
- Configurar ECS Service Connect com namespace de Cloud Map e resolver um serviço pelo nome, sem IP em nenhum lugar da configuração.
- Habilitar TLS no Service Connect com AWS Private CA e descrever, com precisão, o que a documentação da AWS afirma sobre esse mecanismo — sem inflar o termo.
- Justificar por escrito por que VPC Lattice NÃO é a escolha certa neste cenário de um cluster e uma conta, e em que condição a escolha mudaria.
- Provar, com medição, que o nome continua resolvendo para o IP correto depois de um redeploy do serviço chamado.
- Diagnosticar uma falha de descoberta de serviço distinguindo DNS não resolvido, TLS recusado e acesso negado pelo sintoma observado.
O que a certificação cobra disto
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| Descoberta de serviço em contêiner | SAP-C02, DVA-C02 | Cloud Map isolado vs Service Connect vs VPC Lattice | qual pergunta cada opção responde, e por que não são três graus da mesma coisa |
| Isolamento de rede vs autenticação | SAP-C02, SCS-C02 | estar na VPC não prova identidade de quem chama | diferença entre "quem consegue alcançar" e "quem é aceito como quem diz ser" |
| TLS entre serviços internos | DVA-C02, SCS-C02 | certificado emitido por AWS Private CA compartilhada, rotação automática | de onde vem o certificado, quem o gira, e o vocabulário exato que a doc usa |
| IAM como controle de acesso de rede | SAP-C02 | auth policy do VPC Lattice é documento IAM anexado ao service network | onde a decisão de acesso mora quando não é security group nem NACL |
| Fronteira de conta e de VPC | SAP-C02 | quando VPC Lattice se justifica e quando é overhead | ler a fronteira do requisito antes de escolher o serviço |
| DNS privado gerenciado | SAA-C03 | Cloud Map cria e mantém namespace privado sem zona editada à mão | diferença entre registro automático por ciclo de vida da task e Route 53 manual |
| Ciclo de vida de certificado de curta duração | SCS-C02 | modo short-lived do Private CA, validade máxima de 7 dias | por que rotação curta é a defesa quando o serviço não suporta revogação |
Onde isto costuma ser cobrado errado
A pergunta clássica descreve dois serviços na mesma VPC se comunicando com sucesso e pergunta se isso prova que a comunicação é autenticada. A resposta é não: alcançar pela rede prova ROTA, não identidade. Qualquer coisa com rota até a sub-rede pode enviar uma requisição se apresentando como "estoque", e o isolamento de rede sozinho não tem como desconfiar disso.
Requisitos, e como cada um muda o desenho
Requisito não funcional que não aparece numa linha de configuração é intenção. A coluna da direita é onde cada um deixou marca.
| Requisito | Valor declarado | O que ele decide no desenho |
|---|---|---|
| Comunicação sobrevive a redeploy sem intervenção humana | obrigatório | descarta IP fixo e SSM Parameter escrito à mão; exige registro DNS automático |
| Criptografia em trânsito entre serviços internos | exigido pela política de segurança da Cadência | Service Connect com TLS via AWS Private CA — não Cloud Map isolado, que resolve nome mas não criptografa |
| Fronteira de conta e de VPC | um cluster, uma conta, hoje — sem plano de multi-conta declarado para 2026 | descarta VPC Lattice como NECESSIDADE atual — não como possibilidade futura |
| Métrica de conexão sem instrumentar código | desejado pela equipe de duas pessoas | Service Connect emite métrica automática no CloudWatch; Cloud Map isolado não |
| Latência adicional por chamada | não pode comprometer o SLA de confirmação de pedido | proxy soma um salto local; handshake TLS é amortizado por conexão mantida — medir, não presumir |
| Rollback deste laboratório sem quebrar o L32 | obrigatório | Service Connect é ADITIVO à task definition existente; não substitui security group nem load balancer |
| Custo adicional não pode exigir aprovação de orçamento | declarado pela equipe | Service Connect não cobra por hora ligada; o custo é por uso de Secrets Manager e Private CA |
Arquitetura mínima: o IP escrito à mão
Este é o desenho que a Cadência tem hoje. Ele é legítimo como ponto de partida: resolve o problema no mesmo dia, sem aprender Cloud Map nem tocar Terraform de malha nenhuma. O laboratório começa por medir a janela de quebra dele, porque um número torna o defeito discutível, e "às vezes falha" não.
- → grava o IP à mão, uma vez
- → ESTOQUE_HOST injetado na task definition
- → HTTP direto para o IP congelado
- → substitui a task a cada implantação
- Fora da AWS
- Gestão e governança
- Compute
Este desenho funciona no dia em que alguém escreve o parâmetro — e é por isso que ele sobrevive em produção sem que ninguém trate como bug. O defeito não é a ausência de um serviço caro: é confundir "endereço da task de hoje" com "endereço do serviço para sempre". Percorra os passos e note onde a suposição quebra.
- O endereço pertence à task, não ao serviço. O nome "estoque" identifica o serviço para o ECS, mas nenhuma das duas partes expõe isso como um endereço estável: quem tem IP é a interface de rede da task, e uma interface nova é provisionada a cada implantação, em modo `awsvpc`.
- O parâmetro é escrito uma vez, e nunca mais. Alguém rodou `describe-tasks`, copiou o IP privado e gravou no SSM Parameter. Não existe automação que atualize esse valor — ele é tão correto quanto o último deploy do "estoque", e falso a partir do próximo.
- O deploy do "estoque" muda o IP sem avisar "pedidos". As duas equipes publicam de forma independente. Quando "estoque" sobe uma revisão nova, "pedidos" continua com o IP antigo na variável de ambiente — não há nenhum sinal entre os dois serviços de que o endereço mudou.
- A chamada trava no timeout de conexão TCP, não num erro HTTP. O IP antigo pode nem existir mais na sub-rede, ou pertencer a outra task agora. O cliente HTTP não recebe um 4xx nem um 5xx — ele espera a conexão abrir e falha só quando o timeout configurado esgota.
- Nada aparece no log do "estoque", porque a conexão nunca chega lá. É o que mais atrasa o diagnóstico: o time de "estoque" jura que o serviço está saudável — e está, só que ninguém está batendo na porta certa. O sintoma mora inteiramente do lado de quem chama.
- Por que alguém publica assim. Porque resolve no mesmo dia, sem aprender Cloud Map, sem tocar Terraform de malha nenhuma. É a solução de menor esforço para "como o serviço A acha o serviço B", e ela não parece errada até o segundo deploy.
Antes de mudar qualquer coisa, prove o defeito. Este comando derruba e sobe uma task de "estoque" e mede quantas chamadas de "pedidos" falham durante e depois da troca — é a linha de base contra a qual o desenho de produção vai ser comparado.
# Rode ANTES de aplicar qualquer mudanca. O numero que sair daqui e a linha de base.
CLUSTER=ffv-lab
aws ecs update-service --cluster "$CLUSTER" --service ffv-lab-estoque --force-new-deployment
for i in $(seq 1 60); do
curl -s -o /dev/null -w "%{http_code} " http://IP-ANTIGO-DO-PARAMETRO:8080/saude
sleep 1
done
# Na Cadencia: apos o redeploy, 100% das chamadas passam a dar timeout — nao um
# codigo de erro, silencio ate o timeout do cliente HTTP configurado (3 s). O
# problema nao se resolve sozinho: continua ate alguem atualizar o parametro a mao.O defeito não é intermitente — é garantido a cada deploy do outro serviço
Confundir isto com "flakiness de rede" é o erro mais caro do diagnóstico: alguém adiciona retry, aumenta timeout, ou pior, marca como "instabilidade conhecida" — e nenhuma dessas ações resolve, porque a causa é um endereço que deixou de existir, não uma rede instável. Enquanto o parâmetro não for atualizado à mão de novo, toda chamada síncrona de "pedidos" falha, e cada uma delas é um pedido que a loja não consegue confirmar.
Arquitetura para produção
Cada peça nova abaixo rastreia a uma linha da tabela de requisitos. A topologia muda — não é o desenho anterior com uma caixa a mais: o parâmetro manual desaparece, e entram um namespace gerenciado, um proxy por task e uma autoridade certificadora.
- → nova revisão da task definition
- → registra o IP atual no namespace
- → resolve "estoque" para o IP vigente
- → emite certificado de curta duração ao proxy
- → emite certificado de curta duração ao proxy
- → tráfego criptografado entre os dois proxies
- → métrica de conexão por DiscoveryName
- Fora da AWS
- Rede e entrega
- Segurança e identidade
- Compute
- Gestão e governança
A pergunta deixa de ser "qual é o IP" e passa a ser "qual é o nome" — e o nome não muda a cada deploy. O que entra é um namespace privado gerenciado, um proxy por task e um certificado emitido por uma CA compartilhada. Percorra os passos: o quarto é o que exige precisão de vocabulário, não just marketing de "malha".
- O nome substitui o IP. "estoque.cadencia.local" resolve para o endereço vigente porque o ECS mantém o registro atualizado — não porque alguém lembrou de atualizar um parâmetro.
- Quem registra é o próprio ECS, a cada implantação. Não existe passo manual para esquecer: a task nova se anuncia no namespace sozinha, e a task velha se remove sozinha quando é substituída.
- O tráfego é criptografado por certificado de uma CA compartilhada — e é esta a frase exata da AWS. A página oficial descreve "automatic traffic encryption with TLS certificates" emitidos por uma AWS Private CA. Ela não usa "mutual TLS" nem "mTLS" nessa página. Na prática, os DOIS proxies apresentam certificado da mesma CA — o que dá autenticação mútua entre os proxies, mesmo sem esse rótulo formal na fonte.
- O certificado gira sozinho, e o prazo é curto de propósito. No modo de curta duração, o certificado vale no máximo 7 dias e roda a cada 5 por padrão. É mais barato que o modo geral e reduz a janela de um certificado comprometido, sem exigir revogação — a doc afirma explicitamente que o Service Connect NÃO suporta revogação de certificado, e depende da rotação curta.
- As métricas de conexão nascem sem instrumentar nada. O proxy do Service Connect emite métrica automaticamente no CloudWatch, com dimensão por nome de descoberta — o time de "pedidos" nunca escreveu uma linha de telemetria para isso.
- Por que isto NÃO é VPC Lattice. Só existe um cluster e uma conta hoje. VPC Lattice resolveria uma fronteira de conta ou de VPC que a Cadência não tem — pagar por ele agora compraria uma garantia que o problema atual não precisa.
A diferença estrutural em relação ao desenho mínimo não é "adicionar Service Connect": é que nenhum humano mais escreve um endereço em lugar nenhum. O ECS registra, resolve e criptografa — e é exatamente por isso que este desenho sobrevive ao próximo deploy de "estoque" sem que ninguém precise saber que ele aconteceu.
A frase que resume a mudança
No desenho mínimo, "pedidos" sabe o endereço de "estoque". No desenho de produção, "pedidos" sabe apenas o NOME de "estoque" — e delega ao proxy local a tarefa de descobrir, a cada chamada, qual endereço esse nome significa agora.
Como funciona ponta a ponta
Os nomes dos componentes não são jargão de marketing de malha: cada um tem um papel observável. O proxy resolve o nome; a Private CA emite o certificado; a aplicação em C# não faz nenhuma das duas coisas — só chama um hostname por HTTP simples.
O corpo real da chamada síncrona de "pedidos" para "estoque" — o mesmo payload que passa pela conexão criptografada entre os dois proxies, sem que o código de nenhum dos dois serviços trate de TLS.
// chamada-estoque.json — o corpo real da requisição síncrona pedidos -> estoque
// POST http://estoque:8080/reservar (nome resolvido pelo proxy, nunca um IP)
{
"pedidoId": "8f2b9e2a-6e35-4b8a-9a5a-2e7e6f6e9a11",
"lojaId": "loja-014",
"itens": [
{ "sku": "CAM-AZ-M", "quantidade": 2 },
{ "sku": "CAL-PT-42", "quantidade": 1 }
]
}
// Resposta de "estoque" — decide se o pedido pode ser confirmado ANTES de
// gravar qualquer coisa. E por isso a chamada e sincrona (decisao do L32).
{
"pedidoId": "8f2b9e2a-6e35-4b8a-9a5a-2e7e6f6e9a11",
"reservado": true,
"itens": [
{ "sku": "CAM-AZ-M", "disponivel": true, "quantidadeReservada": 2 },
{ "sku": "CAL-PT-42", "disponivel": true, "quantidadeReservada": 1 }
],
"expiraEm": "2026-08-07T18:45:00Z"
}
O hostname não é resolvido pelo sistema operacional
Quando o código C# chama "http://estoque:8080", a resolução não passa pelo resolver DNS padrão da task: o proxy do Service Connect intercepta a chamada e a resolve usando o registro que o próprio ECS mantém atualizado. É por isso que o hostname funciona igual dentro e fora do Service Connect no nível do código — a mágica está inteira na camada de rede da task, não na aplicação.
As decisões, e o que se perde em cada uma
📋 "pedidos" e "estoque" no mesmo cluster ECS, mesma conta, chamada síncrona obrigatória (decisão do L32), sem plano declarado de multi-conta em 2026 e sem orçamento para operar uma malha de serviço completa.
Service Connect resolve exatamente a fronteira que a Cadência tem: nome estável dentro de um cluster, com proxy que cuida de retry de conexão e métrica sem tocar o código da aplicação. O TLS entre os dois proxies atende ao requisito de criptografia em trânsito sem que a aplicação C# precise gerenciar certificado. Ele não resolve (e não promete resolver) autorização por identidade granular nem fronteira entre contas — se um dia a Cadência precisar disso, a migração é para VPC Lattice, e não uma reescrita: o nome continua funcionando, muda o que fica por trás dele.
Alt: Cloud Map isolado (sem Service Connect) — Resolve o nome e nada além disso: sem proxy, sem TLS automático, sem métrica de conexão, e a aplicação continua responsável pelos próprios retries. Falha o requisito de criptografia em trânsito declarado.
Alt: VPC Lattice — Resolve fronteira de conta e de VPC que a Cadência não tem hoje. Cobra por hora de service network e por dado processado — paga-se pela garantia sem consumir o problema que ela existe para resolver.
Alt: AWS App Mesh — Descontinuado: sem onboarding de cliente novo desde 24 de setembro de 2024, com desligamento anunciado para 30 de setembro de 2026 — a própria AWS aponta Service Connect e VPC Lattice como sucessores. Não é opção viável para carga nova.
Alt: DNS público com Route 53 Resolver — Exigiria expor o serviço fora da VPC ou operar uma zona privada à mão, sem nenhum dos ganhos automáticos de registro/desregistro que o Service Connect já entrega dentro do próprio ciclo de vida da task.
| Decisão | Escolha | Alternativas | Motivo | O que se perde |
|---|---|---|---|---|
| Mecanismo de descoberta | ECS Service Connect | Cloud Map isolado; VPC Lattice; App Mesh | resolve nome, proxy e TLS dentro do cluster sem custo por hora | não resolve fronteira entre contas — fica para o nível 5 da evolução |
| Fonte do certificado | AWS Private CA, modo curta duração | AWS Private CA modo geral; certificado próprio importado | validade máxima de 7 dias reduz a janela de um certificado comprometido, e é o modo recomendado pela doc para Service Connect | Service Connect não suporta revogação — a defesa é a rotação curta, não uma lista de revogação |
| Namespace | privado, por cluster | namespace compartilhado entre contas via AWS RAM | a Cadência tem um cluster e uma conta; compartilhar hoje é superfície sem necessidade | se um segundo cluster precisar chamar "estoque", o namespace atual não alcança sem mudança |
| Retry no cliente | só em GET/consulta, com backoff e jitter | retry em toda chamada, inclusive reserva | reserva de estoque não é idempotente sem chave dedicada; reenviar às cegas duplica baixa | consulta de disponibilidade pode ficar levemente desatualizada durante o retry |
A dívida que este desenho não paga
Se amanhã a Cadência abrir uma segunda conta — para um novo time, um ambiente de sandbox isolado ou uma aquisição — o namespace privado por cluster não alcança lá. A migração para VPC Lattice nesse momento reaproveita o nome ("estoque" continua sendo "estoque"), mas exige desenhar a auth policy IAM do zero. Isto não é um defeito de hoje: é uma decisão que registra QUANDO ela deixaria de ser suficiente.
Construir: o namespace privado e o cluster que o usa
O namespace é a peça que substitui o parâmetro escrito à mão. Ele não guarda um IP: guarda a REGRA de onde procurar o IP atual, e quem aplica essa regra é o próprio ECS.
# namespace.tf — o namespace privado, e o cluster que sabe usá-lo
# Cloud Map cria e mantem a zona sozinho a partir daqui. Nao existe "criar
# registro DNS": o proprio ECS registra e desregistra a task, a cada deploy.
resource "aws_service_discovery_private_dns_namespace" "interno" {
name = "cadencia.local"
description = "Namespace privado para descoberta de servico entre pedidos e estoque"
vpc = aws_vpc.principal.id
}
# Declarar o namespace default no CLUSTER e o que liga Service Connect a ele.
# Um servico so ganha proxy e nome estavel se o cluster souber para qual
# namespace publicar.
resource "aws_ecs_cluster" "principal" {
name = var.projeto
service_connect_defaults {
namespace = aws_service_discovery_private_dns_namespace.interno.arn
}
}
Por que o namespace vive no cluster, não no serviço
Declarar `service_connect_defaults` uma vez no cluster evita repetir a referência ao namespace em cada serviço novo — e mais importante, garante que todo serviço do cluster resolve nomes no MESMO namespace, o que é a premissa para "pedidos" achar "estoque" sem configuração cruzada entre os dois times.
Construir: a autoridade certificadora e a role que a usa
Esta é a seção onde a precisão de vocabulário importa mais. O que se implanta aqui é uma AWS Private CA em modo de curta duração, mais a role de infraestrutura que o Service Connect usa para pedir certificado a ela — não um serviço de "mTLS" com nome próprio na AWS.
# tls.tf — a CA que emite certificado para os dois proxies, e a role que a usa
# Raiz de confianca so para este laboratorio (um cluster, uma conta). Em
# ambiente com varios times, a pratica recomendada e uma CA subordinada por
# dominio de confianca, nao uma raiz por equipe.
resource "aws_acmpca_certificate_authority" "interna" {
type = "ROOT"
certificate_authority_configuration {
key_algorithm = "RSA_2048"
signing_algorithm = "SHA256WITHRSA"
subject {
common_name = "cadencia-service-connect-ca"
}
}
# Modo de curta duracao: certificado com validade MAXIMA de 7 dias, e
# rotacao padrao a cada 5. A doc recomenda este modo para Service Connect
# justamente porque ele NAO suporta revogacao de certificado — a defesa
# contra chave comprometida e o proprio prazo curto, nao uma lista de
# revogacao.
usage_mode = "SHORT_LIVED_CERTIFICATE"
# A CA continua cobrando durante a janela de exclusao pendente. Nao e
# recurso que "some" ao rodar destroy — ver a secao de limpeza.
permanent_deletion_time_in_days = 7
tags = {
# Tag EXIGIDA pela documentacao para o Service Connect aceitar esta CA.
AmazonECSManaged = "true"
}
}
resource "aws_acmpca_certificate" "raiz" {
certificate_authority_arn = aws_acmpca_certificate_authority.interna.arn
certificate_signing_request = aws_acmpca_certificate_authority.interna.certificate_signing_request
signing_algorithm = "SHA256WITHRSA"
template_arn = "arn:aws:acm-pca:::template/RootCACertificate/V1"
validity {
type = "YEARS"
value = 2
}
}
resource "aws_acmpca_certificate_authority_certificate" "interna" {
certificate_authority_arn = aws_acmpca_certificate_authority.interna.arn
certificate = aws_acmpca_certificate.raiz.certificate
certificate_chain = aws_acmpca_certificate.raiz.certificate_chain
}
data "aws_iam_policy_document" "confianca_infraestrutura" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs.amazonaws.com"]
}
}
}
# A role de INFRAESTRUTURA, distinta de execution role e task role. E ela
# quem pede o certificado a Private CA e escreve a chave privada no Secrets
# Manager em nome do Service Connect — a aplicacao nunca ve nenhuma das duas.
resource "aws_iam_role" "infraestrutura_ecs" {
name = "${var.projeto}-infra-service-connect"
assume_role_policy = data.aws_iam_policy_document.confianca_infraestrutura.json
}
# A AWS publica esta policy gerenciada especificamente para a role de
# infraestrutura emitir e girar certificado de Service Connect TLS. Nao
# escrevemos a permissao a mao: ela cobre Private CA, Secrets Manager e KMS
# em conjunto, e a lista de acoes muda com a evolucao do recurso — anexar a
# policy gerenciada e a forma que a propria AWS documenta.
resource "aws_iam_role_policy_attachment" "infra_tls" {
role = aws_iam_role.infraestrutura_ecs.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity"
}
O "*" que aparece implícito na policy gerenciada, e por que ele não é escrito à mão
A policy `AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity` concede acesso administrativo a Private CA, Secrets Manager e KMS necessário para o recurso funcionar — e a própria AWS a mantém, porque a lista de ações muda com a evolução do serviço. Escrever essa policy à mão, tentando adivinhar cada ação, produziria uma role que quebra silenciosamente na primeira atualização do recurso.
Construir: quem expõe nome e quem só consome
"estoque" e "pedidos" usam o MESMO recurso do Terraform de formas diferentes — só quem expõe um nome declara o bloco `service` dentro de `service_connect_configuration`.
# servicos.tf — "estoque" expoe um nome; "pedidos" so consome
resource "aws_ecs_service" "estoque" {
name = "${var.projeto}-estoque"
cluster = aws_ecs_cluster.principal.id
task_definition = aws_ecs_task_definition.estoque.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.privada[*].id
security_groups = [aws_security_group.estoque.id]
assign_public_ip = false
}
# "estoque" EXPOE um servico com nome estavel. O client_alias e o que
# "pedidos" vai resolver: http://estoque:8080 — nunca um IP.
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_private_dns_namespace.interno.arn
service {
port_name = "http"
discovery_name = "estoque"
client_alias {
port = 8080
dns_name = "estoque"
}
# O bloco que liga o proxy a CA. O papel da role de infraestrutura e
# buscar o certificado nesta CA e entregar ao proxy — a aplicacao em
# si nunca importa um certificado.
tls {
issuer_cert_authority {
aws_pca_authority_arn = aws_acmpca_certificate_authority.interna.arn
}
role_arn = aws_iam_role.infraestrutura_ecs.arn
}
}
log_configuration {
log_driver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.service_connect.name
"awslogs-region" = var.regiao
"awslogs-stream-prefix" = "estoque"
}
}
}
}
resource "aws_ecs_service" "pedidos" {
name = "${var.projeto}-pedidos"
cluster = aws_ecs_cluster.principal.id
task_definition = aws_ecs_task_definition.pedidos.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.privada[*].id
security_groups = [aws_security_group.pedidos.id]
assign_public_ip = false
}
# "pedidos" so CONSOME. Precisa do proprio proxy Service Connect ligado
# (para falar TLS com o proxy de "estoque"), mas nao expoe nome nenhum —
# por isso nao ha bloco `service` aqui, so `enabled` e o namespace.
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_private_dns_namespace.interno.arn
log_configuration {
log_driver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.service_connect.name
"awslogs-region" = var.regiao
"awslogs-stream-prefix" = "pedidos"
}
}
}
}
# Security group referencia security group, nunca faixa de IP — igual ao
# padrao do L01/L03. Aqui a porta 8080 so abre para quem tem o SG de
# "pedidos", nao para a sub-rede inteira.
resource "aws_security_group_rule" "estoque_recebe_de_pedidos" {
type = "ingress"
security_group_id = aws_security_group.estoque.id
from_port = 8080
to_port = 8080
protocol = "tcp"
source_security_group_id = aws_security_group.pedidos.id
}
client_alias é o nome que o CÓDIGO vai chamar, não um apelido cosmético
O par `port` + `dns_name` dentro de `client_alias` é o contrato entre infraestrutura e aplicação: é literalmente o hostname que o `HttpClient` do C# vai usar. Mudar o `dns_name` aqui sem avisar quem escreve o cliente quebra a chamada sem nenhum erro de Terraform — o `apply` passa, e só o tráfego real revela a divergência.
Construir: o cliente que resolve por nome e nunca vê certificado
Do lado da aplicação, a mudança é pequena de propósito: trocar um `EstoqueClient` apontado para um IP por um apontado para o `client_alias`. Tudo o que envolve TLS, proxy e resolução de nome fica fora do processo em C#.
// EstoqueClient.cs — chama por nome; nunca resolve, nunca ve certificado
// O host e o discovery_name do Terraform. Repare: e HTTP, nao HTTPS. O TLS
// acontece entre os dois PROXIES, no salto fora do container — o trafego do
// codigo da aplicacao ate o proxy local e texto puro, dentro da mesma task.
// Escrever "https://estoque:8080" aqui nao criptografaria nada a mais e
// quebraria porque o proxy nao fala TLS pro lado da aplicacao.
builder.Services.AddHttpClient<EstoqueClient>(c =>
{
c.BaseAddress = new Uri("http://estoque:8080");
c.Timeout = TimeSpan.FromSeconds(3); // curto: e chamada sincrona no caminho de confirmar pedido
})
// Resiliencia com backoff E jitter — retry sem jitter transforma instabilidade
// em apagao sincronizado quando varias tasks de "pedidos" tentam de novo no
// mesmo instante (e o assunto do L36).
.AddResilienceHandler("estoque", builder =>
{
builder.AddRetry(new HttpRetryStrategyOptions
{
MaxRetryAttempts = 2,
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
Delay = TimeSpan.FromMilliseconds(100),
// So reenvia GET/consulta de disponibilidade. Reservar estoque NAO e
// idempotente sem chave de idempotencia — reenviar reserva as cegas
// duplicaria a baixa de estoque.
ShouldHandle = args => ValueTask.FromResult(
args.Outcome.Result?.StatusCode is HttpStatusCode.ServiceUnavailable
or HttpStatusCode.GatewayTimeout),
});
builder.AddTimeout(TimeSpan.FromSeconds(2));
});
public sealed class EstoqueClient(HttpClient http)
{
public async Task<ReservaResposta> ReservarAsync(ReservaPedido pedido, CancellationToken ct)
{
// Chave de idempotencia no cabecalho: se o retry acima reenviar,
// "estoque" reconhece a mesma tentativa em vez de reservar duas vezes.
using var msg = new HttpRequestMessage(HttpMethod.Post, "/reservar")
{
Content = JsonContent.Create(pedido)
};
msg.Headers.Add("Idempotency-Key", pedido.PedidoId.ToString());
var resp = await http.SendAsync(msg, ct);
resp.EnsureSuccessStatusCode();
return (await resp.Content.ReadFromJsonAsync<ReservaResposta>(cancellationToken: ct))!;
}
}
public record ReservaPedido(Guid PedidoId, string LojaId, IReadOnlyList<ItemPedido> Itens);
public record ItemPedido(string Sku, int Quantidade);
public record ReservaResposta(Guid PedidoId, bool Reservado, DateTimeOffset ExpiraEm);
Escrever "https://" aqui quebraria a chamada, não a protegeria mais
A criptografia acontece no salto ENTRE os dois proxies, fora dos dois contêineres de aplicação. O tráfego do código C# até o proxy local, na mesma task, é HTTP simples. Forçar HTTPS no `HttpClient` faria a chamada esperar um handshake TLS que o proxy local não fala do lado da aplicação — o sintoma seria erro de protocolo, não mais segurança nenhuma.
Implantar, e provar que o nome sobrevive ao deploy
#!/usr/bin/env bash
# implantar.sh — sobe a revisao nova; nenhum passo grava IP em lugar nenhum
set -euo pipefail
PROJETO="${PROJETO:?defina PROJETO}"
REGIAO="${REGIAO:-us-east-1}"
terraform apply -auto-approve
aws ecs wait services-stable \
--cluster "${PROJETO}" --services "${PROJETO}-estoque" "${PROJETO}-pedidos" \
--region "$REGIAO"
echo "as duas revisoes estao no ar; nenhum IP foi copiado a mao"
Cinco medições, na ordem em que cada uma faz sentido: primeiro que o nome resolve durante um redeploy do lado chamado, depois que ele resolve para o endereço CERTO, depois que o certificado vem de onde deveria, depois que a métrica nasce sem código extra, e por fim — só se o namespace for compartilhado — que revogar acesso realmente corta o TLS, como a documentação descreve.
# provas.sh — cinco medicoes; nenhuma conclusao vem de "parece que resolveu"
PROJETO=ffv-lab; REGIAO=us-east-1; CLUSTER="$PROJETO"
# -- Prova 1: o nome sobrevive a um redeploy do servico chamado -------------
# Roda uma chamada de "pedidos" a cada 500 ms enquanto "estoque" e substituido.
# No desenho MINIMO (IP fixo) isto produz timeout apos o primeiro redeploy; no
# desenho de PRODUCAO (Service Connect) nao deve produzir nenhuma falha.
TASK_PEDIDOS=$(aws ecs list-tasks --cluster "$CLUSTER" --service-name "${PROJETO}-pedidos" \
--query 'taskArns[0]' --output text)
( for i in $(seq 1 40); do
aws ecs execute-command --cluster "$CLUSTER" --task "$TASK_PEDIDOS" --container pedidos \
--interactive --command "curl -s -o /dev/null -w '%{http_code}\n' http://estoque:8080/saude"
sleep 0.5
done ) &
LACO=$!
terraform apply -auto-approve -replace="aws_ecs_service.estoque"
wait $LACO
# Esperado: fileira continua de 200. Qualquer timeout ou 000 no meio significa
# que o proxy nao re-resolveu o nome — investigue o namespace do Cloud Map.
# -- Prova 2: o nome resolve, e resolve para o IP VIGENTE --------------------
NOVO_IP=$(aws ecs describe-tasks --cluster "$CLUSTER" \
--tasks "$(aws ecs list-tasks --cluster "$CLUSTER" --service-name "${PROJETO}-estoque" \
--query 'taskArns[0]' --output text)" \
--query 'tasks[0].containers[0].networkInterfaces[0].privateIpv4Address' --output text)
aws ecs execute-command --cluster "$CLUSTER" --task "$TASK_PEDIDOS" --container pedidos \
--interactive --command "getent hosts estoque"
# Esperado: o IP devolvido bate com $NOVO_IP. Se bater com um IP antigo, o
# proxy local esta com cache desatualizado — meça o TTL de resolucao.
# -- Prova 3: o certificado apresentado vem da CA esperada -------------------
# O proxy escuta em 127.0.0.1:15000 dentro da propria task (porta interna do
# Service Connect). Ver o emissor confirma que o TLS realmente veio da CA
# compartilhada, e nao de um certificado autoassinado por engano de config.
aws ecs execute-command --cluster "$CLUSTER" --task "$TASK_PEDIDOS" --container pedidos \
--interactive --command "openssl s_client -connect estoque:8080 -showcerts </dev/null 2>/dev/null | openssl x509 -noout -issuer"
# Esperado: o Issuer contem o common_name da CA do Terraform (cadencia-service-connect-ca).
# -- Prova 4: a metrica de conexao nasce sem instrumentar a aplicacao --------
aws cloudwatch list-metrics --namespace "ECS/ServiceConnect" \
--dimensions Name=DiscoveryName,Value=estoque --region "$REGIAO"
# O nome exato do namespace de metricas nao foi confirmado na doc consultada
# nesta pesquisa — confira no console CloudWatch > All metrics > ECS antes de
# depender deste comando em automacao.
# -- Prova 5: revogar acesso ao namespace compartilhado quebra o TLS --------
# So se aplica se o namespace for COMPARTILHADO via AWS RAM. Reproduz o
# comportamento documentado: acesso a CA para quando o acesso ao namespace e
# revogado.
aws ram disassociate-resource-share --resource-share-arn "$(terraform output -raw ram_share_arn)" \
--resource-arns "$(terraform output -raw namespace_arn)" --region "$REGIAO" || true
sleep 30
aws ecs execute-command --cluster "$CLUSTER" --task "$TASK_PEDIDOS" --container pedidos \
--interactive --command "curl -s -o /dev/null -w '%{http_code}\n' http://estoque:8080/saude"
# Esperado: a chamada passa a falhar — prova de que a CA compartilhada estava
# de fato escopada ao namespace, como a documentacao descreve.
| Prova | O que aprova | O que a reprovação significa |
|---|---|---|
| 1 — sobrevive ao redeploy | fileira contínua de 200 durante a troca de "estoque" | timeout no meio = o proxy não reagiu ao novo registro; confira o namespace |
| 2 — resolve para o IP vigente | getent hosts devolve o IP da task ATUAL | IP antigo devolvido = cache de resolução desatualizado no proxy |
| 3 — certificado da CA certa | Issuer contém o common_name da CA do Terraform | CA diferente ou erro de handshake = tls block aponta para ARN errado |
| 4 — métrica sem instrumentar | métrica aparece com dimensão DiscoveryName=estoque | métrica ausente = Service Connect não está habilitado no serviço, mesmo com o apply verde |
| 5 — acesso revogado corta TLS (se compartilhado) | chamada passa a falhar após a revogação | chamada continua passando = a CA não estava de fato escopada ao namespace compartilhado |
Quebrar de propósito: três falhas e o diagnóstico
As três falhas abaixo foram reproduzidas de propósito, não são hipotéticas. Cada uma tem um sintoma que engana antes de apontar para a causa certa.
| Falha provocada | Sintoma | Onde olhar | Correção |
|---|---|---|---|
| ARN da CA errado no bloco `tls` | serviço não estabiliza; task fica em PROVISIONING/ACTIVATING sem sair | eventos do serviço ECS e logs da role de infraestrutura | confira `aws_pca_authority_arn` e a tag `AmazonECSManaged = true` na CA |
| Role de infraestrutura sem a policy gerenciada anexada | chamada de "pedidos" falha com erro de handshake TLS, não com timeout de conexão | CloudTrail em torno da role — procure `AccessDenied` em `acm-pca:*` ou `secretsmanager:*` | anexe `AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity` |
| Namespace compartilhado com outra conta, acesso revogado por engano | TLS que funcionava para de funcionar sem nenhuma mudança de código ou deploy | evento de rotação: `RotationFailed` no CloudTrail e alarme de CloudWatch associado | restaure a associação de recurso via AWS RAM ou migre para CA própria não compartilhada |
A falha que mais engana: parece rede, é acesso revogado
Quando o namespace é compartilhado, a CA fica escopada a ele — e a documentação é explícita: revogar o acesso ao namespace para o acesso à CA. O sintoma imita uma falha de rede (conexão recusada), então o primeiro instinto é investigar security group e rota, exatamente os dois lugares onde não está o problema.
"pedidos" lê o IP de "estoque" de uma variável de ambiente, gravada à mão após o primeiro deploy. Depois do próximo redeploy de "estoque", toda chamada de "pedidos" passa a dar timeout. Qual é a causa raiz?
Segurança: o que muda quando o serviço passa a ter nome
Ganhar nome e TLS resolve um risco e expõe outro: o certificado privado agora existe em algum lugar, e esse lugar precisa de controle de acesso próprio.
| Risco | Probabilidade | Impacto | Prevenção | Detecção | Resposta |
|---|---|---|---|---|---|
| Chamador dentro da VPC se apresenta como "estoque" sem TLS (desenho mínimo) | média | alto | migrar para Service Connect com TLS ou, se cruzar conta, para VPC Lattice com auth policy | VPC Flow Logs com padrão de tráfego anômalo entre sub-redes | revogar rota, isolar security group, investigar origem |
| Role com leitura ampla em Secrets Manager acessa a chave privada do certificado | baixa | alto | negar explicitamente `GetSecretValue` no prefixo `ecs-sc!*` para roles não relacionadas | auditoria periódica de roles com `secretsmanager:GetSecretValue` | revogar a policy ampla, rotacionar o certificado manualmente |
| Namespace compartilhado revogado sem aviso ao time consumidor | baixa | médio | monitorar `RotationFailed` e documentar o dono do compartilhamento | alarme de CloudWatch em `RotationFailed` | restaurar o compartilhamento ou migrar para CA própria |
| Rotação de certificado falha e passa 7 dias sem reemitir | baixa | alto — comunicação para de vez, não degrada | alarme cedo, no primeiro `RotationFailed`, não no dia 7 | CloudTrail + CloudWatch Alarm dedicado | investigar a role de infraestrutura e a saúde da CA |
O "*" que se justifica, e o que não é este caso
A policy `AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity` concede acesso amplo por ser uma policy GERENCIADA pela AWS para um recurso que ela própria evolui — isso é diferente de escrever `"Resource": "*"` numa policy própria sem justificar. Aqui o "*" não é preguiça: é a AWS assumindo a manutenção de uma lista de ações que muda com o produto.
Observabilidade: as perguntas que o painel tem de responder
- O nome "estoque" está resolvendo para um endereço saudável agora?
- Quantas conexões ativas o proxy de cada serviço tem, por DiscoveryName?
- A taxa de erro de conexão subiu depois do último deploy de "estoque"?
- A rotação do certificado falhou nas últimas 24 horas?
- Quanto tempo o handshake TLS entre proxies está levando, comparado à linha de base?
| Alarme | Métrica | Limiar inicial | O que ele pega |
|---|---|---|---|
| Falha de rotação de certificado | evento `RotationFailed` (CloudTrail) | 1 ocorrência | certificado que vai expirar em até 7 dias sem substituto |
| Erro de conexão do proxy acima do normal | métrica de conexão do Service Connect, dimensão DiscoveryName=estoque | 2x a linha de base por 5 min | proxy rejeitando conexão — TLS, ACL ou serviço fora do ar |
| Latência de chamada síncrona pedidos→estoque | duração da requisição no cliente C# (métrica de aplicação, não do proxy) | acima do p99 medido no desenho mínimo | handshake ou proxy adicionando tempo que o requisito de SLA não tolera |
O que não foi verificado nesta pesquisa
O nome exato do namespace e das dimensões de métrica do Service Connect no CloudWatch não foi confirmado com uma consulta direta ao console nesta pesquisa — a documentação descreve a EXISTÊNCIA da métrica automática e a dimensão `DiscoveryName`, mas o texto consultado não fecha o nome de namespace em uma única string estável. Confirme no console CloudWatch > All metrics > ECS, no seu ambiente, antes de montar alarme em produção sobre esse nome.
Escala: 10, 10 mil, 1 milhão, e falha de AZ
| Ordem de grandeza | O que muda | Onde o desenho aperta primeiro |
|---|---|---|
| 10 requisições/dia | quase nada disto é estritamente necessário | IP fixo até funcionaria por meses sem redeploy — mas o requisito de criptografia já vale desde o primeiro deploy, não a partir de um volume |
| 10 mil requisições/dia (a Cadência hoje) | proxy e TLS são imperceptíveis no p99 | nenhum aperto conhecido nesta escala; a folga é grande |
| 1 milhão de requisições/dia | mais serviços tendem a entrar no mesmo namespace | volume de métrica e de certificado por serviço cresce; vale revisar cadência de rotação e custo de Secrets Manager, não a arquitetura em si |
| Falha de AZ | a task de "estoque" que morre na AZ afetada sobe em outra AZ com outro IP | é exatamente o caso que o desenho mínimo não sobrevive e o de produção sim — o nome resolve para a task nova sem intervenção |
O gargalo que este laboratório NÃO resolve
Service Connect não distribui carga entre múltiplas tasks de "estoque" com uma política sofisticada — ele resolve para um endereço saudável, e o balanceamento entre réplicas é o comportamento padrão do proxy, não um recurso configurável ponto a ponto como um Application Load Balancer. Se "estoque" precisar de política de roteamento por peso (canário, azul/verde), isso é nível 4 da evolução adiante.
Custo: o que este laboratório acrescenta à fatura
Nenhum valor absoluto abaixo: dimensões de cobrança e o AWS Pricing Calculator para o número exato, que varia por região e muda mais rápido do que este texto.
| Dimensão de custo | Cresce com | Observação |
|---|---|---|
| AWS Private CA (modo curta duração) | existência da CA por mês | mais barato que o modo geral; a AWS recomenda este modo especificamente para Service Connect |
| Secrets Manager | número de segredos criados (um por certificado gerido) e chamadas de rotação | cresce com o número de serviços com TLS habilitado |
| CPU/memória reservada para o proxy | uma fração da task, por task | o proxy roda como contêiner adicional dentro da MESMA task — não é uma task nova, mas consome parte da capacidade já reservada |
| CloudWatch (métricas e logs do proxy) | volume de conexões e de log configurado | log_configuration do Service Connect é opcional; sem ele, menos custo e menos diagnóstico |
| Cenário | Descrição | O que domina o custo |
|---|---|---|
| Protótipo | IP fixo, sem nenhuma peça nova | zero — e é justamente por isso que o antipadrão sobrevive tanto tempo em produção |
| Produção pequena (a Cadência hoje) | Service Connect com TLS, 2 serviços | a CA de curta duração e os segredos do Secrets Manager — ainda assim pequeno frente ao custo do cluster ECS em si |
| Alta escala | dezenas de serviços no mesmo namespace | volume de rotação de certificado e de métrica — cresce linearmente com o número de serviços, não com o tráfego entre eles |
O custo oculto que ninguém calcula: o tempo perdido diagnosticando o IP morto
O desenho mínimo não aparece como linha na fatura da AWS — mas cada incidente de "pedidos parou de confirmar" consome tempo de duas pessoas em diagnóstico que só termina quando alguém lembra de checar o parâmetro manual. Esse custo não é AWS: é operacional, e não some do resumo de custo por não estar na fatura.
Well-Architected nos seis pilares
| Pilar | Situação | Risco | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | IP escrito à mão quebra a cada deploy do outro serviço | alto — falha determinística e recorrente, sem alerta próprio | Service Connect remove o passo manual inteiramente | alta |
| Segurança | chamada interna sem criptografia nem verificação de identidade | médio — depende de rota até a sub-rede para ser explorado | TLS via Private CA compartilhada entre os proxies | alta |
| Confiabilidade | falha de AZ ou redeploy derruba a descoberta de nome | alto — combinado ao requisito de zero erro visível do L03 | registro/desregistro automático por ciclo de vida da task | alta |
| Eficiência de performance | proxy adiciona um salto local e handshake TLS | baixo — amortizado por conexão mantida, mas não medido ainda pela Cadência | medir p99 antes e depois; comparar com a linha de base do desenho mínimo | média |
| Otimização de custo | Cloud Map isolado seria mais barato, mas não cripografa | baixo — a diferença de custo entre as duas opções é pequena frente ao cluster | não trocar TLS por economia marginal; o requisito de criptografia é declarado | baixa |
| Sustentabilidade | nenhuma mudança relevante de utilização de recurso | baixo | proxy compartilha a task já provisionada, sem capacidade extra dedicada | baixa |
Evolução em níveis: o que muda, e o que passa a doer
A terceira arquitetura não é um desenho: é a resposta a QUANDO trocar de mecanismo. Cada nível resolve um risco e compra outro — e a segunda coluna é a que raramente se escreve antes de doer.
O desenho mínimo deste laboratório. Publica de verdade, com zero peça nova — e quebra a cada redeploy do serviço chamado.Registro e resolução de nome automáticos, sem proxy. Resolve a quebra a cada deploy; não resolve criptografia nem métrica.O desenho de produção deste laboratório: proxy por task, nome estável, criptografia via CA compartilhada, métrica de conexão automática.Múltiplos discovery names por versão (canário, azul/verde), dezenas de proxies e certificados sob o mesmo namespace privado.A fronteira muda de cluster para conta: service network do VPC Lattice com auth policy IAM central, cruzando VPCs e contas de times diferentes.Um agente com ferramentas (banda 9) chama "estoque" através da mesma malha — só que quem decide QUANDO chamar deixou de ser código determinístico.A ordem não é negociável, e o motivo é concreto
VPC Lattice no nível 5 depende de identidade de serviço já estabelecida no nível 3 — sem TLS entre proxies dentro do cluster, não há base de confiança para estender a auth policy entre contas. Pular do nível 1 direto para o 5 significaria desenhar controle de acesso entre contas para um serviço que ainda nem prova quem é dentro da própria conta.
Onde IA entra nesta arquitetura, e onde não entra
Neste módulo, IA não resolve o problema central, e forçá-la seria o antipadrão que a própria série critica. "Como um serviço acha o outro depois de um redeploy" tem resposta determinística: registro automático de nome, e nada nisso se beneficia de um modelo — é configuração e ciclo de vida de task, não previsão.
Há um ponto real de contato com IA, e ele fica no nível 6 da evolução: quando o CHAMADOR de "estoque" deixa de ser o código determinístico de "pedidos" e passa a ser um agente com ferramentas decidindo, em tempo real, se e quando reservar estoque. A pergunta de identidade de serviço deste laboratório não desaparece — ela fica mais urgente, porque quem decide invocar a ferramenta não é uma linha de código revisada em pull request.
| Pergunta | Resposta honesta para este módulo |
|---|---|
| Qual problema a IA resolveria aqui? | nenhum — descoberta de nome é mecanismo, não decisão |
| Onde IA entraria de verdade? | no lab de agente com ferramentas em produção (banda 9), quando "estoque" vira uma ferramenta que um agente invoca |
| O que muda quando o chamador é um agente? | a auth policy do VPC Lattice (ou o equivalente de IAM por ferramenta) passa a ser o controle que decide QUEM pode mandar o agente reservar estoque — não mais só "qual serviço" alcança "qual serviço" |
O uso de IA que parece atraente aqui, e é armadilha
"Usar um modelo para prever quando o IP vai mudar e pré-atualizar o parâmetro" resolveria, de forma cara e probabilística, um problema que Service Connect resolve de forma determinística e gratuita. É o padrão que a FFV Academy existe para não vender: IA como resposta a um problema de engenharia que já tem solução exata.
Anti-padrões deste laboratório
| Erro | Por que alguém faz | Sintoma em produção | Forma correta |
|---|---|---|---|
| IP de task hardcoded em variável de ambiente | funciona no dia 1, e ninguém mede o dia do próximo redeploy | timeout determinístico a cada implantação do serviço chamado | Cloud Map ou Service Connect, nunca um endereço literal |
| Cloud Map isolado quando o requisito pede criptografia | configurar TLS parece trabalho extra quando o requisito não estava escrito com todas as letras | auditoria de segurança reprova tráfego interno em texto puro | Service Connect com bloco `tls`, não Cloud Map sozinho |
| Ativar VPC Lattice para um cluster e uma conta | achar que "mais moderno" é sempre melhor, sem checar qual fronteira ele resolve | custo de service network sem nenhum ganho sobre o que Service Connect já dava | ler o requisito de fronteira ANTES de escolher a ferramenta |
| Tratar "TLS habilitado" como "chamador autenticado" | a documentação fala em criptografia, o leitor lê autenticação, e ninguém confere se o outro lado é de fato quem diz ser | incidente de segurança tratado como "impossível, tínhamos TLS" | nomear a garantia certa: TLS protege o conteúdo em trânsito; autorização por identidade é outra camada, como a auth policy IAM do Lattice |
| Copiar exemplo de auth policy do VPC Lattice com Principal amplo | "funciona" em teste porque o teste roda dentro da própria conta | serviço acessível por qualquer role de qualquer conta que descubra o ARN | Principal específico por role e por conta, revisado como qualquer policy IAM |
| Deixar namespace compartilhado sem alarme em RotationFailed | "está funcionando hoje" e ninguém instrumenta o dia em que a rotação falha | comunicação para de vez depois de até 7 dias, sem aviso prévio | CloudWatch Alarm em RotationFailed desde o primeiro deploy |
Quando algo não funciona
| Sintoma | Causa provável | Como investigar | Log/métrica | Correção |
|---|---|---|---|---|
| Nome não resolve (getent/nslookup falha dentro da task) | Service Connect não habilitado no serviço, ou namespace errado | conferir `service_connect_configuration.enabled` e o ARN do namespace | eventos do serviço ECS no console | habilitar Service Connect e apontar o namespace correto |
| Nome resolve, conexão recusada | client_alias com porta diferente da que a aplicação escuta | comparar `client_alias.port` com a porta real do contêiner | log do proxy (`log_configuration`) | alinhar as portas na task definition |
| TLS falha no handshake | role de infraestrutura sem a policy gerenciada, ou ARN de CA errado | CloudTrail em torno da role de infraestrutura | eventos `AccessDenied` em acm-pca/secretsmanager | anexar a policy gerenciada correta |
| Comunicação funcionava e parou sem deploy nenhum | namespace compartilhado teve o acesso revogado, ou certificado não rotacionou | CloudTrail: evento de RAM disassociate ou RotationFailed | alarme de RotationFailed | restaurar o compartilhamento ou investigar a rotação |
| Latência de chamada subiu depois de habilitar Service Connect | handshake TLS sem conexão mantida (keep-alive) entre os proxies | medir com e sem TLS, na mesma carga | métrica de duração no cliente C# | confirmar keep-alive habilitado no HttpClient |
A pergunta que resolve metade destes casos
"Isso é rede, é TLS, ou é acesso?" — os três produzem sintomas parecidos (conexão que não completa), mas a investigação é completamente diferente para cada um. Separar essa pergunta logo no início evita investigar security group quando o problema é uma role sem policy anexada.
Limpeza: o que o destroy não leva
A ordem importa porque a role de infraestrutura e a CA têm dependências que o `terraform destroy` sozinho nem sempre resolve na sequência certa.
#!/usr/bin/env bash
set -euo pipefail
# 1. Desabilitar Service Connect nos servicos ANTES de remover a CA — servico
# apontando para CA que nao existe mais fica em estado inconsistente.
terraform apply -auto-approve -var="service_connect_habilitado=false"
# 2. So entao remover CA, role e namespace.
terraform destroy -auto-approve
# 3. A CA em modo ROOT nao e removida de fato pelo destroy: ela entra em exclusao
# PENDENTE por ate 7 dias (permanent_deletion_time_in_days), e continua cobrando
# durante essa janela. Confirme:
aws acm-pca list-certificate-authorities \
--query "CertificateAuthorities[?Status=='PENDING_DELETION'].Arn" --output text
| Recurso | O destroy remove? | O que fica cobrando |
|---|---|---|
| AWS Private CA (modo curta duração) | inicia exclusão pendente, não some na hora | cobra durante a janela de exclusão pendente (até 7 dias configurados) |
| Segredos do Secrets Manager (prefixo ecs-sc!) | sim, mas com janela de recovery padrão do Secrets Manager | cobra durante o período de recuperação, se não forçado |
| Log group do Service Connect | só se declarado no Terraform; log com retenção infinita não é limpo sozinho | armazenamento de log acumulado indefinidamente |
| Namespace privado do Cloud Map | sim, mas falha se ainda houver serviço registrado | nada — mas o destroy pode travar até os serviços saírem primeiro |
A CA continua cobrando depois do "destroy" concluído com sucesso
Um `terraform destroy` verde não significa fatura zerada: a AWS Private CA em exclusão pendente cobra pelos dias configurados em `permanent_deletion_time_in_days`. Encurtar esse valor para o mínimo permitido reduz a janela, mas não elimina a cobrança — trate isso como parte do custo de ter criado o recurso, não como falha da limpeza.
Resumo: problema, peça e motivo
| Problema | Peça | Motivo |
|---|---|---|
| IP muda a cada deploy da task | namespace privado do Cloud Map, gerido pelo cluster | o registro é automático, não um passo humano a esquecer |
| Requisito de criptografia em trânsito | AWS Private CA em modo curta duração + bloco tls | certificado emitido e rotacionado sem código de aplicação envolvido |
| Rede privada não é autenticação | proxy + certificado por serviço (Service Connect) | dá identidade mínima ao chamador, mesmo sem auth policy formal |
| Um cluster, uma conta, hoje | Service Connect, não VPC Lattice | resolve a fronteira que existe, sem pagar pela que ainda não existe |
| Métrica de conexão sem instrumentar | proxy do Service Connect no CloudWatch | observabilidade nasce do mecanismo, não de código adicional |
| Falha | Proteção |
|---|---|
| Redeploy do serviço chamado | registro/desregistro automático no namespace |
| Certificado comprometido | rotação curta (5 dias) em vez de lista de revogação |
| Namespace compartilhado revogado | alarme em RotationFailed, detectado antes do dia 7 |
- "pedidos" chama "http://estoque:8080" — nunca um IP.
- O proxy local resolve o nome pelo namespace do Cloud Map.
- O ECS mantém esse registro atualizado a cada implantação de "estoque".
- Os dois proxies negociam TLS usando certificado da mesma AWS Private CA.
- A doc chama isso de criptografia de tráfego via CA compartilhada — não de "TLS mútuo".
- O certificado gira sozinho a cada 5 dias, sem revogação suportada.
- A aplicação em C# só vê HTTP simples até o proxy local.
- A métrica de conexão nasce no CloudWatch sem instrumentação de código.
- VPC Lattice fica reservado para quando existir fronteira de conta ou VPC.
Perguntas frequentes
❓ Por que o IP de uma task ECS muda a cada implantação?
❓ O ECS Service Connect usa mTLS entre os serviços?
❓ Qual a diferença entre Cloud Map isolado e ECS Service Connect?
❓ Quando devo usar VPC Lattice em vez de Service Connect?
❓ Rede privada (VPC) já não garante que só serviços autorizados se comunicam?
❓ O proxy do Service Connect adiciona latência perceptível à chamada?
❓ Preciso reescrever o código da aplicação para adotar Service Connect?
❓ O que acontece se eu revogar o acesso a um namespace compartilhado do Cloud Map?
Fixando
Um desenvolvedor lê um artigo afirmando que "ECS Service Connect usa mTLS entre os serviços" e quer confirmar isso na documentação oficial. O que a página "Encrypt Amazon ECS Service Connect traffic" realmente afirma?
A Cadência tem "pedidos" e "estoque" no mesmo cluster ECS e na mesma conta AWS, sem plano de expandir para múltiplas contas em 2026. Um arquiteto sugere adotar VPC Lattice "porque é mais moderno e mais seguro". Qual é o problema com essa recomendação?
Conhecimentos, próximo módulo e documentação
| Item | Conteúdo |
|---|---|
| Conhecimentos anteriores necessários | L32 no ar (dois serviços ECS no mesmo cluster, chamada HTTP síncrona), Terraform e .NET 8 básicos, noções de IAM |
| Conhecimentos adquiridos | por que IP de task é efêmero por desenho; a diferença entre isolamento de rede e autenticação de identidade; o vocabulário exato que a AWS usa para TLS no Service Connect; quando Service Connect basta e quando a fronteira exige VPC Lattice |
| Limitação que fica | Service Connect não dá autorização granular por identidade de chamador — só TLS entre proxies. Um chamador dentro do mesmo cluster, com o proxy correto, ainda é aceito sem prova de identidade de negócio |
| Próximo exemplo recomendado | L34 — EKS quando ECS não basta. Reutiliza a decisão de fronteira deste módulo: o mesmo raciocínio de "qual problema a ferramenta nova resolve de fato" se aplica trocando Service Connect/Lattice por orquestrador |
| Também habilitado por este módulo | L42 (identidade de workload: task role, execution role, IRSA) aprofunda a distinção entre "provar quem é a task" e "a rede confiar na task" |
| Data da última validação técnica | 7 de agosto de 2026 |
Documentação oficial consultada: Encrypt Amazon ECS Service Connect traffic — o mecanismo de TLS, os modos da AWS Private CA e a role de infraestrutura; Amazon ECS Service Connect with shared AWS Cloud Map namespaces — o comportamento de acesso à CA quando o namespace compartilhado é revogado; AmazonECSInfrastructureRolePolicyForServiceConnectTransportLayerSecurity — a policy gerenciada da role de infraestrutura; Available metrics and dimensions for Amazon ECS — a existência da métrica de conexão automática do proxy; e Identity-based policy examples for Amazon VPC Lattice — a natureza de auth policy como documento IAM. Os valores de preço não aparecem neste módulo por decisão: use o AWS Pricing Calculator, porque preço varia por região e envelhece mais rápido que o conteúdo.
O que não foi verificado, e você deve conferir na sua conta
O nome exato do namespace de métrica do Service Connect no CloudWatch (usado na seção de provas) não foi confirmado por consulta direta ao console nesta pesquisa — confirme antes de montar alarme em produção. O estado exato do ciclo de vida da task em que o registro no Cloud Map ocorre também não está nomeado explicitamente na documentação do Service Connect consultada; a seção "ponta a ponta" trata isso como não confirmado, por analogia ao que o L03 mediu para registro em grupo de destino de ALB. A latência adicional do proxy citada nas perguntas frequentes é uma ordem de grandeza de exemplo, não uma garantia documentada — meça no seu ambiente.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…