Services: DNS interno e exposição
2 min de leitura
O Pod é mortal: nasce, morre, ganha IP novo. Se sua API precisa chamar o banco "pelo nome", você não pode confiar no IP do Pod. Service é o que dá identidade estável a um conjunto de Pods.
O que um Service faz
Um Service é um balanceador de carga + DNS interno do cluster.
Você diz "esses Pods com label app=db formam um grupo lógico", e
o k8s cria:
- Um IP virtual estável (chamado de
ClusterIP) que não muda enquanto o Service existir. - Um nome DNS (ex:
db.default.svc.cluster.local) que resolve pro IP virtual. - Regras de roteamento que mandam tráfego pros Pods certos (via labels).
Os 4 tipos principais
ClusterIP(padrão) - só acessível dentro do cluster. É o que você usa pra service-to-service (api -> db).NodePort- expõe o service em uma porta alta (30000-32767) em todos os nodes. Acessível de fora via<node-ip>:30080. Útil pra dev ou em bare-metal, mas não recomendado em cloud.LoadBalancer- provisiona um load balancer do provedor (AWS ELB, GCP LB, Azure LB) e aponta pros nodes. O jeito padrão de expor serviço em cloud.ExternalName- retorna um CNAME (db.rds.amazonaws.com). Não faz proxy, só redireciona DNS. Útil pra apontar pra serviço gerenciado fora do cluster (RDS, Cloud SQL, etc.).
Manifesto
apiVersion: v1
kind: Service
metadata:
name: api
spec:
type: ClusterIP # default
selector:
app: api # manda tráfego pros Pods com essa label
ports:
- port: 80 # porta do Service
targetPort: 3000 # porta do container (no Pod)
protocol: TCP
Aplique:
kubectl apply -f service.yaml
kubectl get service api
kubectl get endpoints api # quais Pods estão atrás do service
Agora qualquer Pod no cluster pode chamar http://api:80 e o DNS
interno resolve. Sem IP, sem Kubernetes API - o Service virou o
"nome" do seu app.
DNS interno - a mágica que ninguém te conta
Quando você cria um Service, o k8s registra automaticamente um nome DNS no formato:
<service-name>.<namespace>.svc.cluster.local
Defaults: namespace default, cluster domain cluster.local. Então
o nome curto api funciona dentro do mesmo namespace. Pra cruzar
namespaces, use api.outro-ns ou o FQDN.
Por isso DATABASE_URL=postgres://user:pass@db:5432/app funciona -
o db é o nome do Service do Postgres, e o DNS interno resolve.
Headless Service (quando você quer todos os Pods, não load balance)
Pra StatefulSets (banco, etc) você não quer load balancer - quer
falar com cada Pod individualmente. Crie um Service sem
ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: db
spec:
clusterIP: None # headless
selector:
app: db
ports:
- port: 5432
Agora dig db.default.svc.cluster.local retorna o IP de cada
Pod, não um virtual IP. StatefulSets usam isso pra cada réplica
ter DNS próprio (db-0.db, db-1.db, ...).
Três conceitos que você acabou de aprender:
- Service = IP estável + DNS + load balancer sobre um conjunto de Pods (selecionados por label).
ClusterIPé o padrão e o que você usa 95% das vezes - comunicação interna entre services.- DNS interno (
api,db) é o jeito idiomático de referenciar services. Nada de IP, nada de env var.
Dica: pra debugar,
kubectl get endpoints <service>mostra exatamente quais Pods estão sendo roteados. Se a lista está vazia, oselectordo Service não bate com nenhumalabelde Pod - o erro mais comum.
No próximo nó, vamos separar configuração do código com ConfigMaps (config normal) e Secrets (config sensível).