Hostování pythoních webových aplikací je nevděčná činnost. Když už se vám podaří server nastavit bezpečně s mod_wsgi a někdo ho najde, začnete řešit problémy jednotlivých projektů. Během posledního roku jsem řešil hned několik takových situací, které s PHP nikdy nemohou vzniknout. Python má standardizované rozhraní pro komunikaci s webovým serverem, ale hned o vrstvu dál si každý framework řeší spojení trochu jinak. Když už se vám povede nastavit server tak, že na něm běží několik pythoních webů a podle svého nejlepšího svědomí všechno nastavíte bezpečně, zjistíte, že nový zákazník by rád Python ve verzi o stupínek výš, a protože je dobrodruh, chtěl by také nové Django, nejlépe ještě z SVN.
Když se vám to podaří nějak vyřešit, většinou rozumným kompromisem na obou stranách, přijde někdo další, kdo má rovnou dvě aplikace, kde každá vyžaduje něco jiného. Administrace, skripty a vůbec vše, co má takový pythoní hosting mít, je napsáno, a protože si uvědomujete, co změna obnáší, opět se snažíte o kompromisy. Jednoho dne to přijde a když nasadíte na server první desítku aplikací vyvinutých v rámci jednoho roku většinou s tím nejnovějším možným, řeknete si, takhle to dál nejde a začnete hledat, jak pythona zahostovat ještě lépe než s mod_wsgi.
Proč uWSGI
V samotném modwsgi není problém. Pokud máte rádi Apache a nechce se vám s hostingem moc trápit, určitě se na modwsgi podívejte, hlavně na jeho daemon režim. Jednou ale přijde chvíle, kdy závislost na Apachi není žádoucí a navíc modwsgi nezvládá změnit pythoní domovský adresář, což znamená, že pro všechny aplikace musíte použít jedno prostředí. Na serveru s několika weby to problém není, ale pokud jste nuceni hostovat aplikace využívající různé frameworky různých verzích s různými závislostmi, s modwsgi daleko nedojdete.
Tyto problémy řeší uWSGI, ale se ztrátou komfortu, který mod_wsgi nepochybně má. uWSGI je malý program, jehož úkol je udržovat běžící procesy s vaším webem a případně upravovat parametry jejich běhu. Vaše aplikace komunikuje s uWSGI přes protokol WSGI a uWSGI má v každém známějším webovém serveru modul a komunikuje protokolem vlastním. Z tohoto konceptu plyne hned několik výhod:
- Aplikace jsou oddělené na úrovni systémových uživatelů (to jde i s mod_wsgi)
- Aplikace lze limitovat na určitou paměť a procesorový čas
- Aplikace lze nezávisle na sobě restartovat a reloadovat (dokonce z aplikace samotné)
- Každá aplikace může běžet ve vlastním prostředí (virtualenv)
Instalace
Základem je instalace, respektive kompilace uWSGI. Zdrojové kódy si stáhneme z domovské stránky a rozbalíme do /usr/src/.
$ cd /usr/src
$ wget http://projects.unbit.it/downloads/uwsgi-0.9.7.1.tar.gz
$ tar xf uwsgi-0.9.7.1.tar.gz
$ apt-get install libxml2-dev #jediná závislost co v mém systému chyběla
$ ./uwsgi -h
Usage: ./uwsgi [options...]
-s|--socket <name> path (or name) of UNIX/TCP socket to bind to
-p|--processes <n> spawn <n> uwsgi worker processes
-p|--workers <n> spawn <n> uwsgi worker processes
-t|--harakiri <sec> set harakiri timeout to <sec> seconds
[...]
Kompilace není složitá a jelikož je výsledkem prakticky jen binárka uwsgi, není složité udělat ani balíček.
Použití
S každou verzí rostou také možnosti uWSGI. Už dávno to není jen daemon, který hlídá pár procesů. Dnes nabízí sdílenou paměť, má v sobě testovací HTTP server, postará se o logování, podporuje konfiguraci přes XML, YAML a mnoho dalšího. Cílem tohoto článku je hlavně spustit uWSGI tak, aby jste se o aplikaci dál nemuseli starat a ta běžela, pokud možno rychle a stabilně.
Nejprve si vytvoříme skript, který se postará o vytvoření handleru pro komunikaci s uWSGI. Ten jsme si ukázali už v článku o mod_wsgi, takže ho sem jenom zkopíruji.
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'projekt.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
Tento konkrétní je určen pro Django.
Pokud máme hotovo, můžeme spustit uWSGI server s aplikací.
/usr/src/uwsgi-0.9.6.8/uwsgi --wsgi-file=/cesta/ke/skriptu/s/wsgi/handlerem
--python-path=/vlastni/python/path \
--python-path=/druha/vlastni/python/path \
--master \
--processes 5 \
--optimize 1 \
--home /cesta/k/virtualenv \
--limit-as 64 \
--chmod-socket 660 \
--uid uzivatel \
--gid skupina \
--pidfile /var/run/muj_web.pid \
--socket /var/run/muj_web.socket \
--daemonize /var/log/muj_web.log
První tři parametry jsou určitě jasné. Čtvrtým parametrem řekneme uWSGI, že má kromě procesů s naší aplikací spustit také proces, který bude tyto kontrolovat. Celkově má aplikace běžet v pěti instancích a když přidáme ještě parametr –thread, poběží aplikace místo v procesech v threadech a ušetří se nějaká paměť. Optimalizaci můžeme nastavit na 0 (bez), 1 (základní pythoní optimalizace) a 2 (agresivnější, ignorování doc řetězců). Parametr –home nastaví cestu k domácímu adresáři Pythonu, resp. k virtualenv. Dále omezíme paměť na 64 MB RAM pro každý proces a socket bude mít práva 660, tedy vlastník a skupiny mohou číst a zapisovat a ostatní nic. Aplikace poběží pod zadaným –uid a skupinou –gid. Předposlední dva parametry nastavují cestu k souboru s PID a socketem a poslední říká uWSGI, že má běžet jako daemon a logovat výstup do zadaného souboru.
Takto spuštěnou aplikaci budeme občas potřebovat resetovat, reloadovat a zastavovat. To se dělá pomocí signálů a například programu kill. Při změně konfigurace běžícího uWSGI se musí aplikace zastavit a spustit znovu. Většinou však bude docházet ke změnám v aplikaci a tak uWSGI může běžet dál. K tomu slouží reload a k němu máme hned dva signály. Prvním je HUP s číslem 1, který obnoví procesy hned jak dokončí to co dělají. Uživatelé by tak neměli pocítit výpadek a když už, tak ne víc jak na jeden klik. Druhý reload je brutální a dělá se pomocí signálu TERM s číslem 15. Ten obnoví procesy, ať už dělají cokoli. Zastavení uWSGI se provádí signálem QUIT s číslem 3. V praxi to pak vypadá třeba takto:
# Jemný reload aplikace
$ kill -s 1 `cat /var/run/muj_web.pid`
Spojení s Apachem
Já byl z několika důvodů donucen spojit uWSGI s Apachem, ale nejvhodnější volbou je v současnosti webový server Nginx. Do Apache stačí dokompilovat modul, který najdeme ve zdrojovém kódu uWSGI, zavést ho a do VirtualHostu doplnit celkově čtyři řádky. Začneme kompilací modulu a zavedením:
$ cd /usr/src/uwsgi-0.9.6.8/apache2
$ apxs2 -i -a -c mod_uwsgi.c
$ /etc/init.d/apache2 restart
A přidáme do nějakého VirtualHostu uWSGI handler:
<Location />
SetHandler uwsgi-handler
uWSGISocket /var/run/muj_web.socket
</Location>
Po reloadu Apache by měl web běžet:
$ /etc/init.d/apache2 reload
Závěr
Aplikace nám běží, ale tady možnosti uWSGI nekončí. Dobrým zdrojem informací o všem, co se uWSGI týká, jsou příklady použití na domovské stránce projektu a také dokumentace.