Sviluppo su Ansible con Molecule, AWS e TravisCI
“Automation is awesome!” – Questo è uno degli slogan che leggo sempre più spesso su blog e articoli che si occupano di DevOps e CI/CD. In effetti credo di essere d’accordo: le possibilità che esistono oggi per automatizzare i processi di integrazione e deployment sono incredibili.
Supponiamo, per esempio, di dover automatizzare l’installazione di Apache su Ubuntu. Scriveremo un ruolo Ansible che svolga tale operazione e lo potremo poi riutilizzare come building block di un processo automatizzato più ampio, come il deployment di una soluzione.
Viene da chiedersi: come testare il ruolo durante il suo sviluppo e i successivi aggiornamenti? Avendo recentemente affrontato una simile challenge, ho avuto modo di realizzare una pipeline di CI utilizzando degli strumenti molto potenti (e gratuiti!). Vediamoli.
Ansible Role
Se non lo abbiamo ancora fatto, preoccupiamoci di installare Ansible.
Andremo a realizzare il nostro script my-httpd nel modo più semplice possibile: creiamo tutte le directory che descrivono normalmente i ruoli Ansible, nell’ambito di un progetto di esempio awesome-ci.
mkdir -p awesome-ci/roles/my-httpd
cd awesome-ci/roles/my-httpd
mkdir defaults files handlers meta templates tasks vars
Nella directory tasks creiamo il file main.yml con il codice Ansible per l’installazione di Apache. Nella directory root del progetto creiamo un playbook.yml che potrà utilizzare il nostro ruolo. Il contenuto dei due file sarà per esempio:
Ora che abbiamo scritto tutto il codice necessario per installare Apache con Ansible ci concentriamo sulla realizzazione della nostra pipeline, iniziando con i primi test.
Code linting
Cos’è il code linting? Wikipedia ci suggerisce essere “uno strumento che analizza il codice sorgente per contrassegnare errori di programmazione, bug, errori stilistici e costrutti sospetti”.
ansible-lint ci consente di effettuare questo tipo di verifica sul nostro playbook e ruolo. Per installarlo si usa PIP:
pip install ansible-lint
Per verificare quanto abbiamo scritto ci spostiamo nella directory root del progetto ed eseguiamo:
ansible-lint playbook.yml
ansible-lint verificherà il contenuto del playbook e dei ruoli utilizzati nei confronti di alcune regole, riportandoci eventuali errori e violazioni di best practices:
[201] Trailing whitespace
/home/awesome-ci/roles/my-httpd/tasks/main.yml:8
name: httpd
Ogni segnalazione riporta un codice identificativo (in questo caso 201). La relativa documentazione descrive cause e soluzioni. Nel mio fortunato caso l’unica segnalazione riportata riguarda uno semplice spazio di troppo alla fine della riga indicata. E’ possibile ignorare alcune regole specificandone i codici identificativi sulla command line.
ansible-lint playbook.yml -x 201
Ogni qualvolta andremo a modificare il nostro ruolo potremo utilizzare ansible-lint per verificare se abbiamo rispettato le opportune best practices. Non fermiamoci però qui! E’ arrivato il momento di verificare se il nostro ruolo fa effettivamente ciò di cui abbiamo bisogno.
Molecule
Molecule è uno strumento pensato per testare i propri ruoli Ansible, in diversi scenari e con diversi sistemi operativi, appoggiandosi a diversi provider di virtualizzazione e Cloud.
Per procedere alla sua installazione si usa PIP. Consiglio però di verificare la documentazione ufficiale per l’installazione dei requirements.
pip install molecule
Ad installazione terminata ci posizioniamo nella directory roles e ci occupiamo di ricreare il nostro ruolo con Molecule. Spostiamo poi il nostro file main.yml nel nuovo ruolo.
# Save old role
mv my-httpd my-httpd-orig
# Init new role using Molecule
molecule init role -r my-httpd -d docker
# Move task file to new role
cp my-httpd-orig/tasks/main.yml my-httpd/tasks/
rm -r my-httpd-orig
Con il comando di inizializzazione del ruolo abbiamo indicato a Molecule di utilizzare docker come environment di test. Spostiamoci nella directory del ruolo e proviamo.
cd my-httpd
molecule test
Purtroppo vedremo subito che il nostro test fallisce durante la fase di linting. Molecule crea infatti un ruolo utilizzando ansible-galaxy, completo quindi di tutte le directory necessarie, META compresa. Quest’ultima andrebbe opportunamente corretta sostituendo le informazioni generiche riportate con quelle relative al nostro ruolo. In alternativa è possibile indicare ad ansible-lint di ignorare le relative regole. Andiamo quindi a modificare il file di configurazione di Molecule.
La directory molecule del nostro ruolo comprende una subdirectory per ciascuno scenario di test. Lo scenario che abbiamo creato è default. In questo folder troviamo il file molecule.yml che riporta la configurazione di ogni fase, compresa quella di linting. Aggiungiamo l’opzione per escludere determinate rules.
provisioner:
name: ansible
lint:
name: ansible-lint
options:
x: [201, 701, 703]
Eseguiamo di nuovo il test: la fase di code linting viene superata con successo ma poi incappiamo in un altro errore.
fatal: [instance]: FAILED! => {"changed": false, "cmd": "apt-get update", "msg": "[Errno 2] No such file or directory", "rc": 2}
Il motivo? Non così immediatamente identificabile, ma analizzando un pò il file di configurazione di molecule che abbiamo precedentemente modificato, ci accorgiamo che l’immagine docker utilizzata per testare il ruolo è basata su CentOS. Decidiamo invece di usare Ubuntu 18.04, per il quale il comando “apt-get update” ha un senso.
platforms:
- name: instance
image: ubuntu:18.04
Lanciamo di nuovo il test e.. boom! Superato! Ci manca però ancora qualcosa: le operazioni indicate nel nostro ruolo vengono svolte senza errori ma non ci siamo effettivamente occupati di verificare che il servizio Apache2 sia installato ed attivo. Possiamo configurare Molecule in modo che lo verifichi per noi al termine dell’esecuzione del playbook, indipendentemente dalle azioni previste nel ruolo.
Dobbiamo modificare il file test_default.py posto nella directory tests di Molecule.
Eseguiamo di nuovo il test prestando attenzione all’azione “verify”.
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/awesome-ci/roles/my-httpd/molecule/default/tests/...
============================= test session starts ==============================
platform darwin -- Python 3.8.1, pytest-4.6.4, py-1.8.1, pluggy-0.13.1
rootdir: /home/awesome-ci/roles/my-httpd/molecule/default
plugins: testinfra-3.3.0
collected 2 items
tests/test_default.py .. [100%]
=========================== 2 passed in 3.14 seconds ===========================
Verifier completed successfully.
Ottimo! Ricapitoliamo: abbiamo configurato Molecule per verificare mediante ansible-lint il nostro ruolo. Successivamente lo stesso viene utilizzato in un playbook per installare e avviare Apache in un container docker basato su Ubuntu. Al termine viene inoltre verificato che Apache sia effettivamente installato e avviato. Il tutto in modo automatizzato.
E’ arrivato il momento di spostarci in Cloud. Perché non provare il nostro ruolo in uno scenario totalmente differente? Ad esempio su AWS EC2?
Molecule & AWS EC2
Per testare il nostro ruolo su AWS dobbiamo creare un nuovo scenario di Molecule. Dalla directory del ruolo inizializziamo il nuovo scenario aws-ec2 con:
molecule init scenario -d ec2 -r my-httpd -s aws-ec2
Come si può notare, questa volta usiamo ec2 come environment di test. Il file molecule.yml è ovviamente diverso dal precedente scenario, in quanto prevede le impostazioni specifiche per la piattaforma AWS.
platforms:
- name: instance
image: ami-0047b5df4f5c2a90e # change according to your EC2
instance_type: t2.micro
vpc_subnet_id: subnet-9248d8c8 # change according to your VPC
L’immagine AMI e la subnet specificate nel file di configurazione devono essere modificate in accordo con la region che si intende utilizzare per il test. Per trovare la AMI Ubuntu corretta consiglio di utilizzare questo sito.
Valgono inoltre le configurazioni previste nello scenario docker: specificare le esclusioni nelle regole di code linting e i test a termine deployment per la verifica del servizio Apache.
Dobbiamo specificare region e credenziali AWS: lo si può fare impostando alcune variabili ambiente prima di avviare il test con Molecule.
export EC2_REGION=eu-west-2
export AWS_ACCESS_KEY_ID=YOUR_ACCESSKEY
export AWS_SECRET_ACCESS_KEY=YOUR_SECRETACCESSKEY
molecule test
Successo! Abbiamo rapidamente creato un nuovo scenario e siamo in grado di testare il nostro ruolo anche su AWS. Se collegati alla console EC2, vedremo infatti che Molecule si occuperà di avviare una nuova virtual machine su cui effettuare i test. La stessa sarò terminata concluse le operazioni. Vediamo ora come creare una pipeline di CI che, ad ogni push sul nostro repository GitHub, provveda all’esecuzione di questi test con Molecule su AWS.
Travis CI
Una volta registrati su TravisCI è possibile configurare la nostra pipeline per il repository GitHub che ospita il nostro progetto di automazione. La configurazione avviene creando il file .travis.yml in root.
Come si vede nella sezione install, il file test-requirements.txt riporta l’elenco dei pacchetti da installare. Il suo contenuto è:
ansible-lint
molecule
boto
boto3
Nella sezione script sono invece indicate le operazioni da svolgere: ansible-lint viene utilizzato preliminarmente per le verifiche di playbook e ruolo; viene poi utilizzato Molecule per effettuare il test del ruolo nello scenario AWS-EC2.
Manca qualcosa? Si, le credenziali AWS. Per condividerle in modo sicuro utilizziamo la CLI di Travis. Per prima cosa installiamola:
gem install travis
Procediamo poi a codificare le credenziali in modo sicuro, aggiungendole al file di configurazione di Travis. Le stesse saranno poi passate sotto forma di variabili di ambiente a Molecule durante l’esecuzione dei test.
travis login --pro
travis encrypt AWS_ACCESS_KEY_ID="<your_key>" --add env.global --pro
travis encrypt AWS_SECRET_ACCESS_KEY="<your_secret>" --add env.global --pro
Proviamo una build con TravisCI e … bingo! Abbiamo terminato.
Conclusioni
Abbiamo visto come l’utilizzo di questi strumenti consenta di sviluppare ruoli e playbook Ansible di qualità sottoponendoli costantemente ad una pipeline in grado di verificarne la correttezza, su ambienti e scenari completamente differenti.
Un progetto per il quale ho utilizzato gli stessi strumenti è disponibile su questo repository GitHub: si tratta di un playbook Ansible dedicato alla realizzazione di un cluster Docker Swarm.
Ci siamo divertiti? Alla prossima!