Ingress no Kubernetes: levando sua aplicação para o mundo

1. Visão geral

Quando você já tem um Service rodando no cluster mas quer que usuários externos acessem por um domain bacana como api.nonladev.com em vez de um IP:porta confuso, é hora de usar Ingress. Este artigo explica o que é Ingress, como diferencia de Service type LoadBalancer, como instalar Ingress Controller e escrever Ingress resources para situações comuns: path-based routing, host-based routing, TLS. Depois de ler, você consegue expor sua aplicação Spring Boot para fora com um domain e HTTPS.

2. Por que precisa de Ingress

2.1. O problema com Service

Service type NodePort ou LoadBalancer funcionam bem, mas têm limitações:

  • LoadBalancer: cada Service precisa de um IP externo próprio → custa caro se tiver muitos services (cada cloud LB cobra separado).
  • NodePort: expõe uma porta estranha (30000-32767), sem TLS termination, sem routing por domain/path.

Suponha que você tem 5 microservices para expor para fora, usar LoadBalancer significa 5 cloud load balancers, 5 IPs externos. Ingress resolve isso usando um entry point para rotear tráfego para muitos Services dentro do cluster, baseado no hostname ou path.

2.2. O que é Ingress

Ingress é um objeto de API que define regras de roteamento HTTP/HTTPS de fora para Services dentro do cluster. Ingress por si só não faz nada — precisa de um Ingress Controller (um Pod rodando um reverse proxy como NGINX, Traefik, HAProxy) para ler a regra e executá-la.

Cliente → Ingress Controller (LoadBalancer/NodePort) → Regras Ingress → Service → Pod

Nota: Se o cluster ainda não tem um Ingress Controller instalado, criar um Ingress resource não vai servir para nada — é só uma configuração “esperando” o controller ler.

3. Instalando o Ingress Controller

O mais comum é o ingress-nginx. Instale com Helm:

<!-- não aplicável — use Helm/kubectl, não é uma dependência Maven -->
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace

Este comando cria um namespace ingress-nginx, faz deploy do Ingress Controller junto com um Service type LoadBalancer para receber tráfego de fora. Verifique:

kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
NAME                                       TYPE           EXTERNAL-IP     PORT(S)
ingress-nginx-controller                   LoadBalancer   34.123.45.67    80:31234/TCP,443:31235/TCP

Este IP externo é o único ponto de entrada para todos os Ingresses do cluster.

4. Deployment e Service de exemplo

Antes de escrever o Ingress, você precisa de um Service para rotear tráfego. Suponha uma aplicação order-service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: nonladev/order-service:1.0
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
    - port: 80
      targetPort: 8080

O Service order-service expõe a porta 80, encaminhando para a porta 8080 do container. Note que este Service não precisa ser type LoadBalancer, usar ClusterIP (padrão) é suficiente, pois o Ingress Controller vai chamar o Service pela rede interna.

5. Ingress resource básico — host-based routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: api.nonladev.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80

Explicação de cada parte:

  • ingressClassName: nginx — especifica qual controller processa esta regra (importante quando o cluster tem vários controllers).
  • rules.host — apenas tráfego com header Host: api.nonladev.com vai corresponder esta regra.
  • path + pathType: Prefix — qualquer requisição começando com / (tudo) será encaminhada para order-service:80.
  • annotation rewrite-target — específica do ingress-nginx, usada para reescrever a URL antes de encaminhar (cada controller tem suas próprias annotations).

Aplique e teste:

kubectl apply -f order-ingress.yaml
curl -H "Host: api.nonladev.com" http://34.123.45.67/
{"service":"order-service","status":"UP"}

Em ambiente real, você aponta o DNS record api.nonladev.com para o IP externo do Ingress Controller, sem precisar fazer headers Host falsos como acima.

6. Path-based routing — múltiplos services compartilhando o mesmo domain

Situação comum: um domain, vários services em paths diferentes.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-gateway-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: api.nonladev.com
      http:
        paths:
          - path: /orders
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80
          - path: /customers
            pathType: Prefix
            backend:
              service:
                name: customer-service
                port:
                  number: 80

Requisição para api.nonladev.com/orders/123 roteia para order-service, enquanto /customers/55 roteia para customer-service. pathType: Prefix significa corresponder por prefixo — diferente de Exact (tem que corresponder exatamente) e ImplementationSpecific (o controller interpreta por conta própria, geralmente regex).

Nota: A ordem dos paths não afeta o matching em Ingress v1 — o controller escolhe o path mais longo que corresponde primeiro. Mas escreva paths de forma clara, sem se sobrepor, para evitar bugs difíceis de debugar.

7. TLS — HTTPS para Ingress

Para ter HTTPS, você precisa de um Secret com cert e key, depois referenciar no Ingress.

kubectl create secret tls nonladev-tls \
  --cert=tls.crt --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-ingress-tls
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.nonladev.com
      secretName: nonladev-tls
  rules:
    - host: api.nonladev.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80

spec.tls declara qual host usa qual cert. O controller automaticamente faz TLS termination — quer dizer, descriptografa HTTPS no Ingress, depois encaminha HTTP puro para o Service dentro. Seu Pod não precisa saber nada sobre TLS.

Na prática, você geralmente usa cert-manager para automaticamente pedir certificados Let’s Encrypt em vez de criar Secrets à mão:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-ingress-tls
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.nonladev.com
      secretName: nonladev-tls
  rules:
    - host: api.nonladev.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80

A annotation cluster-issuer pede ao cert-manager para criar e renovar automaticamente o Secret nonladev-tls — você não precisa fazer mais nada manualmente.

8. Escrevendo testes para verificar endpoints depois de expor

Depois que o Ingress está funcionando, você pode escrever um test de integração simples para verificar que a classe controller do Spring Boot processa o path corretamente (teste na camada de aplicação Spring Boot, não teste o YAML diretamente):

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderControllerIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldReturnOrderWhenCalledWithOrdersPrefix() {
        ResponseEntity<String> response =
                restTemplate.getForEntity("/orders/123", String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("orderId");
    }
}

Este teste garante que a aplicação Spring Boot responde corretamente para o path /orders/123 que o Ingress reescreve para — separando erros do código backend de erros de configuração do Ingress.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

9. Erros comuns

  • Esqueceu de ingressClassName: se o cluster tem vários Ingress Controllers (por exemplo, tanto nginx quanto traefik), o Ingress não sabe qual controller processa → não funciona ou é processado errado pelos dois.
  • Tipo de Service errado: deixar o Service backend como LoadBalancer é desnecessário, gasta recursos — use apenas ClusterIP.
  • pathType não coincide com o esperado: usar Exact quando precisa de Prefix faz com que requisições filhas (/orders/123) retornem 404 porque apenas /orders corresponde exatamente.
  • DNS ainda não aponta: teste com curl -H "Host: ..." antes de configurar DNS real, evite perder tempo esperando DNS propagar.
  • Annotation de rewrite errada para o controller: as annotations do ingress-nginx não funcionam para Traefik ou AWS ALB Ingress Controller — cada controller tem seu próprio conjunto de annotations, leia cuidadosamente a documentação correspondente.

Quando NÃO usar Ingress: se sua aplicação usa um protocolo que não é HTTP/HTTPS (por exemplo, TCP puro, gRPC streaming complexo, ou UDP), Ingress padrão não suporta bem — considere Gateway API ou Service type LoadBalancer direto para TCP/UDP.

10. Conclusão

Ingress é uma camada de roteamento HTTP/HTTPS centralizada que ajuda você a expor múltiplos Services para fora por um único entry point, economizando custos com LoadBalancer e facilitando o gerenciamento de domain/path/TLS. O ponto-chave a lembrar: Ingress resource é só uma regra, você precisa de um Ingress Controller para executar; escolha o ingressClassName e pathType corretos; use cert-manager para automatizar TLS. Para aplicações que não são HTTP puro, considere usar Gateway API em vez de forçar Ingress.

kubernetesingressnetworkingdevopsnginx