Facebook
Twitter
Google+
Kommentare
6

PHP-FPM: Mit der Lizenz zum Verwalten

Es gibt ja Themen, in denen ich nicht tief drinstecke. Deswegen bin ich froh, dass es Leute wie Oliver gibt, die mir dann mit ihrem Wissen unter die Arme greifen und ihr wissen mit uns teilen. Also schon mal vielen Dank und los geht’s mit dem Artikel:

Gastartikel von Oliver Sperke: Ich bin 34 Jahre alt und seit 10 Jahren selbständiger Webentwickler. Mein Fokus liegt dabei auf der Erstellung, Beratung und Optimierung in den Bereichen High Performance, Usability und Sicherheit in den gängisten Internetsprachen: PHP, HTML, Javascript und CSS.

Der FastCGI ProcessManager ist im Vergleich zu den üblichen PHP Varianten als Webservermodul oder klassisches (Fast)CGI eher unbekannt. Lange Zeit fristete er auch deshalb ein Schattendasein, weil die Einrichtung sehr aufwendig war, denn für die Verwendung mussten PHP Quellen gepatcht und dann neu kompiliert werden. Aber wie der aufmerksame Leser sofort bemerkt hat, das war einmal!

Seit PHP 5.3 ist der FPM direkt in PHP integriert, d. h. kein patchen und kein neu kompilieren. Also raus aus der Vergessenheit und mal angesehen das Teil.

Webserver und dynamische Inhalte

Um FastCGI zu verstehen, muss man etwa 15 Jahre zurück gehen. Ein Webserver kann standardmässig nur statische Dateien ausliefern. Genau dafür wurde er programmiert. Sobald man Dynamik in seine Internetseiten bringen will, steht man mit dem Webserver alleine erst einmal blöd da, denn der kann keine Programmiersprachen interpretieren. Um eine Webseite dynamisch zu erzeugen, muss er also den Request weitergeben, auf das Ergebnis warten und dann ausliefern. Um dieses Vorgehen zu vereinheitlichen, haben sich schlaue Menschen eine Schnittstelle überlegt, über die diese Daten ausgetauscht werden. Die Idee des Common Gateway Interfaces (CGI) war geboren.

CGI in seiner Ur-Version funktionierte so, dass die Anfrage eines Besuchers vom Webserver angenommen wurde, dieser erkennt anhand der Endung, kann ich nicht und startet die Laufzeitumgebung der verwendeten Sprache. Klingt soweit gut, hat aber große Nachteile. Scriptsprachen wie Perl oder PHP sind im Vergleich zu den ausgeführten Scripten sehr groß und brauchen daher auch viel Platz im Arbeitsspeicher. Ausserdem muss der Prozess erst einmal geladen werden, was ebenfalls vgl. zeitintensiv ist. In Kombination führt das zu einer wirklich lausigen Performance.

Dauerhaft kam man so also nicht weiter. Um dieses Problem zu lösen, wurde als zweiter Ansatz mod_php (oder mod_python, mod_perl usw.) erfunden. Die Idee ist, den Webserver mit den Funktionen der Scriptsprache auszustatten und dann die Prozesse quasi selbst abzuarbeiten. Das funktioniert wunderbar, hat aber den Nachteil der fehlenden Skalierbarkeit, denn man kann Prozesse nicht auslagern. Ausserdem kann das auch nicht jeder Webserver, denn die Umsetzung ist aufwendig und muss für jede Scriptsprache und jeden Webserver seperat erstellt werden.

Ein einfacherer Ansatz ist FastCGI. Dieses „schnelle Gateway“ ist eine Weiterentwicklung von CGI. Die Idee ist, Laufzeitumgebungen in begrenzter Anzahl zu starten, z. B für jeden Prozessor einmal und die Aufgaben diesen gestarten Prozessen „rüber zu schieben”. Die Vorteile liegen auf der Hand. Der Prozess muss nicht gestartet werden – er ist schon da. Ausserdem belegt die Laufzeitumgebung nur so viel Arbeitsspeicher, wie es unbedingt für die gestarteten Umgebungen benötigt. FastCGI ist so auch über mehrere Systeme skalierbar, denn die Kommunikation kann über TCP ablaufen.

Der Webserver muss dabei aber sicher gehen, dass die Prozesse auch gestartet sind. Dafür gibt es einige Brücken, wie z. B. die spawn-php.sh von lighty, was aber wieder die Skalierbarkeit einschränkt. Weiter haben FastCGI und CGI Prozesse immer Nachteile bei der Sicherheit. Da eine Laufzeitumgebung immer mit den Rechten läuft, mit denen sie gestartet wurde, also meistens root. So kann man sich mit der Verwendung von FastCGI richtig tolle und umfangreiche Sicherheitslücken reißen. Eine sichere Abgrenzung der Prozesse nach Benutzern ist möglich, aber aufwendig.

Was PHP-FPM kann

Sicherheit und Performance

PHP-FPM füllt genau diese Lücken, denn wie der Name schon sagt verwaltet der Manager sich und seine Prozesse selbst. FPM arbeitet als Dienst im Hintergrund wie mysql oder memcache. Einmal installiert, kann man über die Konfigurationsdatei Benutzergruppen definieren, die sog. Pools, mit denen es sehr einfach ist, PHP Prozesse je nach Anfrage über verschiedene Ports diesen Pools zuzuordnen. Der Sicherheitsaspekt ist dabei nicht zu unterschätzen, denn PHP-FPM kann Prozessen so gezielt Benutzerrechte oder eigene chroot Umgebungen zuordnen. Auch die Verwendung von unterschiedlichen php.ini Dateien ist damit problemlos machbar. Schluss mit dem Safe Mode.

Um für Stabilität zu sorgen ist es auch möglich, Ressourcen auf Benutzer unterschiedlich zu verteilen, denn man kann festlegen, dass ein Prozess von Benutzer A maximal 5 Sekunden dauern darf, während ich Benutzer B auch mal 30 Sekunden rechnen lasse. Amoklaufende Prozesse werden gezielt „erlegt“. Und man kann jedem Benutzer eine bestimmte Anzahl von Prozessen zurordnen, die er nicht überschreiten darf.

PHP-FPM verbessert nicht die Performance, ist aber auch nicht langsamer als reguläres FastCGI. Richtig eingesetzt verbessert es vor allem auf Virtuellen Hosts die Sicherheit und vereinfachen dabei die Verwaltung. Aber selbst auf einzelnen Systemen, auf denen nur ein Benutzer oder nur eine Domain aktiv ist, kann sich der Einsatz lohnen, denn PHP-FPM ist innerhalb von Sekunden so aufgesetzt, dass es in einer chroot mit eigener uid/gid läuft. Wer das mal mit FastCGI „auf die Schnelle“ machen wollte, wird sich jetzt die Augen gerieben haben – ja, Sekunden.

PHP-FPM kann sich aber trotzdem auch aus Performancegründen lohnen und zwar dann, wenn man wenig Arbeitsspeicher zur Verfügung hat und noch andere Prozesse laufen. PHP-FPM startet nämlich immer nur die Anzahl Laufzeitumgebungen, die es wirklich benötigt und beendet im Gegensatz zu regulärem FastCGI aktiv alle, die nicht mehr benötigt werden. Auch Anfragen werden aktiv beendet, z. B. wenn die Zeit von set_time_limit() erreicht ist oder wenn der Prozess eine in FPM hinterlegte Zeit überschreitet. So wird der Speicherbedarf ständig minimiert und keine unnötigen Ressourcen verschwendet.

Weiche Neustarts

Manchmal kommt es dazu, dass man selbst in einem Livesystem an der php.ini oder an den Pools Änderungen vornehmen muss. Die Folge lautet: Alle laufenden PHP Prozesse müssen beendet werden und alle aktiven Besucher sehen eine weiße Seite. Unschön! PHP-FPM kann einen sog. „graceful restart“, also einen „weichen“ Neustart machen, bei dem die alten Prozesse abgearbeitet und parallel neue Prozesse gestartet werden. Ein regulärer Restart, bei dem die alten Prozesse aktiv beendet und dann neu gestartet werden ist natürlich ebenfalls möglich. Weiche Starts sind dann praktisch, wenn man zur Hauptbesucherzeit einen Pool hinzufügen will oder muss.

# Neustart ohne Prozessverlust
/etc/init.d/php5-fpm start

# Neustart auch mit Prozessverlust
/etc/init.d/php5-fpm restart

Fehlerbehandlung

Ein weitere Vorteil ist die Fehlerbehandlung. Während FastCGI bei einem schweren Systemfehler lautlos aussteigt und wahrscheinlich gar nichts anzeigt oder noch schlimmer, den Prozess vom Webserver einfach offen lässt, schickt PHP-FPM eine einstellbare Fehlermeldung zurück, die vom Webserver ausgewertet werden kann. So bekommen Besucher zumindest noch den Fail Whale zu sehen.

Abkopplung des PHP Prozess vom Webserver

PHP-FPM erweitert zusätzlich den Funktionsumfang von PHP selbst. Die Funktion fastcgi_finish_request() kann u. U. die Anzeige beim Besucher beschleunigen. Nehmen wir an, wir möchten ein aufwendiges PDF generieren und rufen die Datei auf. Unser Script sieht, „Aha, das hab ich noch nicht erzeugt“ und steigt mit einem Hinweis aus, dass die Anfrage jetzt bearbeitet wird. Der PHP Prozess läuft weiter, aber für den Webserver ist der Vorgang erledigt. Auch für Cronjobs kann das sehr praktisch sein, wenn man an den Ausgaben eh nicht interessiert ist.

<?PHP

if(!is_file('document.pdf'))
{
	echo "Bitte nach 30 Sekunden aktualisieren!";
	// Webserver steigt aus und sendet die Ausgabe
	fastcgi_finish_request();
	// Der PHP Prozess läuft weiter
	generate_pdf();
}
else show_pdf();

?>

Opcode Cache Notsystem

Es kann passieren, dass der Opcode Cache von APC, PHP Accelerator, eAccelerator usw. Amok laufen und zwar dann, wenn ein anderer Serverprozess den gemeinsamen Speicher überschreibt. PHP-FPM kontrolliert die Funktion und Integrität der Daten und greift mit einer Art Notsystem ein, sobald die Integrität nicht mehr gesichert ist. PHP Prozesse werden dann neu gestartet und längere Ausfälle vermieden.

Optimierung des Upload

Für Seiten auf denen viele und vor allem große Dateien hochgeladen werden, bietet PHP-FPM die Möglichkeit, nicht den gesamten Requestbody an die FastCGI Schnittstelle zu senden, sondern den Request vom Webserver in eine temporäre Datei schreiben zu lassen und danach dem PHP Prozess nur noch den Ort der Datei mitzuteilen. So verhindert man (unnötig) offene PHP Prozesse. Derzeit kann aber nur nginx damit arbeiten.

Langsame Abfragen

Mein persönliches Highlight ist das slow-log. Sobald man eine neue Webseite startet, kommt es immer wieder mal zu Performanceproblemen, an die man nicht gedacht hat. In mysql gibt es das slow-log mit dem es möglich ist, besonders langsame oder unoptimierte Datenbankabfragen zu finden und so zu debuggen. PHP-FPM bietet genau diese Funktion für PHP Prozesse. Sobald ein Aufruf länger dauert als ein einstellbarer Wert, speichert PHP-FPM den Prozessaufruf inkl. Backtrace und zwar in dem Moment, in der das Limit überschritten wurde. Diese Einstellung ist für jeden Pool einzeln einstellbar. In der Logdatei sieht das dann so aus.

[13-Jul-2011 13:11:03] [Pool www] pid 3079
script_filename = /var/www/anonsphere.com/index.php
[0x00007fa2e0570148] curl_exec() /var/www/anonsphere.com/protected/extensions/TwitterOAuth.php:1113
[0x00007fa2e056fbf8] http() /var/www/anonsphere.com/protected/extensions/TwitterOAuth.php:1074
[0x00007fa2e056f780] oAuthRequest() /var/www/anonsphere.com/protected/extensions/TwitterOAuth.php:1017
[0x00007fa2e056d220] get() /var/www/anonsphere.com/protected/controllers/SiteController.php:670
[0x00007fa2e056ce70] actionCron() /var/www/yii-1.1.8.r3324/framework/web/actions/CInlineAction.php:50
[0x00007fa2e056cb50] runWithParams() /var/www/yii-1.1.8.r3324/framework/web/CController.php:300
[0x00007fa2e056c8b0] runAction() /var/www/yii-1.1.8.r3324/framework/web/CController.php:278
[0x00007fa2e056c450] runActionWithFilters() /var/www/yii-1.1.8.r3324/framework/web/CController.php:257
[0x00007fa2e056bf98] run() /var/www/yii-1.1.8.r3324/framework/web/CWebApplication.php:277
[0x00007fa2e056ba88] runController() /var/www/yii-1.1.8.r3324/framework/web/CWebApplication.php:136
[0x00007fa2e056b708] processRequest() /var/www/yii-1.1.8.r3324/framework/base/CApplication.php:158
[0x00007fa2e056b068] run() /var/www/anonsphere.com/index.php:26

Schnittstellen

Zusätzliche Boni sind Schnittstellen zu den Webserverprozessen in Text, HTML und JSON mit denen man genau verfolgen kann, was der Webserver derzeit macht und eine Statusabfrage, wo man testen kann, ob FPM überhaupt noch da ist.

Ausgabe von /status?json

{

    pool: "www"
    process manager: "dynamic"
    accepted conn: 306
    listen queue len: 0
    max listen queue len: 128
    idle processes: 49
    active processes: 1
    total processes: 50
    max children reached: 0

}

Ausgabe von /ping

pong

Haben will

Wer PHP-FPM mal testen möchte und schon FastCGI einsetzt, kann das weitgehend gefahrlos tun. PHP-FPM arbeitet nämlich auf eigenen Ports und damit auch parallel zu FastCGI. Da für die Nutzung seit PHP 5.3 keine Patches mehr nötig sind, ist die Installation sehr einfach. Unter Debian oder Ubuntu loggen wir uns als root ein und tippen.

apt-get install php5-fpm

Anmerkung: Für aktuelle Quellen sollte man zumindest unter Debian die Dotdeb Paketquellen verwenden.

deb http://php53.dotdeb.org stable all
deb-src http://php53.dotdeb.org stable all

FPM wird nach der Installation gestartet und läuft als Hintergrunddienst. Als nächstes kann man seine Einstellungen in den Dateien /etc/php5/php-fpm.conf, /etc/php5/conf.d/* und /etc/php5/pool.d/* vornehmen bzw. diese Dateien anlegen. Die Konfigurationsdateien sind gut dokumentiert, also kein Problem für den erfahrenen lesenden Admin. Ein harter bzw. weicher Neustart übernimmt die Änderungen.

Natürlich muss man noch dem Webserver sagen, wo er mit den PHP Scripten hin muss. Das funktioniert genau so wie bei FastCGI, z. B. über die http.conf.

LoadModule fastcgi_module modules/mod_fastcgi.so

<IfModule mod_fastcgi.c>
ScriptAlias /fcgi-bin/ "/usr/local/apache2/fcgi-bin/"
FastCGIExternalServer /usr/local/apache2/fcgi-bin/php-cgi -host 127.0.0.1:9000
AddHandler PHP-fastcgi .php
Action PHP-fastcgi /fcgi-bin/php-cgi
</IfModule>

Solltet Ihr einen anderen Webserver einsetzen, wie lighttpd, nginx, cherokee usw. muss eigentlich nur der Port angepasst werden und evtl. die Scripte zum Starten der Prozesse raus, denn das übernimmt alles FPM. Da man bei den meisten Webservern individuell regeln kann, welcher Host welche Schnittstelle anspricht, ist es problemlos möglich, dass z. B. Host A noch auf FCGI, aber Host B schon auf FPM läuft. Gefahrlosen Tests ohne Ausfall steht also nichts im Wege.

Fazit

Der PHP FastCGI Prozessmanager ist seit PHP 5.3 einfach einzurichten und bietet mächtige Tools, um die Sicherheit auf Webservern zu verbessern und bestehende Ressourcen besser zu nutzen. Wer sich noch nie mit seinen PHP Prozessen auseinander gesetzt hat, aber das immer mal wollte, dürte sich mit FPM einfacher zurecht finden als mit normalem FastCGI. Alle Einstellungen liegen übersichtlich in /etc/php5/fpm/ und sind nicht in mehreren Dateien oder Scripten auf dem Webserver verteilt.

Wer schon Erfahrungen mit der Einrichtung von FastCGI hat und sich dabei immer über den Aufwand ärgerte, es wirklich sicher zu machen, wird sich hier sofort zu Hause fühlen. Kritikpunkt ist sicherlich die schlechte Dokumentation, allerdings sind die Konfigurationsdateien selbst gut beschrieben, die Einrichtung mit FastCGI vergleichbar und mit Google kann man eine ganze Menge Hilfe zur Einrichtung und Konfiguration finden.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

6 Comments

  1. Sehr schöne Einweisung ins Thema. Einen Hinweis zu Debian noch. php5-fpm gibt es in Squeeze (stable) noch nicht. Man ist also nicht nur auf dotdeb angewiesen wenn man aktuelle Versionen haben möchte, sondern generell wenn man den Paketmanager benutzen will.

    Ansonsten ist nginx, php5.3 und php5-fpm aktuell mein Lieblingssetup für Webserver.

    Reply
  2. Sehr schöne Einleitung in das Thema. Habe gestern einen Artikel zum Thema Nginx + PHP-FPM veröffentlicht und wurde von einem Leser auf deinen Artikel aufmerksam gemacht.

    Um die Performance weiter zu steigern, kann man den Webserver auch über einen Unix-Socket mit PHP-FPM sprechen lassen – dadurch fällt der Overhead von TCP/IP weg. 🙂

    Reply
  3. Danke für den Artikel. Habe mich auch von den Vorteilen überzeugen lassen. Ein großer Nachteil ist aber

    _SERVER[„ORIG_SCRIPT_FILENAME“] /var/www/cgi-bin/php5.external
    _SERVER[„ORIG_SCRIPT_NAME“] /cgi-bin/php5.external

    Manche PHP-Scripte arbeiten mit dem original Scriptnamen das Problem ist dass dann folgendes Urls ausgegebene werden:
    http://localhost/php5.external?do=something anstatt
    http://localhost/index.php?do=something

    Suche nach einer Lösung ohne über auto_prepend_file den korrekten Pfad zu setzen.

    Reply
  4. Hi,

    du schreibst oben im Artikel: „Auch die Verwendung von unterschiedlichen php.ini Dateien ist damit problemlos machbar…“. Leider hab ich bis jetzt nirgends was verständliches und funktionierendes dazu finden können. Hast du evtl. ne Idee, wie das zu bewerkstelligen ist?

    Reply
  5. Auch von meiner Steite her ein großes Danke. Es ist ein sehr verständlicher Artikel für jemanden der sich mit dem Umstieg von PHP als Modul auf FPM/FastCGI befassen muss. Auch wenn man einen ManagedServer hat und sich eigentlich jemand anderes um die Details kümmert, ist das hier wertvolles Basiswissen um die Hintergründe zu verstehen.

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen