
Gostaria de falar sobre a nova ferramenta CLI que escrevi para resolver um problema antigo.
O problema
Terraform tem sido o padrão na comunidade Devops / Cloud / IT. A coisa é muito conveniente e útil para criar infraestrutura como código. Existem muitos encantos no Terraform, bem como muitos garfos, facas afiadas e ancinhos.
Com o Terraform, é muito conveniente fazer coisas novas e depois gerenciar, alterar ou excluí-las. E aqueles que têm uma infraestrutura enorme na nuvem e não são criados por meio do Terraform? Reescrever e recriar toda a nuvem é algo caro e inseguro.
Eu encontrei esse problema em 2 trabalhos, o exemplo mais simples é quando você deseja que tudo esteja na forma de arquivos de terraform, e você tem mais de 250 baldes e escreve muitos deles para terraform com as mãos.
Há um
problema desde 2014 no terrafom que foi fechado em 2016 com a esperança de que haverá importação.
Em geral, tudo é como na imagem apenas da direita para a esquerda
Advertências: o autor não mora na Rússia por metade da vida e escreve pouco em russo. Cuidado com erros de ortografia.
Soluções
1. Existem soluções prontas para uso e antigas para a
terraformação da AWS. Quando tentei colocar o meu balde de 250+ nele, percebi que tudo estava ruim por lá. A AWS já apresenta muitas novas opções há muito tempo e a terraformação não as conhece e, em geral, possui um
modelo de rubi
que parece ruim . Depois das 2 da noite, enviei uma
solicitação Pull para adicionar mais recursos lá e percebi que essa solução não se encaixa.
Como a terraformação funciona, ele obtém dados do AWS SDK e gera tf e tfstate por meio do modelo.
Existem 3 problemas:
1. Sempre haverá atrasos nas atualizações
2. os arquivos tf às vezes saem quebrados
3. tfstate é coletado separadamente de tf e nem sempre converge
Em geral, é difícil obter um resultado em que o `plano de terraform 'diga que não há mudanças
2. `terraform import` - um comando embutido no terraform. Como isso funciona?
Você escreve um arquivo TF vazio com o nome e o tipo do recurso, depois executa `terraform import` e passa o ID do recurso. O Terraform chama o provedor recebe os dados e cria um arquivo tfstate.
Existem 3 problemas:
1. Nós obtemos apenas o arquivo tfstate e o tf empty deve ser gravado ou convertido com tfstate
2. Capaz de trabalhar com apenas um recurso de cada vez e não suporta todos os recursos. E o que devo fazer novamente com mais de 250 baldes
3. Você precisa conhecer o ID do recurso - ou seja, precisa envolvê-lo no código que extrai uma lista de recursos
Em geral, o resultado é parcial e não escala bem
Minhas decisões
Requisitos:
1. Capacidade de criar arquivos tf e tfstate por recursos. Por exemplo, faça o download de todos os buckets / grupo de segurança / balanceador de carga e se o `plano de terraform` retornou, sem alterações
2. São necessárias 2 nuvens de GCP + AWS
3. Uma solução global que é fácil de atualizar todas as vezes e não perde tempo em cada recurso por três dias de trabalho
4. Crie código aberto - todo mundo tem esse problema
Go language - é por isso que eu adoro isso, e ele tem uma biblioteca para criar arquivos HCL que são usados em terraform + muito código em terraform que pode ser útil
O caminho
Primeira tentativa
Iniciou uma opção simples. Acesse a nuvem através do SDK para o recurso desejado e converta-o em campos para terraform. A tentativa morreu imediatamente no grupo de segurança, porque eu não gostei de 1,5 dias para converter apenas o grupo de segurança (e há muitos recursos). Campos longos e depois podem ser alterados / adicionados
Segunda tentativa
Com base na ideia descrita
aqui . Basta pegar e converter tfstate em tf. Todos os dados estão lá e os campos são os mesmos. Como obter o estado completo para muitos recursos? Então o comando `terraform refresh` veio em socorro. O terraform pega todos os recursos no estado e extrai os dados por ID e grava tudo no estado. Ou seja, crie um estado vazio com apenas nomes e IDs, execute `terraform refresh`, obteremos o estado completo. Viva!
Agora vamos fazer a
pornografia recursiva de escrever um conversor de tfstate para tf. Para quem nunca leu tfstate, isso é JSON, mas especial.
Aqui estão os atributos de parte importantes
"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",
Existem:
1. id - string
2. metadados - uma matriz de tamanho 1 e nela um objeto com campos descritos abaixo
3. spec - hash do tamanho 1 e na chave, valor
Em resumo, um formato divertido, tudo pode ser aprofundado 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",
Em geral, quem deseja uma tarefa de programação para uma entrevista, basta pedir para escrever um analisador para este assunto :)
Após longas tentativas de escrever um analisador sem erros, encontrei parte dele no código do terraform e a parte mais importante. E tudo parecia funcionar bem
Tentativa três
O provedor terraform é um binário no qual existe um código com todos os recursos e lógica para trabalhar com a API da nuvem. Cada nuvem tem seu próprio provedor e o terraform em si só os chama através de seu protocolo RPC entre dois processos.
Agora, decidi acessar os provedores de terraform diretamente por meio de chamadas RPC. O resultado foi bonito e possibilitou a mudança de fornecedores de terraform para novos e a obtenção de novas oportunidades sem alterar o código. Descobriu-se que nem todos os campos em tfstate devem estar em tf, mas como posso descobrir? Basta perguntar ao provedor sobre isso. Em seguida, outra
pornografia recursiva na montagem de expressões regulares começou com a pesquisa com a pesquisa de campos dentro do estado em todos os níveis em profundidade.
No final, obtivemos uma ferramenta CLI útil, que possui uma infraestrutura comum para todos os provedores de terraform e você pode facilmente adicionar uma nova. A adição de recursos também requer pouco código. Além disso, todos os tipos de brindes, como a conexão entre recursos. Claro que havia muitos problemas diferentes que nem todos podem ser descritos.
Ele chamou o pequeno animal de Terrafomer.
Final
Usando o Terrafomer, geramos 500-700 mil linhas de código tf + tfstate a partir de duas nuvens. Eles poderiam pegar coisas herdadas e começar a tocá-las apenas através de terraformas, como nas melhores idéias de infraestrutura como código. É simplesmente mágico quando você pega uma nuvem enorme e a realiza através do comando na forma de arquivos de terraform. E então grep / replace / git e assim por diante.
Ele penteou e ordenou, recebeu permissão. Lançado no github para todos na quinta-feira (19/02/19).
github.com/GoogleCloudPlatform/terraformerJá recebeu 600 estrelas, 2 solicitações pull, adicionando suporte ao openstack e kubernetes. Boas críticas. Em geral, o projeto é útil para pessoas
Aconselho a todos que desejam começar a trabalhar com o Terraform e não reescrevem tudo para isso.
Eu ficaria feliz em receber solicitações, problemas, estrelas.
Demo

Atualizações: estragou o suporte ao Openstack e o suporte ao kubernetes está quase pronto, graças às pessoas por PRs