A partir do segundo commit, qualquer código se torna legado, pois as ideias iniciais começam a divergir da dura realidade. Isto não é bom nem mau, é um dado difícil de argumentar e que deve ser vivido. Parte desse processo é a refatoração. Refatorando a infraestrutura como código. Deixe a história começar sobre como refatorar o Ansible em um ano e não enlouquecer.
O Nascimento do Legado
Dia #1: Paciente Zero
Era uma vez um projeto condicional. Tinha uma equipe de desenvolvimento Dev e engenheiros de operações. Eles estavam resolvendo o mesmo problema: como implantar servidores e executar um aplicativo. O problema é que cada equipe resolveu esse problema à sua maneira. No projeto, optou-se por utilizar Ansible para sincronizar o conhecimento entre as equipes de Dev e Ops.
Dia #89: O Nascimento do Legado
Sem perceberem, eles queriam fazer o melhor possível, mas acabou sendo um legado. Como isso acontece?
Temos uma tarefa urgente aqui, vamos fazer um hack sujo e depois consertar.
Você não precisa escrever documentação e tudo fica claro sobre o que está acontecendo aqui.
Eu conheço Ansible/Python/Bash/Terraform! Olha como posso me esquivar!
Sou desenvolvedor Full Stack Overflow e copiei isso do stackoverflow, não sei como funciona, mas parece legal e resolve o problema.
Como resultado, você pode obter um tipo de código incompreensível para o qual não há documentação, não está claro o que faz, se é necessário, mas o problema é que você precisa desenvolvê-lo, modificá-lo, adicionar muletas e suportes , tornando a situação ainda pior.
O modelo IaC inicialmente concebido e implementado não atende mais aos requisitos dos usuários/negócios/outras equipes, e o tempo para fazer alterações na infraestrutura não é mais aceitável. Nesse momento chega o entendimento de que é hora de agir.
Refatoração IaC
Dia #139: Você realmente precisa de refatoração?
Antes de se apressar para refatorar, você deve responder a uma série de perguntas importantes:
Por que você precisa de tudo isso?
Tens tempo?
O conhecimento é suficiente?
Se você não souber responder às perguntas, a refatoração terminará antes mesmo de começar, ou poderá apenas piorar. Porque tinha experiência ( O que aprendi testando 200 linhas de código de infraestrutura), então o projeto recebeu um pedido de ajuda para fixar as funções e cobri-las com testes.
Dia #149: Preparando a refatoração
A primeira coisa é se preparar. Decida o que faremos. Para fazer isso, nos comunicamos, encontramos áreas problemáticas e descobrimos maneiras de resolvê-las. Registramos os conceitos resultantes de alguma forma, por exemplo um artigo em confluência, para que quando surgir a pergunta “o que é melhor?” ou "o que é correto?" Não nos perdemos. No nosso caso, mantivemos a ideia dividir para reinar: dividimos a infraestrutura em pequenos pedaços/tijolos. Essa abordagem permite que você pegue uma parte isolada da infraestrutura, entenda o que ela faz, cubra-a com testes e altere-a sem medo de quebrar nada.
Acontece que os testes de infraestrutura se tornam a pedra angular e aqui vale a pena mencionar a pirâmide de testes de infraestrutura. Exatamente a mesma ideia que está em desenvolvimento, mas para infraestrutura: estamos migrando de testes rápidos baratos que verificam coisas simples, como indentação, para testes completos e caros que implantam toda a infraestrutura.
Tentativas de teste Ansible
Antes de descrevermos como cobrimos os testes Ansible no projeto, descreverei as tentativas e abordagens que tive a oportunidade de usar anteriormente para entender o contexto das decisões tomadas.
Dia nº -997: Fornecimento de SDS
A primeira vez que testei o Ansible foi em um projeto de desenvolvimento de SDS (Software Defined Storage). Há um artigo separado sobre este tópico Como quebrar bicicletas com muletas ao testar sua distribuição, mas, resumindo, acabamos com uma pirâmide de testes invertida e nos testes passamos de 60 a 90 minutos em uma função, o que é muito tempo. A base foram os testes e2e, ou seja, implantamos uma instalação completa e depois a testamos. O que foi ainda mais agravante foi a invenção da sua própria bicicleta. Mas devo admitir que esta solução funcionou e permitiu uma versão estável.
Dia # -701: Ansible e cozinha de teste
O desenvolvimento da ideia de teste Ansible consistiu na utilização de ferramentas prontas, nomeadamente test kitchen / kitchen-ci e inspec. A escolha foi determinada pelo conhecimento de Ruby (para mais detalhes veja o artigo no Habré: Os programadores YML sonham em testar o Ansible?) trabalhou mais rápido, cerca de 40 minutos para 10 funções. Criamos um pacote de máquinas virtuais e executamos testes dentro dele.
Em geral, a solução funcionou, mas houve algum sedimento devido à heterogeneidade. Quando o número de pessoas testadas aumentou para 13 funções básicas e 2 metafunções combinando funções menores, de repente os testes começaram a durar 70 minutos, o que é quase 2 vezes mais longo. Foi difícil falar sobre práticas XP (programação extrema) porque... ninguém quer esperar 70 minutos. Esta foi a razão para mudar a abordagem
Dia # -601: Ansible e molécula
Conceitualmente, isso é semelhante ao testkitchen, apenas movemos o teste de função para o docker e alteramos a pilha. Como resultado, o tempo foi reduzido para estáveis 20-25 minutos para 7 funções.
Aumentando o número de funções testadas para 17 e linting 45 funções, executamos isso em 28 minutos em 2 escravos Jenkins.
Dia #167: Adicionando testes Ansible ao projeto
Muito provavelmente, não será possível realizar a tarefa de refatoração rapidamente. A tarefa deve ser mensurável para que você possa quebrá-lo em pequenos pedaços e comer o elefante pedaço por pedaço com uma colher de chá. Deve haver uma compreensão se você está se movendo na direção certa, quanto tempo falta.
Em geral, não importa como será feito, você pode escrever em um pedaço de papel, pode colocar adesivos no armário, pode criar tarefas no Jira ou pode abrir o Google Docs e anotar o status atual lá. As pernas crescem porque o processo não é imediato, será longo e tedioso. É improvável que alguém queira que você fique sem ideias, cansado e sobrecarregado durante a refatoração.
A refatoração é simples:
Coma.
Dormir.
Código.
Teste IaC.
repetição
e repetimos isso até atingirmos o objetivo pretendido.
Pode não ser possível começar a testar tudo imediatamente, então nossa primeira tarefa foi começar com linting e verificação de sintaxe.
Dia #181: Mestre de Construção Verde
Linting é um pequeno primeiro passo em direção ao Green Build Master. Isso não quebrará quase nada, mas permitirá que você depure processos e faça compilações verdes no Jenkins. A ideia é desenvolver hábitos entre a equipe:
Os testes vermelhos são ruins.
Vim consertar algo e ao mesmo tempo deixar o código um pouco melhor do que era antes de vocês.
Dia #193: Do linting aos testes unitários
Depois de construir o processo de inserção do código no mestre, você pode iniciar o processo de melhoria passo a passo - substituindo o linting por funções de lançamento, você pode até fazer isso sem idempotência. Você precisa entender como aplicar funções e como elas funcionam.
Dia #211: Da unidade aos testes de integração
Quando a maioria das funções estiver coberta por testes unitários e tudo estiver linted, você poderá prosseguir para a adição de testes de integração. Aqueles. testar não um único componente da infraestrutura, mas uma combinação deles, por exemplo, uma configuração completa da instância.
Usando jenkins, geramos vários estágios que vinculavam funções/playbooks em paralelo, depois testes unitários em contêineres e, finalmente, testes de integração.
Jenkins + Docker + Ansible = Testes
Faça check-out do repositório e gere estágios de construção.
Execute os estágios do manual do lint em paralelo.
Execute estágios de função lint em paralelo.
Execute estágios de função de verificação de sintaxe em paralelo.
Execute estágios de função de teste em paralelo.
Papel de fiapos.
Verifique a dependência de outras funções.
Verifique a sintaxe.
Criar instância do Docker
Execute molécula/default/playbook.yml.
Verifique a idempotência.
Execute testes de integração
Acabamento
Dia #271: Fator de ônibus
Inicialmente, a refatoração era realizada por um pequeno grupo de duas ou três pessoas. Eles revisaram o código no mestre. Com o tempo, a equipe desenvolveu conhecimento de como escrever código e a revisão de código contribuiu para a disseminação do conhecimento sobre a infraestrutura e como ela funciona. O destaque aqui foi que os revisores foram selecionados um a um, de acordo com um cronograma, ou seja, com algum grau de probabilidade você entrará em uma nova peça de infraestrutura.
E deve ser confortável aqui. É conveniente fazer uma revisão, ver no âmbito da tarefa que foi realizada e o histórico das discussões. Integramos jenkins + bitbucket + jira.
Mas, como tal, uma revisão não é uma panaceia; de alguma forma, entramos no código mestre, o que nos fez fracassar nos testes:
Com o tempo, houve mais testes, as compilações ficaram mais lentas, até uma hora na pior das hipóteses. Em uma das retros havia uma frase como “é bom que existam testes, mas são lentos”. Como resultado, abandonamos os testes de integração em máquinas virtuais e os adaptamos ao Docker para torná-los mais rápidos. Também substituímos o testinfra pelo verificador ansible para reduzir o número de ferramentas utilizadas.
A rigor, houve um conjunto de medidas:
Mude para a janela de encaixe.
Remova o teste de função, que está duplicado devido a dependências.
Aumentar o número de escravos.
Ordem de execução de teste.
Capacidade de fiapos ALL localmente com um comando.
Como resultado, o Pipeline no Jenkins também foi unificado
Gere estágios de construção.
Lint tudo em paralelo.
Execute estágios de função de teste em paralelo.
Concluir.
As lições aprendidas
Evite variáveis globais
Ansible usa variáveis globais, há uma solução parcial no formulário private_role_vars, mas isso não é uma panacéia.
Deixe-me lhe dar um exemplo. Deixe-nos ter role_a и role_b
O engraçado é que o resultado dos playbooks dependerá de coisas que nem sempre são óbvias, como a ordem em que as funções são listadas. Infelizmente essa é a natureza do Ansible e o melhor que pode ser feito é utilizar algum tipo de acordo, por exemplo, dentro de uma função, utilizar apenas a variável descrita nesta função.
BOM: em funções para variáveis, use variáveis prefixadas com o nome da função; isso, ao observar o inventário, facilitará a compreensão do que está acontecendo.
Concordamos em usar prefixos de variáveis; não seria supérfluo verificar se eles estão definidos como esperamos e, por exemplo, não foram substituídos por um valor vazio
BOM: Verifique variáveis.
- name: "Verify that required string variables are defined"
assert:
that: ahs_var is defined and ahs_var | length > 0 and ahs_var != None
fail_msg: "{{ ahs_var }} needs to be set for the role to work "
success_msg: "Required variables {{ ahs_var }} is defined"
loop_control:
loop_var: ahs_var
with_items:
- ahs_item1
- ahs_item2
- ahs_item3
Evite dicionários de hashes, use estrutura plana
Se uma função espera um hash/dicionário em um de seus parâmetros, então, se quisermos alterar um dos parâmetros filhos, precisaremos substituir todo o hash/dicionário, o que aumentará a complexidade da configuração.
As funções e os manuais devem ser idempotentes, porque reduz o desvio de configuração e o medo de quebrar alguma coisa. Mas se você usar molécula, esse será o comportamento padrão.
Evite usar módulos de shell de comando
Usar um módulo shell resulta em um paradigma de descrição imperativo, em vez do declarativo, que é o núcleo do Ansible.
Teste seus papéis via molécula
A molécula é uma coisa muito flexível, vejamos alguns cenários.
Molécula Múltiplas Instâncias
В molecule.yml na seção platforms você pode descrever muitos hosts que podem ser implantados.
No molecular é possível usar o ansible para verificar se a instância foi configurada corretamente, além disso, este é o padrão desde o release 3. Não é tão flexível quanto testinfra/inspec, mas podemos verificar se o conteúdo do arquivo corresponde às nossas expectativas:
Ou implante o serviço, espere que ele fique disponível e faça um teste de fumaça:
---
- name: Verify
hosts: solr
tasks:
- command: /blah/solr/bin/solr start -s /solr_home -p 8983 -force
- uri:
url: http://127.0.0.1:8983/solr
method: GET
status_code: 200
register: uri_result
until: uri_result is not failed
retries: 12
delay: 10
- name: Post documents to solr
command: /blah/solr/bin/post -c master /exampledocs/books.csv
Coloque lógica complexa em módulos e plug-ins
Ansible defende uma abordagem declarativa, portanto, quando você faz ramificação de código, transformação de dados, módulos shell, o código fica difícil de ler. Para combater isto e mantê-lo simples de compreender, não seria supérfluo combater esta complexidade criando os seus próprios módulos.
Resuma dicas e truques
Evite variáveis globais.
Variáveis de função de prefixo.
Use variável de controle de loop.
Verifique as variáveis de entrada.
Evite dicionários de hashes, use estrutura plana.
Crie manuais e funções idempotentes.
Evite usar módulos de shell de comando.
Teste seus papéis via molécula.
Coloque lógica complexa em módulos e plug-ins.
Conclusão
Você não pode simplesmente refatorar a infraestrutura em um projeto, mesmo se tiver IaC. Este é um processo longo que requer paciência, tempo e conhecimento.
UPD1 2020.05.01 20:30 — Para criação de perfil primário de manuais que você pode usar callback_whitelist = profile_tasks para entender o que exatamente funciona por um longo tempo. Então passamos Clássicos de aceleração Ansible. Você também pode tentar mitógeno UPD2 2020.05.03 16:34 - Versão em Inglês