PHP-FPM chroot + Zend OpCache = Problem

In letzter Zeit hatte ich wirklich merkwürdige Probleme. Ich habe testweise einmal den Zend OpCache ausprobiert, der seit PHP 5.5.0 direkt mit im Bundle enthalten ist und war eigentlich ziemlich zufrieden. Der Speicherverbrauch bei der Ausführung von WordPress war auf 1/5 geschrumpft.

Allerdings haben sich nun ein paar Probleme gezeigt, bei denen ich noch nicht genau weiß, wie ich sie beheben soll. Der Hintergrund ist, dass ich die Webseite von calc.pw nun auf Basis von WordPress neu aufgesetzt habe. Da nun sowohl WeizenSpr.eu als auch calc.pw auf WordPress basierten und die gleichen Pfade verwendeten, entstanden merkwürdige Seiteneffekte.

So kam es vor, dass ich im Adminbereich von calc.pw eine Einstellung tätigen wollte und plötzlich auf der Loginseite des WeizenSpr.eu Adminbereichs landete oder dass mir im Browserfenster von WeizenSpr.eu per Popup das Loginfenster angeboten wurde, weil angeblich meine Session abgelaufen war.

Anfangs ging ich davon aus, dass evtl. Safari Probleme mit der Verwaltung der Cookies haben könnte, deshalb verwendete ich anschließend verschiedene Browser für beide Seiten - einmal Safari und einmal Firefox. Doch auch dieses Vorgehen half nichts. Erschwerend kam hinzu, dass calc.pw sporadisch gar nicht laden wollte und stattdessen eine leere Seite anzeigte - ohne auffällige Einträge im Errorlog.

Erst langsam kam mir der Gedanke, dass es am Zend OpCache liegen könnte. Die grundlegenden Einstellungen von WordPress (z.B. Datenbank-Zugänge, Session-Cookie-Schlüssel, korrekte URL der Webseite, etc.) lagern in einer PHP-Konfigurationsdatei ("wp-config.php"). Offenbar verwendete der Zend OpCache mal die richtige Datei und mal die völlig falsche Datei, was die kaputten Sessions und auch die Redirects zur anderen Webseite erklären würde.

Allerdings war ich davon ausgegangen, dass der Cache so intelligent sein würde, die korrekte Datei anhand des vollen Pfades zu erkennen... wenn... ja wenn ich nicht die chroot-Funktion von PHP-FPM verwenden würde. Relativ vom chroot-Heimverzeichnis aus gesehen waren die WordPress-Pfade natürlich komplett identisch. Offenbar hat der Zend OpCache in dieser Konstellation Probleme damit, die Dateien zu unterscheiden - ein Fakt, der ihn für mich derzeit absolut unbrauchbar macht.

Nach dem Deaktivieren des Caches und dem Neustarten von PHP-FPM funktionierten beide Seiten problemlos - die Sessions gingen nicht kaputt, ich erhielt keine fehlerhaften Redirects mehr und auch die sporadischen Totalausfälle von calc.pw verschwanden.

Die Frage, die sich jetzt nur stellt, ist, wann wohl jemand von PHP dieses Problem beheben wird. Solange das Problem nicht gelöst ist, werde ich den Cache nicht weiter einsetzen können. 🙁

Ungecachte Grüße, Kenny

7 Kommentare » Schreibe einen Kommentar

  1. Servus Kevin,

    ich habe mich aus dem gleichen Grund wie Du mit dem Problem auseinander gesetzt aber habe bisher keine Lösung gefunden - bisher. Nachdem ich nun auf die Idee mit dem OpCache gebracht wurde, bisher habe ich verschlafe das der nun per Default bei PHP aktiviert ist, habe ich mich der Problematik angenommen und möchte dir hier zwei funktionierende Lösungen präsentieren.

    Das Problem liegt mit dem durch dich verwendeten CHROOT zusammen, wie Du richtig erkannt hast. Das Problem beim OpCache und APC ist folgendes:

    Wenn eine Datei im Arbeitsspeicher gecacht wird und somit nicht zur Laufzeit kompiliert werden muss, dann merkt sich der Cache den Dateinamen und (wenn angegeben) den Pfad des Scriptes zur Laufzeit. Hierfür ist der Parameter "opcache.use_cwd" gedacht, welcher neben dem Namen der Datei auch den Pfad speichert.

    Nun ist das Problem bei dem durch dich aufgebauten Chroot, dass alle Pfade im Chroot (denn im Chroot sieht der Opcache eben nur die Pfade _ab_ dem Chroot) gleich sind. Genau das ist die zentrale Problematik. Um dieses zu umgehen kann man wie folgt vorgehen (so mache ich es). Man baut eine neue Pfadstruktur auf:

    /srv/customers//

    In diesem Verzeichnis liegen meine Chroots. Dafür habe ich dein Script angepasst und vergeben jetzt Nutzernamen und nutze nicht nur die URL als Nutzernamen, denn das hat einen weiteren Vorteil, dass man die Nutzernamen nicht erraten kann. Dann folgen die Chroots.

    /srv/customers//domains/weizenspr_eu/

    Wie Du siehst, wird jedem Nutzer ein "domains"-Verzeichnis angelegt, in welchem seine Domains liegen. Bei Dir wie oben gezeigt eben "weizenspr_eu". Darunter kommen dann die Subdomains:

    /srv/customers//domains/weizenspr_eu/www (www + ohne www bei mir)
    /srv/customers//domains/weizenspr_eu/wiki (z.B. wiki.weizenspr.eu)

    Usw. Wenn nun im Chroot im Verzeichnis /domains/weizenspr_eu/wp-config.php gecacht wird, dann ist es nicht schlimm wenn im Wiki die gleiche Datei genutzt wird (gleicher Nutzer, anderer Pfad), denn durch die Opcache Einstellung wird der Pfad mit zum Identifizieren genutzt.

    Das gleiche gilt natürlich für andere Nutzer. Vorraussetzung ist, das keine zwei Nutzer die gleiche Domain habe (das geht ja auch garnicht). Somit hat man das Problem mit dem Cache elegant gelöst.

    ---

    Die zweite Variante ist etwas uneleganter wie ich finde, bietet aber den Vorteil, dass Nutzer nicht den gesamten Opcache auslesen können, denn der Opcache wird über alle PHP-FPM-Instanzen geshared! D.h. Nutzer 1 kann sehen was im gesamten Opcache liegt, Nutzer 2 auch etc.

    Dafür startest Du einfach für jeden Nutzer einen eigenen PHP-FPM-Master Prozess. Dann hat jeder Nutzer seinen eigenen Opcache, dediziert. Natürlich musst Du dann die Sache mit den Basepfaden im Chroot nicht länger beachten.

    Ich hoffe das war soweit verständlich 🙂

    LG

    Florian

    • Hinter /srv/customers sollte eigentlich noch etwas kommen, hat WordPress wohl ausgefilter, darum hier nochmal neu:

      /srv/customers/-customername-/domains/weizenspr_eu/www
      /srv/customers/-customername-/domains/weizenspr_eu/wiki

      So 🙂

  2. Servus,

    ich habe das gleiche Problem bei mehreren WordPress-Seiten auch ohne den OpCache. Habe schon viel probiert, fliege aber wie du manchmal bei mehreren Seiten raus und lande plötzlich auf einem ganz anderen WordPressauftritt auf dem selben Server.

    Hast Du dafür schon eine Erklärung?

    • Seit ich den OpCache deaktiviert habe, ist das Problem erstmal nicht aufgetreten. Hast du evtl. andere Caches (WP-Total-Cache, einen vorgeschalteten Varnish Cache oder ähnliches) im Einsatz?

      Eine Idee wäre, mal das LoginSession-Plugin zu nehmen: https://weizenspr.eu/2014/wordpress-mit-verschiedenen-datenbanknutzern-absichern/

      Damit könnte man dann für eingeloggte Nutzer z.B. über die wp-config.php noch HTTP-Header mitsenden, um Caching zu unterbinden:

      1
      2
      header("Pragma: no-cache");
      header("Cache-Control: no-cache");

      Du könntest ja mal prüfen, ob das das Problem behebt.

  3. Benenn die wp-config.php doch um bis das Problem behoben ist … oder schieb die Datei einen Ordner höher. Die wp-config wird nur in der wp-load geladen, d. h. Du musst nur eine Datei ändern.

    • Umbenennen würde nicht funktionieren, da es ja wiederum eine Datei gibt, die die "wp-config.php" lädt und diese würde (je nachdem, welche gerade im Cache ist) dann an der falschen Stelle suchen. Die "wp-config.php" einen Ordner nach oben schieben würde auch nicht funktionieren, da es durchaus sein könnte, dass eine "wp-config.php" bereits an der ersten Stelle im Cache liegt, obwohl die "wp-config.php" an der zweiten Stelle genutzt werden soll.

      Die einzig sichere Variante wäre derzeit, den Document-Root umzubenennen, was aber auch nur hilft, solange man keine Helper-Scripte außerhalb des Docroots verwendet (was ich tue). Man sieht: es zieht einen ganzen Rattenschwanz an Seiteneffekten nach sich.

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.