BLOG | NGINX

Habilitando DNS de autoatendimento e gerenciamento de certificados no Kubernetes

Miniatura de Jason Schmidt
Jason Schmidt
Publicado em 01 de novembro de 2022

O objetivo final do desenvolvimento de aplicativos é, obviamente, expor aplicativos na Internet. Para um desenvolvedor, o Kubernetes simplifica esse processo até certo ponto ao fornecer o controlador Ingress como mecanismo de roteamento de solicitações para o aplicativo. Mas nem tudo é tão self-service quanto você provavelmente gostaria: você ainda precisa de um registro no Sistema de Nomes de Domínio (DNS) para mapear o nome de domínio do aplicativo para o endereço IP do controlador Ingress e um certificado TLS para proteger conexões usando HTTPS. Na maioria das organizações, você não é o proprietário do DNS ou do TLS e, portanto, precisa coordenar com o grupo operacional (ou grupos!) que o possui.

As coisas não são necessariamente mais fáceis para os operadores. Na maioria das organizações, a necessidade de atualizar registros DNS é rara o suficiente para que os procedimentos – tanto as regras de negócios quanto as etapas técnicas reais – tendam a ser esparsos ou inexistentes. Isso significa que quando você precisa adicionar um registro DNS, primeiro precisa encontrar a documentação, perguntar a um colega ou (na pior das hipóteses) descobrir. Você também precisa garantir que está em conformidade com todas as regras de segurança corporativa e certificar-se de que a entrada esteja marcada corretamente para os firewalls.

Felizmente, existe uma maneira de facilitar a vida tanto dos desenvolvedores quanto dos operadores. Nesta postagem, mostramos como os operadores podem configurar uma implantação do Kubernetes para permitir o autoatendimento para desenvolvedores atualizarem registros DNS e gerarem certificados TLS em um ambiente Kubernetes. Ao construir a infraestrutura com antecedência, você pode garantir que todos os requisitos comerciais e técnicos necessários sejam atendidos.

Visão geral e pré-requisitos

Com a solução implementada, tudo o que um desenvolvedor precisa fazer para expor um aplicativo à Internet é criar um controlador Ingress seguindo um modelo fornecido que inclui um nome de domínio totalmente qualificado (FQDN) dentro de um domínio gerenciado pela instalação do Kubernetes. O Kubernetes usa o modelo para alocar um endereço IP para o controlador Ingress, criar o registro DNS A para mapear o FQDN para o endereço IP e gerar certificados TLS para o FQDN e adicioná-los ao controlador Ingress. A limpeza é igualmente fácil: quando o Ingress é removido, os registros DNS são limpos.

A solução aproveita as seguintes tecnologias (fornecemos instruções de instalação e configuração abaixo):

Antes de configurar a solução, você precisa:

  • Uma instalação em nuvem do Kubernetes com um objeto de saída ( LoadBalancer ). A solução usa Linode, mas outros provedores de nuvem também funcionam.
  • Um nome de domínio hospedado com Cloudflare , que escolhemos porque é um dos provedores de DNS suportados pelo cert-manager e oferece suporte ao ExternalDNS ( em beta no momento da redação deste artigo). Recomendamos fortemente que o domínio não seja usado para produção ou qualquer outro propósito crítico.
  • Acesso à API do Cloudflare, que está incluída no nível gratuito.
  • Helm para instalar e implantar o Kubernetes.
  • kubectl como interface de linha de comando para Kubernetes.
  • Opcionalmente, K9s , uma interface de usuário tangível (TUI) bem construída que fornece uma maneira mais estruturada de interagir com o Kubernetes.

Também presumimos que você tenha um conhecimento básico do Kubernetes (como aplicar um manifesto, usar um gráfico Helm e emitir comandos kubectl para visualizar a saída e solucionar problemas). Entender os conceitos básicos do Let's Encrypt é útil, mas não obrigatório; para uma visão geral, consulte nosso blog . Você também não precisa saber como o cert-manager funciona, mas se estiver interessado em como ele (e os certificados em geral) funcionam com o NGINX Ingress Controller, veja minha postagem recente, Automatizando o gerenciamento de certificados em um ambiente Kubernetes .

Testamos a solução no macOS e no Linux. Não testamos no Windows Subsystem for Linux versão 2 (WSL2), mas não prevemos problemas.

Observação:  A solução foi concebida como uma prova de conceito de amostra e não para uso em produção. Em particular, ele não incorpora todas as melhores práticas de operação e segurança. Para obter informações sobre esses tópicos, consulte a documentação do cert-manager e do ExternalDNS .

Implementando a solução

Siga as etapas nestas seções para implantar a solução:

Baixar software
  1. Baixe seu token de API do Cloudflare .
  2. Clone o repositório do NGINX Ingress Controller:

    $ git clone https://github.com/nginxinc/kubernetes-ingress.gitCloning into 'kubernetes-ingress'...
    remote: Enumerating objects: 45176, done.
    remote: Counting objects: 100% (373/373), done.
    remote: Compressing objects: 100% (274/274), done.
    remote: Total 45176 (delta 173), reused 219 (delta 79), pack-reused 44803
    Receiving objects: 100% (45176/45176), 60.45 MiB | 26.81 MiB/s, done.
    Resolving deltas: 100% (26592/26592), done.
  3. Verifique se você consegue se conectar ao cluster do Kubernetes.

    $ kubectl cluster-infoKubernetes control plane is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443
    KubeDNS is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
     
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Implantar o NGINX Ingress Controller

  1. Usando o Helm, implante o NGINX Ingress Controller. Observe que estamos adicionando três opções de configuração não padronizadas :

    • controller.enableCustomResources – Instruímos o Helm a instalar as definições de recursos personalizados (CRDs) que permitem criar os recursos personalizados NGINX VirtualServer e VirtualServerRoute.
    • controller.enableCertManager – Configura o NGINX Ingress Controller para se comunicar com os componentes do cert-manager .
    • controller.enableExternalDNS – Configura o Ingress Controller para se comunicar com componentes ExternalDNS.
    $ helm install nginx-kic nginx-stable/nginx-ingress --namespace nginx-ingress  --set controller.enableCustomResources=true --create-namespace  --set controller.enableCertManager=true --set controller.enableExternalDNS=trueNAME: nginx-kic
    LAST DEPLOYED: Day Mon  DD hh:mm:ss YYYY
    NAMESPACE: nginx-ingress
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    The NGINX Ingress Controller has been installed.
  2. Verifique se o NGINX Ingress Controller está em execução e anote o valor no campo EXTERNAL-IP – é o endereço IP do NGINX Ingress Controller (aqui, www.xxx.yyy.zzz ). A saída é distribuída em duas linhas para maior legibilidade.

    $ kubectl get services --namespace nginx-ingressNAME                      TYPE           CLUSTER-IP      ...
    nginx-kic-nginx-ingress   LoadBalancer   10.128.152.88   ... 
    
       ... EXTERNAL-IP       PORT(S)                      AGE
       ... www.xxx.yyy.zzz   80:32457/TCP,443:31971/TCP   3h8m

Implantar cert-manager

Na solução, o cert-manager usa o tipo de desafio DNS-01 ao obter um certificado TLS, o que exige que o token da API do Cloudflare seja fornecido durante a criação do recurso ClusterIssuer . Na solução, o token da API é fornecido como um Kubernetes Secret .

  1. Usando o Helm, implante o cert-manager :

    $ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.9.1  --set installCRDs=trueNAME: cert-manager
    LAST DEPLOYED: Day Mon  DD hh:mm:ss YYYY
    NAMESPACE: cert-manager
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    cert-manager v1.9.1 has been deployed successfully!
  2. Implante o token da API do Cloudflare como um segredo do Kubernetes, substituindo-o por <seu-token-API>:

    $ kubectl apply -f - <<EOFapiVersion: v1
    kind: Secret
    metadata:
      name: Cloudflare-api-token-secret
      namespace: cert-manager
    type: Opaque
    stringData:
      api-token: "<your-API-token>"
    EOF
    secret/Cloudflare-api-token-secret created
  3. Crie um objeto ClusterIssuer, especificando Cloudflare-api-token-secret (definido na etapa anterior) como o local para recuperar o token. Se desejar, você pode substituir example-issuer no campo metadata.name (e example-issuer-account-key no campo spec.acme.privateKeySecretRef.name ) por um nome diferente.

    $ kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: example-issuer
      namespace: cert-manager
    spec:
      acme:
        email: example@example.com
        server: https://acme-v02.api.letsencrypt.org/directory
        privateKeySecretRef:
          name: example-issuer-account-key
        solvers:
          - dns01:
              Cloudflare:
                apiTokenSecretRef:
                  name: Cloudflare-api-token-secret
                  key: api-token
    EOF
    clusterissuer.cert-manager.io/example-issuer created
  4. Verifique se o ClusterIssuer está implantado e pronto (o valor no campo READY é True ).

    $ kubectl get clusterissuerNAME             READY   AGE
    example-issuer   True    3h9m

Implantar ExternalDNS

Assim como o cert-manager, o projeto ExternalDNS requer um token de API do Cloudflare para gerenciar o DNS. O mesmo token pode ser usado para ambos os projetos, mas isso não é obrigatório.

  1. Crie os CRDs do ExternalDNS para o NGINX Ingress Controller para permitir a integração entre os projetos.

    $ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yamlcustomresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org created
  2. Crie o serviço DNS externo ( external-dns ). Como o manifesto é bastante longo, aqui o dividimos em duas partes. A primeira parte configura contas, funções e permissões:

    • Cria um objeto ServiceAccount chamado external-dns para gerenciar todas as operações de gravação e atualização para gerenciar o DNS.
    • Cria um objeto ClusterRole (também chamado external-dns ) que define as permissões necessárias.
    • Vincula o ClusterRole ao ServiceAccount.
    $ kubectl apply -f - <<EOFapiVersion: v1
    kind: ServiceAccount
    metadata:
      name: 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: ["externaldns.nginx.org"]
      resources: ["dnsendpoints"]
      verbs: ["get","watch","list"]
    - apiGroups: ["externaldns.nginx.org"]
      resources: ["dnsendpoints/status"]
      verbs: ["update"]
    - apiGroups: [""]
      resources: ["nodes"]
      verbs: ["list","watch"]
    ---
    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: default
    EOF
    serviceaccount/external-dns created
    clusterrole.rbac.authorization.k8s.io/external-dns created
    clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created

    A segunda parte do manifesto cria a implantação do ExternalDNS:

    • Cria um filtro de domínio, que limita o escopo de possíveis danos causados pelo ExternalDNS ao gerenciar domínios. Por exemplo, você pode especificar os nomes de domínio dos ambientes de preparação para evitar alterações nos ambientes de produção. Neste exemplo, definimos domain-filter como example.com .
    • Define a variável de ambiente CF_API_TOKEN para seu token de API do Cloudflare. Para <seu-token-API>, substitua o token real ou um Segredo contendo o token. No último caso, você também precisa projetar o Secret no contêiner usando uma variável de ambiente.
    • Define a variável de ambiente FREE_TIER como "true" (apropriado, a menos que você tenha uma assinatura paga do Cloudflare).
    $  kubectl apply -f - <<EOF 
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: external-dns
    spec:
      strategy:
        type: Recreate
      selector:
        matchLabels:
          app: external-dns
      template:
        metadata:
          labels:
            app: external-dns
        spec:
          serviceAccountName: external-dns
          containers:
          - name: external-dns
            image: k8s.gcr.io/external-dns/external-dns:v0.12.0
            args:
            - --source=service
            - --source=ingress
            - --source=crd
            - --crd-source-apiversion=externaldns.nginx.org/v1
            - --crd-source-kind=DNSEndpoint
            - --domain-filter=example.com
            - --provider=Cloudflare
            env:
              - name: CF_API_TOKEN
                value: "<your-API-token>"
              - name: FREE_TIER
                value: "true"
    EOF
    serviceaccount/external-dns created
    clusterrole.rbac.authorization.k8s.io/external-dns created
    clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
    deployment.apps/external-dns created

Implantar o aplicativo de exemplo

Use o aplicativo de exemplo padrão do NGINX Ingress Controller chamado Cafe para fins de teste.

  1. Implante o aplicativo Cafe.

    $ kubectl apply -f ./kubernetes-ingress/examples/ingress-resources/complete-example/cafe.yamldeployment.apps/coffee created
    service/coffee-svc created
    deployment.apps/tea created
    service/tea-svc created
  2. Implante o NGINX Ingress Controller para o aplicativo Cafe. Observe as seguintes configurações:

    • tipo: VirtualServer – Estamos usando o recurso personalizado NGINX VirtualServer, não o recurso padrão do Kubernetes Ingress.
    • spec.host – Substitua cafe.example.com pelo nome do host que você está implantando. O host deve estar dentro do domínio que está sendo gerenciado com ExternalDNS.
    • spec.tls.cert-manager.cluster-issuer – Se você estiver usando os valores especificados nesta postagem, este é example-issuer . Se necessário, substitua o nome escolhido na Etapa 3 de Implantar cert‑manager .
    • spec.externalDNS.enable – O valor true informa ao ExternalDNS para criar um registro DNS A.

    Observe que o tempo que leva para essa etapa ser concluída depende muito do provedor de DNS, pois o Kubernetes está interagindo com a API de DNS do provedor.

    $ kubectl apply -f - <<EOFapiVersion: k8s.nginx.org/v1
    kind: VirtualServer
    metadata:
      name: cafe
    spec:
      host: cafe.example.com
      tls:
        secret: cafe-secret
        cert-manager:
          cluster-issuer: example-issuer
      externalDNS:
        enable: true
      upstreams:
      - name: tea
        service: tea-svc
        port: 80
      - name: coffee
        service: coffee-svc
        port: 80
      routes:
      - path: /tea
        action:
          pass: tea
      - path: /coffee
        action:
          pass: coffee
    EOF
    virtualserver.k8s.nginx.org/cafe created

Validar a solução

  1. Verifique o registro DNS A – em particular se no bloco ANSWER SECTION o FQDN (aqui, cafe.example.com ) está mapeado para o endereço IP correto ( www.xxx.yyy.zzz ).

    $ dig cafe.example.com 
    ; <<>> DiG 9.10.6 <<>> cafe.example.com
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22633
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
     
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4096
    ;; QUESTION SECTION:
    ;cafe.example.com.		IN	A
     
    ;; ANSWER SECTION:
    cafe.example.com.	279	IN	A	www.xxx.yyy.zzz
     
    ;; Query time: 1 msec
    ;; SERVER: 2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359#53(2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359)
    ;; WHEN: Day Mon  DD hh:mm:ss TZ YYYY
    ;; MSG SIZE  rcvd: 67
  2. Verifique se o certificado é válido (o valor no campo READY é True ).

    $ kubectl get certificatesNAME          READY   SECRET        AGE
    cafe-secret   True    cafe-secret   8m51s
  3. Verifique se você consegue acessar o aplicativo.

    $ curl https://cafe.example.com/coffeeServer address: 10.2.2.4:8080
    Server name: coffee-7c86d7d67c-lsfs6
    Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
    URI: /coffee
    Request ID: 91077575f19e6e735a91b9d06e9684cd
    $ curl https://cafe.example.com/tea
    Server address: 10.2.2.5:8080
    Server name: tea-5c457db9-ztpns
    Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
    URI: /tea
    Request ID: 2164c245a495d22c11e900aa0103b00f

O que acontece quando um desenvolvedor implanta o NGINX Ingress Controller

Muita coisa acontece nos bastidores quando a solução é implementada. O diagrama mostra o que acontece quando um desenvolvedor implanta o NGINX Ingress Controller com um recurso personalizado do NGINX VirtualServer. Observe que alguns detalhes operacionais são omitidos.

  1. O desenvolvedor implanta um recurso VirtualServer usando o NGINX CRD
  2. O Kubernetes cria o VirtualServer usando o NGINX Ingress Controller
  3. O NGINX Ingress Controller chama o ExternalDNS para criar um registro DNS A
  4. ExternalDNS cria o registro A no DNS
  5. O NGINX Ingress Controller chama o cert-manager para solicitar um certificado TLS
  6. cert-manager adiciona um registro DNS para uso durante o desafio DNS-01
  7. cert-manager contata Let’s Encrypt para completar o desafio
  8. Let’s Encrypt valida o desafio contra DNS
  9. Let’s Encrypt emite o certificado TLS
  10. cert-manager fornece o certificado TLS para o NGINX Ingress Controller
  11. O NGINX Ingress Controller roteia solicitações externas protegidas por TLS para os pods do aplicativo

solucao-problemas

Dada a complexidade do Kubernetes e dos componentes que estamos usando, é difícil fornecer um guia de solução de problemas abrangente. Dito isso, há algumas sugestões básicas para ajudar você a determinar o problema.

  • Use os comandos kubectl get e kubectl describe para validar a configuração de objetos implantados.
  • Use o kubectl registros <componente> comando para visualizar arquivos de log para os vários componentes implantados.
  • Use o K9s para inspecionar a instalação; o software destaca os problemas em amarelo ou vermelho (dependendo da gravidade) e fornece uma interface para acessar logs e detalhes sobre objetos.

Se você ainda estiver tendo problemas, entre em contato conosco no Slack da Comunidade NGINX e peça ajuda! Temos uma comunidade vibrante e estamos sempre felizes em resolver problemas.

Para experimentar o NGINX Ingress Controller baseado no NGINX Plus, comece hoje mesmo seu teste gratuito de 30 dias ou entre em contato conosco para discutir seus casos de uso .


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."