Montador de DNS interno do Kubernetes

Monte o endereço de um Service e veja, de verdade, de onde cada forma curta resolve — com o search e o ndots simulados na tela.

Roda no seu navegador · nada é enviado para servidor

Montar

Domínio do cluster diferente de cluster.local?

Alguns clusters sobem com outro sufixo — a flag --cluster-domain do kubelet. Se o seu for um desses, troque aqui.

De onde isso resolve

O que você digitaResolve?

O que o resolver faz, consulta por consulta

Escolha uma forma acima e veja a sequência real de perguntas que o pod dispara antes de achar (ou desistir).

resolv.conf do pod em web
Consulta

    A regra, em uma linha

    Todo Service do Kubernetes ganha um nome de DNS com quatro pedaços fixos, sempre na mesma ordem:

    api.producao.svc.cluster.local
    │   │        │   └── domínio do cluster
    │   │        └────── tipo de registro (svc = Service, pod = Pod)
    │   └─────────────── namespace
    └─────────────────── nome do Service

    É só isso. O problema nunca foi decorar essa linha — é que quase nunca você precisa dela inteira, e a forma curta que funciona muda conforme de onde você chama. É aí que a coisa vira mistério.

    Por que curl api funciona aqui e quebra ali

    Abra um pod qualquer e leia o /etc/resolv.conf. Você vai ver algo assim — e este arquivo é a resposta para 90% dos casos de “o DNS não resolve”:

    search producao.svc.cluster.local svc.cluster.local cluster.local
    nameserver 10.96.0.10
    options ndots:5

    A linha search é uma lista de sufixos que o resolver experimenta quando o nome que você digitou não é absoluto. Repare que o primeiro sufixo carrega o namespace do pod que está chamando — não o do serviço que você quer.

    Por isso curl api de dentro do namespace producao acha na primeira tentativa: vira api.producao.svc.cluster.local. E por isso o mesmo curl api, de um pod no namespace web, testa api.web.svc.cluster.local, api.svc.cluster.local, api.cluster.local, desiste e devolve NXDOMAIN. O serviço está lá, no ar, saudável — só que ninguém perguntou por ele.

    A regra prática: dentro do mesmo namespace, o nome do Service basta. Atravessou o namespace, você precisa de pelo menos servico.namespace.

    O que é ndots:5 — e o custo escondido

    O ndots decide a ordem das tentativas. A regra: se o nome digitado tiver menos pontos que o ndots, o resolver tenta primeiro colando cada sufixo do search, e só depois tenta o nome como absoluto. Se tiver pontos igual ou mais, ele inverte — tenta absoluto primeiro.

    O Kubernetes usa ndots:5, um valor bem alto, para que formas curtas como api.producao (1 ponto) e api.producao.svc (2 pontos) continuem funcionando. A conta fecha para dentro do cluster. Para fora, ela cobra caro:

    Você chamaPontosConsultas até a resposta
    api (mesmo namespace)01 — acerta de primeira
    api.producao12
    api.github.com24 — três erram antes
    api.producao.svc.cluster.local44 — ainda passa pelo search
    api.producao.svc.cluster.local.1 — o ponto final corta tudo

    É a mesma conta que o simulador acima faz: cada linha ali é um round-trip até o CoreDNS.

    As duas saídas

    A primeira é o ponto final: um nome terminado em ponto é absoluto, e o resolver não encosta no search. O efeito disso na latência, com a conta feita, está em rota interna vs externa no Kubernetes.

    A segunda é baixar o ndots só no pod que precisa — e essa não está no artigo:

    spec:
      dnsConfig:
        options:
          - name: ndots
            value: "2"
    Cuidado ao baixar: com ndots:2, a forma api.producao.svc (2 pontos) passa a ser tentada como absoluta primeiro e só depois pelo search — e api.producao (1 ponto) continua funcionando. Quem quebra é quem depende de forma com 2 pontos ou mais dentro do cluster. Se a sua aplicação só chama servico ou servico.namespace, é seguro.

    As variantes que ninguém decora

    Headless Service

    Um Service com clusterIP: None não tem IP próprio. O nome dele devolve os IPs de todos os pods por trás, em vez de um IP virtual único. É o que você usa quando o cliente precisa enxergar as réplicas individualmente — banco em cluster, fila, qualquer coisa que faça o próprio balanceamento.

    Pod de StatefulSet

    É aqui que quase todo mundo trava. Pod de StatefulSet ganha nome estável e individual, e o padrão inclui o Service que governa o conjunto:

    api-0.api.producao.svc.cluster.local
    │     │   └── namespace
    │     └────── nome do serviceName do StatefulSet
    └──────────── nome do pod, com o ordinal

    Repare que o Service aparece duas vezes na prática: uma como pai do pod, outra como o próprio Service. E isso só funciona se o serviceName do StatefulSet apontar para um Service headless — com ClusterIP normal, o nome do pod não é publicado.

    Registro SRV, para porta nomeada

    Se a porta do Service tem nome, existe um registro SRV que devolve porta e host juntos — útil para cliente que descobre a porta em vez de assumi-la:

    _http._tcp.api.producao.svc.cluster.local

    ExternalName

    Um Service do tipo ExternalName não tem pod nenhum: ele vira um CNAME para um endereço de fora. Serve para dar um nome interno estável a algo externo — e trocar o destino sem mexer na aplicação.

    Quando não resolve: o roteiro

    Nesta ordem, que vai do mais comum para o mais raro:

    1. Confira de onde você está chamando. Metade dos casos morre aqui — é o namespace errado na forma curta.

    kubectl exec -it meu-pod -n web -- cat /etc/resolv.conf

    2. Pergunte de dentro do cluster, não da sua máquina. Um pod descartável resolve a dúvida:

    kubectl run -it --rm dbg --image=busybox:1.36 --restart=Never \
      -n web -- nslookup api.producao.svc.cluster.local

    3. Veja se o Service tem endpoint. Este é o caso cruel: o DNS resolve certinho, devolve um IP, e nada responde — porque o selector do Service não casa com o label de nenhum pod.

    kubectl get endpoints api -n producao
    # ENDPOINTS vazio ou <none> = o seletor não pegou ninguém

    4. Só então desconfie do CoreDNS. Ele quase nunca é o culpado, mas quando é, aparece no log:

    kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

    E se o domínio do cluster não for cluster.local

    Ele é o padrão, mas não é lei — quem sobe o cluster pode trocar pela flag --cluster-domain do kubelet, e alguns provedores gerenciados fazem isso. Se o seu for diferente, todo o resto continua igual: só o último pedaço muda. A ferramenta lá em cima aceita o sufixo que você usar.

    Quer o passo anterior — quando usar endereço interno e quando expor pelo Ingress? Está em rota interna vs externa no Kubernetes.

    Newsletter

    Receba os artigos novos por e-mail

    Sem spam. Só o aviso quando sai um tutorial ou artigo novo.