Uso APIs .NET com Docker Compose há anos. Funciona bem numa única máquina. Defines os teus serviços, corres docker-compose up, e tudo se conecta. Simples.
Depois o tráfego cresce. Ou a equipa cresce. Ou alguém pergunta: "O que acontece se aquele contentor crashar às 3 da manhã?" O Compose não tem uma boa resposta. O Kubernetes tem.
Isto não é um tutorial de Kubernetes para iniciantes. É sobre a mudança específica de mentalidade que os developers .NET enfrentam quando deixam de correr contentores localmente e começam a corrê-los num cluster real.
O que o Compose te dá (e o que não dá)
O Docker Compose é excelente para definir como os serviços se relacionam entre si. Uma API web, uma cache Redis, uma instância SQL Server. Escreves um ficheiro YAML, e tudo arranca junto. O desenvolvimento local torna-se reproduzível.
Mas o Compose não foi desenhado para produção. Não tem o conceito de estado desejado. Se um contentor morrer, o Compose não o reinicia automaticamente (a menos que uses restart: always, que é uma solução rudimentar). Não há balanceamento de carga, atualizações rolling, nem redirecionamento de tráfego baseado em health checks.
O Kubernetes resolve tudo isto, mas pede-te que penses de uma forma completamente diferente.
A Mudança de Mentalidade
No Compose, pensas em contentores. No Kubernetes, pensas em estado desejado.
Declaras o que queres. "Quero três réplicas desta API a correr, na porta 80, com pelo menos 512MB de memória cada." O Kubernetes descobre como concretizar isso, e continua a concretizá-lo mesmo quando nós falham, contentores crasham, ou fazes deploy de uma nova versão.
Para developers .NET, isto significa que a tua aplicação precisa de ser genuinamente stateless. Se estás a guardar dados de sessão em memória, vais ter problemas no momento em que o Kubernetes fizer balanceamento de carga de um segundo pedido para um pod diferente. Move o estado de sessão para Redis ou uma base de dados antes de tocares no K8s.
Traduzir o teu Ficheiro Compose
Aqui está um serviço Compose típico para uma API .NET:
services:
api:
image: myregistry.azurecr.io/my-api:latest
ports:
- "8080:80"
environment:
- ConnectionStrings__Default=Server=db;Database=mydb;...
depends_on:
- dbNo Kubernetes, isto torna-se no mínimo três recursos: um Deployment (define os teus pods e réplicas), um Service (expõe-os dentro do cluster), e um Ingress (encaminha tráfego externo). Moves também as tuas connection strings para um Secret.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api
spec:
replicas: 3
selector:
matchLabels:
app: my-api
template:
metadata:
labels:
app: my-api
spec:
containers:
- name: my-api
image: myregistry.azurecr.io/my-api:latest
ports:
- containerPort: 80
env:
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: my-api-secrets
key: connection-string
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"O bloco resources não é opcional. Sem ele, um único processo descontrolado pode esgotar o nó inteiro. Define os requests suficientemente baixos para escalonar com eficiência e os limits suficientemente altos para aguentar picos de carga.
Health Checks Não São Opcionais
O Kubernetes precisa de saber quando o teu pod está pronto para receber tráfego e quando algo correu mal. É para isso que servem as probes de liveness e readiness.
No .NET, adiciona o middleware de health checks:
builder.Services.AddHealthChecks();
app.MapHealthChecks("/healthz");Depois liga-o ao teu Deployment:
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10A readiness probe diz ao Kubernetes para não enviar tráfego a um pod até este estar efetivamente pronto. A liveness probe diz ao Kubernetes para reiniciar o pod se ficar bloqueado. Ambas são importantes em produção.
Atualizações Rolling Sem Downtime
Uma das melhores coisas que o Kubernetes oferece por defeito são as atualizações rolling. Quando fazes push de uma nova imagem, ele arranca novos pods, espera que passem nos readiness checks, e só depois termina os antigos. Os teus utilizadores nunca sentem uma interrupção.
Para que isto funcione de forma fiável, fixa as tags das imagens. Nunca uses :latest num Deployment de produção. Usa uma versão específica ou uma SHA de commit. Caso contrário, o Kubernetes pode não saber que existe uma nova versão, e perdes o histórico do que está efetivamente a correr.
O que o Azure Kubernetes Service Acrescenta
Se já estás no Azure com aplicações .NET, o AKS é o destino natural. Tens um plano de controlo gerido, integração com o Azure Container Registry para as tuas imagens, e Azure Active Directory para autenticação no cluster.
O comando az aks get-credentials liga o teu kubectl local diretamente ao cluster. A partir daí, o mesmo YAML que testaste localmente funciona em produção.
O AKS integra também com o Azure Monitor e Application Insights. A telemetria .NET existente continua a funcionar. Apenas apontas a instrumentation key para o recurso correto.
O Trade-off Honesto
O Kubernetes é mais complexo que o Compose. Só a superfície de YAML é intimidante. Vais gastar tempo a depurar escalonamento de pods, resource limits, e configuração do ingress controller. Esse custo é real.
Mas os ganhos de fiabilidade também são reais. Deployments self-healing, escalamento horizontal, releases sem downtime. Para qualquer coisa de que as pessoas dependam, o trade-off costuma valer a pena.
Começa pequeno. Migra um serviço. Habitua-te ao kubectl get pods, kubectl logs, e kubectl describe. Esses três comandos vão dizer-te a maior parte do que precisas de saber quando algo corre mal.



