Capistrano, deploy sexy come mai prima

Gestire la fase di deploy di un sito o applicazione web complessa può diventare un compito delicato e prono all’errore. Dimenticare, ad esempio, di settare i permessi giusti su file o cartelle dopo un aggiornamento potrebbe introdurre malfunzionamenti, o peggio problemi di sicurezza, e gli script di shell in alcuni casi risultano strumenti rigidi e poco adattabili a scenari complessi, che potrebbero coinvolgere diverse macchine con ruoli distinti.

Capistrano (prima SwitchTower) nasce per automatizzare e semplificare la fase di messa in produzione e aggiornamento delle applicazioni Rails, consentendo una grande flessibilità d’uso. Nulla vieta, come vedremo, di utilizzare questo strumento per servire qualsiasi tipo di file o applicazione, su qualsiasi architettura che rispetti i pochi prerequisiti richiesti.

È opera di Jamis Buck, si basa su Rake e permette di definire una serie di operazioni (tasks) legate alla fase di deploy del progetto: installazione di software, checkout dei file da repository, rollback istantanei, migrazioni di database e avvio di application server sono solo alcuni dei compiti che è possibile automatizzare e ripartire su macchine differenti, il tutto mediante l’uso di un DSL facilissimo da comprendere e padroneggiare. L’approccio transazionale di Capistrano e la sua flessibilità lo rendono idoneo all’uso in scenari anche decisamente complessi, in cui ad esempio esistono ambienti di produzione, sviluppo e collaudo con diverse architetture e diverse configurazioni.

Installazione e Setup

È necessario installare Capistrano soltanto sulle macchine da cui si lanciano i comandi di deploy, e l’unico prerequisito richiesto sulle macchine remote è la presenza di SSH ed una shell compatibile POSIX. Installare Capistrano mediante rubygems è semplice:

host:~ user$ sudo gem install capistrano

La gemma installa due binari in /usr/bin, che sono cap e capify. Per servire la propria applicazione mediante Capistrano è necessario creare un file che contiene le istruzioni necessarie al deploy. Un modo automatico per creare questo file (chiamato Capfile) è recarsi nella cartella del progetto e digitare il comando

host:Sites/myapp user$ capify .

Se lanciato sulla root di una applciazione Rails, capify produce due file: il Capfile e deploy.rb, nella cartella config. Purtroppo capify è praticamente inutile se ciò che stiamo servendo non è una applicazione Rails, vedremo in seguito come partire con un Capfile pulito.

Vediamo il contenuto del Capfile:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
load 'config/deploy'

e di deploy.rb:

set :application, "set your application name here"
set :repository,  "set your repository location here"
 
# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}"
 
# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion
 
role :app, "your app-server here"
role :web, "your web-server here"
role :db,  "your db-server here", :primary => true

Naturalmente è possibile tenere tutte le istruzioni di deploy nel solo Capfile, ed è quello che faremo in questo articolo.

Concetti base di Capistrano

Vediamo adesso alcuni dei concetti base per capire il funzionamento di Capistrano:

  • I task sono le unità funzionali che contengono i comandi di shell da eseguire sulle macchine contemplate dal nostro scenario di deploy. Possono essere chiamati direttamente dall’utente oppure da altri task, e come per Rake è possibile raggrupparli logicamente in namespaces;
  • Le ricette (recipes) sono gruppi di task che si riferiscono a determinati compiti. Possono avere a che fare direttamente con il deploy dell’applicazione, ma possono anche essere rivolte a compiti di altra natura, come ad esempio setup di componenti software e monitoraggio di applicazioni in background.
  • I ruoli (roles) sono ambiti in base ai quali è possibile raggruppare le macchine destinatarie dei task. Capistrano ci suggerisce tre ruoli di default, che sono :app, :db e :web.

Capistrano, come anticipato, assume che sia possibile raggiungere le macchine remote mediante SSH, e la connessione avviene mediante la classe NET::SSH di Ruby.

Con il comando

host:Sites/myapp user$ cap -Tv

viene visualizzata la lista di tutti task che è possibile eseguire sulla propria applicazione, ad ognuno dei quali è associata una breve descrizione. Se abbiamo in precedenza eseguito capify sulla root della nostra applicazione notiamo che molti di questi task appartengono al namespace deploy. Questo perché nel Capfile viene caricato, nella prima riga, il file deploy.rb, che possiamo trovare (in Mac OS X) sotto /Library/Ruby/Gems/1.8/gems/capistrano-2.x.x/lib/capistrano/recipes/deploy.rb. In questo file è contenuta la gran parte dei task di default di Capistrano, quindi ne consiglio la lettura.

Vediamo ora come istruire un Capfile d’esempio per il nostro primo deploy.

load 'deploy'
 
# Il nome che sarà assegnato alla cartella che conterrà l'applicazione
set :application, "myapp" 
 
# Il repository che contiene il codice da servire
set :repository, "svn://repository.com/myapp/trunk" 
 
# Il server che Capistrano raggiungerà tramite SSH
set :domain, "host.com"
 
# Usiamo solo una macchina per il deploy.
server domain, :app, :web, :db, :primary => true

Questa configurazione minimale è possibile perché Capistrano fa le seguenti ipotesi sullo scenario di deploy:

  • Stiamo servendo una applicazione Rails;
  • L’utente che lancia il comando di deploy è presente anche sulla/e macchina/e remota/e (Capistrano cerca quindi di accedere con le stesse credenziali dell’utente che ha lanciato il deploy);
  • Vogliamo servire una copia versionata della nostra applicazione;
  • Stiamo usando Subversion come SCM;
  • Il repository ha le stesse credenziali d’accesso della macchina remota, qualora non consenta checkout anonimo;
  • L’applicazione viene servita dalla cartella /u/apps/myapp;

Possiamo adeguare ciascuno dei defaults alle nostre necessità. Adesso vediamo l’esempio di un Capfile più completo:

load 'deploy'
 
 # Il nome che sarà assegnato alla cartella che conterrà l'applicazione
set :application, "myapp"
 
# Il repository che contiene il codice da servire
set :repository, "svn://repository.com/myapp/trunk"
 
set :domain, "host.com" 
 
# Usiamo macchine diverse per ciascun ruolo.
role :app, "app.#{domain}"
role :web, "www.#{domain}"
role :db,  "db.#{domain}", :primary => true
 
# Non cerca di eseguire i task come root
set :use_sudo, false 
 
# Le release da tenere sulla macchina remota
set :keep_releases, 3 
 
# Credenziali  d'accesso alla macchina remota,
# se diverse da quelle dell'utente che lancia il deploy
set :user, "deployuser"
# set :password, "deploypassword"
 
# Possiamo anche evitare di tenere la password nel Capfile
set(:password) { Capistrano::CLI.ui.ask("Password: ") }
 
# Posizione dell'applicazione sulla macchina remota
set :deploy_to, "/var/www/#{application}"
 
# Strategia di deploy, in questo caso svn export
set :deploy_via, :export
 
# Specifica alcune dipendenze
depend :remote, :gem, "parseexcel"
depend :remote, :directory, :writeable, "/var/www/current/config"
depend :local, :command, "svn"
 
# Esempio di callback: dopo la fine di un task è possibile
# invocarne automaticamente un altro
after "deploy:finalize_update", "deploy:cleanup"
 
# Altro modo di definire una callback: prependere before_ o after_
# al nome di un task definito.
desc "Crea la cartella uploaded_pictures"
task :after_setup
  run "mkdir -p #{shared_path}/uploaded_pictures"
end

Capistrano ci permette di riassumere in un unico file tutte le istruzioni necessarie per servire l’applicazione, aspetto molto vantaggioso perché rende facile la manutenzione e riduce al minimo i punti di fallimento.

Il primo deploy

Vediamo ora come procedere al primo deploy. Una volta istruito il Capfile con le impostazioni che descrivono il nostro scenario, lanciamo dalla root della nostra applicazione il comando

host:Sites/myapp user$ cap deploy:setup

In questo modo Capistrano tenta di connettersi alle macchine remote, e su ciascuna di esse crea, nella cartella indicata in :deploy_to, le seguenti cartelle:

releases
current
shared
shared/log
shared/system
shared/pids

La cartella releases ospita tutti i rilasci dell’applicazione, e ciascun rilascio è contenuto a sua volta in una cartella nominata con il timestamp del momento in cui si è lanciato il deploy. current è un link che punta all’ultima release servita, oppure ad una release precedente se abbiamo lanciato il comando di rollback (vedremo a breve come). shared è destinata a contenere tutti quei file generati dall’applicazione come i log, i pid ma anche ad esempio immagini caricate dagli utenti ed altri assets generati successivamente. Dopo aver lanciato cap deploy:setup, possiamo servirci di cap deploy:check per verificare che tutte le dipendenze siano soddisfatte e che tutti i permessi siano impostati correttamente. Se qualcosa non dovesse essere al suo posto, questo comando ci aiuterà a risolvere i problemi. A questo punto possiamo lanciare:

host:Sites/myapp user$ cap deploy

che effettuerà il checkout della nostra applicazione sulle macchine specificate, avvierà i server ed eseguirà in successione tutti i task che abbiamo specificato. Se in qualsiasi momento volessimo tornare indietro ad una release precedente:

host:Sites/myapp user$ cap deploy:rollback

sposterà il link current sulla release precedente.

Il prossimo appuntamento

Nel prossimo post vedremo:

  • come sovrascrivere alcuni task di default riferiti a Rails
  • le strategie di deploy
  • la creazione di namespaces contenenti task personalizzati
  • la maniera migliore per servire la nostra applicazione in una pluralità di ambienti (collaudo, produzione, etc)
  • come è possibile raggiungere mediante Capistrano delle macchine dietro un proxy

Commenta

Reload Image