Hostujeme Python weby bezpečně a flexibilně s uWSGI

1. 4. 2011
Doba čtení: 5 minut

Sdílet

Je to už rok a půl, co jsem se rozepisoval o hostování webových aplikací napsaných v Pythonu pod Apachem s modwsgi. Za tu dobu se toho hodně změnilo a nevýhody modwsgi začínají být řešeny projektem uWSGI. Ten umožňuje aplikace hostovat flexibilněji a není problém vytvořit ani bezpečný sdílený hosting.

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:

bitcoin_skoleni

<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.

Autor článku

Adam Štrauch je redaktorem serveru Root.cz a svobodný software nasazuje jak na desktopech tak i na routerech a serverech. Ve svém volném čase se stará o komunitní síť, ve které je již přes 100 členů.