💻 Introduzione
Ansible è un prodotto Open Source di RedHat che permette di automatizzare la configurazione di server Linux like e Window. In questo breve post proverò a riassumere i concetti salienti per iniziarlo ad usare in modo produttivo.
L’architettura di base consiste in una macchina controllore (dove viene eseguito ansible) e i nodi target.
Per la comunicazione ci si basa su SSH mentre i prerequisiti per la macchina controllore sono aver installato python, pip ed ansible.
In questa pagina ho cercato di riassumere i concetti base del prodotto.
Teoria
- Ansible si può paragonare ad un manager di macchine a stati per server Linux e Windows. Permette di dichiarare con del codice lo stato che si vuole impostare ad un server.
- Realizza un modo per ottenere la Configuration as code
- Permette di automatizzare task complessi e comuni.
Cosa vuole risolvere
- Evitare errori umani
- Rendere la configurazione trasparente
- Rendere la configurazione ripetibile e deterministica
- Documentare la configurazione dal codice stesso
- Rendere la configurazione portabile
- Ridurre il tempo di configurazione
Installazione
Per poter installare sulla macchina di controllo ansible serve avere python3 e pip3 installato nel proprio sistema
sudo apt install python3-pip
pip install ansible
ansible --version
Creazione di una chiave ssh
La chiave ssh ci permette di accedere in maniera controllata al server privato in cloud o on premise che vogliamo configurare. Questo è il prerequisito per eseguire ansible. Creaiamo una chiave con il comando:
ssh-keygen -f ~/.ssh/nomefile
scegliamo il percorso e il nome file e una passphrase. Ora è possibile vedere la chiave pubblica con un semplice
cat $HOME/.ssh/nomefile.pub
Installazione della chiave nel server target
Per installare la chiave ssh appena creata nel server target usiamo il comando:
ssh-copy-id -i ~/.ssh/nomefile.pub user@host
Test della nuova chiave SSH
Possiamo provare la chiave appena installata con il comando:
ssh -i ~/.ssh/nomefile user@host
System inventory
Un inventory Ansbile:
- contiene tutti gli indirizzi dei server (hostnames o IP)
- si possono usare dei gruppi per organizzare i server da gestire con Ansible
- può essere un file statico o provisionato da linea di comando o dinamicamente con API
es INI file inventory.ini
[webservers] #<- GROUP 1
104.248.97.73
104.248.97.74
[databases] #<- GROUP 2
104.248.97.75
ma possiamo usare anche file yaml
Test del file inventory.ini
Adesso possiamo usarlo per eseguire un ping a tutti i server con ansible
ansible -i inventory.ini -u root -m ping all
dove:
- -u user di login
- -m module
- all per tutti i gruppi
Ad-hoc commands
Il ping è solo uno dei comandi che possiamo usare. In generale questi comandi che eseguono un compito atomico vengono chiamati comandi ad hoc o moduli e possono servire per:
- Riavviare i server
- Gestire file
- Gestire pacchetti
- Gestire utenti e gruppi
- Gestire servizi
- Raccogliere informazioni
Creare un utente Linux con il comando user
Uno dei comandi ad hoc è il modulo user che serve per creare delle utenze.
ansible -i inventory.ini -u root -m user -a "name=capitanfuturo state=present" all
dove:
- -a per gli argomenti
- state present per creare, absent per eliminare
Installare un package
modulo package per installere dei pacchetti
ansible -i inventory.ini -u root -m package -a "name=nginx state=present" webserver
Playbook
Il playbook è come un linguaggio dichiaritivo della configurazione, installazione e orchestrazione di Ansible.
Riprendendo la guida ufficiale:
se i moduli sono gli strumenti, i playbook sono i manuali di istruzioni e l’inventory elencano i materiali grezzi gli host.
le caratterisctiche pricipali sono quindi:
- flessibilità
- possono essere innestati
- è una collezione di azioni, Plays, che sono a loro volta una collezione di Task che eseguono moduli (vedi i comandi ad hoc)
- documenta la configurazione di un server in modo dichiarativo
Per iniziare a scrivere un playbook creiamo un file master.yml. Come abbiamo scritto prima un playbook è una collezione di Plays che contiene task. Di seguito un esempio:
- name: CONFIGURA WEBSERVER # <- si vedrà in console per il plays
hosts: webservers # <- vedasi i gruppi in inventory.ini
tasks:
- name: Crea un utente non root # <- si vedrà in console per il task
user: # <--- vedasi i moduli dei comandi ad-hoc
name: capitanfuturo
state: present
- name: Installa nginx
package:
name: nginx
state: present
per eseguire il playbook eseguiamo il comando:
ansible-playbook master.yml -i inventory.ini -u root
una volta eseguito potremmo vedere il risultato dell’esecuzione nella sezione PLAY RECAP.
Ruoli
Un ruolo in Ansible è un pezzo di codice rieseguibile. Possiamo immaginarlo come un package di playbooks e configurazione che dovrebbe gestire una sola cosa, per un solo obiettivo.
Questo lo rende molto portabile e riusabile in diversi progetti di configuration as a code.
Essendo dichiaritivo e sotto forma di codice è anche versionabile
Vediamo per esempio la struttura del ruolo common. Il ruolo più usato
roles/
common/
tasks/
handlers/
files/
templates/
vars/
defaults/
meta/
Alcune directory sono obbligatorie:
- tasks
- defaults
- meta
mentre le altre sono opzionali.
Nella cartella tasks si possono scrivere tutti i playbook che si vogliono e che verranno eseguiti.
I ruoli possono essere pubblicati in un repository pubblico come per esempio Ansible galaxy
Defaults
In defaults possiamo dichiare le varibili di default per il nostro role. Il file si chiama main.yaml ed è sttutturato come tale. Proviamo ad esempio a dichiare una lista di utenti:
users:
- username: pippo
state: present
- username: pluto
state: present
- username: paperino
state: absent
Tasks
Nella cartella tasks possiamo mettere i nostri playbook, la cosa interessante è che possiamo richiamare le variabili della direcotry dei defaults attraverso la sintassi del motore di template di python Jinjia come nell’esempio seguente per il solito file main.yaml:
- name: Gestione utenti
user: {% raw
name: "{{ item.username }}"
state: "{{ item.state }}"
loop: "{{ users }}" # <- cicla sull'array users dei defaults
{% endraw
Uso dei ruoli nel playbook
Per usare un ruolo ein un playbook si usa la chiave roles che deve sempre essere scritta prima dei tasks. Prendendo l’esempio precedente e immaginando di aver creato un ruolo chiamato users per la gestione degli utenti possiamo scrivere quanto segue:
- name: CONFIGURA WEBSERVER # <- si vedrà in console per il plays
hosts: webservers # <- vedasi i gruppi in inventory.ini
roles:
- users
tasks:
- name: Installa nginx
package:
name: nginx
state: present
Variabili, Condizioni booleane e cicli
Alcuni dei costrutti più comuni nei linguaggi di programmazione sono disponibili anche in Ansible.
Variabili
Se vogliamo dichiarare delle varibili come visto per i default possiamo farlo direttamente nel playbook. La dichiarazione va fatta sotto la sezione vars e poi può essere usata per esempio nella sezione tasks come segue:
- name: CONFIGURA WEBSERVER
hosts: webservers
vars:
message: "Hello World" # <- valore scalare, (chiave, valore)
mylist: [1,2,3] # <- lista numerica
myliststr: # <- lista di stringhe
- "Hello"
- "World"
mymap: # <- mappa
uno: "hello"
due: "world"
tasks: {% raw
- debug:
msg: "message: {{ message }}"
- debug:
msg: "mylist: {{ mylist }}"
- debug:
msg: "myliststr: {{ mylist1 }}"
- debug:
msg: "myliststr: {{ mymap.uno }} {{ mymap.due }}"
{% endraw
Condizioni
Ansible permette di eseguire dei task a fronte di alcune condizioni. Per farlo possiamo usare la keyword when come nell’esempio che segue.
Altri operatori sono:
- not per negare una condizione
- and inteso come AND logico
- or inteso come OR logico
Per scrivere condizioni complesse si possono usare le parentesi come si usa normalmente in un linguaggio di programmazione.
- name: CONFIGURA WEBSERVER
hosts: webservers
vars:
runTask: true
mylist: 1
tasks:
- debug:
msg: "Go go go!"
when: runTask
- dbug:
msg: "Do not run"
when: not runtask
AND come elenco di condizioni
E’ possibile usare un elenco di condizioni che verranno considerate di default come degli AND:
- name: CONFIGURA WEBSERVER
hosts: webservers
vars:
runTask: true
mylist: [1, 2, 3]
tasks:
- debug:
msg: "Go go go!"
when:
- runTask
- mylist[1] == true
OR come elenco di condizioni
E’ possibile usare un elenco di condizioni ma affinchè siano valutate come OR dobbiamo scrivere le condizioni come segue:
- name: CONFIGURA WEBSERVER
hosts: webservers
vars:
runTask: true
mylist: [1, 2, 3]
tasks:
- debug:
msg: "Go go go!"
when:
- runTask
- or # <- Dobbiamo aggiungere qui un OR logico
- mylist[1] == true
Cicli, Loop
Dalla versione 2.5 è disponibile la keyword loop usato per semplici cicli. Esiste anche la keyword with_items ma per la maggior parte dei casi basterà il loop.
- name: CONFIGURA WEBSERVER
hosts: webservers
vars:
mylist: [1, 2, 3]
mymaplist:
- key: 1
value: a
- key: 2
value: b
myobject:
name: capitanfuturo
age: 39
tasks:
- debug:
msg: "Go list {{item}}"
loop: "{{ mylist }}" # <- allo stesso livello del nome del modulo
- debug:
msg: "Go map list values: {{item.value}}"
loop: "{{ mymaplist }}"
- debug:
msg: "Go obj properties {{item}}"
loop: "{{ myobject|dict2items }}" # <- converte le property in mappe (chiave,valore)
Tags
I tags sono molto utili a livello di Task, e possono essere usati per selezionare quale task eseguire.
I tags vengono dichiarati con la keyword tags e va inserita alla fine del task da raggruppare.
Tutti i task con lo stesso tag vengono raggruppati logicamente.
Per eseguire un tag il comando è:
ansible-playbook master.yml -i inventory.ini -u root --tags=<nome del tag>
Per non eseguire un tag il comando è
ansible-playbook master.yml -i inventory.ini -u root --skip-tags=<nome del tag>
Ansible Galaxy
Ansible Galaxy è un HUB, un registry dove trovare dei ruoli utili per costruire la propria configurazione as code: https://galaxy.ansible.com/
Per usare un ruolo il comando da usare è:
ansible-galaxy install <nome del ruolo>
Di default ansible installa i ruoli in /etc/ansible/roles che richiede permesso di root per poter scrivere. Se si vuole sofrascrivere questo comportamento si può usare il flag —roles-path oppure la variabile di ambiente ANSIBLE_ROLES_PATH
ansible-galaxy install --roles-path ~/ansible-roles <nome del ruolo>
Dichiarazione delle dipendenze
Un buon metodo per descrivere le dipendenze ai ruoli in ansible galaxy è l’udo del file requirements.yml
ansible-galaxy install -r requirements.yml
un esempio di file è:
- src: yatesr.timezone
# from GitHub
- src: https://github.com/bennojoy/nginx
# from GitHub, overriding the name and specifying a specific tag
- src: https://github.com/bennojoy/nginx
version: master
name: nginx_role
# from a webserver, where the role is packaged in a tar.gz
- src: https://some.webserver.example.com/files/master.tar.gz
name: http-role
Conclusione
Ansible è uno dei taselli che possiamo usare per relaizzare i processi di dev-ops in azienda. Ci può aiutare in qui contesti nei quali ci è utile installare nuovi nodi della nostra architettura in maniera controllata e dichiarativa. Questa pagina non è altro che un blocco note e non pretende di essere esaustiva ma un punto di partenza per imparare sempre di più ad usare questo strumento.
Links
La felicità si trova nel fare, non solo nel possedere. (Napoleon Hill).