O autor escolheu a Diversity in Tech Fund para receber uma doação como parte do programa Write for DOnations.
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.
Para seguir este tutorial, você precisará de:
kubectl
. Não se preocupe em configurá-lo ainda, pois você fará isso mais tarde.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:
- mkdir do-k8s
Em seguida, vá para o diretório recém-criado:
- cd do-k8s
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:
- pulumi new typescript -y
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
:
- ls
Os seguintes arquivos estarão agora presentes:
OutputPulumi.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.
O próximo passo é instalar e adicionar dependências nos pacotes DigitalOcean e Kubernetes. Primeiro, instale-os usando o NPM:
- npm install @pulumi/digitalocean @pulumi/kubernetes
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:
- nano index.ts
Substitua o conteúdo do seu index.ts
pelo seguinte:
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.
Agora você está pronto para criar um cluster Kubernetes na DigitalOcean. Comece reabrindo o arquivo index.ts
:
- nano index.ts
Adicione estas linhas no final do seu arquivo 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
:
- 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:
OutputPreviewing 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:
OutputUpdating (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:
OutputNAME 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.
A seguir, você descreverá a configuração de uma aplicação Kubernetes usando infraestrutura como código. Isso consistirá em três partes:
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.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:
- nano index.ts
Depois de abrir o arquivo, acrescente este código ao final dele:
...
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:
- pulumi up
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:
OutputUpdating (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
OutputNAME 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
:
- pulumi up
Observe que ele apenas mostra o que mudou e que ao selecionar detalhes, exibe a diferença precisa:
OutputPreviewing 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.
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:
...
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
:
...
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:
OutputUpdating (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.
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!
- pulumi destroy
Assim como no comando up
, destroy
exibe uma visualização e um prompt antes de executar uma ação:
OutputPreviewing 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:
OutputDestroying (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:
- pulumi stack rm
Você receberá uma saída pedindo para confirmar a exclusão digitando o nome da pilha:
OutputThis 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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!