Nesta aula, vamos explorar um dos pilares do Kubernetes: os Services e o networking que permite que os pods se comuniquem entre si e com o mundo externo. Entender como os serviços funcionam é essencial para projetar aplicações resilientes e escaláveis no Kubernetes.

Vamos começar com os três tipos principais de Service (ClusterIP, NodePort e LoadBalancer), depois veremos como a descoberta de serviços acontece, uma visão geral do Ingress e, por fim, o DNS interno que o Kubernetes fornece automaticamente.

ClusterIP, NodePort, LoadBalancer

Um Service no Kubernetes é uma abstração que define um conjunto lógico de pods e uma política para acessá-los. Ele fornece um endereço IP estável e um nome DNS para um conjunto de pods que podem ser criados e destruídos dinamicamente. Existem três tipos principais de Service, cada um com um escopo de exposição diferente.

ClusterIP é o tipo padrão. Ele cria um IP virtual dentro do cluster (no intervalo configurado para serviços) que só é acessível de dentro do cluster. Esse é o tipo mais comum para comunicação interna entre serviços. Por exemplo, um backend que só precisa ser acessado por outros pods internamente.

NodePort expõe o serviço em cada nó do cluster em uma porta estática (entre 30000-32767). Isso permite que o serviço seja acessado de fora do cluster usando <IP-do-nó>:<porta-do-nó>. NodePort é útil para testes simples ou quando você não tem um balanceador de carga externo.

LoadBalancer é o tipo que integra com o balanceador de carga do provedor de nuvem (como AWS ELB, GCP LB, etc.). Ele cria um balanceador de carga externo que encaminha tráfego para os nós, e de lá para os pods. Esse é o tipo mais comum para expor serviços para o mundo externo em produção.

Vamos ver um exemplo de cada tipo. Primeiro, um Service ClusterIP para uma aplicação web simples:

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80          # porta do serviço
      targetPort: 8080  # porta do pod

Esse Service seleciona pods com o label app: web e encaminha o tráfego da porta 80 do serviço para a porta 8080 dos pods.

Para criar um NodePort, basta adicionar type: NodePort e especificar uma porta no intervalo permitido:

apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
      nodePort: 30080  # opcional, se omitido o Kubernetes escolhe

E para um LoadBalancer, usamos type: LoadBalancer:

apiVersion: v1
kind: Service
metadata:
  name: web-lb
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

No caso do LoadBalancer, o Kubernetes provisiona um balanceador de carga externo (se o ambiente suportar) e retorna o endereço IP externo.

Descoberta de serviços

A descoberta de serviços é o mecanismo pelo qual um pod encontra o endereço de outro serviço. No Kubernetes, isso é feito principalmente por duas vias: variáveis de ambiente e DNS.

Quando um Service é criado, o Kubernetes injeta automaticamente um conjunto de variáveis de ambiente nos pods (se eles foram criados depois do Service). Essas variáveis seguem o padrão <SERVICE_NAME>_SERVICE_HOST e <SERVICE_NAME>_SERVICE_PORT. Por exemplo, para um serviço chamado web-service, o pod terá WEB_SERVICE_SERVICE_HOST e WEB_SERVICE_SERVICE_PORT.

No entanto, o método mais recomendado e moderno é usar o DNS interno do cluster. O Kubernetes roda um serviço DNS (geralmente o CoreDNS) que cria registros DNS para cada Service. O nome DNS é o nome do Service no namespace, então um pod pode simplesmente usar http://web-service para acessar o serviço. Isso é muito mais limpo e não depende da ordem de criação.

Vamos ver um exemplo prático. Suponha que temos um Deployment para um backend e um Service chamado backend. Um frontend pode acessar o backend usando a URL http://backend:8080 (onde 8080 é a porta do Service).

Para testar a descoberta de serviços, podemos executar um pod temporário e fazer uma requisição:

kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -qO- http://backend:8080

Isso demonstra que o pod consegue resolver o nome do serviço e se conectar a ele.

Ingress (visão geral)

O Ingress é um recurso do Kubernetes que gerencia o acesso externo aos serviços, geralmente HTTP e HTTPS. Ele fornece roteamento baseado em regras, balanceamento de carga, TLS/SSL e virtual hosting. O Ingress não é um Service, mas sim um controlador que implementa as regras definidas.

Para usar Ingress, você precisa instalar um controlador de Ingress, como o NGINX Ingress Controller, Traefik, ou o controlador do seu provedor de nuvem. O controlador observa os recursos Ingress e configura o proxy de acordo.

Um exemplo de Ingress que roteia o tráfego para dois serviços diferentes com base no caminho:

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

Nesse exemplo, requisições para meuapp.com/api são encaminhadas para o serviço api-service, e as demais para web-service. O Ingress também pode configurar TLS com certificados, usando um Secret.

É importante notar que o Ingress só é útil para tráfego HTTP/HTTPS. Para outros protocolos, você precisará de LoadBalancer ou NodePort.

DNS interno

O DNS interno do Kubernetes é um componente essencial para a descoberta de serviços. Ele é implementado pelo CoreDNS (ou kube-dns em versões antigas) e roda como um conjunto de pods no namespace kube-system. O DNS resolve nomes de serviços para os IPs de ClusterIP correspondentes.

Cada Service recebe um registro DNS no formato <nome-do-serviço>.<namespace>.svc.cluster.local. Dentro do mesmo namespace, você pode usar apenas o nome do serviço (ex: backend). De outro namespace, use <nome>.<namespace> (ex: backend.producao).

Além de serviços, o DNS também resolve pods (se habilitado) e pode ser configurado para domínios personalizados. Vamos ver como testar a resolução DNS:

kubectl exec -it meu-pod -- nslookup backend

ou usando o busybox:

kubectl exec -it meu-pod -- nslookup backend.default.svc.cluster.local

O CoreDNS também suporta políticas de DNS (como ClusterFirst, que é o padrão), que definem como as consultas DNS são encaminhadas para o DNS externo.

Entender o DNS interno é crucial para configurar a comunicação entre serviços de forma confiável.

Boas práticas e observações finais

Ao trabalhar com Services e networking no Kubernetes, siga estas boas práticas:

  • Use Services ClusterIP para comunicação interna, evitando expor desnecessariamente seus serviços.
  • Prefira Ingress para exposição HTTP/HTTPS em produção, pois oferece controle mais fino e economia de IPs públicos.
  • Use LoadBalancer apenas quando precisar de balanceamento externo para protocolos não HTTP.
  • Nomeie seus Services de forma clara e consistente, pois eles são usados como nomes DNS.
  • Aproveite o DNS interno para descoberta de serviços, em vez de variáveis de ambiente, pois é mais flexível.
  • Monitore o CoreDNS e os endpoints dos Services para garantir que o tráfego está sendo roteado corretamente.

Com esses conceitos, você está pronto para projetar a rede da sua aplicação no Kubernetes de forma eficiente.

Exercícios

  1. Crie um Service ClusterIP para um deployment chamado app que escuta na porta 80 do pod, usando a porta 8080 no Service. Escreva o YAML.
  2. ✓ Resposta:
    apiVersion: v1
    kind: Service
    metadata:
      name: app-service
    spec:
      selector:
        app: app
      ports:
        - protocol: TCP
          port: 8080
          targetPort: 80
  3. Qual é a diferença entre ClusterIP e NodePort? Em que situação você usaria NodePort?
  4. ✓ Resposta: ClusterIP é acessível apenas dentro do cluster, enquanto NodePort expõe o serviço em uma porta estática em cada nó, permitindo acesso externo. NodePort é útil para testes rápidos, demos, ou quando não há um balanceador de carga externo disponível.
  5. Explique como funciona a descoberta de serviços usando DNS interno. Dê um exemplo de como um pod pode acessar um serviço chamado db no namespace producao.
  6. ✓ Resposta: O DNS interno do Kubernetes (CoreDNS) cria um registro DNS para cada Service no formato <nome-do-serviço>.<namespace>.svc.cluster.local. Um pod pode acessar o serviço db no namespace producao usando o nome db.producao (ou o FQDN completo). Por exemplo, um pod pode conectar-se a db.producao:5432.
  7. O que é um Ingress? Quais são os benefícios de usar Ingress em vez de expor cada serviço com LoadBalancer?
  8. ✓ Resposta: Ingress é um recurso que gerencia o acesso externo a serviços HTTP/HTTPS, oferecendo roteamento baseado em regras, TLS e virtual hosting. Os benefícios incluem economia de IPs públicos (um único LoadBalancer para múltiplos serviços), controle centralizado de roteamento, e funcionalidades avançadas como rewrite, autenticação e balanceamento de carga.
  9. Como você verifica se o DNS interno está resolvendo corretamente um serviço? Dê um comando exemplo.
  10. ✓ Resposta: Você pode executar um pod temporário com a imagem busybox e usar o comando nslookup ou wget para testar a resolução. Por exemplo:
    kubectl run test-pod --image=busybox --rm -it --restart=Never -- nslookup nome-do-serviço

Referências