Terraformer — Infraestrutura como Código

Terraformer — Infraestrutura como Código
Gostaria de falar sobre uma nova ferramenta CLI que criei para resolver um antigo problema.

O problema

O Terraform já se tornou um padrão nas comunidades de DevOps/Cloud/IT. É uma ferramenta muito prática e útil para trabalhar com infraestrutura como código. Há muitos aspectos positivos no Terraform, assim como muitos desafios, armadilhas e complicações.
Com o Terraform, é muito fácil criar novas coisas e então gerenciá-las, alterá-las ou excluí-las. Mas o que fazer para aqueles que têm uma infraestrutura enorme na nuvem e que não foi criada através do Terraform? Reescrever e reconstruir toda a nuvem é caro e arriscado.
Enfrentei esse problema em dois empregos, o exemplo mais simples é quando você deseja que tudo esteja em git na forma de arquivos Terraform, mas você tem mais de 250 buckets, e escrevê-los manualmente para o Terraform é um trabalho muito grande.
Há issue Já desde 2014, no Terraform que foi descontinuado em 2016, com a esperança de que haveria importação.

Enfim, tudo como na imagem, mas da direita para a esquerda.

Aviso: O autor vive fora da Rússia há metade da vida e escreve pouco em russo. Cuidado com erros de ortografia.

Soluções

1. Existem soluções antigas e prontas para AWS. terraforming. Quando tentei usar isso para obter meus mais de 250 buckets, percebi que estava tudo ruim. AWS há muito tempo adicionou muitas novas opções e o terraforming não as conhece e, além disso, é escrito em Ruby. O template é bastante limitado.. Depois de duas noites, enviei um pull request para adicionar mais funcionalidades lá e percebi que tal solução não se encaixa de forma alguma.
Como funciona o terraforming? Ele obtém dados do SDK da AWS e gera arquivos tf e tfstate através do template.
Aqui estão três problemas:
1. Sempre haverá um atraso nas atualizações.
2. Os arquivos tf às vezes saem corruptos.
3. O tfstate é gerado separadamente do tf e nem sempre corresponde.
Na verdade, é difícil obter um resultado no qual o `terraform plan` diga que não há alterações.

2. `terraform import` — um comando embutido no Terraform. Como funciona?
Você escreve um arquivo TF vazio com o nome e o tipo do recurso, depois executa `terraform import` e fornece o ID do recurso. O Terraform se conecta ao provedor, obtém os dados e cria o arquivo tfstate.
Aqui estão três problemas:
1. Você obtém apenas o arquivo tfstate, enquanto o tf está vazio e precisa ser escrito manualmente ou convertido a partir do tfstate.
2. Funciona apenas com um recurso por vez e não suporta todos os recursos. E o que devo fazer novamente com mais de 250 buckets?
3. É necessário saber o ID dos recursos — ou seja, você precisa encapsular isso em um código que obtenha a lista de recursos.
Na verdade, o resultado é parcial e não escalona bem.

Minha solução

Requisitos:
1. A capacidade de criar arquivos tf e tfstate a partir dos recursos. Por exemplo, baixar todos os buckets/security groups/load balancers e certificar-se de que `terraform plan` retorna que não há alterações.
2. Precisamos de 2 nuvens: GCP + AWS.
3. Uma solução global que seja fácil de atualizar sempre e que não consuma tempo com cada recurso, levando 3 dias de trabalho.
4. Tornar isso um projeto de código aberto — este é um problema comum a todos.

A linguagem Go — que eu gosto, e possui uma biblioteca para criar arquivos HCL que é utilizada no terraform + muito código no terraform que pode ser útil.

Caminho

Primeira tentativa
Comecei com uma versão simples. Chamadas para a nuvem através do SDK para obter o recurso desejado e convertê-lo nos campos para terraform. A tentativa fracassou logo na security group, pois não gostei de passar 1.5 dias apenas convertendo a security group (sendo que há muitos recursos). Foi longo e depois os campos podem mudar/adicionar.

Segunda tentativa
Baseado na ideia descrita aqui. Simplesmente pegar e converter tfstate em tf. Todos os dados estão lá e os campos são os mesmos. Como obter um tfstate completo para muitos recursos? Aqui, o comando `terraform refresh` foi útil. O terraform pega todos os recursos no tfstate e extrai os dados por ID, escrevendo tudo no tfstate. Portanto, criar um tfstate vazio apenas com nomes e IDs, e executar `terraform refresh` nos dá tfstate completos. Ura!
Agora vamos nos dedicar a escrever um conversor recursivo de tfstate para tf. Para aqueles que nunca leram tfstate, é um JSON, mas especial.
Aqui está sua parte importante attributes

 "attributes": {
                            "id": "default/backend-logging-load-deployment",
                            "metadata.#": "1",
                            "metadata.0.annotations.%": "0",
                            "metadata.0.generate_name": "",
                            "metadata.0.generation": "24",
                            "metadata.0.labels.%": "1",
                            "metadata.0.labels.app": "backend-logging",
                            "metadata.0.name": "backend-logging-load-deployment",
                            "metadata.0.namespace": "default",
                            "metadata.0.resource_version": "109317427",
                            "metadata.0.self_link": "\/apis\/apps\/v1\/namespaces\/default\/deployments\/backend-logging-load-deployment",
                            "metadata.0.uid": "300ecda1-4138-11e9-9d5d-42010a8400b5",
                            "spec.#": "1",
                            "spec.0.min_ready_seconds": "0",
                            "spec.0.paused": "false",
                            "spec.0.progress_deadline_seconds": "600",
                            "spec.0.replicas": "1",
                            "spec.0.revision_history_limit": "10",
                            "spec.0.selector.#": "1",

Aqui estão:
1. id — string
2. metadata — array de tamanho 1 que contém um objeto com os campos descritos abaixo
3. spec — hash de tamanho 1 contendo key,value
Basicamente, um formato divertido, tudo pode ir a profundidade também em vários níveis.

                   "spec.#": "1",
                            "spec.0.min_ready_seconds": "0",
                            "spec.0.paused": "false",
                            "spec.0.progress_deadline_seconds": "600",
                            "spec.0.replicas": "1",
                            "spec.0.revision_history_limit": "10",
                            "spec.0.selector.#": "1",
                            "spec.0.selector.0.match_expressions.#": "0",
                            "spec.0.selector.0.match_labels.%": "1",
                            "spec.0.selector.0.match_labels.app": "backend-logging-load",
                            "spec.0.strategy.#": "0",
                            "spec.0.template.#": "1",
                            "spec.0.template.0.metadata.#": "1",
                            "spec.0.template.0.metadata.0.annotations.%": "0",
                            "spec.0.template.0.metadata.0.generate_name": "",
                            "spec.0.template.0.metadata.0.generation": "0",
                            "spec.0.template.0.metadata.0.labels.%": "1",
                            "spec.0.template.0.metadata.0.labels.app": "backend-logging-load",
                            "spec.0.template.0.metadata.0.name": "",
                            "spec.0.template.0.metadata.0.namespace": "",
                            "spec.0.template.0.metadata.0.resource_version": "",
                            "spec.0.template.0.metadata.0.self_link": "",
                            "spec.0.template.0.metadata.0.uid": "",
                            "spec.0.template.0.spec.#": "1",
                            "spec.0.template.0.spec.0.active_deadline_seconds": "0",
                            "spec.0.template.0.spec.0.container.#": "1",
                            "spec.0.template.0.spec.0.container.0.args.#": "3",

De forma geral, quem quiser um desafio de programação para entrevistas, basta pedir para escrever um parser para isso 🙂
Após muitas tentativas de escrever um parser sem bugs, encontrei uma parte dele no código do terraform, especificamente a parte mais importante. E tudo parecia funcionar bem.

Tentativa três
O provider do terraform é um binário que contém código com todos os recursos e lógica para interagir com as APIs das nuvens. Cada nuvem tem seu próprio provider, e o terraform apenas os chama através de seu protocolo RPC entre dois processos.
Agora decidi me comunicar diretamente com os providers do terraform através de chamadas RPC. Ficou bonito e permitiu atualizar os providers do terraform para versões mais novas e obter novos recursos sem alterar o código. Além disso, descobri que nem todos os campos no tfstate precisam estar no tf, e como saber isso? Apenas perguntando ao provider sobre isso. Depois começou outra pornografia recursiva com a construção de expressões regulares para pesquisar campos dentro do tfstate em todos os níveis de profundidade.

No final, consegui um CLI tool útil que possui uma infraestrutura comum para todos os providers do terraform e é fácil adicionar novos. Além disso, a adição de recursos ocupa pouco código. E teve vários recursos extras, como conexões entre recursos. Claro, houve muitos problemas diversos que não podem ser todos descritos.
Chamei a criatura de Terrafomer.

Final

Usamos o Terrafomer para gerar de 500 a 700 mil linhas de código tf + tfstate em duas nuvens. Conseguimos pegar itens legados e começar a manipulá-los apenas através do Terraform, como nas melhores ideias de infraestrutura como código. É uma mágica quando você pega uma enorme nuvem e a obtém através de um comando na forma de arquivos terraform funcionais. E então é grep/replace/git e assim por diante.

Concluí e organizei tudo, obtive permissões. Publicado no GitHub para todos na quinta-feira (02.05.19). github.com/GoogleCloudPlatform/terraformer
Já recebi 600 estrelas, 2 pull requests para adicionar suporte ao OpenStack e Kubernetes. Boas avaliações. O projeto é realmente útil para as pessoas.
Recomendo a todos que querem começar a trabalhar com Terraform e não reescrever tudo para isso.
Ficarei feliz com pull requests, issues e estrelas.

Demonstração
Terraformer — Infraestrutura como Código

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster