External DNS: sincronizando DNS com Ingress resources
1. Introdução ao External DNS e seus fundamentos
Gerenciar registros DNS manualmente em ambientes Kubernetes é uma tarefa repetitiva, propensa a erros e que não escala. Cada novo serviço exposto via Ingress exige a criação manual de um registro A, CNAME ou TXT no provedor DNS, além da manutenção contínua quando endpoints mudam ou são removidos.
O External DNS resolve esse problema automatizando a sincronização entre os recursos Ingress do Kubernetes e os registros DNS no provedor externo. A arquitetura é simples: um pod watcher monitora continuamente os recursos Ingress no cluster. Quando um Ingress é criado, atualizado ou removido, o External DNS reflete automaticamente essa mudança no provedor DNS configurado, criando, atualizando ou excluindo os registros correspondentes.
Isso elimina o gargalo operacional e garante que o DNS esteja sempre consistente com o estado real do cluster, permitindo que equipes de DevOps entreguem aplicações com roteamento de tráfego funcional sem intervenção manual.
2. Pré-requisitos e preparação do ambiente
Antes de instalar o External DNS, é necessário:
- Cluster Kubernetes funcional com acesso ao kubeconfig e permissões de cluster-admin.
- Ingress Controller instalado (NGINX Ingress Controller, Traefik, Contour, etc.). O External DNS depende dos recursos Ingress para extrair os hostnames.
- Provedor DNS suportado: AWS Route53, Google Cloud DNS, Azure DNS, Cloudflare, DigitalOcean, entre outros.
- Credenciais de acesso ao provedor DNS:
- AWS Route53: IAM Role ou Access Key com permissão
route53:ChangeResourceRecordSetseroute53:ListHostedZones. - Google Cloud DNS: Service Account com papel
dns.admin. - Cloudflare: Token de API com permissão
Zone:ReadeDNS:Edit.
Para este artigo, usaremos AWS Route53 como provedor de exemplo.
3. Instalação e configuração do External DNS
A instalação pode ser feita via Helm chart oficial ou via manifestos YAML manuais.
Instalação via Helm
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm repo update
helm install external-dns external-dns/external-dns \
--namespace external-dns --create-namespace \
--set provider=aws \
--set aws.zoneType=public \
--set domainFilters[0]=example.com \
--set policy=sync \
--set registry=txt \
--set txtOwnerId=my-cluster
Instalação via manifestos YAML
Crie um arquivo external-dns.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services", "endpoints", "pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["extensions", "networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.14.2
args:
- --source=ingress
- --provider=aws
- --domain-filter=example.com
- --policy=sync
- --registry=txt
- --txt-owner-id=my-cluster
- --aws-zone-type=public
env:
- name: AWS_REGION
value: us-east-1
Aplique o manifesto:
kubectl apply -f external-dns.yaml
4. Sincronizando DNS com Ingress resources
O External DNS monitora automaticamente o campo spec.rules[].host dos recursos Ingress. Quando um Ingress com hostname é criado, o External DNS cria o registro DNS correspondente.
Exemplo prático
Crie um Ingress para uma aplicação de exemplo:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: default
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
external-dns.alpha.kubernetes.io/ttl: "300"
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
Aplique o Ingress:
kubectl apply -f my-app-ingress.yaml
Após alguns segundos, verifique se o registro foi criado no Route53:
kubectl logs -n external-dns deployment/external-dns
A saída deve mostrar:
time="2025-04-10T14:30:00Z" level=info msg="Updating DNS records" action=CREATE record=app.example.com type=A
5. Estratégias avançadas de sincronização
TTL customizado
Use a anotação external-dns.alpha.kubernetes.io/ttl para definir o TTL do registro DNS:
metadata:
annotations:
external-dns.alpha.kubernetes.io/ttl: "60"
Múltiplos domínios e filtros
Para sincronizar múltiplos domínios, use --domain-filter repetidamente ou --regex-domain-filter:
args:
- --domain-filter=example.com
- --domain-filter=test.org
Ou com regex:
args:
- --regex-domain-filter=.*\.example\.com
Integração com cert-manager para HTTPS
Combine External DNS com cert-manager para provisionamento automático de certificados TLS:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- secure.example.com
secretName: secure-tls
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-app-service
port:
number: 443
6. Tratamento de conflitos e resolução de problemas
Modo dry-run
Antes de aplicar alterações reais, use --dry-run para validar:
args:
- --dry-run
O External DNS mostrará o que faria sem efetuar mudanças.
Logs e debugging
Ative logs detalhados:
args:
- --log-level=debug
Políticas de ownership
sync(padrão): o External DNS assume controle total dos registros, removendo registros órfãos.upsert-only: apenas cria e atualiza registros, nunca os remove. Útil para evitar exclusões acidentais.
Configure com:
args:
- --policy=upsert-only
7. Boas práticas e considerações de segurança
- Namespaces isolados: instale o External DNS em um namespace dedicado (
external-dns). - RBAC mínimo: conceda apenas as permissões necessárias (ClusterRole com acesso a ingress, services, endpoints).
- Credenciais seguras: use secrets do Kubernetes para armazenar tokens de API ou chaves de acesso, nunca em texto plano no Deployment.
- Monitoramento: exponha métricas Prometheus na porta 7979 e configure alertas para falhas de sincronização:
args:
- --metrics-address=:7979
8. Cenários avançados e integrações
External DNS com Istio Gateway e VirtualService
Para ambientes service mesh, o External DNS pode sincronizar com recursos Istio:
args:
- --source=ingress
- --source=istio-gateway
Sincronização para zonas privadas
Para endpoints internos, configure a zona privada:
args:
- --aws-zone-type=private
Atualização zero-downtime
Combine health checks do Ingress Controller com o External DNS para garantir que registros DNS só apontem para endpoints saudáveis. Use probes de readiness nos Deployments e configure o Ingress com nginx.ingress.kubernetes.io/service-upstream: "true" para balanceamento adequado.
Referências
- Documentação oficial do External DNS — Guia completo de instalação, configuração e todos os provedores suportados.
- External DNS com AWS Route53 - AWS Docs — Tutorial oficial da AWS para integração do External DNS com Route53.
- External DNS + cert-manager: HTTPS automático — Tutorial do cert-manager mostrando como combinar com External DNS para certificados automáticos.
- Boas práticas de segurança para External DNS — Artigo sobre hardening, RBAC e gerenciamento de credenciais para External DNS.
- Monitoramento do External DNS com Prometheus — Guia de configuração de métricas e alertas para sincronização DNS.