Apache + PHP + mod_fastcgi + spawn-fcgi
Ostatnio natknąłem się na coś zwane spawn-fcgi, w skrócie jest to programik który odpowiada za uruchomienie serwera fcgi (pierwszy z brzego przykład: PHP) z zadanym userem, grupą, ew. chroot na danym ip/porcie, lub sockecie unixowym. W swojej idei pomyślany był jako sposób żeby lighttpd mógł uruchamiać PHPa z innym userem niż ten na którym sam pracuje (trochę tak jak apache + mod_fastcgi + suexec), ale bez żadnych SUIDów (którego admini jednak unikają z dobrych powodów) i skomplikowaniej konfiguracji (bo podłączenie się do niego z lightego to jedna linijka configa).
I jakby nie spojrzeć spisuje się świetnie, ale że jest napisany jako uniwersalny “klocek” to nie jest jedyne jego zastosowanie. Po podłączeniu go do apacha i odpalenia prostego benchmarku (ab -c 32 -n 200000 na malutkim phpie zeby sprawdzic szybkosc “wchodzenia i wychodzenia” z phpa) rezultaty były ciekawe, ponieważ apache + spawn-fcgi był szybszy od lightego (zarowno z “wbudowanym fcgi jak i z spawn-fcgi) i apacha z mod_php o jakies 15-20% (YMMV, testowany był tylko na malutkim skrypcie więc im dłużej wykonuje się wasza aplikacja PHPowa tym różnica będzie mniejsza, w RL pewnie z 2, góra 5%), więc pomyślałem że warto byłoby opisać instalację tego, zwłaszcza że jest jeden niemiły “bug” z którym poradzenie sobie zajęło mi chwilę.
1.Instalacja:
- Ściągamy i kompilujemy spawn-fcgi (starsza wersja jest w pakiecie lighttpd w debianie, ale nowsza potrafi zmieniać uprawnienia socketu a to jest coś czego potrzebujemy)
Podstawowa instalka apacha + daemontools (do startowania spawn-fcgid
aptitude install apache2-mpm-worker php5-cgi daemontools daemontools-run
Tworzymy katalog na sockety, ja wybrałem /var/run/apache2/php
Czas odpalić spawn-fcgid, tworzymy katalog /etc/service/php-user1 a potem plik /etc/service/pgp/user1/run :
#!/bin/sh USER=app01 PROC=16 #liczta phpow do spawniecia rm -f /var/run/apache2/php/$USER-php5.fcgi exec /usr/local/bin/spawn-fcgi -n -U www-data -M 0600 -u app01 -g www-data -C $PROC -s /var/run/apache2/php/$USER-php5.fcgi -f /usr/bin/php5-cgi
ps aux i szukamy czy nasze phpiki się odpaliły ;] Co do opcji powyżej, -u i -g to user i grupa do odpalenia serwera fcgi, -U i -G to user i grupa socketu -M to uprawnienia socketu. Soft obsługuje też tryb tcp, oszczędza trochę roboty (nie trzeba sprzątać ;]) ale jeżeli php jest lokalnie to tcp jest marnotrawstwem procka ;]
Czas na apacha, uruchamiamy mod_fastcgi (nie wiem jak gdzie indziej, w debie a2enmod fastcgi ), wchodzimy do naszego vhosta i dodajemy:
AddHandler php5-script .php AddType application/x-httpd-fastphp5 .php Action application/x-httpd-fastphp5 /jakies-nieistniejace-cos Action php5-script /jakies-nieistniejace-cos FastCgiExternalServer /var/www/nasz_app/jakies-nieistniejace-cos -socket /var/run/apache2/php/app01-php5.fcgi -pass-header HTTP_AUTHORIZATION
I teraz pewnie co niektórzy będą się zastanawiać wtf is*** jakies-nieistniejace-cos ***(i slusznie). Otóż, autorzy mod_fastcgi zrobili coś źle i gdy użyjesz “FastCgiExternalServer /var/www/app” fastcgi wysle wszystkie żądania kierujace na /var/ww/app do serwera fastcgi, kompletnie olewając wszelkie inne opcje Apacha (czyli np gify będą szły do php-cgi a puste stony beda zwracane komunikatem interpretera phpa ze nie znalazl pliku), czego raczej nie chcemy.
Więc ten kawałek kodu mówi fastcgi ze scieżka na której ma “działać” jest /nasz/app/wyimagionwane-cuś a potem każe plikom php działać przez */nasz/app/wyimaginowane-cuś.* Grzebanie w necie i apachu zajęło mi trochę zanim do tego doszedłem ale w końcu działa całkiem ładnie ;]. Chociaż rozwiązanie od strony apacha trochę “brzydkie” ale konkurencja (mod_fcgid) nie potrafi podłączać się do “zdalnego” fcgi wcale więc dużego wyboru nie ma ;]
Zalety:
- Separacja uprawnień
- Możliwośc restartu/upgrade Apacha bez restartu PHPa (czyszczenia opcode cache itd.)
- Szybkość (separacja uprawnień przez “standardowy” suexec jest wolna a suexec + fastcgi jest jeszcze bardziej skomplikowany w konfiguracji), przynajmniej u mnie był szybszy od mod_php i od lighttpd
Wady
- Trochę roboty w ustawianiu wszystkiego na początku
- Każdy nowy “user” wymaga oddzielnego spawn-fcgi (ale przy odpowiednim skrypcie startowym jest to prawie “copy-paste”)