Services: expondo aplicações no cluster

1. Por que Precisamos de Services?

1.1. A natureza efêmera dos Pods: IPs voláteis e a necessidade de descoberta

No Kubernetes, Pods são criados e destruídos constantemente — seja por escalonamento automático, atualizações rolling update ou falhas. Cada Pod recebe um endereço IP interno único, mas esse IP desaparece quando o Pod é recriado. Se um componente A precisa se comunicar com um componente B, não podemos depender de IPs fixos. Services resolvem esse problema fornecendo um ponto de acesso estável.

1.2. Balanceamento de carga interno: distribuindo tráfego entre réplicas

Quando executamos múltiplas réplicas de um Deployment, precisamos distribuir as requisições uniformemente entre elas. Um Service atua como um balanceador de carga interno, roteando tráfego automaticamente para todos os Pods saudáveis que correspondem ao seletor definido.

1.3. Abstração de acesso: separando consumidores da implementação dos Pods

Services criam uma camada de abstração entre consumidores (outros Pods, serviços externos) e a implementação real dos Pods. Podemos alterar versões, escalar ou substituir Pods sem que os consumidores precisem ser atualizados — eles continuam apontando para o mesmo Service.

2. Tipos de Service no Kubernetes

2.1. ClusterIP: comunicação interna entre componentes do cluster

ClusterIP é o tipo padrão. O Service recebe um IP virtual acessível apenas dentro do cluster. Ideal para comunicação entre microsserviços internos.

apiVersion: v1
kind: Service
metadata:
  name: api-backend
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: ClusterIP

2.2. NodePort: expondo aplicações para fora via porta do nó

NodePort expõe o Service em uma porta estática (30000-32767) em todos os nós do cluster. Útil para testes locais ou ambientes on-premise.

apiVersion: v1
kind: Service
metadata:
  name: web-frontend
spec:
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30080
  type: NodePort

2.3. LoadBalancer: integração com balanceadores de carga cloud-native

Em clusters cloud (EKS, AKS, GKE), o tipo LoadBalancer provisiona automaticamente um balanceador de carga externo. Ideal para produção.

apiVersion: v1
kind: Service
metadata:
  name: app-external
spec:
  selector:
    app: minha-app
  ports:
    - port: 443
      targetPort: 8443
  type: LoadBalancer

3. Criando e Configurando um Service

3.1. Estrutura do manifesto YAML: selector, ports e type

O manifesto de um Service possui três elementos críticos:

  • selector: define quais Pods serão alvo (baseado em labels)
  • ports: mapeia portas entre Service e Pod
  • type: define o tipo de exposição

3.2. Mapeamento de portas: targetPort, port e nodePort

spec:
  ports:
    - name: http
      protocol: TCP
      port: 80          # Porta do Service
      targetPort: 8080  # Porta do container
      nodePort: 30080   # Porta do nó (apenas NodePort)
  • port: porta que o Service escuta
  • targetPort: porta do container que recebe o tráfego
  • nodePort: porta no nó (opcional, apenas para NodePort)

3.3. Aplicando e verificando: kubectl apply, kubectl get svc e kubectl describe svc

# Aplicar o manifesto
kubectl apply -f service.yaml

# Listar Services
kubectl get svc

# Obter detalhes completos
kubectl describe svc api-backend

# Verificar endpoints (Pods associados)
kubectl get endpoints api-backend

4. Descoberta de Serviços e DNS Interno

4.1. Como o CoreDNS resolve nomes de Service dentro do cluster

O Kubernetes possui o CoreDNS como DNS interno padrão. Quando um Service é criado, o CoreDNS registra automaticamente um registro DNS para ele. Qualquer Pod pode resolver o nome do Service e obter seu IP ClusterIP.

4.2. Padrão de nomenclatura: ..svc.cluster.local

# Formato completo
<service-name>.<namespace>.svc.cluster.local

# Exemplo prático
api-backend.default.svc.cluster.local

# Formato abreviado (dentro do mesmo namespace)
api-backend

4.3. Variáveis de ambiente vs DNS: diferenças e boas práticas

O Kubernetes também injeta variáveis de ambiente (como API_BACKEND_SERVICE_HOST), mas o DNS é mais flexível e recomendado:

  • DNS: funciona mesmo se o Service for recriado com IP diferente
  • Variáveis de ambiente: podem conter IPs obsoletos se o Service for recriado após o Pod iniciar

Boas práticas: prefira DNS para descoberta de serviços entre namespaces diferentes e variáveis de ambiente apenas para configurações estáticas.

5. Services Headless e Casos Especiais

5.1. Quando usar ClusterIP: None para descoberta direta de Pods

Um Service headless (ClusterIP: None) não cria um IP virtual nem faz balanceamento. Em vez disso, o DNS retorna os IPs individuais de todos os Pods selecionados. Útil quando o cliente precisa se conectar diretamente a cada Pod específico.

apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None
  selector:
    app: database
  ports:
    - port: 5432
      targetPort: 5432

5.2. Integração com StatefulSets e bancos de dados distribuídos

Services headless são essenciais para StatefulSets, onde cada Pod precisa de uma identidade estável (nome de host único). Bancos de dados como Cassandra, Elasticsearch e MongoDB usam esse padrão para descoberta de nós.

5.3. ExternalName: apontando Services para recursos fora do cluster

apiVersion: v1
kind: Service
metadata:
  name: db-externo
spec:
  type: ExternalName
  externalName: meu-banco.exemplo.com

Esse tipo cria um alias DNS dentro do cluster para um serviço externo, sem proxy ou balanceamento.

6. Roteamento Avançado e Ingress

6.1. Limitações do Service Layer 4 e a necessidade de Layer 7

Services operam na camada de transporte (TCP/UDP). Eles não entendem HTTP, não podem rotear baseado em hostname ou path, nem realizar terminação SSL. Para isso, precisamos de um Ingress Controller.

6.2. Ingress Controller: como expor múltiplos serviços com um único IP

O Ingress é um recurso que define regras de roteamento HTTP/HTTPS. O Ingress Controller (como NGINX Ingress, Traefik, AWS ALB Ingress) implementa essas regras.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: meu-ingress
spec:
  rules:
    - host: app1.exemplo.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-backend
                port:
                  number: 80

6.3. Regras de roteamento baseadas em hostname e path

spec:
  rules:
    - host: loja.exemplo.com
      http:
        paths:
          - path: /api
            backend:
              service:
                name: api-loja
                port: 8080
          - path: /web
            backend:
              service:
                name: web-loja
                port: 80

7. Monitoramento e Troubleshooting de Services

7.1. Verificando conectividade com kubectl port-forward e curl

# Encaminhar porta local para o Service
kubectl port-forward svc/api-backend 8080:80

# Testar conectividade
curl http://localhost:8080/health

# Verificar resolução DNS dentro de um Pod
kubectl exec -it pod-teste -- nslookup api-backend

7.2. Logs e eventos: identificando falhas de selector ou porta

# Verificar eventos recentes do Service
kubectl describe svc api-backend

# Verificar logs do CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns

# Verificar endpoints ativos
kubectl get endpoints api-backend

Se kubectl get endpoints mostrar vazio, o selector não está encontrando Pods — verifique as labels.

7.3. Ferramentas de diagnóstico: kubectl exec, nslookup e tcpdump

# Testar conectividade TCP de dentro de um Pod
kubectl exec -it pod-teste -- curl -v http://api-backend:80

# Verificar resolução DNS
kubectl exec -it pod-teste -- nslookup api-backend.default.svc.cluster.local

# Instalar tcpdump em um Pod para debug avançado
kubectl exec -it pod-teste -- tcpdump -i any port 80

Referências