Zum Inhalt springen

Chrome-Renderer für NextPDF Artisan einrichten

Die Brücke startet und steuert einen lokalen Chrome-/Chromium-Prozess über chrome-php/chrome. Diese Seite zeigt, wie Sie diese Laufzeitumgebung so einrichten, dass Renderings gelingen, und erläutert die Entscheidungen zu Containern und Sandbox.

BrowserPool erzeugt eine chrome-php/chromeBrowserFactory (optional mit explizitem Binärpfad) und startet Chrome mit einem festen Satz von Flags: headless: true, keepAlive: true, windowSize: [1200, 800], sendSyncDefaultTimeout: renderTimeout * 1000 sowie den benutzerdefinierten Flags, die auf der Seite /integrations/artisan/configuration/ aufgeführt sind. Anschließend steuert die Brücke den gestarteten Prozess über das Chrome DevTools Protocol an. Sie verbindet sich nicht über einen Remote-Debugging-Port mit einem separat laufenden Chrome; es gibt also keinen Netzwerkendpunkt, der freigegeben oder authentifiziert werden müsste. Chrome läuft als Kindprozess des PHP-Workers. Der Test tests/Unit/Artisan/BrowserPoolTest.php::getBrowserCreatesAndReusesInstanceWithExpectedOptions prüft genau diese Startoptionen.

Installieren Sie einen Chrome- oder Chromium-Build, den der Worker-Benutzer ausführen kann:

Terminal-Fenster
# Debian / Ubuntu
apt-get install -y chromium
# RHEL / Fedora
dnf install -y chromium
# Alpine (containers)
apk add --no-cache chromium nss freetype harfbuzz ttf-freefont

Prüfen Sie, ob der Browser als Worker-Benutzer headless läuft:

Terminal-Fenster
chromium --headless --dump-dom about:blank

Ein Exit-Code 0 mit leerem DOM bedeutet, dass die Binärdatei und ihre Shared Libraries vorhanden sind. Ein Exit-Code ungleich null entspricht demselben Fehler, den die Brücke als ChromeRenderException meldet. Beheben Sie ihn zuerst hier.

Die automatische Erkennung (der Standard von chrome-php/chrome) funktioniert, wenn eine Binärdatei in einem Standardpfad liegt. Für deterministisches Verhalten in der Produktion legen Sie sie explizit fest:

$config = new ChromeRendererConfig(
chromeBinaryPath: '/usr/bin/chromium',
);

oder per Array-Konfiguration:

$config = ChromeRendererConfig::fromArray([
'chrome_binary' => '/usr/bin/chromium',
]);

In einem Container kann die Chrome-OS-Sandbox als root / PID 1 oft nicht ohne zusätzliche Kernel-Capabilities initialisiert werden. Sie haben zwei Möglichkeiten:

  1. Sandbox beibehalten (bevorzugt). Führen Sie den Worker als Nicht-root-Benutzer aus und gewähren Sie dem Container die Capabilities, die die Sandbox von Chrome benötigt (häufig SYS_ADMIN oder ein seccomp-Profil, das das Anlegen von User-Namespaces erlaubt). So bleibt die Prozessisolation von Chrome intakt.
  2. Sandbox deaktivieren. Setzen Sie no_sandbox: true. Chrome startet dann mit --no-sandbox. Dadurch wird die Prozessisolations-Sandbox von Chrome entfernt; das verringert die Abschottung tatsächlich und ist kein kosmetisches Flag. Nutzen Sie dies nur dort, wo die Sandbox nicht aktiviert werden kann, führen Sie Chrome als Nicht-root-Benutzer in einem eingeschränkten Container aus und behandeln Sie das Deployment als Umgebung mit höheren Vertrauensanforderungen an die Eingabe. Die Netzwerkbarrieren der Brücke (CSP + CDP-Sperre) bleiben in beiden Fällen in Kraft, sie ersetzen aber keine Prozessisolation. Das steht im Einklang mit den OWASP-ASVS-Vorgaben zu minimalen Rechten und Isolation beim Rendern nicht vertrauenswürdiger Inhalte.

Die vollständige Abgrenzung dazu, was die Sandbox schützt und was nicht, steht auf der Seite /integrations/artisan/security-and-operations/. Diese Seite behauptet nicht, dass das Deaktivieren der Sandbox sicher ist.

FROM php:8.4-cli
RUN apt-get update && apt-get install -y --no-install-recommends \
chromium fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 10001 worker
USER worker
ENV CHROME_BINARY=/usr/bin/chromium
# Set CHROME_NO_SANDBOX=1 only if the sandbox cannot be enabled in your runtime.

Führen Sie den Worker als worker (uid 10001) aus, nicht als root. Die Brücke setzt bereits das Flag --disable-dev-shm-usage, das Abstürze durch ein zu kleines /dev/shm vermeidet, wie sie in Containern ohne weitere Anpassung häufig auftreten.

Die Brücke blockiert das Nachladen entfernter Schriftarten (--disable-remote-fonts und CSP). Installieren Sie die benötigten Schriftarten auf OS-Ebene oder betten Sie sie als data:-URI-@font-face-Quellen innerhalb von defaultCss oder in das HTML ein. Für die CJK-Ausgabe muss ein CJK-Schriftpaket (zum Beispiel fonts-noto-cjk) im Image installiert sein.

Nutzen Sie diese eigenständige Probe, um den vollständigen Brückenpfad ohne die Host-Anwendung durchzuspielen:

chrome-health.php
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
require __DIR__ . '/vendor/autoload.php';
$renderer = new ChromeHtmlRenderer(
ChromeRendererConfig::fromArray([
'chrome_binary' => getenv('CHROME_BINARY') ?: null,
'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'),
]),
);
$result = $renderer->render('<p>ok</p>', 200.0, 0.0);
fwrite(STDOUT, strlen($result->getPdfData()) > 0 ? "CHROME_OK\n" : "CHROME_EMPTY\n");
$renderer->close();

CHROME_OK bestätigt Start, Rendering und Import. Eine geworfene Ausnahme gibt den genauen Fehler an. Ordnen Sie ihn auf der Seite /integrations/artisan/troubleshooting/ zu. Binden Sie dies in orchestrierten Deployments als Readiness-Check ein.

  • Führen Sie Chrome als dedizierten Nicht-root-Benutzer aus.
  • Legen Sie ein Host-Speicherlimit fest; die Brücke begrenzt das Wachstum durch einen Neustart nach 100 Renderings, ein Host-Limit ist aber trotzdem erforderlich.
  • Kombinieren Sie render_timeout auf jedem von nicht vertrauenswürdiger Eingabe erreichbaren Pfad mit einem vorgelagerten Request-Budget.
  • Geben Sie keinen Chrome-Remote-Debugging-Port frei. Die Brücke verwendet keinen, und ein offener CDP-Port ist ein nicht authentifizierter Steuerkanal.
SymptomWahrscheinliche UrsacheWo nachsehen
ChromeNotAvailableExceptionchrome-php/chrome nicht installiert/integrations/artisan/install/
ChromeRenderException beim ersten RenderingBinärdatei fehlt / Sandbox kann nicht initialisierenDiese Seite; /integrations/artisan/troubleshooting/
Leeres PDFKein sichtbarer Kasten / Chrome-Absturz/integrations/artisan/troubleshooting/
Leere entfernte BilderNetzwerk per Design blockiert/integrations/artisan/security-and-operations/
Periodische LatenzspitzeNeustart nach 100 Renderings/integrations/artisan/production-usage/
  • /integrations/artisan/install/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/troubleshooting/
  • /integrations/artisan/production-usage/