O que aprendi testando 200.000 linhas de código de infraestrutura


A abordagem IaC (Infraestrutura como código) consiste não apenas no código armazenado no repositório, mas também nas pessoas e processos que cercam esse código. É possível reutilizar abordagens do desenvolvimento de software ao gerenciamento e descrição da infraestrutura? Não será supérfluo manter essa idéia em mente enquanto você lê o artigo.


Versão russa


Esta é uma transcrição do meu desempenho no DevopsConf 2019-05-28 .



Infraestrutura como histórico do bash



Suponha que você venha para um novo projeto e eles lhe digam: "temos infraestrutura como código ". Na realidade, verifica-se, Infra-estrutura como histórico de bash ou, por exemplo, Documentação como histórico de bash . Esta é uma situação muito real, por exemplo, um caso semelhante foi descrito por Denis Lysenko em seu discurso Como substituir toda a infraestrutura e começar a dormir em paz , ele contou que, a partir do histórico do bash, eles obtiveram uma infraestrutura esbelta no projeto.


Se desejar, você pode dizer que Infraestrutura como histórico do bash é como código:


  1. reprodutibilidade : você pode pegar o histórico do bash, executar comandos a partir daí; talvez, a propósito, você tenha uma configuração funcional na saída.
  2. versionamento : você sabe quem entrou e o que fez, novamente, não o fato de que isso o levará a uma configuração funcional na saída.
  3. história : história de quem fez o quê. somente você não poderá usá-lo se perder o servidor.

O que fazer?


Infraestrutura como código



Mesmo um caso tão estranho como Infraestrutura e histórico do bash pode ser atraído para Infraestrutura como Código , mas quando queremos fazer algo mais complicado que o bom e velho servidor LAMP, chegamos à conclusão de que esse código precisa ser modificado, modificado, modificado de alguma forma . Além disso, gostaríamos de considerar paralelos entre infraestrutura como código e desenvolvimento de software.


SECO



No projeto para o desenvolvimento de sistemas de armazenamento, havia uma subtarefa para configurar periodicamente o SDS : estamos lançando um novo release - ele precisa ser implementado para testes adicionais. A tarefa é extremamente simples:


  • venha aqui pelo ssh e execute o comando.
  • copie o arquivo lá.
  • aqui está o ajuste na configuração.
  • iniciar o serviço lá
  • ...
  • LUCRO!

O Bash é mais do que suficiente para a lógica descrita, especialmente nos estágios iniciais de um projeto quando ele está apenas começando. Não é ruim o uso do bash , mas com o tempo você é solicitado a implantar algo semelhante, mas um pouco diferente. A primeira coisa que vem à mente: copiar e colar. E agora temos dois scripts muito semelhantes que fazem quase a mesma coisa. Com o tempo, o número de scripts aumentou, e somos confrontados com o fato de que existe uma certa lógica de negócios para implantar a instalação, que deve ser sincronizada entre diferentes scripts, e isso é bastante difícil.



Acontece que existe uma prática seca (não se repita). A idéia é reutilizar o código existente. Parece simples, mas não chegou a isso imediatamente. No nosso caso, era uma idéia comum: separar configurações de scripts. I.e. lógica de negócios como a instalação é implementada separadamente, configura separadamente.


SOLID para CFM



Com o tempo, o projeto cresceu e uma extensão natural foi o surgimento do Ansible. O principal motivo de sua aparência é a presença de experiência na equipe e esse bash não se destina a lógica complexa. O Ansible também começou a conter lógica complexa. Para que uma lógica complexa não se transforme em caos, existem princípios de organização do código SOLID no desenvolvimento de software.Por exemplo, Grigory Petrov em seu relatório "Por que a TI precisa de uma marca pessoal" levantou a questão de que uma pessoa é tão projetada que é mais fácil operar com alguns entidades sociais, no desenvolvimento de software esses são objetos. Se você combinar essas duas idéias e continuar desenvolvendo-as, notará que também poderá usar o SOLID na descrição da infraestrutura, para que seja mais fácil manter e modificar essa lógica no futuro.


O princípio da responsabilidade única



Cada classe executa apenas uma tarefa.


Não há necessidade de misturar código e criar monstros monolíticos de massa divina. A infraestrutura deve consistir em tijolos simples. Acontece que, se você dividir o manual do Ansible em pequenos pedaços, leia os papéis do Ansible e eles serão mais fáceis de manter.


Os princípios fechados abertos



O princípio da abertura / fechamento.


  • Aberto para expansão: significa que o comportamento de uma entidade pode ser expandido criando novos tipos de entidades.
  • Fechado para alteração: como resultado da expansão do comportamento de uma entidade, nenhuma alteração deve ser feita no código que usa essas entidades.

Inicialmente, implantamos a infraestrutura de teste em máquinas virtuais, mas devido ao fato de a lógica de implantação de negócios ser separada da implementação, adicionamos facilmente um rolamento ao bare-metall.


O princípio da substituição de Liskov



O princípio da substituição de Barbara Liskov. objetos no programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção do programa


Se você olhar de maneira mais ampla, não é um recurso de nenhum projeto específico que você possa aplicar ao SOLID , geralmente trata-se de CFM, por exemplo, em outro projeto, você precisará implantar um aplicativo Java in a box sobre vários Java, servidores de aplicativos, bancos de dados, SO, etc. Neste exemplo, vou considerar outros princípios do SOLID .


No nosso caso, como parte da equipe de infraestrutura, existe um acordo de que, se instalarmos a função de imbjava ou oraclejava, teremos um executável java binário. Isso é necessário porque papéis mais altos dependem desse comportamento; eles esperam que o java esteja presente. Ao mesmo tempo, isso nos permite substituir uma implementação / versão do java por outra sem alterar a lógica de implantação do aplicativo.


O problema aqui reside no fato de que na Ansible é impossível implementar, como resultado, alguns acordos aparecem dentro da equipe.


O Princípio de Segregação de Interface



O princípio da separação de interfaces “muitas interfaces projetadas especificamente para os clientes são melhores que uma única interface de uso geral.


Inicialmente, tentamos colocar toda a variação na implantação do aplicativo em um manual Ansible, mas era difícil de manter, e a abordagem quando temos uma interface (o cliente espera a porta 443) é especificada, então podemos compor a infraestrutura de tijolos separados para uma implementação específica.


O princípio da inversão de dependência



O princípio da inversão de dependência. Os módulos de nível superior não devem depender dos módulos de nível inferior. Ambos os tipos de módulos devem depender de abstrações. As abstrações não devem depender dos detalhes. Os detalhes devem depender de abstrações.


Aqui o exemplo será baseado no antipadrão.


  1. Um dos clientes tinha uma nuvem privada.
  2. Dentro da nuvem, pedimos máquinas virtuais.
  3. Mas, tendo em vista os recursos da nuvem, a implantação do aplicativo estava ligada a qual hipervisor a VM estava.

I.e. lógica de implementação de aplicativos de alto nível, as dependências fluíram para os níveis mais baixos do hypervisor e isso significava problemas ao reutilizar essa lógica. Não faça isso.


Interação



A infraestrutura como código não é apenas sobre código, mas também sobre o relacionamento entre o código e uma pessoa, sobre as interações entre os desenvolvedores da infraestrutura.


Fator de barramento



Suponha que você tenha Vasya no projeto. Vasya sabe tudo sobre sua infraestrutura, o que acontecerá se Vasya desaparecer de repente? Esta é uma situação muito real, porque pode ser atropelada por um ônibus. Às vezes isso acontece. Se isso acontecer e o conhecimento sobre o código, sua estrutura, como ele funciona, aparências e senhas não forem distribuídos na equipe, você poderá encontrar várias situações desagradáveis. Várias abordagens podem ser usadas para minimizar esses riscos e distribuir conhecimento dentro da equipe.


Par de devopsing



Não é como uma piada que os administradores bebiam cerveja, alterava senhas, mas um análogo da programação em pares. I.e. dois engenheiros sentam-se em um computador, um teclado e começam a configurar sua infraestrutura juntos: configurar o servidor, escrever a função Ansible etc. Parece bom, mas não funcionou para nós. Mas os casos especiais dessa prática funcionaram. Chegou um novo funcionário, seu mentor leva consigo uma tarefa real, trabalha, transfere conhecimento.


Outro caso especial é uma chamada de incidente. Durante o problema, um grupo de pessoas de plantão e envolvidos se reúne, um líder é nomeado, que compartilha sua tela e expressa a linha de pensamento. Outros participantes seguem o pensamento do líder, espionam os truques do console, verificam se não perderam uma linha no log, aprendem coisas novas sobre o sistema. Essa abordagem funcionou e não.


Revisão de código



Subjetivamente, com mais eficiência, a disseminação do conhecimento sobre a infraestrutura e como ela foi organizada foi realizada usando a revisão de código:


  • A infraestrutura é descrita pelo código no repositório.
  • As alterações ocorrem em uma ramificação separada.
  • Com uma solicitação de mesclagem, você pode ver o delta das alterações na infraestrutura.

O destaque aqui foi que os revisores foram selecionados por sua vez, de acordo com o cronograma, ou seja, com alguma probabilidade, você entrará em uma nova peça de infraestrutura.


Estilo do código



Com o tempo, brigas começaram a aparecer durante a revisão, como os revisores tinham seu próprio estilo e a capacidade de rotação dos revisores empilhados com estilos diferentes: 2 espaços ou 4, camelCase ou snake_case. Implementar isso não funcionou imediatamente.


  • A primeira idéia foi recomendar o uso de linter, porque todos os mesmos engenheiros, todos inteligentes. Mas editores diferentes, SO, não é conveniente
  • Isso evoluiu para um bot, que para cada commit se compromete com o problema escrito na folga e aplicava a saída do linter. Mas, na maioria dos casos, foram encontrados assuntos mais importantes e o código não foi corrigido.

Mestre de construção verde



O tempo passa e chegamos à conclusão de que você não deve permitir confirmações que não passam em certos testes no mestre. Voila! inventamos o Green Build Master, que é praticado há muito tempo no desenvolvimento de software:


  • O desenvolvimento está em um ramo separado.
  • Os testes são executados neste segmento.
  • Se os testes falharem, o código não entrará no assistente.

Tomar essa decisão foi muito doloroso porque causou muita controvérsia, mas valeu a pena, porque os pedidos de fusões começaram a ser analisados ​​sem discordância de estilo e, com o tempo, o número de áreas problemáticas começou a diminuir.


Teste IaC



Além da verificação de estilo, você pode usar outras coisas, por exemplo, para verificar se sua infraestrutura pode realmente ser implantada. Ou verifique se as alterações na infraestrutura não resultam em perda de dinheiro. Por que isso pode ser necessário? A questão é complexa e filosófica, é melhor responder com a história de que de alguma forma havia um auto-scaler no Powershell que não verificou as condições de contorno => mais VMs foram criadas do que o necessário => o cliente gastou mais dinheiro do que o planejado. Não é agradável o suficiente, mas seria bastante realista capturar esse erro em estágios anteriores.


Alguém pode perguntar, por que tornar a infraestrutura complexa ainda mais difícil? Os testes para a infraestrutura, bem como para o código, não são sobre simplificação, mas sobre como a infraestrutura deve funcionar.


Pirâmide de Teste IaC



Teste IaC: análise estática


Se você implantar imediatamente toda a infraestrutura e verificar se ela funciona, é possível que demore muito tempo e exija muito tempo. Portanto, a base deve ser algo que funcione rapidamente, é muito e abrange muitos lugares primitivos.


Bash é complicado


Aqui está um exemplo trivial. selecione todos os arquivos no diretório atual e copie para outro local. A primeira coisa que vem à mente:


for i in * ; do cp $i /some/path/$i.bak done 

Mas e se houver um espaço no nome do arquivo? Bem, ok, somos inteligentes, podemos usar aspas:


 for i in * ; do cp "$i" "/some/path/$i.bak" ; done 

Bem feito? não! E se não houver nada no diretório, ou seja, Globbing não vai funcionar.


 find . -type f -exec mv -v {} dst/{}.bak \; 

Agora bem feito? not ... Esqueceu que o nome do arquivo pode ser \n .


 touch x mv x "$(printf "foo\nbar")" find . -type f -print0 | xargs -0 mv -t /path/to/target-dir 

Ferramentas de análise estática


O problema da etapa anterior pode ser detectado quando esquecemos as aspas, por isso, por natureza, existem muitas ferramentas Shellcheck , existem muitas e provavelmente você pode encontrar seu próprio empilhador no IDE.


LinguagemFerramenta
festançaShellcheck
RubyRubocop
pythonPylint
ansibleFiapos Ansible

Teste IaC: testes de unidade



Como vimos no exemplo anterior, linter não é onipotente e não pode apontar para todas as áreas problemáticas. Além disso, por analogia com os testes no desenvolvimento de software, podemos recuperar testes de unidade. Então shunit , junit , rspec , pytest vêm imediatamente à mente. Mas o que fazer com ansible, chef, saltstack e outros como eles?


No início, conversamos sobre o SOLID e o fato de que nossa infraestrutura deve consistir em pequenos tijolos. Chegou a hora deles.


  1. A infraestrutura é esmagada em pequenos tijolos, por exemplo, papéis Ansible.
  2. Algum tipo de ambiente está se desenvolvendo, seja docker ou VM.
  3. Aplicamos nossa função Ansible neste ambiente de teste.
  4. Verificamos que tudo funcionou como esperado (execute os testes).
  5. Nós decidimos ok ou não ok.

Teste IaC: ferramentas de teste de unidade


A questão é: o que são testes para CFM? você pode executar o script brega ou usar soluções prontas para isso:


CFMFerramenta
AnsibleTestinfra
ChefInspec
ChefServerspec
saltstackGoss

Exemplo para testinfra, verificamos se os usuários test1 , test2 existem e estão no grupo sshusers :


 def test_default_users(host): users = ['test1', 'test2' ] for login in users: assert host.user(login).exists assert 'sshusers' in host.user(login).groups 

O que escolher? A questão é complexa e ambígua. Aqui está um exemplo de uma mudança nos projetos no github para 2018-2019:



Estruturas de teste IaC


Há como juntar tudo e correr? Você pode fazer e fazer tudo sozinho se tiver um número suficiente de engenheiros. E você pode usar soluções prontas, embora não existam muitas delas:


CFMFerramenta
AnsibleMolécula
ChefCozinha de teste
TerraformTerratest

Um exemplo de alteração nos projetos no github para 2018-2019:



Molécula vs. Testkitchen



Inicialmente, tentamos usar o testkitchen :


  1. Crie VMs em paralelo.
  2. Aplique Funções Ansíveis.
  3. Afaste o inspec.

Para 25 a 35 funções, isso funcionou por 40 a 70 minutos, o que levou muito tempo.



O próximo passo foi mudar para jenkins / docker / ansible / molécula. Idiologicamente, tudo é igual


  1. Playbooks de algodão.
  2. Para derramar papéis.
  3. Executar contêiner
  4. Aplique Funções Ansíveis.
  5. Afaste o testinfra.
  6. Verifique a idempotência.


Uma régua para 40 papéis e testes para uma dúzia começaram a levar cerca de 15 minutos.



O que escolher depende de muitos fatores, como a pilha usada, a experiência na equipe etc. aqui todos decidem como encerrar a questão dos testes de unidade


Teste IaC: testes de integração



No próximo estágio da pirâmide de testes de infraestrutura, serão exibidos testes de integração. Eles são semelhantes aos testes de unidade:


  1. A infraestrutura é esmagada em pequenos tijolos, como papéis Ansible.
  2. Algum tipo de ambiente está se desenvolvendo, seja docker ou VM.
  3. Existem muitas funções Ansible aplicadas a esse ambiente de teste.
  4. Verificamos que tudo funcionou como esperado (execute os testes).
  5. Nós decidimos ok ou não ok.

Grosso modo, não verificamos a operacionalidade de um elemento individual do sistema, como nos testes de unidade, verificamos como o servidor está configurado como um todo.


Teste de IaC: testes de ponta a ponta



No topo da pirâmide, somos atendidos pelos testes de ponta a ponta. I.e. não verificamos a operação de um servidor separado, um script separado, um bloco separado de nossa infraestrutura. Verificamos que muitos servidores estão combinados, nossa infraestrutura funciona como esperamos. Infelizmente, eu não vi nenhuma solução de caixa pronta, provavelmente porque a infraestrutura geralmente é única e difícil de modelar e cria uma estrutura para testá-la. Como resultado, todos criam sua própria solução. Existe demanda, mas não há resposta. Portanto, vou lhe dizer o que há para levar outras pessoas a emitir pensamentos ou cutucar meu nariz, que tudo foi inventado muito antes de nós.



Um projeto com uma história rica. Usado em grandes organizações e provavelmente cada um de vocês se cruza indiretamente. O aplicativo suporta muitos bancos de dados, integrações, etc., etc. Saber como a infraestrutura pode se parecer com isso é um monte de arquivos de composição do docker e saber quais testes executar em qual ambiente é Jenkins.



Esse esquema funcionou por um longo tempo, até que, como parte do estudo , tentamos transferi-lo para o Openshift. Os contêineres permaneceram os mesmos, mas o ambiente de inicialização mudou (olá DRY novamente).



A ideia da pesquisa foi mais longe e, no turno da noite, houve uma coisa do tipo APB (Ansible Playbook Bundle) que permite incluir no contêiner conhecimento sobre como implantar a infraestrutura. I.e. Há um ponto de conhecimento reproduzível e testável de como implantar a infraestrutura.



Tudo isso soou bem, até que nos enterramos em uma infraestrutura heterogênea: precisávamos do Windows para testes. Como resultado, o conhecimento sobre onde implantar e testar está em jenkins.


Conclusão



Infraestrutura como código é


  • O código no repositório.
  • A interação das pessoas.
  • Teste de infraestrutura.

Source: https://habr.com/ru/post/pt467171/


All Articles