Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

Esta é a transcrição performances em DevOps-40 2020/03/18:

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

- hosts: localhost
  tasks:
    - shell: echo -n Z >> a.txt && cat a.txt
      register: output
      delay: 1
      retries: 5
      until: not output.stdout.find("ZZZ")

Dia #109: Conscientização do problema

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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?

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

Antes de se apressar para refatorar, você deve responder a uma série de perguntas importantes:

  1. Por que você precisa de tudo isso?
  2. Tens tempo?
  3. 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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

  1. Faça check-out do repositório e gere estágios de construção.
  2. Execute os estágios do manual do lint em paralelo.
  3. Execute estágios de função lint em paralelo.
  4. Execute estágios de função de verificação de sintaxe em paralelo.
  5. Execute estágios de função de teste em paralelo.
    1. Papel de fiapos.
    2. Verifique a dependência de outras funções.
    3. Verifique a sintaxe.
    4. Criar instância do Docker
    5. Execute molécula/default/playbook.yml.
    6. Verifique a idempotência.
  6. Execute testes de integração
  7. Acabamento

Dia #271: Fator de ônibus

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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:

- get_url:
    url: "{{ actk_certs }}/{{ item.1 }}"
    dest: "{{ actk_src_tmp }}/"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ item.1 }}"
    dest: "{{ actk_dst_tmp }}"
  with_subelements:
    - "{{ actk_cert_list }}"
    - "{{ actk_certs }}"

Aí eles consertaram, mas o resíduo permaneceu.

get_url:
    url: "{{ actk_certs }}/{{ actk_item }}"
    dest: "{{ actk_src_tmp }}/{{ actk_item }}"
    username: "{{ actk_mvn_user }}"
    password: "{{ actk_mvn_pass }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"
  delegate_to: localhost

- copy:
    src: "{{ actk_src_tmp }}/{{ actk_item }}"
    dest: "{{ actk_dst_tmp }}"
  loop_control:
    loop_var: actk_item
  with_items: "{{ actk_cert_list }}"

Dia #311: Acelerando os testes

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

A rigor, houve um conjunto de medidas:

  1. Mude para a janela de encaixe.
  2. Remova o teste de função, que está duplicado devido a dependências.
  3. Aumentar o número de escravos.
  4. Ordem de execução de teste.
  5. Capacidade de fiapos ALL localmente com um comando.

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

Como resultado, o Pipeline no Jenkins também foi unificado

  1. Gere estágios de construção.
  2. Lint tudo em paralelo.
  3. Execute estágios de função de teste em paralelo.
  4. 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

# cat role_a/defaults/main.yml
---
msg: a

# cat role_a/tasks/main.yml
---
- debug:
    msg: role_a={{ msg }}

# cat role_b/defaults/main.yml
---
msg: b

# cat role_b/tasks/main.yml
---
- set_fact:
    msg: b
- debug:
    msg: role_b={{ msg }}

- hosts: localhost
  vars:
    msg: hello
  roles:
    - role: role_a
    - role: role_b
  tasks:
    - debug:
        msg: play={{msg}}

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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.

BAD: use variável global.

# cat roles/some_role/tasks/main.yml
---
debug:
  var: java_home

BOM: V defaults defina as variáveis ​​​​necessárias e posteriormente utilize apenas elas.

# cat roles/some_role/defaults/main.yml
---
r__java_home:
 "{{ java_home | default('/path') }}"

# cat roles/some_role/tasks/main.yml
---
debug:
  var: r__java_home

Variáveis ​​de função de prefixo

BAD: use variável global.

# cat roles/some_role/defaults/main.yml
---
db_port: 5432

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.

# cat roles/some_role/defaults/main.yml
---
some_role__db_port: 5432

Use variável de controle de loop

BAD: Use variável padrão em loops item, se esta tarefa/manual estiver incluída em algum lugar, isso poderá levar a um comportamento inesperado

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item }}"
      loop:
        - item1
        - item2

BOM: Redefina uma variável em um loop via loop_var.

---
- hosts: localhost
  tasks:
    - debug:
        msg: "{{ item_name }}"
      loop:
        - item1
        - item2
      loop_control:
        loop_var: item_name

Verifique variáveis ​​de entrada

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.

BAD: Use hash/dicionário.

---
user:
  name: admin
  group: admin

BOM: Use uma estrutura variável plana.

---
user_name: admin
user_group: "{{ user_name }}"

Crie manuais e funções idempotentes

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.

---
    driver:
      name: docker
    platforms:
      - name: postgresql-instance
        hostname: postgresql-instance
        image: registry.example.com/postgres10:latest
        pre_build_image: true
        override_command: false
        network_mode: host
      - name: app-instance
        hostname: app-instance
        pre_build_image: true
        image: registry.example.com/docker_centos_ansible_tests
        network_mode: host

Assim, esses hosts podem então ser converge.yml use:

---
- name: Converge all
  hosts: all
  vars:
    ansible_user: root
  roles:
    - role: some_role

- name: Converge db
  hosts: db-instance
  roles:
    - role: some_db_role

- name: Converge app
  hosts: app-instance
  roles:
    - role: some_app_role

Verificador Ansible

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:

---
- name: Verify
  hosts: all
  tasks:
    - name: copy config
      copy:
        src: expected_standalone.conf
        dest: /root/wildfly/bin/standalone.conf
        mode: "0644"
        owner: root
        group: root
      register: config_copy_result

    - name: Certify that standalone.conf changed
      assert:
        that: not config_copy_result.changed

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

  1. Evite variáveis ​​globais.
  2. Variáveis ​​de função de prefixo.
  3. Use variável de controle de loop.
  4. Verifique as variáveis ​​de entrada.
  5. Evite dicionários de hashes, use estrutura plana.
  6. Crie manuais e funções idempotentes.
  7. Evite usar módulos de shell de comando.
  8. Teste seus papéis via molécula.
  9. Coloque lógica complexa em módulos e plug-ins.

Conclusão

Como começar a testar o Ansible, refatorar o projeto em um ano e não enlouquecer

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

Fonte: habr.com

Adicionar um comentário