Lições aprendidas ao testar Mais de 200.000 linhas de código de infraestrutura


IaC (Infraestrutura como Código) é uma abordagem moderna e acredito que infraestrutura é código. Isso significa que devemos usar a mesma filosofia para infraestrutura e para desenvolvimento de software. Se estamos falando de que infraestrutura é código, devemos reutilizar práticas do desenvolvimento para infraestrutura, como teste de unidade, programação de pares, revisão de código. Lembre-se dessa idéia ao ler o artigo.


Versão em russo


É a tradução do meu discurso ( vídeo RU ) no DevopsConf 2019-05-28 .



Infraestrutura como histórico do bash



Vamos imaginar que você está participando de um projeto e ouve algo como: "Usamos a infraestrutura como abordagem de código ". Infelizmente, o que eles realmente querem dizer é Infraestrutura como histórico de bash ou Documentação como histórico de bash . Esta é uma situação quase real. Por exemplo, Denis Lysenko descreveu essa situação em seu discurso Como substituir a infraestrutura e parar de se preocupar (RU) . Denis compartilhou a história de como converter o histórico do bash em uma infraestrutura sofisticada.


Vamos verificar a definição do código fonte: a text listing of commands to be compiled or assembled into an executable computer program . Se quisermos, podemos apresentar Infraestrutura como histórico de bash, como código. Este é um texto e uma lista de comandos. Ele descreve como um servidor foi configurado. Além disso, é:


  1. Reproduzível : você pode obter o histórico do bash, executar comandos e provavelmente obter a infraestrutura de trabalho.
  2. Controle de versão : você sabe quem efetuou login, quando e o que foi feito.
    Infelizmente, se você perder o servidor, não poderá fazer nada porque não há histórico do bash, você o perdeu com o servidor.

O que deve ser feito?


Infraestrutura como código



Por um lado, esse caso anormal, Infraestrutura como histórico do bash , pode ser apresentado como Infraestrutura como Código , mas, por outro lado, se você quiser fazer algo mais complexo que o servidor LAMP, precisará gerenciar, manter e modificar o código. . Vamos conversar sobre paralelos entre infraestrutura como desenvolvimento de código e desenvolvimento de software.


SECO



Estávamos desenvolvendo SDS (armazenamento definido por software). O SDS consistia em distribuidores de SO personalizados, servidores de alto padrão, muita lógica de negócios; como resultado, tinha que usar hardware real. Periodicamente, havia uma subtarefa de instalação do SDS . Antes de publicar o novo lançamento, tivemos que instalá-lo e fazer o check-out. A princípio, parecia que era uma tarefa muito simples:


  • SSH para hospedar e executar o comando.
  • SCP um arquivo.
  • Modifique uma configuração.
  • Execute um serviço.
  • ...
  • LUCRO!

Acredito que Make CM, não bash, é uma boa abordagem. No entanto, o bash é usado apenas em casos extremos e limitados, como no início de um projeto. Portanto, o bash foi uma escolha bastante boa e razoável no início do projeto. O tempo estava passando. Estávamos enfrentando solicitações diferentes para criar novas instalações em uma configuração ligeiramente diferente. Estávamos fazendo SSHing nas instalações e executando os comandos para instalar todo o software necessário, editando os arquivos de configuração por scripts e, finalmente, configurando o SDS via API de descanso HTTP da Web. Afinal, a instalação foi configurada e funcionando. Essa era uma prática bastante comum, mas havia muitos scripts bash e a lógica de instalação estava se tornando mais complexa a cada dia.


Infelizmente, cada script era como um pequeno floco de neve, dependendo de quem o estava copiando e colando. Também foi uma dor real quando estávamos criando ou recriando a instalação.


Espero que você tenha a idéia principal de que, nesta fase, tivemos que ajustar constantemente a lógica dos scripts até que o serviço estivesse bom. Mas havia uma solução para isso. Estava SECO



Existe uma abordagem DRY (não se repita). A idéia principal é reutilizar o código já existente. Parece extremamente simples. No nosso caso, DRY significava: configurações e scripts divididos.


SOLID para CFM



Como o projeto estava crescendo, decidimos usar o Ansible. Havia razões para isso:


  1. O Bash não deve conter lógica complexa .
  2. Tivemos uma certa experiência em Ansible.

Havia uma quantidade de lógica de negócios dentro do código Ansible. Existe uma abordagem para colocar coisas no código fonte durante o processo de desenvolvimento de software. É chamado de SOLID . Do meu ponto de vista, podemos reutilizar o SOLID for Infrastructure como código . Deixe-me explicar passo a passo.


O princípio da responsabilidade única



Uma classe deve ter apenas uma única responsabilidade, ou seja, apenas alterações em uma parte da especificação do software devem poder afetar a especificação da classe.


Você não deve criar um código espaguete dentro do seu código de infraestrutura. Sua infraestrutura deve ser feita a partir de simples tijolos previsíveis. Em outras palavras, pode ser uma boa idéia dividir o imenso manual do Ansible em papéis independentes do Ansible. Será mais fácil de manter.


Os princípios aberto-fechado



Entidades de software ... devem estar abertas para extensão, mas fechadas para modificação.


No início, estávamos implantando o SDS em máquinas virtuais, um pouco mais tarde adicionamos a implantação em servidores bare metal . Nós tínhamos feito isso. Foi tão fácil quanto nós, porque acabamos de adicionar uma implementação para peças específicas de bare metal sem modificar a lógica de instalação do SDS.


O princípio da substituição de Liskov



Os objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção desse programa.


Sejamos abertos. É possível usar o SOLID no CFM em geral, não foi um projeto de sorte. Eu gostaria de descrever outro projeto. É uma solução corporativa pronta para uso. Ele suporta diferentes bancos de dados, servidores de aplicativos e interfaces de integração com sistemas de terceiros. Vou usar este exemplo para descrever o restante do SOLID


Por exemplo, no nosso caso, há um acordo dentro da equipe de infraestrutura: se você implantar a função ibm java ou oracle java ou openjdk, terá o binário java executável. Precisamos disso porque as funções Ansible de nível superior dependem disso. Além disso, permite trocar a implementação java sem modificar a lógica de instalação do aplicativo.


Infelizmente, não há açúcar de sintaxe para isso nos manuais do Ansible. Isso significa que você deve manter isso em mente ao desenvolver papéis Ansible.


O Princípio de Segregação de Interface



Muitas interfaces específicas do cliente são melhores que uma interface de uso geral.


No começo, estávamos colocando a lógica de instalação do aplicativo no único manual, tentando cobrir todos os casos e as arestas de corte. Enfrentávamos a questão difícil de manter, por isso mudamos nossa abordagem. Entendemos que um cliente precisa de uma interface nossa (por exemplo, https na porta 443) e conseguimos combinar nossas funções Ansible para cada ambiente específico.


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



Deve-se "depender de abstrações, e não de concreções".


  • Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações (por exemplo, interfaces).
  • Abstrações não devem depender de detalhes. Detalhes (implementações concretas) devem depender de abstrações.

Eu gostaria de descrever esse princípio via antipadrão.


  1. Havia um cliente com uma nuvem privada.
  2. Estávamos solicitando VMs na nuvem.
  3. Nossa lógica de implantação dependia de qual hipervisor uma VM estava localizada.

Em outras palavras, não foi possível reutilizar nosso IaC em outra nuvem porque a lógica de implantação de nível superior dependia da implementação de nível inferior. Por favor não faça


Interação



Infraestrutura não é apenas código, também trata de código de interação <-> DevOps, DevOps <-> DevOps, IaC <-> pessoas.


Fator de barramento



Vamos imaginar, há o engenheiro de DevOps, John. John sabe tudo sobre sua infraestrutura. Se John for atropelado por um ônibus, o que acontecerá com sua infraestrutura? Infelizmente, é quase um caso real. Algum tempo as coisas acontecem. Se isso aconteceu e você não compartilhar conhecimento sobre IaC, Infraestrutura entre os membros da sua equipe, enfrentará muitas consequências imprevisíveis e embaraçosas. Existem algumas abordagens para lidar com isso. Vamos conversar sobre eles.


Emparelhar DevOpsing



É como programação em pares. Em outras palavras, existem dois engenheiros do DevOps e eles usam um único laptop \ teclado para configurar a infraestrutura: configurar um servidor, criar uma função Ansible etc. Parece ótimo, no entanto, não funcionou para nós. Houve alguns casos personalizados quando funcionou parcialmente.


  • Integração : o mentor e a nova pessoa realizam uma tarefa real de um backlog e trabalham juntos - transfira o conhecimento do mentor para a pessoa.
  • Chamada de incidente : durante a solução de problemas, há um grupo de engenheiros que estão procurando uma solução. O ponto principal é que existe uma pessoa que lidera esse incidente. A pessoa compartilha tela e idéias. Outras pessoas o seguem cuidadosamente e percebem truques, erros, análise de logs etc.

Revisão de código



Do meu ponto de vista, a revisão de código é uma das maneiras mais eficientes de compartilhar conhecimento dentro de uma equipe sobre sua infraestrutura. Como isso funciona?


  • Existe um repositório que contém a descrição da sua infraestrutura.
  • Todo mundo está fazendo suas alterações em um ramo dedicado.
  • Durante a solicitação de mesclagem, você pode revisar o delta das alterações em sua infraestrutura.

O mais interessante é que estávamos rotacionando um revisor. Isso significa que, a cada dois dias, elegemos um novo revisor e ele analisava todas as solicitações de mesclagem. Como resultado, teoricamente, todas as pessoas tiveram que tocar em uma nova parte da infraestrutura e ter um conhecimento médio sobre nossa infraestrutura em geral.


Estilo do código



O tempo estava passando, às vezes discutíamos durante a revisão, porque o revisor e o responsável pelo uso poderiam usar um estilo de código diferente: 2 espaços ou 4, camelCase ou snake_case . Nós o implementamos, no entanto, não era um piquenique.


  • A primeira idéia foi recomendar o uso de linters. Todo mundo tinha seu próprio ambiente de desenvolvimento: IDE, SO ... era difícil sincronizar e unificar tudo.
  • A ideia evoluiu para um bot frouxo. Após cada confirmação, o bot estava verificando o código-fonte e enviando mensagens com uma lista de problemas. Infelizmente, na grande maioria dos casos, não houve alterações no código fonte após as mensagens.

Mestre de construção verde



Em seguida, o passo mais doloroso foi restringir o envio ao ramo principal para todos. Somente através de solicitações de mesclagem e testes verdes devem estar ok. Isso é chamado de mestre de construção verde . Em outras palavras, você tem 100% de certeza de que pode implantar sua infraestrutura a partir da ramificação principal. É uma prática bastante comum no desenvolvimento de software:


  • Existe um repositório que contém a descrição da sua infraestrutura.
  • Todo mundo está fazendo suas alterações em um ramo dedicado.
  • Para cada filial, estamos executando testes.
  • Você não poderá mesclar na ramificação principal se os testes estiverem falhando.

Foi uma decisão difícil. Felizmente, como resultado durante o processo de revisão, não houve discussão sobre o estilo do código e a quantidade de cheiro do código estava diminuindo.


Teste IaC



Além da verificação de estilo de código, você pode verificar se é possível implantar ou recriar sua infraestrutura em uma sandbox. Para que serve? É uma pergunta sofisticada e eu gostaria de compartilhar uma história em vez de uma resposta. Were era um auto-scaler personalizado para a AWS, escrito em Powershell. O auto-scaler não verificou as arestas de corte quanto a parâmetros de entrada; como resultado, criou toneladas de máquinas virtuais e o cliente ficou insatisfeito. É uma situação embaraçosa, espero que seja possível pegá-la nos estágios iniciais.


Por um lado, é possível testar o script e a infraestrutura, mas, por outro lado, você está aumentando uma quantidade de código e tornando a infraestrutura mais complexa. No entanto, o verdadeiro motivo disso é que você está testando seu conhecimento sobre infraestrutura. Você está descrevendo como as coisas devem funcionar juntas.


Pirâmide de Teste IaC



Teste IaC: análise estática


Você pode criar toda a infraestrutura do zero para cada confirmação, mas, geralmente, existem alguns obstáculos:


  • O preço é estratosférico.
  • Isso requer muito tempo.

Felizmente, existem alguns truques. Você deve ter muitos testes simples, rápidos e primitivos em sua base.


Bash é complicado


Vamos dar uma olhada em um exemplo extremamente simples. Eu gostaria de criar um script de backup:


  • Obtenha todos os arquivos do diretório atual.
  • Copie os arquivos para outro diretório com um nome modificado.

A primeira ideia é:


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

Muito bom. No entanto, e se o nome do arquivo contiver espaço ? Somos caras inteligentes, usamos citações:


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

Nós terminamos? Nope! E se o diretório estiver vazio? Globing falhar neste caso.


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

Nós terminamos? Ainda não ... Esquecemos que o nome do arquivo pode conter \n caractere.


 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


Você pode pegar alguns problemas do exemplo anterior via Shellcheck . Existem muitas ferramentas como essa, elas são chamadas de linters e você pode descobrir as mais adequadas para seu IDE, pilha e ambiente.


LinguagemFerramenta
festançaShellcheck
RubyRubocop
pythonPylint
AnsibleFiapos Ansible

Teste IaC: testes de unidade



Como você pode ver, os linters não conseguem pegar tudo, eles só podem prever. Se continuarmos a pensar em paralelos entre desenvolvimento de software e infraestrutura como código, devemos mencionar testes de unidade. Existem muitos sistemas de testes de unidade como shunit , JUnit , RSpec , pytest . Mas você já ouviu falar sobre testes de unidade para Ansible, Chef, Saltstack, CFengine?


Quando estávamos falando sobre o SOLID for CFM, mencionei que nossa infraestrutura deveria ser feita a partir de tijolos / módulos simples. Agora chegou a hora:


  1. Divida a infraestrutura em módulos / quebras simples, ou seja, funções possíveis.
  2. Crie um ambiente, por exemplo, Docker ou VM.
  3. Aplique seu único quebra / módulo ao ambiente.
  4. Verifique se está tudo bem ou não.
    ...
  5. LUCRO!

Teste IaC: ferramentas de teste de unidade


Qual é o teste para CFM e sua infraestrutura? ou seja, você pode simplesmente executar um script ou usar uma solução pronta para produção, como:


CFMFerramenta
AnsibleTestinfra
ChefInspec
ChefServerspec
saltstackGoss

Vamos dar uma olhada no testinfra, gostaria de verificar se os usuários test1 , test2 existem e fazem parte do 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 

Qual é a melhor solução? Não há uma resposta única para essa pergunta, no entanto, eu criei o mapa de calor e comparei as mudanças nesses projetos durante 2018-2019:



Estruturas de teste IaC


Depois disso, você pode enfrentar uma pergunta como executar tudo isso junto? Por um lado, você pode fazer tudo sozinho se tiver grandes engenheiros suficientes, mas, por outro lado, pode usar soluções prontas para produção de código aberto:


CFMFerramenta
AnsibleMolécula
ChefCozinha de teste
TerraformTerratest

Criei o mapa de calor e comparei as mudanças nesses projetos durante 2018-2019:



Molécula vs. Testkitchen



No começo, tentamos testar funções ansible via testkitchen no hyper-v :


  1. Crie VMs.
  2. Aplique funções Ansible.
  3. Execute o Inspec.

Foram necessários 40-70 minutos para 25-35 papéis Ansible. Foi muito longo para nós.



O próximo passo foi usar Jenkins / docker / Ansible / molécula. É aproximadamente a mesma ideia:


  1. Lint Ansible playbooks.
  2. Papéis Ansible do fiapo.
  3. Execute um contêiner de docker.
  4. Aplique funções Ansible.
  5. Execute testinfra.
  6. Verifique a idempotência.


Linting para 40 papéis e teste para dez deles levou cerca de 15 minutos.



Qual é a melhor solução? Por um lado, não quero ser a autoridade final, mas, por outro, gostaria de compartilhar meu ponto de vista. Não existe uma bala de prata, no entanto, no caso da molécula Ansible é uma solução mais adequada do que a cozinha de teste.


Teste IaC: testes de integração



No próximo nível da pirâmide de testes IaC , há testes de integração . Os testes de integração da infraestrutura se parecem com testes de unidade:


  1. Divida a infraestrutura em módulos / quebras simples, ou seja, funções possíveis.
  2. Crie um ambiente, por exemplo, Docker ou VM.
  3. Aplique uma combinação de quebra / módulo simples ao ambiente.
  4. Verifique se está tudo bem ou não.
    ...
  5. LUCRO!

Em outras palavras, durante testes de unidade, verificamos um módulo simples (ou seja, função Ansible, script python, módulo Ansible etc.) de uma infraestrutura, mas, no caso de testes de integração, verificamos toda a configuração do servidor.


Teste de IaC: testes de ponta a ponta



No topo da pirâmide de testes IaC , há testes de ponta a ponta . Nesse caso, não verificamos servidor dedicado, script, módulo de nossa infraestrutura; Verificamos se toda a infraestrutura funciona corretamente. Infelizmente, não há uma solução pronta para isso ou eu não ouvi falar deles (por favor, sinalize-me se você souber sobre eles). Geralmente, as pessoas reinventam a roda, porque há demanda de ponta a ponta nos testes de infraestrutura. Então, eu gostaria de compartilhar minha experiência, espero que seja útil para alguém.



Antes de mais, gostaria de descrever o contexto. É uma solução corporativa pronta para uso, suporta diferentes bancos de dados, servidores de aplicativos e interfaces de integração com sistemas de terceiros. Geralmente, nossos clientes são uma empresa imensa com um ambiente completamente diferente. Temos conhecimento sobre diferentes combinações de ambientes e os armazenamos como arquivos de docker-compondo diferentes. Além disso, havendo correspondência entre arquivos e testes de docker-compose, os armazenamos como trabalhos de Jenkins.



Esse esquema estava funcionando durante um período silencioso de log, durante a pesquisa em turno aberto , tentamos migrá-lo para o Openshift. Usamos aproximadamente os mesmos contêineres (inferno DRY novamente) e alteramos apenas o ambiente ao redor.



Continuamos a pesquisar e encontramos o APB (Ansible Playbook Bundle). A idéia principal é que você empacote todas as coisas necessárias em um contêiner e execute o contêiner dentro do Openshift. Isso significa que você tem uma solução reproduzível e testável.



Tudo estava bem até que enfrentamos mais um problema: tivemos que manter uma infraestrutura heterogênea para ambientes de teste. Como resultado, armazenamos nosso conhecimento de como criar infraestrutura e executar testes nos trabalhos de Jenkins.


Conclusão



Infraestrutura como código, é uma combinação de:


  • Código
  • Interação de pessoas.
  • Teste de infraestrutura.

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


All Articles