PHP mit einem Apache oder einem nginx zu nutzen ist inzwischen kein Hexenwerk mehr. Überall im Netz findet man haufenweise Konfigurationsbeispiele, die mehr oder minder formal beschreiben, warum man diesen oder jenen Voodoo treiben muss. Was aber scheinbar niemand wirklich erklärt, ist ein kleines, aber essentielles Detail, was einem einiges an Resourcen spart, wenn man über mehrere Container hinweg seine Services isolieren möchte.
Das übliche Setup sieht in dieser Umgebung so aus, dass man einen zentralen Server – aus Performance-Gründen meist etwas Performantes wie nginx – auf einem Container installiert, und von diesem via Reverse Proxy die Anfragen zu seinem eigentlichen Service weiterleitet. Der Webservice hat in diesem Setup wiederum einen Webserver laufen, der die Anfragen verarbeitet und an PHP durchreicht.
Einfach. Langweilig. Und Bloated!
Denn warum eigentlich den zweiten Webserver? nginx kann doch schon direkt FastCGI sprechen; da brauch man doch keinen eigenen Server, dessen einzige Aufgabe es ist – neben Taktzyklen verbrennen -, die Anfragen von A nach B durchzureichen.
Gesagt, getan und auf einer meiner Service-Maschinen lediglich den PHP-FPM für die Verarbeitung der Anfragen aufgesetzt. Diesen im Pool auf’s LAN-Interface der Bridge geklemmt (mit Firewalling gegen das böse Internet geschützt) und als erlaubten Client den Proxy eingetragen. Einmal Service neustarten und der PHP-FPM läuft.
Was jetzt noch fehlt, ist der grandios undokumentierte Teil der Aufgabe: Wie bekommt der PHP-FPM seine Requests vom nginx? Und vor allem: Woher weiß er, welches Skript auszuführen ist?
Nunja, anhand der Konfiguration des nginx natürlich! In dieser gibt es üblicherweise eine Reihe Konfigurationszeilen mit fastcgi_param zum verdrehen diverse Einstellungen und zur Übergabe so manch sinnvoller Information wie der verwendeten Server-Software 😉 Unter anderem bietet der nginx hierbei auch einen Wert mit dem unscheinbaren Namen „SCRIPT_FILENAME“ (nicht verwechseln mit dem „SCRIPT_NAME“!!!), welcher 1:1 vom PHP-FPM hinter dem konfigurierten TCP-Socket ausgeführt wird. Der hier angegebene Dateiname bezieht sich auf das von PHP-FPM gesehene Dateisystem; inkluse ggf. vorhandener chroot-Umgebungen.
Soweit so einfach, ABER: Da der nginx in diesem Setup keine Dateien der Webanwendung kennt, müssen ALLE Anfragen über den PHP-FPM laufen (ja, selbst das Ausliefern von Bildern muss via PHP gemacht werden). Da in diesem Setup auch die Gültigkeitsprüfung der Dateinamen erst vom angehängten Webservice erfolgen kann, ist dieser für die korrekte Beantwortung verantwortlich.
Im besten Falle hat man also eine Anwendung, die sowieso Single Entry Point läuft und daher alles direkt erledigen kann. Dann vereinfacht sich die Server-Konfiguration auf folgende wenige Zeilen:
location / {
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
fastcgi_pass 12.34.56.78:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /srv/webapp/index.php;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
Danach auch dem nginx einen kurzen Tritt geben, damit er die Konfig neu liest und der schlank aufgesetzte Webservice ist verfügbar. Die üblichen Maßnahmen zur Absicherung von PHP-Installationen gelten natürlich uneingeschränkt.