Come isolare un server Meteor con Dokku e Vagrant

Alcuni mesi fa un amico ed ex collega a cui devo tutto quello che so sull'umorismo inglese e su come pronunciare con perfetto accento the F-word (please, give Derry a Glasgow kiss on his blog), ha dato dimostrazione di abilità creando una job board con Meteor in 48 righe di codice. Al termine, mi sfida a fare di meglio in Python.

Finalmente mi sono deciso a raccogliere il guanto della sfida.

Di cosa parlerò in questo post e nel prossimo

Ora non si dica che ho paura di raccogliere il vessillo col serpente, ma non vedrete codice Python in questo post. La mia natura metodica mi impone prima di analizzare bene il codice inglese per capire cosa faccia esattamente. Ma non in questo post.

Piuttosto, cogliendo la recente passione per Docker e la 12-factor methodology, voglio descrivere come ho usato Dokku, un PaaS molto semplice, in una macchina virtuale Vagrant per lanciare un server di sviluppo di Meteor che mi consenta di modificare il codice senza dover rigenerare il container Docker.

Nel prossimo post farò lo stesso con Python e mi cimenterò nell'impresa.

Perché Dokku e Vagrant

Il motivo per cui la prendo tanto da lontano è che sto iniziando a usare Deis a lavoro e voglio unire un po' di utile al dilettevole. Per il decimo principio della metodologia 12 factor, bisogna sviluppare in un ambiente analogo a quello di produzione. Solitamente abbiamo cercato di raggiungere questa parità utilizzando lo stesso sistema operativo della produzione, Ubuntu. Ma oramai metà del team di sviluppo non usa Linux e anche tra chi usa Ubuntu c'è chi lo fa più per imposizione che per scelta.

Dato che siamo per l'autodeterminazione degli strumenti di sviluppo, quale migliore soluzione di far girare l'ambiente di sviluppo in una macchina virtuale con Ubuntu, invece di installare Ubuntu direttamente sulla macchina?

Vagrant sembra lo strumento migliore a questo scopo perché si integra bene con gli altri strumenti e piattaforme per Docker. Lo stesso Deis può usare Vagrant per creare i nodi del cluster. Di per sé Vagrant non è un software di virtualizzazione, ma uno strumento per definire una macchina virtuale. La macchina stessa deve girare con VirtualBox.

Vagrant è già in grado di dichiarare le dipendenze e fornire isolamento come richiesto dal secondo principio di 12 factor. Tirare su una macchina virtuale per ogni progetto però rischia di essere eccessivamente pesante in fase di sviluppo, soprattutto se si lavora a più progetti interdipendenti. Per questo utilizzeremo la macchina virtuale per far girare dei container Docker.

Dato che i container Docker non devono contenere servizi esterni, come database o cache, bisogna trovare un modo per gestire agilmente queste risorse. La mia scelta è caduta su Dokku, un PaaS molto leggero pensato per girare su un'unica macchina. Anche Docker Compose potrebbe essere una valida alternativa, ma di Dokku mi piace il fatto che abbia comandi e procedure del tutto simili a quelli di Deis (che sono poi quelli di Heroku).

Installare VirtualBox, Vagrant e Dokku

Innanzitutto bisogna installare VirtualBox scaricandolo dal sito. Poi si installa Vagrant. Il pacchetto Ubuntu va benissimo:

$ sudo apt-get install vagrant

Per altri sistemi operativi si può andare alla sezione download. Seguiamo poi le istruzioni per installare Dokku tramite Vagrant.

Si comincia con il clonare il repository che contiene la configurazione di Vagrant e gli script di installazione:

$ git clone https://github.com/dokku/dokku.git

Si associa nel file /etc/hosts il dominio dokku.me all'IP della macchina virtuale:

10.0.0.2 dokku.me

Tutte le applicazioni Docker che saranno create saranno raggiungibili tramite un dominio di terzo livello di dokku.me. Ovviamente saranno visibili solo sulla macchina di sviluppo.

Nella directory del progetto clonato, il file più importante è Vagrantfile, un file Ruby che definisce le specifiche della futura macchina virtuale. Consiglio quanto meno di aumentare la memoria nel parametro BOX_MEMORY, se ne avete a disposizione. 2048 MB andrà bene iniziare.

Dato che nella macchina virtuale vogliamo eseguire codice che dovrà essere modificato sulla macchina host, configuriamo una directory condivisa tra host e guest alla fine del Vagrantfile:

  end
  # Prima dell'ultimo end, dopo le altre configurazioni
  config.vm.synced_folder "/path/to/apps", "/opt/apps"
end

Ora si può creare la macchina virtuale con

$ vagrant up

La creazione della macchina virtuale è molto lunga (30 minuti sul mio portatile), ma non bisogna disperare, neanche quando sembra che sia tutto bloccato. Per essere sicuri che tutto stia andando bene, già prima che l'installazione di Dokku sia finita si aprire un altro terminale, ed entrare nella macchina virtuale con vagrant ssh (da lanciare sempre nella directory del Vagrantfile).

Alla fine del processo di installazione, bisogna configurare dominio e la propria chiave pubblica tramite interfaccia web su dokku.me. È anche da spuntare Use virtualhost naming. Dato che siamo in sviluppo coninuiamo a usare dokku.me.

Mi è capitato che il web installer non partisse automaticamente al termine dell'installazione (probabilmente a causa di mie interferenze). In tal caso si può rimediare lanciando dentro la macchina virtuale python contrib/dokku-installer.py come utente root in /root/dokku.

Se tutto è andato bene si dovrebbe vedere la macchina virtuale che gira dentro VirtualBox.

Installare Meteor in Docker

Fin qui la parte facile. Ora proviamo a installare Meteor in un container Docker. Per ora mi limiterò a installare l'applicazione di esempio simple-todos, la prossima volta mi cimenterò con il Job Board di Derry.

Faccio quindi il clone di simple-todos nella directory apps del mio portatile (quella che nel Vagrantfile è chiamata /path/to/apps).

git clone https://github.com/meteor/simple-todos.git

Dentro creo un Dockerfile, che contiene la istruzioni per creare il container:

FROM ubuntu:14.04

RUN apt-get update && apt-get install -y curl

RUN curl https://install.meteor.com/ | sh

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get install -y locales

RUN locale-gen en_US.UTF-8

RUN dpkg-reconfigure locales

ENV LC_ALL="en_US.UTF-8"

ENV LANG="en_US.UTF-8"

ENV LANGUAGE="en_US.UTF-8"

EXPOSE 3000

WORKDIR /opt/simple-todos

CMD meteor

La prima riga dice quale sistema operativo utilizzare. Anche se ci sono molte alternative Linux più leggere, Ubuntu mi è sembrato quello che crea meno problemi e consente di arrivare il più velocemente possibile ad avere un Docker funzionante. La messa appunto di un Dockerfile è solitamente un processo di tentativi e fallimenti.

Dopo aver installato curl da repository e aver lanciato l'installer di Meteor, faccio tutta una serie di passi relativi all'encoding che mi hanno dato problemi con Meteor e il suo Mongo DB interno.

Nel Docker file va dichiarata la porta su cui ascolterà l'applicazione, nel caso di Meteor la 3000. In ogni caso Dokku esporrà l'applicazione sulla porta 80.

Indico come directory di lavoro /opt/simple-todos. Dato che il nostro obiettivo è modificare il codice senza rigenerare il container, è importate che questa directory sia condivisa con la macchina virtuale, e da qui con la macchina host. Dato che una condivisione di questo genere non può essere configurata nel Dockerfile per validi motivi che non affronterò qui, bisognerà indicarla a Dokku prima dell'avvio del container.

Infine il comando meteor lancia il server dopo aver installato tutte le dipendenze.

A questo punto bisogna commitare localmente il Dockerfile perché Dokku, come Deis, lavora su un repository interno e non da file system.

Meteor di default lancia un MongoDB interno. MongoDB dovrebbe invece essere gestito esternamente. Per far questo, installiamo il plugin Dokku per MongoDB. Entrando con vagrant ssh nella macchina virtuale:

$ sudo dokku plugin:install https://github.com/dokku/dokku-mongo.git mongo 

Sempre dentro la macchina virtuale creiamo l'applicazione

$ cd /opt/apps/simple-todos
$ dokku apps:create simple-todos
$ git remote add dokku dokku@dokku.me:simple-todos
$ dokku docker-options:add simple-todos deploy "-v /opt/apps/simple-todos:/opt/simple-todos"
$ dokku mongo:create tasks
$ dokku mongo:link tasks simple-todos

dokku apps:create crea un repository in Dokku. Il comando git successivo lega il repository che abbiamo clonato a questo nuovo remote.

A questo punto configuriamo la condivisione di directory a cui abbiamo fatto riferimento nel Dockerfile: al path /opt/apps/simple-todos nella macchina vituale corrisponde /opt/simple-todos nel Docker.

Come ultimo passo di creazione del container c'è la crezione e configurazione della collection tasks di Mongo. Il link in particolare imposta la variabile d'ambiente MONGO_URL utilizzata da Meteor.

A questo punto si può fare il deploy con il push della branch master nel remote dokku:

$ git push dokku master

Alla fine dovrebbe arrivare qualcosa del genere:

Per vedere l'applicazione dal proprio browser bisogna aggiungere il dominio al proprio /etc/hosts come fatto precedentemente:

10.0.0.2 dokku.me simple-todos.dokku.me

Ora si può aprire il browser all'indirizzo simple-todos.dokku.me. Al primo avvio meteor ci mette un po' a installare tutti i pacchetti, nonostante il deploy risulti finito per Docker. L'applicazione potrebbe non essere raggiungibile per alcuni minuti. Si può verificare i processi dentro Docker con docker ps simple-todos (con docker help si può avere una lista di tutti questi comandi):

Con un po' di pazienza comunque l'applicazione sarà disponibile:

Per verifica, si può modificare l'html direttamente nella working directory originale. L'applicazione si aggiorna senza bisogno di rigenerare il container. Missione compiuta!


Cover by Trevor Bexon, some rights reserved