Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Approccio IaC (Infrastructure as Code) non consiste solo nel codice archiviato nel repository, ma anche nelle persone e nei processi che circondano quel codice. È possibile riutilizzare gli approcci dello sviluppo software nella gestione e nella descrizione dell'infrastruttura? Tenere a mente questa idea mentre leggete l'articolo non farebbe male.

Versione in inglese

Questa è la registrazione del mio dell'esibizione con DevopsConf 2019-05-28.

Diapositive e video

Infrastructure as bash history

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Immaginate di arrivare in un nuovo progetto e che vi dicono: “da noi Infrastructure as Code“. In realtà, si scopre che Infrastructure as bash history oppure ad esempio Documentation as bash history. Questa è una situazione del tutto reale, come descritto da Denis Lysenko nel suo intervento Come sostituire l'intera infrastruttura e iniziare a dormire tranquilli, ha raccontato come dalla bash history siano riusciti a ottenere una solida infrastruttura nel progetto.

Con un po' di volontà, si può dire che Infrastructure as bash history è come il codice:

  1. riproducibilità: puoi prendere la bash history, eseguire i comandi da lì e, a proposito, potresti ottenere una configurazione funzionante in uscita.
  2. versionamento: sai chi chi è entrato e cosa ha fatto, comunque non è sicuro che questo ti porti a una configurazione funzionante.
  3. storia: una cronologia di chi ha fatto cosa. ma non potrai usarla se perdi il server.

Cosa fare?

Infrastructure as Code

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Persino un caso così strano come Infrastructure as bash history può essere forzato a Infrastructure as Code, ma quando vorrà fare qualcosa di più complesso di un vecchio e fidato server LAMP, arriveremo al punto in cui questo codice deve essere modificato, cambiato, migliorato. Inoltre, vorremmo esaminare le somiglianze tra Infrastructure as Code e lo sviluppo software.

D.R.Y.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Nel progetto per lo sviluppo di un sistema di archiviazione, c'era un sotto-compito di configurare periodicamente l'SDS: stiamo rilasciando una nuova versione — deve essere distribuita per ulteriori test. Il compito è estremamente semplice:

  • collegati qui via ssh e esegui il comando.
  • copia il file lì.
  • qui modifica il config.
  • lì avvia il servizio
  • PROFIT!

Per la logica descritta, bash è più che sufficiente, soprattutto nelle fasi iniziali del progetto, quando sta appena iniziando. Non è male che tu stia usando bash. non è male che tu stia usando bash, ma col passare del tempo emergono richieste per espandere qualcosa di simile, ma leggermente diverso. La prima cosa che viene in mente: copia e incolla. E ora abbiamo già due script molto simili che fanno quasi la stessa cosa. Col tempo il numero di script è aumentato, e ci siamo trovati di fronte a una certa logica di business per il dispiegamento delle installazioni che deve essere sincronizzata tra gli script diversi, il che è piuttosto complesso.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Si scopre che esiste una pratica denominata D.R.Y. (Do not Repeat Yourself). L'idea è di riutilizzare il codice esistente. Sembra semplice, ma non ci siamo arrivati subito. Nel nostro caso è stata una semplice idea: separare le configurazioni dagli script. Cioè, la logica di business di come avviene il dispiegamento delle installazioni è separata, le configurazioni sono separate.

S.O.L.I.D. per CFM

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Col passare del tempo il progetto è cresciuto e una naturale continuazione è stata l'emergere di Ansible. La principale ragione della sua nascita è la presenza di competenze nel team e che bash non è adatto per logiche complesse. Ansible ha cominciato a contenere logiche complesse. Affinché la logica complessa non si trasformasse in caos, nello sviluppo software esistono principi di organizzazione del codice. S.O.L.I.D. Ad esempio, Grigorij Petrov nel suo rapporto "Perché un IT manager ha bisogno di un marchio personale" ha sollevato la questione del fatto che gli esseri umani sono strutturati in modo tale da operare più facilmente con entità sociali, che nel contesto dello sviluppo software si traducono in oggetti. Se uniamo queste due idee e continuiamo a svilupparle, possiamo notare che anche nella descrizione dell'infrastruttura è possibile utilizzare S.O.L.I.D. per rendere più facile in futuro mantenere e modificare questa logica.

Il Principio di Responsabilità Singola

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Ogni classe ha un solo compito.

Non è necessario mescolare il codice e creare mostri di pasta divina monolitici. L'infrastruttura dovrebbe essere costituita da semplici mattoni. Si scopre che se si suddivide un playbook di Ansible in piccole parti, leggasi ruoli di Ansible, è più facile mantenerli.

Il Principio di Apertura e Chiusura

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Principio di apertura/chiusura.

  • Aperto per estensione: significa che il comportamento di un'entità può essere esteso creando nuovi tipi di entità.
  • Chiuso per modifica: di conseguenza, a seguito dell'estensione del comportamento di un'entità, non dovrebbero essere apportate modifiche al codice che utilizza tali entità.

Inizialmente abbiamo distribuito l'infrastruttura di test su macchine virtuali, ma poiché la logica aziendale di distribuzione era separata dall'implementazione, siamo stati in grado di aggiungere senza problemi il deployment su baremetal.

Il Principio di Sostituzione di Liskov

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Il principio di sostituzione di Barbara Liskov afferma che gli oggetti in un programma devono essere sostituibili con istanze dei loro sottotipi senza modificare la correttezza dell'esecuzione del programma.

Se si guarda più in dettaglio, non è una caratteristica di un progetto specifico, ma qualcosa che può essere applicato in generale. S.O.L.I.D., in effetti riguarda CFM; ad esempio, in un altro progetto è necessario distribuire un'applicazione Java standard su vari server Java, server applicazioni, database, OS, ecc. In questo esempio esaminerò ulteriori principi. S.O.L.I.D.

Nel nostro caso, all'interno del team infrastrutturale, c'è un accordo che se installiamo il ruolo imbjava o oraclejava, abbiamo un file eseguibile binario java. Questo è necessario poiché i ruoli superiori dipendono da questo comportamento, si aspettano la presenza di java. Allo stesso tempo, questo ci permette di sostituire un'implementazione/versione di java con un'altra senza cambiare la logica di distribuzione dell'applicazione.

Il problema qui sta nel fatto che in Ansible non è possibile implementare simili deduzioni, per cui nella squadra emergono alcune intese.

Il principio di separazione delle interfacce

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Il principio di separazione delle interfacce: "molte interfacce specificamente progettate per i clienti sono migliori di un'unica interfaccia generica".

Inizialmente abbiamo provato a concentrare tutta la variabilità di distribuzione dell'applicazione in un unico playbook di Ansible, ma era difficile da mantenere, mentre l'approccio in cui abbiamo specificato l'interfaccia esterna (il cliente si aspetta la porta 443) consente di comporre l'infrastruttura da singoli mattoni per la specifica implementazione.

Il principio di inversione delle dipendenze

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Il principio di inversione delle dipendenze: i moduli di livello superiore non dovrebbero dipendere dai moduli di livello inferiore. Entrambi i tipi di moduli dovrebbero dipendere da astrazioni. Le astrazioni non dovrebbero dipendere dai dettagli. I dettagli devono dipendere dalle astrazioni.

In questo caso, l'esempio sarà basato su un antipattern.

  1. Uno dei nostri clienti aveva un cloud privato.
  2. All'interno del cloud, ordinavamo macchine virtuali.
  3. Ma a causa delle peculiarità del cloud, la distribuzione dell'applicazione era legata al tipo di hypervisor su cui si trovava la VM.

Cioè, la logica di alto livello per il deployment dell'applicazione, con le sue dipendenze, scende ai livelli sottostanti del hypervisor, e questo crea problemi nel riutilizzo di tale logica. Non farlo.

Interazione

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

L'infrastruttura come codice non riguarda solo il codice, ma anche le relazioni tra il codice e l'uomo, le interazioni tra gli sviluppatori dell'infrastruttura.

Fattore bus

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Immagina di avere un progetto con Vasya. Vasya sa tutto sulla tua infrastruttura; cosa succede se Vasya improvvisamente scompare? Questa è una situazione molto reale, poiché potrebbe essere investito da un autobus. A volte succede. Se accade e la conoscenza del codice, della sua struttura, di come funziona, degli accessi e delle password non è distribuita all'interno del team, si possono affrontare una serie di situazioni spiacevoli. Per minimizzare questi rischi e distribuire la conoscenza nel team, si possono utilizzare diversi approcci.

DevOps in coppia

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Non è come nella barzelletta, che gli amministratori bevevano birra, cambiavano le password, e l'analogo della programmazione in coppia. Ovvero, due ingegneri si siedono a un computer, una tastiera e iniziano a configurare insieme la vostra infrastruttura: configurare il server, scrivere il ruolo Ansible, ecc. Suona bene, ma a noi non ha funzionato. Tuttavia, alcuni casi specifici di questa pratica hanno avuto successo. Quando è arrivato un nuovo dipendente, il suo mentore prende un compito reale e lavora con lui, trasferendo sapere.

Un altro caso specifico è la chiamata per incidenti. Durante un problema, si riunisce un gruppo di guardia e persone coinvolte, e viene nominato un leader che condivide il suo schermo e comunica il suo ragionamento. Gli altri partecipanti seguono il pensiero del leader, sbirciando trucchi dalla console, controllando se non ha saltato righe nei log e apprendendo cose nuove sul sistema. Questo approccio ha funzionato più che non.

Code Review

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Soggettivamente, la diffusione delle conoscenze sull'infrastruttura e su come è strutturata è avvenuta in modo più efficace tramite le revisioni del codice:

  • L'infrastruttura è descritta nel codice nel repository.
  • Le modifiche avvengono in un ramo separato.
  • Durante la richiesta di merge, è possibile vedere la delta delle modifiche all'infrastruttura.

Il punto forte era che i revisori venivano scelti a turno, secondo un programma, quindi con una certa probabilità avresti potuto esplorare una nuova parte dell'infrastruttura.

Stile di Codice

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Col tempo, sono iniziate a sorgere delle liti durante le revisioni, poiché ogni revisore aveva il proprio stile e la rotazione dei revisori mischiava stili diversi: 2 spazi o 4, camelCase o snake_case. Non è stato facile implementare questa cosa.

  • L'idea iniziale era di consigliare di utilizzare un linter, perché alla fine sono tutti ingegneri e intelligenti. Ma diversi editor, sistemi operativi, era scomodo.
  • Questo si è evoluto in un bot che avvisava ogni volta che c'era un commit problematico in Slack, allegando l'output del linter. Ma nella maggior parte dei casi c'erano questioni più importanti e il codice rimaneva non corretto.

Green Build Master

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Col tempo, siamo arrivati alla conclusione che non era possibile permettere commit in master che non passavano determinati test. Ecco! Abbiamo inventato il Green Build Master, che è già ampiamente praticato nello sviluppo software:

  • Lo sviluppo avviene in un ramo separato.
  • Su questo ramo vengono eseguiti i test.
  • Se i test falliscono, il codice non entrerà nel master.

Prendere questa decisione è stato piuttosto doloroso, poiché ha sollevato molte controversie, ma ne è valsa la pena, poiché le richieste di fusione per la revisione sono iniziate a essere presentate senza disaccordi sullo stile e, nel tempo, il numero di punti problematici ha cominciato a diminuire.

Testing IaC

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Oltre a controllare lo stile, si possono utilizzare anche altre cose, come verificare che la vostra infrastruttura possa effettivamente essere implementata. Oppure controllare che le modifiche all'infrastruttura non comportino perdite economiche. Perché potrebbe essere necessario? La domanda è complessa e filosofica; si può rispondere meglio con un aneddoto: c'era una volta un auto-scaler su Powershell che, non controllando le condizioni limite, creò più VM di quante ce ne volessero, e il cliente spese più denaro di quanto avesse programmato. Non è piacevole, ma questo errore avrebbe potuto essere catturato nelle fasi precedenti.

Si potrebbe chiedere: perché complicare ulteriormente un'infrastruttura già complessa? I test per l'infrastruttura, così come per il codice, non riguardano la semplificazione, ma la consapevolezza di come deve funzionare la vostra infrastruttura.

Piramide di Testing IaC

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Testing IaC: Analisi Statica

Se si decide di implementare subito tutta l'infrastruttura e verificare che funzioni, potrebbe rivelarsi molto dispendioso in termini di tempo. Per questo motivo, è essenziale avere qualcosa che funzioni in modo rapido, in grande quantità e che copra molte aree critiche.

Bash è complesso.

Ecco un esempio banale: selezionare tutti i file nella directory corrente e copiarli in un'altra posizione. La prima cosa che ci viene in mente è:

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

E se il nome del file contiene uno spazio? Va bene, siamo intelligenti, sappiamo usare le virgolette:

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

Bravi? No! E se nella directory non ci fosse niente, cioè il globbing non funziona.

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

Ora siete bravi? No… Abbiamo dimenticato che nel nome del file potrebbe esserci n.

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

Strumenti di analisi statica

Il problema del passo precedente sarebbe stato rilevato quando abbiamo dimenticato le virgolette; a questo scopo esistono numerosi strumenti. Shellcheck, ce ne sono molti, e probabilmente riuscirete a trovare un linter per il vostro stack nella vostra IDE.

Lingua
Strumento

bash
Shellcheck

Ruby
RuboCop

python
Pylint

ansible
Ansible Lint

Testing IaC: Test Unitari

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Come abbiamo visto nell'esempio precedente, i linter non sono onnipotenti e non possono individuare tutti i problemi. A questo punto, in analogia con il testing nello sviluppo software, possiamo ricordare i test unitari. Qui ci viene subito in mente shunit, junit, rspec, pytest. Ma cosa fare con ansible, chef, saltstack e simili?

All'inizio abbiamo parlato di S.O.L.I.D. e di come la nostra infrastruttura dovrebbe consistere in piccoli mattoni. È arrivato il loro momento.

  1. L'infrastruttura si frammenta in piccoli mattoni, ad esempio, ruoli di Ansible.
  2. Un certo ambiente viene implementato, che sia docker o una VM.
  3. Su questo ambiente di test applichiamo il nostro ruolo Ansible.
  4. Verifichiamo che tutto funzioni come ci aspettiamo (eseguiamo i test).
  5. Decidiamo se va bene o meno.

IaC Testing: strumenti per test unitari

La domanda è: cosa sono i test per CFM? possiam semplicemente eseguire uno script, oppure possiamo utilizzare soluzioni pronte per questo:

CFM
Strumento

Ansible
Testinfra

Chef
Inspec

Chef
Serverspec

saltstack
Goss

Esempio per testinfra, verifichiamo che gli utenti test1, test2 esistano e appartengano al gruppo 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

Cosa scegliere? È una domanda complessa e non univoca; ecco un esempio di cambiamenti nei progetti su GitHub per il periodo 2018-2019:

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Framework di test IaC

Come mettere tutto insieme e avviarlo? Si può prendere e farlo da soli se si ha un numero sufficiente di ingegneri. Oppure si possono adottare soluzioni pronte, anche se non ce ne sono molte:

CFM
Strumento

Ansible
Molecule

Chef
Test Kitchen

Terraform
Terratest

Esempio di cambiamenti nei progetti su GitHub per il periodo 2018-2019:

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Molecule vs. Testkitchen

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

All'inizio abbiamo cercato di usare testkitchen:

  1. Creare una VM in parallelo.
  2. Applicare i ruoli Ansible.
  3. Eseguire inspec.

Per 25-35 ruoli, questo impiegava 40-70 minuti, il che era lungo.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Il passo successivo è stato passare a Jenkins / Docker / Ansible / Molecule. Ideologicamente è tutto lo stesso.

  1. Lintare i playbook.
  2. Lintare i ruoli.
  3. Avviare il contenitore
  4. Applicare i ruoli Ansible.
  5. Eseguire testinfra.
  6. Verificare l'idempotenza.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Lintare per 40 ruoli e testare per una decina ora richiede circa 15 minuti.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

La scelta dipende da molti fattori, come lo stack utilizzato, l'expertise del team ecc. Qui ognuno decide come affrontare la questione del testing unitario.

Testing IaC: Test di integrazione

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Nella prossima fase della piramide di testing dell'infrastruttura, appariranno i test di integrazione. Sono simili ai test unitari:

  1. L'infrastruttura è suddivisa in piccoli mattoni, come i ruoli di Ansible.
  2. Un certo ambiente viene implementato, che sia docker o una VM.
  3. Questo ambiente di test applica molti ruoli di Ansible.
  4. Controlliamo che tutto funzioni come ci aspettiamo (eseguiamo test).
  5. Decidiamo se va bene o meno.

In poche parole, non verifichiamo il funzionamento di un singolo elemento del sistema come nei test unitari, ma controlliamo come è configurato il server nel suo complesso.

IaC Testing: Test End-to-End

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

In cima alla piramide ci attendono i test End-to-End. Cioè, non verifichiamo il funzionamento di un singolo server, di uno specifico script, o di un singolo mattone della nostra infrastruttura. Controlliamo che molti server, uniti insieme, funzionino come ci aspettiamo. Sfortunatamente, non ho mai visto soluzioni pronte all'uso, probabilmente perché l'infrastruttura è spesso unica e difficile da standardizzare e creare un framework per il suo testing. Come risultato, ognuno crea le proprie soluzioni. C'è domanda, ma non ci sono risposte. Pertanto, condividerò ciò che ho, affinché altri possano avere idee sensate o mi possono indicare che tutto è già stato inventato da tempo.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Un progetto con una storia ricca. Viene utilizzato in grandi organizzazioni e probabilmente ognuno di voi è venuto a contatto con esso in un modo o nell'altro. L'applicazione supporta molteplici database, integrazioni, ecc. La conoscenza di come può apparire l'infrastruttura è rappresentata da vari file docker-compose, mentre sapere quali test eseguire in quale ambiente è compito di Jenkins.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Questo schema ha funzionato per un lungo periodo, fino a quando nell'ambito della ricerca non abbiamo provato a trasferirlo in Openshift. I container sono rimasti gli stessi, mentre l'ambiente di esecuzione è cambiato (salve D.R.Y. di nuovo).

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Il pensiero della ricerca è andato oltre, e in Openshift abbiamo trovato qualcosa chiamato APB (Ansible Playbook Bundle), che consente di impacchettare la conoscenza su come distribuire l'infrastruttura in un container. Cioè, c'è un punto conoscitivo riproducibile e testabile su come distribuire l'infrastruttura.

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Tutto ciò suonava bene, finché non ci siamo imbattuti in un'infrastruttura eterogenea: per i test abbiamo bisogno di Windows. Alla fine, la conoscenza su cosa, dove e come distribuire e testare è rimasta in Jenkins.

Conclusione

Cosa ho appreso testando 200.000 righe di codice infrastrutturale.

Infrastructure as Code è

  • Codice nel repository.
  • Interazione tra persone.
  • Test dell'infrastruttura.

link

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster