Una delle fasi più noiose nella preparazione di un ambiente, sia questo di sviluppo, collaudo o quant'altro, è sicuramente l'installazione e la configurazione delle componenti software. Che versione installiamo? A quale URL trovo il pacchetto? Come si chiama il database che devo creare? Esiste un ordine preciso in cui le singole componenti vanno installate? Tra qualche mese sarò in grado di rifare tutto il processo senza errori?
Queste sono solo alcune delle domande emergono e il più delle volte la risposta è: "Ok, scriviamo un documento!". Perfetto, in alcune situazioni la scrittura di un documento che guidi passo passo la procedura di provisioning è la soluzione più pratica. Spesso però i documenti non vengono adeguatamente condivisi, non sempre è presente un criterio di organizzazione degli stessi e non è difficile che questi siano completamente disallineati da quella che è poi diventata, col tempo, l'infrastruttura. Ma se anche così non fosse c'è un problema ancora peggiore: seguire una procedura passo passo è un'esperienza noiosa, la concentrazione cala e la possibilità di introdurre errori o saltare qualche passaggio è alta.
Quanto appena detto vale per noi esseri umani, non per il nostro computer, lui è un vero fenomeno in questo tipo di operazioni... purché riscriviamo il documento di installazione in modo che possa comprenderlo.
Shell provisioning
In Vagrant abbiamo diversi modi per effettuare il provisioning di una macchina, il primo e più immediato è chiamato shell provisioning in quanto si affida alla shell del sistema ospite. Nel nostro caso, avendo creato una macchina virtuale Linux la shell sarà una classica Bash.
Supponiamo, ad esempio, che la nostra applicazione abbia bisogno per funzionare di MongoDB 2.6.4. Avendo bene in mente i passi per l'installazione (magari sono scritti nel famigerato documento di cui parlavamo ad inizio lezione) creeremo il relativo script di installazione (a questo indirizzo è disponibile un tutorial). Il risultato sarà qualcosa del genere:
#!/usr/bin/env bash
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/mongodb.list
apt-get update
apt-get install mongodb-org=2.6.4 mongodb-org-server=2.6.4 mongodb-org-shell=2.6.4 mongodb-org-mongos=2.6.4 mongodb-org-tools=2.6.4 -y
echo "mongodb-org hold" | dpkg --set-selections
echo "mongodb-org-server hold" | dpkg --set-selections
echo "mongodb-org-shell hold" | dpkg --set-selections
echo "mongodb-org-mongos hold" | dpkg --set-selections
echo "mongodb-org-tools hold" | dpkg --set-selections
sed -i '/bind_ip = 127.0.0.1/,/bind_ip\ =\ 127\.0\.0\.1/s/^/#/' /etc/mongod.conf
service mongod restart
Salviamo il documento nella nostra directory di progetto assegnandogli estensione .sh.
Nulla di trascendentale, ma qualcosa già è cambiato: prima di tutto abbiamo una chiara visione di insieme della lista di comandi da eseguire, partendo dall'update del repository di Ubuntu fino all'avvio del demone di MongoDB. In secondo luogo lo script è versionabile e sarà quindi possibile condividerlo, migliorarlo e correggerlo tenendo sempre traccia di chi ha modificato cosa.
A questo punto, non ci resta che spiegare a Vagrant, dove trovare lo script e questo comando da scrivere nel Vagrantfile è tutto quello che serve:
config.vm.provision "shell", path: "mongo-provision.sh"
In questo modo stiamo istruendo Vagrant per effettuare uno shell provisioning dal file chiamato mongo-provision.sh. Poiché questo file risiede nella directory di progetto non avremo bisogno di specificare un vero e proprio path, basterà solo il nome.
Verifichiamo con un vagrant status
la situazione della nostra macchina, se questa risulterà not created
, al primo vagrant up
sarà eseguito lo script di provisioning. Se la macchina è in running o in poweroff dovremmo forzare il provisioning rispettivamente con:
vagrant reload --provision
o
vagrant up --provision
Shell provisioning oltre i default
Il comando di provisioning è abbastanza flessibile e ci permette di personalizzare il suo comportamento. Possiamo decidere, ad esempio, di eseguire un determinato comando ogni volta che si avvia la macchina virtuale, grazie al modificatore always che impone l'esecuzione dello script ogni volta che lanciamo vagrant up
o vagrant reload
:
config.vm.provision "shell", path: "service-start.sh", run: “always”
Potremmo anche non avere necessità di utilizzare un file .sh
, magari perché il comando da lanciare è abbastanza breve. Supponiamo di voler inserire in un file session.log
la data completa della macchina virtuale quando questa viene avviata, un comando del genere è quello che può fare al caso nostro:
config.vm.provision "shell", inline: "echo $(date) >> session.log", run: "always"
Sebbene del tutto simile al precedente, non possiamo fare a meno di notare l'opzione inline che ha preso il posto di path, al cui interno non è più presente un riferimento al file con i comandi ma direttamente il comando da eseguire.