One place for hosting & domains

      infraestrutura

      Como gerenciar a infraestrutura DigitalOcean e o Kubernetes com o Pulumi


      O autor escolheu a Diversity in Tech Fund para receber uma doação como parte do programa Write for DOnations.

      Introdução

      O Pulumi é uma ferramenta para criar, implantar e gerenciar infraestrutura usando código escrito em linguagens de programação de uso geral. Ele suporta a automação de todos os serviços gerenciados da DigitalOcean, como Droplets, bancos de dados gerenciados, registros DNS e clusters Kubernetes, além da configuração de aplicações. Os deployments são realizados a partir de uma interface de linha de comando fácil de usar que também se integra a uma ampla variedade de sistemas populares de CI/CD.

      O Pulumi suporta várias linguagens, mas neste tutorial você usará TypeScript, uma versão de tipagem estática do JavaScript que utiliza o runtime Node.js. Isso significa que você obterá suporte a IDE e checagem em tempo de compilação que o ajudarão a garantir que você configurou os recursos certos, usou slugs corretos etc., enquanto continua podendo acessar qualquer módulo NPM para tarefas utilitárias.

      Neste tutorial, você provisionará um cluster Kubernetes na DigitalOcean, uma aplicação Kubernetes com balanceamento de carga e um domínio DNS da DigitalOcean que disponibiliza seu aplicativo em um nome de domínio estável de sua escolha. Tudo isso pode ser provisionado em 60 linhas de infraestrutura como código e em uma única execução de linha de comando pulumi up. Após este tutorial, você estará pronto para criar com produtividade, arquiteturas poderosas de nuvem usando a infraestrutura como código do Pulumi que aproveita toda a área de serviços DigitalOcean e Kubernetes.

      Pré-requisitos

      Para seguir este tutorial, você precisará de:

      • Uma conta na DigitalOcean na qual fazer o deployment dos recursos. Se você ainda não possui uma, cadastre-se aqui.
      • Um token de API da DigitalOcean para executar deployments automatizados. Gere um token de acesso pessoal aqui e mantenha-o à mão, pois você o usará no Passo 2.
      • Como você criará e usará um cluster Kubernetes, precisará instalar o kubectl. Não se preocupe em configurá-lo ainda, pois você fará isso mais tarde.
      • Você escreverá sua infraestrutura como código no TypeScript e precisará do Node.js 8 ou posterior. Faça o download aqui ou instale-o usando o gerenciador de pacotes do seu sistema.
      • Você usará o Pulumi para fazer o deploy da infraestrutura, por isso precisará instalar o SDK do Pulumi open source.
      • Para executar o Passo 5 opcional, você precisará de um nome de domínio configurado para usar os servidores de nomes da DigitalOcean. Este guia explica como fazer isso para o registrador de sua escolha.

      Passo 1 — Estruturando um Novo Projeto

      O primeiro passo é criar um diretório que irá armazenar seu projeto Pulumi. Esse diretório conterá o código-fonte para suas definições de infraestrutura, além dos arquivos de metadados que descrevem o projeto e suas dependências NPM.

      Primeiro, crie o diretório:

      Em seguida, vá para o diretório recém-criado:

      A partir de agora, execute comandos a partir do seu diretório do-k8s recém-criado.

      Em seguida, crie um novo projeto Pulumi. Existem diferentes maneiras de fazer isso, mas a maneira mais fácil é usar o comando pulumi new com o modelo de projeto typescript. Este comando primeiro solicitará que você efetue login no Pulumi para que seu projeto e o estado do deployment sejam salvos e, em seguida, criará um projeto TypeScript simples no diretório atual:

      Aqui você passou a opção -y para o comando new, que diz a ele para aceitar as opções padrão do projeto. Por exemplo, o nome do projeto é retirado do nome do diretório atual e, portanto, será do-k8s. Se você quiser usar opções diferentes para o nome do seu projeto, simplesmente elimine o -y.

      Após executar o comando, liste o conteúdo do diretório com ls:

      Os seguintes arquivos estarão agora presentes:

      Output

      Pulumi.yaml index.ts node_modules package-lock.json package.json tsconfig.json

      O arquivo principal que você estará editando é index.ts. Embora este tutorial use apenas esse arquivo único, você pode organizar seu projeto da maneira que achar melhor usando os módulos Node.js. Este tutorial também descreve uma etapa de cada vez, aproveitando o fato de que o Pulumi pode detectar e deployar de forma incremental apenas o que mudou. Se preferir, você pode simplesmente preencher o programa inteiro e fazer o deploy de uma só vez usando o pulumi up.

      Agora que você estruturou seu novo projeto, está pronto para adicionar as dependências necessárias para seguir o tutorial.

      Passo 2 — Adicionando Dependências

      O próximo passo é instalar e adicionar dependências nos pacotes DigitalOcean e Kubernetes. Primeiro, instale-os usando o NPM:

      Isso fará o download dos pacotes NPM, plug-ins do Pulumi e os salvará como dependências.

      Em seguida, abra o arquivo index.ts com seu editor favorito. Este tutorial usará o nano:

      Substitua o conteúdo do seu index.ts pelo seguinte:

      index.ts

      import * as digitalocean from "@pulumi/digitalocean";
      import * as kubernetes from "@pulumi/kubernetes";
      

      Isso disponibiliza todo o conteúdo desses pacotes para o seu programa. Se você digitar "digitalocean." usando um IDE que entenda TypeScript e Node.js, você deverá ver uma lista dos recursos da DigitalOcean suportados por este pacote, por exemplo.

      Salve e feche o arquivo após adicionar o conteúdo.

      Nota: Usaremos um subconjunto do que está disponível nesses pacotes. Para obter a documentação completa dos recursos, propriedades e APIs associadas, consulte a documentação relevante da API para os pacotes @pulumi/digitalocean e @pulumi/kubernetes.

      Em seguida, você configurará seu token DigitalOcean para que o Pulumi possa provisionar recursos em sua conta:

      • pulumi config set digitalocean:token SEU_TOKEN_AQUI --secret

      Observe a flag --secret, que usa o serviço de criptografia do Pulumi para criptografar seu token, garantindo que ele seja armazenado em texto cifrado. Se preferir, você pode usar a variável de ambiente DIGITALOCEAN_TOKEN, mas você vai precisar lembrar-se de defini-la sempre que atualizar seu programa, enquanto o uso da configuração armazena e o utiliza automaticamente para o seu projeto.

      Nesta etapa, você adicionou as dependências necessárias e configurou seu token de API com o Pulumi para poder provisionar seu cluster Kubernetes.

      Passo 3 — Provisionando um Cluster Kubernetes

      Agora você está pronto para criar um cluster Kubernetes na DigitalOcean. Comece reabrindo o arquivo index.ts:

      Adicione estas linhas no final do seu arquivo index.ts:

      index.ts

      ...
      const cluster = new digitalocean.KubernetesCluster("do-cluster", {
          region: digitalocean.Regions.SFO2,
          version: "latest",
          nodePool: {
              name: "default",
              size: digitalocean.DropletSlugs.DropletS2VPCU2GB,
              nodeCount: 3,
          },
      });
      
      export const kubeconfig = cluster.kubeConfigs[0].rawConfig;
      

      Este novo código aloca uma instância do digitalocean.KubernetesCluster e define várias propriedades nele. Isso inclui o uso da sfo2 como slug da região, a versão mais recente, latest, do Kubernetes, o s-2vcpu-2gb slug de tamanho do Droplet, e indica a contagem desejada de três instâncias do Droplet. Sinta-se à vontade para alterar qualquer uma dessas opções, mas lembre-se de que o Kubernetes da DigitalOcean está disponível apenas em determinadas regiões no momento em que este artigo foi escrito. Você pode consultar a documentação do produto para obter informações atualizadas sobre a disponibilidade da região.

      Para obter uma lista completa das propriedades que você pode configurar no seu cluster, consulte a documentação da API do KubernetesCluster.

      A linha final nesse trecho de código exporta o aqruivo kubeconfig resultante do cluster Kubernetes para que seja fácil de usar. As variáveis exportadas são impressas no console e também acessíveis às ferramentas. Você usará isso momentaneamente para acessar nosso cluster a partir de ferramentas padrão como o kubectl.

      Agora você está pronto para implantar seu cluster. Para fazer isso, execute pulumi up:

      Este comando pega o programa, gera um plano para criar a infraestrutura descrita e executa uma série de etapas para deployar essas alterações. Isso funciona para a criação inicial da infraestrutura, além de poder diferenciar e atualizar sua infraestrutura quando as atualizações subsequentes são feitas. Nesse caso, a saída será mais ou menos assim:

      Output

      Previewing update (dev): Type Name Plan + pulumi:pulumi:Stack do-k8s-dev create + └─ digitalocean:index:KubernetesCluster do-cluster create Resources: + 2 to create Do you want to perform this update? yes > no details

      Isso indica que prosseguindo com a atualização será criado um único cluster Kubernetes chamado do-cluster. O prompt yes/no/details permite confirmar que este é o resultado desejado antes que quaisquer alterações sejam realmente feitas. Se você selecionar details, uma lista completa de recursos e suas propriedades serão mostrados. Escolha yes para iniciar o deployment:

      Output

      Updating (dev): Type Name Status + pulumi:pulumi:Stack do-k8s-dev created + └─ digitalocean:index:KubernetesCluster do-cluster created Outputs: kubeconfig: "..." Resources: + 2 created Duration: 6m5s Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/1

      Leva alguns minutos para criar o cluster, mas depois ele estará em funcionamento e o kubeconfig completo será impresso no console. Salve o kubeconfig em um arquivo:

      • pulumi stack output kubeconfig > kubeconfig.yml

      E então use-o com o kubectl para executar qualquer comando do Kubernetes:

      • KUBECONFIG=./kubeconfig.yml kubectl get nodes

      Você receberá uma saída semelhante à seguinte:

      Output

      NAME STATUS ROLES AGE VERSION default-o4sj Ready <none> 4m5s v1.14.2 default-o4so Ready <none> 4m3s v1.14.2 default-o4sx Ready <none> 3m37s v1.14.2

      Nesse ponto, você configurou a infraestrutura como código e tem uma maneira repetível de ativar e configurar novos clusters Kubernetes na DigitalOcean . No próximo passo, você trabalhará em cima disso para definir a infraestrutura do Kubernetes no código e aprender como fazer o deploy e gerenciá-la da mesma forma.

      Passo 4 — Fazendo o Deploy de uma Aplicação no seu Cluster

      A seguir, você descreverá a configuração de uma aplicação Kubernetes usando infraestrutura como código. Isso consistirá em três partes:

      1. Um objeto Provider, que diz ao Pulumi para deployar recursos do Kubernetes no cluster da DigitalOcean, em vez do padrão que qualquer kubectl esteja configurado para usar.
      2. Um Deployment de Kubernetes, que é a maneira padrão do Kubernetes de deployar uma imagem de container Docker que é replicada em qualquer número de Pods.
      3. Um Serviço Kubernetes, que é a maneira padrão para dizer ao Kubernetes para balancear o acesso entre um conjunto alvo de Pods (neste caso, o Deployment acima).

      Essa é uma arquitetura de referência razoavelmente padrão para iniciar e executar um serviço com balanceamento de carga no Kubernetes.

      Para implantar todos os três, abra o arquivo index.ts novamente:

      Depois de abrir o arquivo, acrescente este código ao final dele:

      index.ts

      ...
      const provider = new kubernetes.Provider("do-k8s", { kubeconfig })
      
      const appLabels = { "app": "app-nginx" };
      const app = new kubernetes.apps.v1.Deployment("do-app-dep", {
          spec: {
              selector: { matchLabels: appLabels },
              replicas: 5,
              template: {
                  metadata: { labels: appLabels },
                  spec: {
                      containers: [{
                          name: "nginx",
                          image: "nginx",
                      }],
                  },
              },
          },
      }, { provider });
      const appService = new kubernetes.core.v1.Service("do-app-svc", {
          spec: {
              type: "LoadBalancer",
              selector: app.spec.template.metadata.labels,
              ports: [{ port: 80 }],
          },
      }, { provider });
      
      export const ingressIp = appService.status.loadBalancer.ingress[0].ip;
      

      Esse código é semelhante à configuração padrão do Kubernetes, e o comportamento dos objetos e suas propriedades é equivalente, exceto que ele está escrito em TypeScript ao lado de suas outras declarações de infraestrutura.

      Salve e feche o arquivo depois de fazer as alterações.

      Assim como antes, execute pulumi up para visualizar e deployar as alterações:

      Depois de selecionar yes para prosseguir, a CLI imprimirá atualizações de status detalhadas, incluindo diagnósticos sobre disponibilidade de Pod, alocação de endereço IP e muito mais. Isso ajudará você a entender por que seu deployment pode levar algum tempo para ser concluído ou ficar travado.

      A saída completa será mais ou menos assim:

      Output

      Updating (dev): Type Name Status pulumi:pulumi:Stack do-k8s-dev + ├─ pulumi:providers:kubernetes do-k8s created + ├─ kubernetes:apps:Deployment do-app-dep created + └─ kubernetes:core:Service do-app-svc created Outputs: + ingressIp : "157.230.199.202" Resources: + 3 created 2 unchanged Duration: 2m52s Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/2

      Após a conclusão, observe que o número desejado de Pods está em execução:

      • KUBECONFIG=./kubeconfig.yml kubectl get pods

      Output

      NAME READY STATUS RESTARTS AGE do-app-dep-vyf8k78z-758486ff68-5z8hk 1/1 Running 0 1m do-app-dep-vyf8k78z-758486ff68-8982s 1/1 Running 0 1m do-app-dep-vyf8k78z-758486ff68-94k7b 1/1 Running 0 1m do-app-dep-vyf8k78z-758486ff68-cqm4c 1/1 Running 0 1m do-app-dep-vyf8k78z-758486ff68-lx2d7 1/1 Running 0 1m

      Similar à maneira como o programa exporta o arquivo kubeconfig do cluster, este programa também exporta o endereço IP do balanceador de carga resultante do serviço Kubernetes. Use isto para fazer um curl no endpoint e verifique se ele está funcionando:

      • curl $(pulumi stack output ingressIp)

      Output

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>

      A partir daqui, você pode editar e re-deployar facilmente sua infraestrutura de aplicações. Por exemplo, tente alterar a linha replicas: 5 para digamos replicas: 7 e, em seguida, execute novamente pulumi up:

      Observe que ele apenas mostra o que mudou e que ao selecionar detalhes, exibe a diferença precisa:

      Output

      Previewing update (dev): Type Name Plan Info pulumi:pulumi:Stack do-k8s-dev ~ └─ kubernetes:apps:Deployment do-app-dep update [diff: ~spec] Resources: ~ 1 to update 4 unchanged Do you want to perform this update? details pulumi:pulumi:Stack: (same) [urn=urn:pulumi:dev::do-k8s::pulumi:pulumi:Stack::do-k8s-dev] ~ kubernetes:apps/v1:Deployment: (update) [id=default/do-app-dep-vyf8k78z] [urn=urn:pulumi:dev::do-k8s::kubernetes:apps/v1:Deployment::do-app-dep] [provider=urn:pulumi:dev::do-k8s::pulumi:providers:kubernetes::do-k8s::80f36105-337f-451f-a191-5835823df9be] ~ spec: { ~ replicas: 5 => 7 }

      Agora você tem um cluster Kubernetes em plena operação e uma aplicação em funcionamento. Com a aplicação em funcionamento, você pode querer configurar um domínio personalizado para usar com ela. O próximo passo o guiará na configuração do DNS com o Pulumi.

      Passo 5 — Criando um Domínio DNS (Opcional)

      Embora o cluster e a aplicação Kubernetes estejam em funcionamento, o endereço da aplicação é dependente dos caprichos da atribuição automática de endereços IP pelo seu cluster. Conforme você ajusta e reimplementa as coisas, esse endereço pode mudar. Neste passo, você verá como atribuir um nome DNS personalizado ao endereço IP do balanceador de carga, para que fique estável, mesmo que você altere sua infraestrutura posteriormente.

      Nota: Para concluir este passo, garanta que você possui um domínio usando os servidores de nomes DNS da DigitalOcean, ns1.digitalocean.com, ns2.digitalocean.com e ns3.digitalocean.com. Instruções para configurar isso estão disponíveis na seção Pré-requisitos.

      Para configurar o DNS, abra o arquivo index.ts e acrescente o seguinte código ao final do arquivo:

      index.ts

      ...
      const domain = new digitalocean.Domain("do-domain", {
          name: "seu_domínio",
          ipAddress: ingressIp,
      });
      

      Este código cria uma nova entrada DNS com um registro A que se refere ao endereço IP do seu serviço Kubernetes. Substitua seu_domínio neste trecho pelo nome de domínio escolhido.

      É comum querer subdomínios adicionais, como www, apontando para a aplicação web. É fácil conseguir isso usando um registro DNS da DigitalOcean. Para tornar este exemplo mais interessante, adicione também um registro CNAME que aponte www.seu_domínio.com para seu_domínio.com:

      index.ts

      ...
      const cnameRecord = new digitalocean.DnsRecord("do-domain-cname", {
          domain: domain.name,
          type: "CNAME",
          name: "www",
          value: "@",
      });
      

      Salve e feche o arquivo depois de fazer essas alterações.

      Por fim, execute pulumi up para fazer o deploy das alterações no DNS para apontar para a aplicação e o cluster existentes:

      Output

      Updating (dev): Type Name Status pulumi:pulumi:Stack do-k8s-dev + ├─ digitalocean:index:Domain do-domain created + └─ digitalocean:index:DnsRecord do-domain-cname created Resources: + 2 created 5 unchanged Duration: 6s Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/3

      Após a propagação das alterações no DNS, você poderá acessar seu conteúdo em seu domínio personalizado:

      • curl www.seu_domínio.com

      Você receberá uma saída semelhante à seguinte:

      Output

      <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>

      Com isso, você configurou com êxito um novo cluster Kubernetes na DigitalOcean, fez o deploy de uma aplicação Kubernetes com balanceamento de carga e deu ao balanceador de carga dessa aplicação um nome de domínio estável usando o DNS da DigitalOcean, tudo em 60 linhas de código e um comando pulumi up .

      A próximo passo o guiará na remoção dos recursos, se você não precisar mais deles.

      Passo 6 — Removendo os Recursos (Opcional)

      Antes de concluir o tutorial, você pode querer destruir todos os recursos criados acima. Isso garantirá que você não seja cobrado pelos recursos que não estão sendo usados. Se você preferir manter sua aplicação em funcionamento, fique à vontade para pular esta etapa.

      Execute o seguinte comando para destruir os recursos. Cuidado ao usar isso, pois não pode ser desfeito!

      Assim como no comando up, destroy exibe uma visualização e um prompt antes de executar uma ação:

      Output

      Previewing destroy (dev): Type Name Plan - pulumi:pulumi:Stack do-k8s-dev delete - ├─ digitalocean:index:DnsRecord do-domain-cname delete - ├─ digitalocean:index:Domain do-domain delete - ├─ kubernetes:core:Service do-app-svc delete - ├─ kubernetes:apps:Deployment do-app-dep delete - ├─ pulumi:providers:kubernetes do-k8s delete - └─ digitalocean:index:KubernetesCluster do-cluster delete Resources: - 7 to delete Do you want to perform this destroy? yes > no details

      Supondo que é isso que você deseja, selecione yes e observe as exclusões:

      Output

      Destroying (dev): Type Name Status - pulumi:pulumi:Stack do-k8s-dev deleted - ├─ digitalocean:index:DnsRecord do-domain-cname deleted - ├─ digitalocean:index:Domain do-domain deleted - ├─ kubernetes:core:Service do-app-svc deleted - ├─ kubernetes:apps:Deployment do-app-dep deleted - ├─ pulumi:providers:kubernetes do-k8s deleted - └─ digitalocean:index:KubernetesCluster do-cluster deleted Resources: - 7 deleted Duration: 7s Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/4

      Nesse momento, nada mais resta: as entradas de DNS desaparecem e o cluster Kubernetes — juntamente com a aplicação em execução nele — também desaparece. O link permanente, permalink, ainda está disponível, para que você possa voltar e ver o histórico completo de atualizações para essa pilha. Isso pode ajudá-lo a se recuperar se a destruição for um erro, uma vez que o serviço mantém um histórico completo do estado de todos os recursos.

      Se você gostaria de destruir o seu projeto na sua totalidade, remova a pilha:

      Você receberá uma saída pedindo para confirmar a exclusão digitando o nome da pilha:

      Output

      This will permanently remove the 'dev' stack! Please confirm that this is what you'd like to do by typing ("dev"):

      Ao contrário do comando destroy, que exclui os recursos de infraestrutura em nuvem, a remoção de uma pilha apaga totalmente o histórico completo da sua pilha do alcance do Pulumi.

      Conclusão

      Neste tutorial, você fez o deploy dos recursos de infraestrutura na DigitalOcean — um cluster Kubernetes e um domínio DNS com registros A e CNAME — além da configuração da aplicação Kubernetes que usa esse cluster. Você fez isso usando infraestrutura como código escrita em uma linguagem de programação familiar, TypeScript, que trabalha com editores, ferramentas e bibliotecas existentes e aproveita as comunidades e pacotes existentes. Você fez tudo isso usando um único fluxo de trabalho de linha de comando para realizar deployments que abrangem sua aplicação e a infraestrutura.

      A partir daqui, há uma série de próximos passos que você pode dar:

      O exemplo completo deste tutorial está disponível no GitHub. Para obter maiores detalhes sobre como usar a infraestrutura como código do Pulumi em seus próprios projetos hoje, consulte a Documentação do Pulumi, Tutorials, ou os guias Getting Started. O Pulumi é open source e é livre para usar.



      Source link