Pular para o conteúdo

Expor um serviço com HTTPS

Intermediário16 min de leiturakubernetes

Ao final deste guia, https://loja.exemplo.com.br responde do seu cluster com certificado válido que renova sozinho — e você tem um alerta para o dia em que a renovação falhar.

Pré-requisitos: cluster com um LoadBalancer disponível, e controle sobre a zona DNS do domínio.

Janela do terminal
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx --create-namespace \
--version 4.11.2 \
--set controller.service.type=LoadBalancer
kubectl get svc -n ingress-nginx -w # espere o EXTERNAL-IP aparecer

Aponte o DNS para esse endereço antes de continuar — a validação HTTP do certificado depende disso.

Janela do terminal
dig +short loja.exemplo.com.br # precisa devolver o IP do LoadBalancer
Janela do terminal
helm repo add jetstack https://charts.jetstack.io
helm upgrade --install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace \
--version v1.16.1 --set crds.enabled=true
kubectl get pods -n cert-manager # três Pods: controller, webhook, cainjector

Comece pelo ambiente de staging do Let’s Encrypt. Ele emite certificado não confiável pelo navegador, mas tem limites generosos — e o de produção bloqueia por horas se você errar algumas vezes.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: letsencrypt-staging }
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected] # avisos de expiração vão para cá
privateKeySecretRef: { name: letsencrypt-staging-key }
solvers:
- http01: { ingress: { ingressClassName: nginx } }

Depois de validar o fluxo, duplique o manifesto trocando o nome para letsencrypt-prod e o server para https://acme-v02.api.letsencrypt.org/directory.

Para certificado curinga (*.exemplo.com.br) ou serviço sem exposição pública, o solver HTTP-01 não funciona: use DNS-01, com credencial do seu provedor de DNS em um Secret.

O cert-manager observa a anotação e emite o certificado sozinho.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: loja
namespace: loja
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: [loja.exemplo.com.br]
secretName: loja-tls # o cert-manager cria este Secret
rules:
- host: loja.exemplo.com.br
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: loja, port: { number: 80 } }

A emissão passa por uma cadeia de recursos. Quando algo falha, os eventos dizem onde:

Janela do terminal
kubectl get certificate -n loja # READY deve virar True em ~1 minuto
kubectl describe certificate loja-tls -n loja
kubectl get certificaterequest,order,challenge -n loja
kubectl describe challenge -n loja | sed -n '/Events/,$p'

Confirme de fora:

Janela do terminal
curl -sI https://loja.exemplo.com.br | head -3
openssl s_client -connect loja.exemplo.com.br:443 -servername loja.exemplo.com.br \
</dev/null 2>/dev/null | openssl x509 -noout -issuer -dates

Automação também falha — permissão de DNS revogada, limite atingido, validação bloqueada. O certificado tem 90 dias e a renovação começa aos 60; se ninguém observa, você descobre no dia 90.

# cert-manager expõe esta métrica; alerte com folga
(certmanager_certificate_expiration_timestamp_seconds - time()) / 86400 < 21

Monitore também o certificado servido na porta e não só o objeto no cluster — assim você pega o caso em que o Secret renovou e o controlador não recarregou.

Sintoma Causa provável
Challenge preso em pending DNS não aponta para o LoadBalancer, ou porta 80 fechada
Certificate sem sair de False Emissor errado no cluster-issuer, ou e-mail inválido
Erro de limite de emissão Você usou produção para testar; volte para staging e espere
Navegador reclama do certificado Emissor de staging ainda configurado
404 no domínio, mas TLS ok ingressClassName, host ou backend errado no Ingress
Funciona no navegador, falha no curl Cadeia incompleta servida pelo controlador

Redirecionamento de HTTP para HTTPS já vem ligado por padrão no ingress-nginx. Se você precisa desligá-lo para algum caminho (um webhook legado, por exemplo), faça por anotação naquele Ingress específico, nunca no controlador inteiro.

  • DNS apontando para o LoadBalancer, verificado com dig.
  • Fluxo validado no emissor de staging antes do de produção.
  • Certificate com READY=True e Secret criado.
  • curl -sI devolve 200 em HTTPS, com cadeia completa.
  • Alerta de expiração com pelo menos 21 dias de folga.
  • Registro CAA configurado no domínio.