Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Questa è la trascrizione spettacoli su DevOps-40 2020-03-18:

A partire dal secondo commit, qualsiasi codice diventa legacy, perché le idee iniziali iniziano a divergere dalla dura realtà. Questo non è né buono né cattivo, è un dato con cui è difficile discutere e con cui bisogna convivere. Parte di questo processo è il refactoring. Refactoring dell'infrastruttura come codice. Lascia che la storia abbia inizio su come eseguire il refactoring di Ansible in un anno e non impazzire.

La nascita dell'eredità

Giorno n. 1: Paziente Zero

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

C'era una volta un progetto condizionale. Aveva un team di sviluppo e ingegneri operativi. Stavano risolvendo lo stesso problema: come distribuire i server ed eseguire un'applicazione. Il problema era che ogni squadra ha risolto questo problema a modo suo. Nel progetto si è deciso di utilizzare Ansible per sincronizzare le conoscenze tra i team di sviluppo e operativi.

Giorno #89: La nascita dell'eredità

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Senza accorgersene, volevano farlo nel miglior modo possibile, ma si è rivelato essere un'eredità. Come avviene questo?

  • Abbiamo un compito urgente qui, facciamo un trucco sporco e poi risolviamolo.
  • Non devi scrivere documentazione e tutto è chiaro cosa sta succedendo qui.
  • Conosco Ansible/Python/Bash/Terraform! Guarda come posso schivare!
  • Sono uno sviluppatore Full Stack Overflow e l'ho copiato da StackOverflow, non so come funziona, ma sembra interessante e risolve il problema.

Di conseguenza, puoi ottenere un tipo di codice incomprensibile per il quale non esiste documentazione, non è chiaro cosa faccia, se sia necessario, ma il problema è che devi svilupparlo, modificarlo, aggiungere stampelle e supporti , peggiorando ulteriormente la situazione.

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

Giorno n. 109: Consapevolezza del problema

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Il modello IaC inizialmente concepito e implementato non soddisfa più i requisiti di utenti/azienda/altri team e il tempo necessario per apportare modifiche all’infrastruttura non è più accettabile. In questo momento si capisce che è tempo di agire.

Refactoring IaC

Giorno n. 139: hai davvero bisogno del refactoring?

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Prima di affrettarti a eseguire il refactoring, devi rispondere a una serie di domande importanti:

  1. Perché hai bisogno di tutto questo?
  2. Hai tempo?
  3. La conoscenza è sufficiente?

Se non sai come rispondere alle domande, il refactoring finirà prima ancora di iniziare, o potrebbe solo peggiorare. Perché avuto esperienza ( Cosa ho imparato testando 200 righe di codice dell'infrastruttura), poi il progetto ha ricevuto una richiesta di aiuto per fissare i ruoli e coprirli con dei test.

Giorno n. 149: preparazione del refactoring

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

La prima cosa è prepararsi. Decidi cosa faremo. Per fare ciò, comunichiamo, individuiamo aree problematiche e scopriamo modi per risolverle. Registriamo in qualche modo i concetti risultanti, ad esempio un articolo in confluenza, in modo che quando sorge la domanda "cosa è meglio?" o "quale è corretto?" Non abbiamo perso la strada. Nel nostro caso siamo rimasti fedeli all’idea dividi e governa: scomponiamo l'infrastruttura in piccoli pezzi/mattoni. Questo approccio consente di prendere un pezzo isolato di infrastruttura, capire cosa fa, coprirlo con test e modificarlo senza timore di rompere nulla.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Si scopre che il test dell'infrastruttura diventa la pietra angolare e qui vale la pena menzionare la piramide del test dell'infrastruttura. Esattamente la stessa idea che è in fase di sviluppo, ma per l'infrastruttura: stiamo passando da test rapidi ed economici che controllano cose semplici, come l'indentazione, a test costosi e completi che implementano l'intera infrastruttura.

Tentativi di test Ansible

Prima di andare a descrivere come abbiamo coperto i test Ansible sul progetto, descriverò i tentativi e gli approcci che ho avuto modo di utilizzare in precedenza per comprendere il contesto delle decisioni prese.

Giorno n. -997: fornitura della scheda di sicurezza

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

La prima volta che ho testato Ansible era su un progetto per sviluppare SDS (Software Defined Storage). C'è un articolo separato su questo argomento
Come rompere le biciclette con le stampelle durante il test della distribuzione, ma in breve, ci siamo ritrovati con una piramide di test invertita e nei test abbiamo trascorso 60-90 minuti su un ruolo, che è molto tempo. La base erano i test e2e, vale a dire abbiamo implementato un'installazione completa e poi l'abbiamo testata. Ciò che fu ancora più aggravante fu l'invenzione della propria bicicletta. Ma devo ammettere che questa soluzione ha funzionato e ha consentito un rilascio stabile.

Giorno # -701: Ansible e prova cucina

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Lo sviluppo dell'idea di test Ansible è stato l'uso di strumenti già pronti, vale a dire test kitchen / kitchen-ci e inspec. La scelta è stata determinata dalla conoscenza di Ruby (per maggiori dettagli consultare l'articolo su Habré: I programmatori YML sognano di testare Ansible?) ha funzionato più velocemente, circa 40 minuti per 10 ruoli. Abbiamo creato un pacchetto di macchine virtuali e abbiamo eseguito i test al suo interno.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

In generale, la soluzione ha funzionato, ma c'erano dei sedimenti dovuti all'eterogeneità. Quando il numero di persone testate è stato aumentato a 13 ruoli base e 2 meta-ruoli che combinavano ruoli più piccoli, improvvisamente i test hanno iniziato a durare 70 minuti, ovvero quasi 2 volte di più. Era difficile parlare di pratiche XP (programmazione estrema) perché... nessuno vuole aspettare 70 minuti. Questo è stato il motivo per cambiare approccio

Giorno #-601: Ansible e la molecola

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Concettualmente è simile a testkitchen, solo che abbiamo spostato il testing dei ruoli nella finestra mobile e modificato lo stack. Di conseguenza, il tempo è stato ridotto a 20-25 minuti stabili per 7 ruoli.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Aumentando il numero di ruoli testati a 17 e inserendo 45 ruoli, l'abbiamo eseguito in 28 minuti su 2 Jenkins Slave.

Giorno n. 167: aggiunta dei test Ansible al progetto

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Molto probabilmente non sarà possibile eseguire l'attività di refactoring in fretta. Il compito deve essere misurabile in modo da poterlo spezzare in piccoli pezzi e mangiare l'elefante pezzo per pezzo con un cucchiaino. È necessario capire se ti stai muovendo nella giusta direzione, quanto tempo manca.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

In generale, non importa come verrà fatto, puoi scrivere su un pezzo di carta, puoi mettere adesivi sull'armadio, puoi creare attività in Jira oppure puoi aprire Google Docs e annotare lo stato attuale Là. Le gambe crescono perché il processo non è immediato, sarà lungo e noioso. È improbabile che qualcuno voglia che tu esaurisca le idee, ti stanchi e ti senta sopraffatto durante il refactoring.

Il refactoring è semplice:

  • Mangiare.
  • Dormire.
  • Codice.
  • Prova IaC.
  • Ripetere

e lo ripetiamo finché non raggiungiamo l'obiettivo prefissato.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Potrebbe non essere possibile iniziare a testare tutto subito, quindi il nostro primo compito è stato iniziare con l'inlinting e il controllo della sintassi.

Giorno n. 181: Maestro della costruzione verde

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Il Linting è un piccolo primo passo verso Green Build Master. Ciò non romperà quasi nulla, ma ti consentirà di eseguire il debug dei processi e creare build ecologiche in Jenkins. L’idea è quella di sviluppare abitudini all’interno del team:

  • I test rossi sono cattivi.
  • Sono venuto per sistemare qualcosa e allo stesso tempo rendere il codice un po' migliore rispetto a prima di te.

Giorno n. 193: dallo sfilacciamento ai test unitari

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Dopo aver creato il processo per inserire il codice nel master, puoi iniziare il processo di miglioramento passo passo: sostituendo il linting con i ruoli di avvio, puoi farlo anche senza idempotenza. È necessario comprendere come applicare i ruoli e come funzionano.

Giorno #211: Dall'unità ai test di integrazione

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Quando la maggior parte dei ruoli è coperta da test unitari e tutto è collegato, è possibile passare all'aggiunta di test di integrazione. Quelli. testare non un singolo elemento dell'infrastruttura, ma una combinazione di essi, ad esempio una configurazione completa dell'istanza.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Utilizzando Jenkins, abbiamo generato molte fasi che collegavano ruoli/playbook in parallelo, quindi test unitari in contenitori e infine test di integrazione.

Jenkins + Docker + Ansible = Test

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

  1. Acquista il repository e genera fasi di creazione.
  2. Esegui le fasi del playbook lint in parallelo.
  3. Esegui le fasi del ruolo lint in parallelo.
  4. Esegui le fasi del ruolo di controllo della sintassi in parallelo.
  5. Esegui le fasi del ruolo di test in parallelo.
    1. Ruolo di lanugine.
    2. Controlla la dipendenza da altri ruoli.
    3. Controlla la sintassi.
    4. Crea un'istanza della finestra mobile
    5. Esegui molecola/default/playbook.yml.
    6. Controlla l'idempotenza.
  6. Esegui test di integrazione
  7. Fine

Giorno n.271: Fattore autobus

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Inizialmente il refactoring è stato effettuato da un piccolo gruppo di due o tre persone. Hanno rivisto il codice nel master. Nel corso del tempo, il team ha sviluppato conoscenze su come scrivere codice e la revisione del codice ha contribuito alla diffusione della conoscenza sull'infrastruttura e sul suo funzionamento. Il punto forte qui è che i revisori sono stati selezionati uno per uno, secondo un programma, ad es. con un certo grado di probabilità entrerai in una nuova infrastruttura.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

E dovrebbe essere comodo qui. È conveniente fare una revisione, vedere nell'ambito di quale compito è stato svolto e la cronologia delle discussioni. Abbiamo integrato jenkins + bitbucket + jira.

Ma in quanto tale, una revisione non è una panacea; in qualche modo, siamo entrati nel codice principale, che ci ha fatto fallire i test:

- 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 }}"

Poi l'hanno riparato, ma il sedimento è rimasto.

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 }}"

Giorno n. 311: Accelerazione dei test

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Nel corso del tempo, ci furono più test, le build funzionarono più lentamente, fino a un'ora nel peggiore dei casi. Su uno dei retro c'era una frase del tipo "è positivo che ci siano dei test, ma sono lenti". Di conseguenza, abbiamo abbandonato i test di integrazione sulle macchine virtuali e li abbiamo adattati a Docker per renderlo più veloce. Abbiamo anche sostituito testinfra con ansible verifier per ridurre il numero di strumenti utilizzati.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

A rigor di termini, c'era una serie di misure:

  1. Passa alla finestra mobile.
  2. Rimuovere il test dei ruoli, che è duplicato a causa delle dipendenze.
  3. Aumenta il numero degli schiavi.
  4. Ordine di esecuzione dei test.
  5. Capacità di sfilacciarsi TUTTI localmente con un comando.

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Di conseguenza, anche Pipeline su Jenkins è stata unificata

  1. Genera fasi di costruzione.
  2. Lint tutto in parallelo.
  3. Esegui le fasi del ruolo di test in parallelo.
  4. Fine.

Le lezioni apprese

Evita le variabili globali

Ansible utilizza variabili globali, nel modulo è presente una soluzione parziale private_role_vars, ma questa non è una panacea.

Lasciate che vi faccia un esempio. Facciamolo 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}}

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

La cosa divertente è che il risultato dei playbook dipenderà da cose che non sono sempre ovvie, come l’ordine in cui sono elencati i ruoli. Sfortunatamente, questa è la natura di Ansible e la cosa migliore che si può fare è utilizzare una sorta di accordo, ad esempio, all'interno di un ruolo, utilizzare solo la variabile descritta in questo ruolo.

BAD: usa la variabile globale.

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

BUONA: V defaults definire le variabili necessarie e successivamente utilizzarle solo.

# 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

Prefisso delle variabili di ruolo

BAD: usa la variabile globale.

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

BUONA: Nei ruoli per le variabili, utilizzare variabili con prefisso il nome del ruolo; questo, guardando l'inventario, renderà più semplice capire cosa sta succedendo.

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

Utilizza la variabile di controllo del loop

BAD: utilizza la variabile standard nei cicli item, se questa attività/playbook è inclusa da qualche parte, ciò potrebbe causare un comportamento imprevisto

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

BUONA: Ridefinire una variabile in un ciclo tramite loop_var.

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

Controllare le variabili di input

Abbiamo concordato di utilizzare prefissi variabili; non sarebbe superfluo verificare che siano definiti come ci aspettiamo e, ad esempio, non siano sovrascritti da un valore vuoto

BUONA: Controlla le variabili.

- 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

Evita i dizionari hash, usa la struttura piatta

Se un ruolo prevede un hash/dizionario in uno dei suoi parametri, se vogliamo modificare uno dei parametri figli, dovremo sovrascrivere l'intero hash/dizionario, il che aumenterà la complessità della configurazione.

BAD: usa hash/dizionario.

---
user:
  name: admin
  group: admin

BUONA: Utilizza una struttura variabile piatta.

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

Crea playbook e ruoli idempotenti

I ruoli e i playbook devono essere idempotenti, perché riduce la deriva della configurazione e la paura di rompere qualcosa. Ma se usi la molecola, questo è il comportamento predefinito.

Evitare l'uso dei moduli della shell dei comandi

L'utilizzo di un modulo shell si traduce in un paradigma di descrizione imperativo, invece di quello dichiarativo, che è il nucleo di Ansible.

Metti alla prova i tuoi ruoli tramite la molecola

La molecola è una cosa molto flessibile, diamo un'occhiata ad alcuni scenari.

Molecola Istanze multiple

В molecule.yml nella sezione platforms puoi descrivere molti host che puoi distribuire.

---
    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

Di conseguenza, questi host possono quindi essere converge.yml uso:

---
- 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

Verificatore Ansible

In molecola è possibile utilizzare ansible per verificare che l'istanza sia stata configurata correttamente, inoltre questo è il default dalla release 3. Non è flessibile come testinfra/inspec, ma possiamo verificare che il contenuto del file corrisponda alle nostre aspettative:

---
- 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

Oppure distribuisci il servizio, attendi che diventi disponibile ed esegui un test del fumo:

---
  - 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

Inserisci logica complessa in moduli e plugin

Ansible sostiene un approccio dichiarativo, quindi quando si esegue la ramificazione del codice, la trasformazione dei dati e i moduli shell, il codice diventa difficile da leggere. Per combattere questo problema e mantenerlo semplice da comprendere, non sarebbe superfluo combattere questa complessità creando i propri moduli.

Riepilogare suggerimenti e trucchi

  1. Evita le variabili globali.
  2. Prefisso delle variabili di ruolo.
  3. Utilizza la variabile di controllo del loop.
  4. Controllare le variabili di input.
  5. Evita i dizionari hash, usa la struttura piatta.
  6. Crea playbook e ruoli idempotenti.
  7. Evitare l'uso dei moduli della shell dei comandi.
  8. Metti alla prova i tuoi ruoli tramite la molecola.
  9. Inserisci logica complessa in moduli e plugin.

conclusione

Come iniziare a testare Ansible, rifattorizzare il progetto in un anno e non impazzire

Non puoi semplicemente procedere al refactoring dell'infrastruttura su un progetto, anche se disponi di IaC. Questo è un processo lungo che richiede pazienza, tempo e conoscenza.

UPD1 2020.05.01 20:30 — Per la profilazione primaria dei playbook che puoi utilizzare callback_whitelist = profile_tasks per capire cosa funziona esattamente per molto tempo. Poi procediamo Classici dell'accelerazione Ansible. Puoi anche provare mitogeno
UPD2 2020.05.03 16:34 - English version

Fonte: habr.com

Aggiungi un commento