Rails-Applikationen mit nginx und Thin


Wer Rails-Appli­ka­tio­nen deploy­en möch­te, wird ver­mut­lich als ers­tes auf die Kom­bi­na­ti­on Apa­che + Phusi­on Pas­sen­ger stos­sen. So ging es auch mir. Prak­tisch inso­fern, da auf fast jedem Web­ser­ver ohne­hin ein Apa­che läuft und der Pas­sen­ger in Win­des­ei­le instal­liert ist. Beim Pas­sen­ger han­delt es sich um ein Apa­che-Modul (mod_rails), die Kon­fi­gu­ra­ti­on in einem vHost ist ein Drei­zei­ler. Also eigent­lich alles toll… wenn da nicht die Geschwin­dig­keit wäre. Wenn der Pas­sen­ger erst mal läuft, ist die Geschwin­dig­keit durch­aus als gut zu bewer­ten, aber wehe, wenn die frag­li­che Rails-Appli­ka­ti­on eine Wei­le nicht mehr benutzt wur­de. Dann wer­den die Pas­sen­ger-Instan­zen mei­ner Beob­ach­tung nach näm­lich voll­stän­dig abge­schos­sen und müs­sen neu gestar­tet wer­den, wenn doch wie­der auf die Appli­ka­ti­on zuge­grif­fen wird. Auf mei­nem Ser­ver (AMD Athlon64 X2 5.600+, 4 GB RAM, 2 x 400 GB HDD im RAID‑1; drei vir­tu­el­le Maschi­nen) dau­ert das mit­un­ter ca. 20 — 30 Sekun­den und macht in die­ser Zeit die kom­plet­te VM unbe­nutz­bar, ande­re Web­sei­ten wer­den also nicht mehr aus­ge­lie­fert, der SSH-Dae­mon reagiert nicht. 

Seit ich ange­fan­gen habe, mich mit Rails zu beschäf­ti­gen, lese ich eini­ge Blogs von Fir­men, die sich “haupt­be­ruf­lich” mit Rails auf die eine oder ande­re Art und Wei­se beschäf­ti­gen. Dazu gehö­ren unter ande­rem ame­ri­ka­ni­sche Unter­neh­men wie Engi­ne Yard [1], Rackspace[2] und GitHub[3]. In Deutsch­land ist Rails lei­der noch immer nicht so recht ange­kom­men, die Anzahl wirk­lich inter­es­san­ter, inno­va­ti­ver Unter­neh­men in die­sem Bereich hält sich also stark in Gren­zen. Die soeben genann­ten Unter­neh­men betrei­ben ent­we­der für sich selbst oder im Kun­den­auf­trag gro­ße Rails-Instal­la­tio­nen und kön­nen sich sol­che Pro­ble­me, wie die im vor­he­ri­gen Absatz geschil­der­ten, abso­lut nicht erlau­ben. Ins­be­son­de­re Engi­ne Yard und Git­Hub betrei­ben viel Social Net­wor­king und haben sehr akti­ve gepfleg­te Blogs. Und in die­sen bin ich auf so eini­ge inter­es­san­te Infor­ma­tio­nen gestos­sen, unter ande­rem auch mein Pro­blem betreffend. 

Wenn man eine Appli­ka­ti­on in Rails ent­wi­ckelt, ver­wen­det man wäh­rend der Ent­wick­lungs­pha­se der Ein­fach­heit hal­ber einen sehr rudi­men­tä­ren klei­nen Web­ser­ver namens WEB­rick. Er tut das, was er tun soll, star­tet schnell, eig­net sich für den Pro­dukt­be­trieb auf­grund feh­len­der Sicher­heits­fea­tures und ziem­lich mau­er Per­for­mance nur bedingt. Alter­na­tiv kann man Mon­grel nut­zen. Auch die­ser ist fest mit Rails ver­bun­den, bie­tet aber deut­lich mehr Per­for­mance als WEB­rick. Mon­grel exis­tiert auch in einer spe­zi­el­len Clus­ter-Ver­si­on. Mit Clus­te­ring ist hier­mit gar nicht mal das ver­teil­te Rech­nen auf ver­schie­de­nen Sys­te­men gemeint, son­dern nur die Tat­sa­che, dass meh­re­re Instan­zen gestar­tet wer­den kön­nen. Dies ist ein im Rails-Bereich mei­nen Infor­ma­tio­nen nach ein übli­ches Set­up. Es ist ein­fach auf­zu­set­zen und Mon­grel genügt für die meis­ten Zwe­cke. Aber Mon­grel ist nun mal kein Apa­che und steht die­sem in sei­nen Kon­fi­gu­ra­ti­ons­mög­lich­kei­ten mas­siv nach. Einen rei­nen Mon­grel-Clus­ter zu betrei­ben scheint also auch kein pro­ba­tes Mit­tel zu sein. 

Nach dem Stu­di­um eini­ger Blog-Arti­kel ent­schied ich mich dann für den Tool­stack, um den es hier gehen soll: nginx und Thin. nginx[4] als voll-funk­tio­na­len Web­ser­ver mit wenig Res­sour­cen­hun­ger und Thin[5] als App­li­ca­ti­on Ser­ver. Thin beschreibt sich selbst als die opti­ma­le Kom­bi­na­ti­on aus dem Mon­grel par­ser, Event Machi­ne (einer Netzwerk‑I/O‑Bibliothek) und Rack (Mini­mal-Inter­face zwi­schen Web­ser­ver und Ruby-Appli­ka­ti­on). Die­ser Tool­stack begeis­tert mich noch immer, ob er das auch unter wirk­li­cher Last tun wür­de, kann ich (noch) nicht sagen, ich wer­de dann berichten. 

Nun zur Installation. 

Der ers­te Schritt soll­te die Instal­la­ti­on des ent­spre­chen­den Gems (und eines zuge­hö­ri­gen) sein. Mittels 

sudo gem install thin; sudo gem install eventmachine --source http://code.macournoyer.com

instal­lie­ren wir Thin. Um zu tes­ten, ob alles in Ord­nung ist, neh­men wir einen Ver­zeich­nis­wech­sel in zu unse­rer Rails-Appli­ka­ti­on vor und tippen 

thin start

ein. Es soll­ten ein paar Sta­tus­mel­dun­gen aus­ge­ge­ben wer­den, danach könnt ihr über http://domain.tld:3000 eure Rails-Appli­ka­ti­on auf­ru­fen. Soll­te Thin nicht star­ten, was bei mir der Fall war, müsst ihr Thin noch zu eurer PATH-Varia­ble hin­zu­fü­gen. Thin liegt auf mei­nem Ser­ver (Ubun­tu 9.04 Ser­ver) im Verzeichnis 

/var/lib/gems/1.8/bin

Stellt sicher, dass die­ser Ein­trag als aller­ers­ter in der PATH-Varia­ble steht. Ich habe zu die­sem Zweck mei­ner ~/.bashrc ein­fach am Ende fol­gen­de Zei­le hinzugefügt: 

export PATH=/var/lib/gems/1.8/bin:$PATH

Da das aber nicht das ist, was wir wol­len, instal­lie­ren wir nun nginx. Ich habe mich dafür ent­schie­den, die aktu­el­le Sta­ble aus dem Source Code zu kom­pi­lie­ren. Ein Blick auf http://nginx.org/en/download.html zeigt uns alle ver­füg­ba­ren Ver­sio­nen. Ich arbei­te aktu­ell mit Ver­si­on 0.7.65. Nach dem Down­load mittels 

wget http://nginx.org/download/nginx-0.7.65.tar.gz

ent­pa­cken wir das Archiv mit 

tar xvfz nginx-0.7.65.tar.gz

in das aktu­el­le Ver­zeich­nis. In die­ses Ver­zeich­nis wird nun gewech­selt. Ich habe mei­nen nginx mit den fol­gen­den Optio­nen kompiliert: 

 
./configure \
--prefix=/usr \
--conf-path=/etc/nginx/nginx.conf \
--http-log-path=/var/log/nginx/access_log \
--error-log-path=/var/log/nginx/error_log \
--pid-path=/var/run/nginx.pid \
--http-client-body-temp-path=/var/tmp/nginx/client \
--http-proxy-temp-path=/var/tmp/nginx/proxy \
--http-fastcgi-temp-path=/var/tmp/nginx/fastcgi \
--with-md5-asm --with-md5=/usr/include \
--with-sha1-asm \
--with-sha1=/usr/include \
--with-http_realip_module \
--with-http_ssl_module \
--with-http_stub_status_module

Der abschlie­ßen­de Build und die Instal­la­ti­on erle­di­gen wir so: 

make && make install

Nun geben wir noch 

sudo thin install && sudo /usr/sbin/update-rc.d -f thin defaults

um Thin auto­ma­tisch star­ten zu lassen. 

Wei­ter geht es mit der Konfiguration. 

Kon­fi­gu­rie­ren wir als ers­tes Thin für den Live-Betrieb: 

sudo thin config -C /etc/thin/railsapp.yml -c /var/www/railsapp --servers 2 --socket /var/run/thin/railsapp.sock -e production

Die­ser Ein­zei­ler legt im Ver­zeich­nis /etc/thin/ eine Kon­fi­gu­ra­ti­ons­da­tei im YAML-For­mat mit dem Namen railsapp.yml an. In die­ser Datei wird fest­ge­hal­ten, wie Thin arbei­ten soll. In die­sem kon­kre­ten Fal­le wird, was sehr emp­feh­lens­wert ist, eine Socket- anstel­le einer TCP/IP-Ver­bin­dung ver­wen­det. Es wer­den zwei Ser­ver­in­stan­zen gestar­tet, die par­al­lel lau­fen. ACHTUNG! Wer hier die Zahl zu hoch wählt, macht es sei­nem Ser­ver ganz schnell ganz schwer. Also vor­sich­tig erhö­hen und nur, wenn es unbe­dingt nötig ist. Bemerkt man einen Fla­schen­hals, kann man hier natür­lich rum­schrau­ben. Das Haupt­ver­zeich­nis der Rails-Anwen­dung ist in die­sem Fal­le /var/www/railsapp und die Sockets wer­den in /var/run/thin/ ange­legt. Der letz­te Schal­ter legt noch die Rails-Envi­ron­ment fest, hier wird also das Pro­duc­tion Envi­ron­ment genutzt. 

Die Kon­fi­gu­ra­ti­on von nginx ist recht sim­pel. Mei­ne /etc/nginx/nginx.conf sieht wie folgt aus: 

  
user www-data;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
# multi_accept on;
}
http {
include /etc/nginx/mime.types;
access_log /var/log/nginx/access.log;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
gzip on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

Als Sys­tem­be­nut­zer setzt die­ses Set­up einen Benut­zer mit dem Namen www-data vor­aus. Außer­dem wer­den zwei Worker-Pro­zes­se gestar­tet. Wer in sei­ner Rails-Anwen­dung eine Benut­zer­ver­wal­tung ein­set­ze, soll­te natür­lich auf htt­ps anstel­le von http set­zen. Wie man einen htt­ps-Con­tai­ner mit nginx ein­rich­tet, wer­de ich geson­dert an die­ser Stel­le beschreiben. 

Im Ver­zeich­nis /et­c/n­ginx/­si­tes-avail­ab­le legen wir nun einen vHost-Con­tai­ner für unse­re Rails-Anwen­dung an. Die­ser soll­te einen spre­chen­den Namen als Datei­na­men erhal­ten, also bspw. railsapp.domain.tld. Bei mir sieht der Inhalt wie folgt aus: 

  
upstream railsapp {
server unix:/var/run/thin/railsapp.0.sock;
server unix:/var/run/thin/railsapp.1.sock;
}
server {
listen 80;
server_name railsapp.domain.tld;
access_log /var/www/railsapp/log/access.log;
error_log /var/www/railsapp/log/error.log;
root /var/www/railsapp/public/;
index index.html;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://railsapp;
break;
}
}
}

Mittels 

ln -s /etc/nginx/sites-available/railsapp.domain.tld /etc/nginx/sites-enabled/railsapp.domain.tld

legen wir einen Sym­link in das Ver­zeich­nis sites-enab­led an. In die­sem befin­den sich alle vHost-Con­tai­ner, die nginx bedie­nen soll. 

Nun star­ten wir noch nginx und Thin neu 

sudo /etc/init.d/nginx restart; sudo /etc/init.d/thin restart

Gebt Thin ein paar Sekun­den, um wirk­lich ant­wor­ten zu kön­nen. Danach soll­te eure Rails-Anwen­dung über Port 80 aus­ge­lie­fert werden. 

Soll­te ich beim Erstel­len die­ser Anlei­tung einen Feh­ler gemacht haben, lasst es mich bit­te wis­sen, möch­te ja kei­ne fal­schen Infor­ma­tio­nen im Netz publizieren. 

[1] http://www.engineyard.com/
[2] http://www.rackspace.com/
[3] https://github.com/
[4] http://wiki.nginx.org/Main
[5] http://code.macournoyer.com/thin/

Pos­ted via email from ulfklose’s pos­te­rous


Eine Antwort zu “Rails-Applikationen mit nginx und Thin”

  1. For peop­le get­ting a 500 Inter­nal Error mes­sa­ge, make sure you crea­te your pro­duc­tion data­ba­se first by run­ning “rake db:migrate RAILS_ENV=production” befo­re you try to run your Rails app on your ser­ver — other­wi­se Rails won’t be able to access your data­ba­se and errors galo­re. (Duh, right? Well, I forgot 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.