In questo articolo, cerchiamo di capire le opzioni possibili per rendere fruibile un‘applicazione Rails in ambiente di esercizio. Da un web server proprietario quale WebBrick ai FastCGI di Lighttpd, passando per Apache Http Server, esaminiamo pregi e limiti delle varie soluzioni per fruire di applicazioni web dinamiche basate su Ruby.
Introduzione
Esistono sul mercato (diciamo sul mercato ??!!) varie soluzioni per dare il giusto coronamento alla nostra attività di sviluppo di un’applicazione Rails. Cerchiamo di delimitare quelle che possono essere considerate scelte sagge e ragionate
Facciamo una rapida panoramica degli ambienti di produzione di un’applicazione Rails. Cerchiamo di evidenziare vantaggi e svantaggi di ogni soluzione presa in considerazione.
A noi interessa disporre di una soluzione efficiente, sovente scalabile, ma anche facilmente gestibile in termini di eventuali workaround in caso di necessità.
Una panoramica generale
Innanzitutto premettiamo che ogni web server che supporta CGI potrebbe essere considerato un ambiente di deployment di un’applicazione Rails.
Indubbiamente CGI non è la nostra soluzione e il resto dell’articolo metterà in evidenza che questa è sicuramente l’ultima delle ipotesi che vogliamo perseguire in ambiente di esercizio.
In linea di massima diciamo che possiamo prendere in considerazione due macrocategorie di soluzioni per deployare applicazioni Rails. In realtà ci sarebbe anche la possibilità di un Application Server Java con JRuby, ma è tutta un’altra storia, e quindi vediamo le due macrocategorie da prendere in considerazione
- server web di tipo Ruby-based
- server web di tipo C-based
Come potrebbe essere intuitivo si tratta di soluzioni più semplici da configurare e gestire le prime, ma meno performanti e scalabili; esattamente il complemento delle seconde.
Passiamo ad analizzare qualche prodotto.
WEBrick
Soluzione disponibile embedded in qualsiasi progetto Rails, risulta essere l’ambiente ideale in fase di sviluppo o in ambienti in cui performance e scalabilità non risultano essere un requisito.
Supponiamo di avere Ruby On Rails installato su di una qualsiasi macchina; per eseguire WEBrick è sufficiente il comando
ruby {rails-application-root}/script/server
e attendere che sia disponibile (di default sulla porta 3000).
Il punto di forza è indubbiamente l’assenza di configurazione necessaria per il suo setup, l’assenza di dipendenze esterne e la capacità di fare il redeploy (o refresh) dell’applicazione “a caldo”.
Assolutamente ideale anche nel caso in cui si vogliano distribuire delle instant application.
Quando invece si ha l’intenzione di rendere fruibile un’applicazione in un contesto un pochino più serio è molto probabile che questa soluzione risulti essere inadeguata e venga rapidamente abbandonata.
RoR non è thread safe e WEBrick utilizza un workaround per gestire questo problema: serializza le richieste. Questo va, ovviamente a discapito delle prestazioni, all’aumentare delle richieste.
Mongrel
Altra soluzione in Ruby “easy to setup”.
Si ottiene rapidamente via gem, compreso il modulo mongrel_cluster, che ci consente di gestire delle situazioni leggermente più critiche rispetto a WEBrick.
Mongrel consente una certa scalabilità e questo ci permette spesso di evitare l’utilizzo di FastCGI (magari “dietro” Apache Http Server o altro Web Server).
In ogni caso si tratta di una soluzione Ruby-based che, non tanto all’aumentare delle richieste ma piuttosto alla loro “pesantezza”, rivela i propri limiti: non supporta SSL!
Potrebbe essere una soluzione nel caso di applicazioni che non necessitano di SSL, con molti contenuti statici (oltre che dinamici basati su Ruby), che necessitano di essere “scalate” per il numero delle richieste.
Mongrel è molto flessibile e può essere configurato dietro load balancer, con un Apache Http Server (o altro web server) che serve i contenuti statici.
Come accennato, l’installazione di Mongrel avviene anche direttamente con gem quindi con una difficoltà di setup praticamente nulla.
gem install mongrel
o
gem install mongrel_cluster
e tutte le dipendenze saranno a noi disponibili.
Per mandarlo in esecuzione ci spostiamo in {rails-application-root} e digitiamo
mongrel_rails start -d
Apache Http Server con CGI
Apache Http Server con il modulo CGI è probabilmente la soluzione meno performante di quelle qui prospettate. l’interprete ruby viene inizializzato ad ogni singola richiesta. Questo sta a significare che non poche richieste necessitano di più di qualche secondo per essere soddisfatte. Nonostante questo grosso limite, la poniamo tra le alternative possibili semplicemente perchè si tratta della più semplice soluzione basata sul server web più utilizzato al mondo (con tutte le sue features a corredo). Inoltre la configurazione di Apache Http Server con CGI diventa senza troppa difficoltà Apache con FastCGI.
Una volta modificato il nostro public/.htaccess
RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
RewriteEngine On
Apriamo il file di configurazione httpd.conf (o equivalente a seconda dei sistemi operativi), e facciamo in modo di riportare almeno questa configurazione di base per rendere funzionanti i CGI (la medesima configurazione vale anche per FastCGI).
DocumentRoot /path/to/rails-application/public/
ErrorLog /path/to/my-error_log
Options Indexes ExecCGI FollowSymLinks
Una volta verificato il corretto funzionamento sarebbe il caso di andare nella direzione di impostare un virtual host per ogni nostra applicazione sia per pulizia, sia per facilitare la gestione nel caso di più applicazioni residenti sullo stesso server.
Molto probabilmente non utilizzeremo mai questa soluzione in ambiente di esercizio, ma diciamo che questa può essere considerato uno step intermedio prima di giungere alla successiva soluzione.
Apache Http Server con FastCGI
Al momento la soluzione migliore per far fronte ai requisiti più vari: stabilità, performance, scalabilità, diffusione di skill. Indubbiamente richiede una configurazione non proprio immediata, ma personalmente non ritengo essere questo il limite che ne possa impedire setup e l’utilizzo in un ambiente di produzione.
Anche perchè gli sforzi sostenuti vengono ampiamente ripagati a posteriori.
Il progetto FastCGI è rimasto piuttosto “calmo” fino al 2003, poi ha subito, anche sotto la spinta dell’affermazione di RubyOnRails una sorta di risveglio che ha consentito di colmare molte lagune presenti. (con Apache 2.0 si può utilizzare anche il più recente fcgid oltre che fastcgi)
Vediamone un ipotesi di configurazione.
Abilitato in Apache il modulo FastCGI
LoadModule fastcgi_module /path/to/apache2/modules/mod_fastcgi.so
è necessario fare riferimento all’applicazione Rails di nostro interesse con qualcosa del tipo:
AddHandler fastcgi-script .fcgi
FastCgiIpcDir /path/to/lib/apache2/fastcgi
FastCgiServer /path/to/rails-application/public/dispatch.fcgi
-initial-env RAILS_ENV=development
-processes 15 -idle-timeout 60
L’istruzione di maggiore interesse è il parametro FastCgiServer che ci consente di puntare al dispatcher della nostra applicazione.
A questo punto il lavoro si sposta nella dispatch.fcgi della nostra applicazione Rails.
Attenzione a non dimenticare di editare .htaccess dell’ applicazione (in /path/to/rails-application/public/) al fine di consentire di redizionare verso Rails tutto ciò che non viene trovato sul file system. Qualcosa del tipo:
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
RewriteEngine On
Ora dovrebbe essere tutto a posto e dovremmo essere in grado di eseguire l’applicazione Rails con Apache Http Server+FastCGI.
Lighttpd con FastCGI
Mentre Apache Http Server è la base per una miriade di soluzioni per ambienti anche molto eterogenei tra di loro, ha moltissima documentazione e moduli per le esigenze più svariate, esistono altri prodotti molto specializzati per consentire la fruizione di applicativi Rails.
Tra questi prendiamo in considerazione Lighttpd.
Noto per le prestazioni, ma anche per le numerose “defaillance”, lo rendono una soluzione di produzione da monitorare con la massima accortezza.
La configurazione non è particolarmente onerosa e comunque è concentrata in un unico file.
Supponendo di far girare una sola applicazione avremmo una configurazione (lighttpd.conf) del tipo:
# moduli caricati in lighttpd
server.modules = ("mod_rewrite","mod_redirect","mod_fastcgi")
# document root del server web
# non è presente un virtual host, quindi puntiamo all'applicazione direttamente
server.document-root = "/path/to/rails-application/public/"
# log
server.errorlog = "/path/to/log/error.log"
# file a cui puntare di default
server.indexfiles = ("dispatch.fcgi")
# regola di rewrite
url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
# alcuni parametri del nostro server
# nessun virtual host configurato
fastcgi.server = (".fcgi" => ( "localhost" =>
( "min-procs" => 1,
"max-procs" => 5,
"socket" => "/path/to/tmp/railsapp.fcgi.socket",
"bin-path" => "/path/to/rails-application/public/dispatch.fcgi",
"bin-environment" => ("RAILS_ENV" => "development")
))
Con questa configurazione di base dovremmo (poter) far “girare” la nostra applicazione.
Se abbiamo necessità di qualcosa di più fine, facciamo riferimento alla documentazione di Lighttpd.
Una considerazione
Scegliere una soluzione piuttosto che l’altra non ha comunque impatti sull’applicazione realizzata. Possiamo tranquillamente mettere in linea un servizio con una scelta veloce ma poco performante e scalabile prendendoci il tempo di preparare l’ambiente più serio (a meno che dal primo giorno non siamo certi di avere centinaia di migliaia di utenti). Lo switch dalla prima soluzione all’altra non ha impatti sul codice scritto (per i lettori Java-based questa considerazione potrebbe sembrare superflua e scontata, ma non sempre lo è!!!).
Una tabella di confronto
A seguire riportiamo una tabella di riepilogo che consente, con una quantificazione degli aspetti qualitativi finora menzionati, di dare una veloce visione di insieme della situazione attuale in merito agli ambienti di deployment di applicazioni Rails. Non essendo basata su dati quantitativi sentitevi in dovere di revisionarla (in funzione di nuove versioni, di nuove features, in base ai sistemi operativi utilizzati, ecc). L’idea di fondo è semplicemente quella di dare un’idea di fondo sull’argomento evitando così sgradevoli improvvise sorprese.
(1 = valore minimo, 5 = valore massimo)
Conclusioni
Al momento, una serie di motivi tra i quali stabilità e scalabilità, ma non ultimi anche la diffusione del prodotto, la possibile coesistenza con applicativi basati su altra tecnologia (per esempio PHP), il supporto e gli skill piuttosto diffusi, ci spingono ad utilizzare, in caso di applicazioni critiche dal punto di vista delle performance, un ambiente basato su Apache Http Server con FastCGI. Negli altri casi… sperimentate senza problemi…
Riferimenti
[1] Dave Thomas, “Agile Software Development with Rails. The Pragmatic Programmer’s Guide”, Pragmatic Bookshelf, 2005
http://www.pragmaticprogrammer.com
[2] Mongrel Web Site
http://mongrel.rubyforge.org/
[3] Apache Http Server
http://httpd.apache.org/
[4] FastCGI Web Site
http://www.fastcgi.com/
[5] Lighttpd Web Site
http://www.lighttpd.net/