コンテンツにスキップ

Artisan の本番環境利用

本番環境では、構成済みのレンダラーと PSR-3 ロガーを注入します。稼働中の Chrome プロセスは複数回のレンダリングで再利用し、複数要素で構成されるドキュメントには明示的な高さを指定し、上流のタイムアウトでレンダリングパスを制限します。

BrowserPool は 1 つの Chrome プロセスを稼働状態に保ち(keepAlive: true)、メモリ使用量の増加を抑えるために 100 回のレンダリングごとに再起動します。これは、長時間稼働する CDP クライアントで知られているメモリ蓄積パターンへの対応です。多数のドキュメントをレンダリングするワーカーでは、リクエストごとにレンダラーを 1 つ作るのではなく、長時間稼働するレンダラーを 1 つ用意することで、Chrome の起動コストが発生する頻度を抑えられます。

render-service.php
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
use NextPDF\Artisan\Exception\ChromeNotAvailableException;
use NextPDF\Artisan\Exception\ChromeRenderException;
use Psr\Log\LoggerInterface;
final class ReportRenderer
{
private ChromeHtmlRenderer $renderer;
public function __construct(LoggerInterface $logger)
{
$config = ChromeRendererConfig::fromArray([
'chrome_binary' => getenv('CHROME_BINARY') ?: null,
'render_timeout' => 45,
'max_html_size' => 2_000_000,
'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'),
]);
$this->renderer = new ChromeHtmlRenderer($config, $logger);
}
public function render(string $html, float $widthPt, float $heightPt = 0.0): string
{
try {
return $this->renderer->render($html, $widthPt, $heightPt)->getPdfData();
} catch (ChromeNotAvailableException $e) {
// Deployment fault: Chrome runtime missing. Page the on-call owner.
throw $e;
} catch (ChromeRenderException $e) {
// Render-time fault: timeout, crash, empty output. Retryable once.
throw $e;
}
}
public function shutdown(): void
{
$this->renderer->close();
}
}

レンダラーは一度だけ構築して再利用します。デストラクターを待つのではなく、Chrome プロセスを確定的に解放するため、ワーカーのシャットダウン時に close() を呼び出します。2 つの catch 節は、デプロイ時の障害(ランタイムの欠如)とレンダリング時の障害(再試行可能)を区別します。空の catch ブロックは使用しません。

コンテナーにはシングルトンとして登録します。

$container->singleton(ReportRenderer::class, fn ($c) =>
new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));

高さが省略された場合、ブリッジは Chrome 内でコンテンツの高さを測定し(body/document のスクロール高さとオフセット高さの max)、それをポイントに変換して、約 0.2 インチ(約 14.4 pt)の安全余白を追加します。この余白は、Chrome のビューポートレイアウトと印刷レイアウトのリフロー間の差異を吸収します。この余白がないと、printToPDF がコンテンツを 2 ページ目にはみ出させ、PageImporter(ページ 0 のみ)によってその部分が切り取られるおそれがあります。用紙の高さには最小値として 0.1 インチが適用されます。ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault::renderAutoFitBufferIsAddedNotSubtracted、および ::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight の各テストがこれを検証します。

固定レイアウトのドキュメント(請求書や証明書など)では、高さをポイント単位で明示的に渡します。高さが明示的に指定されている場合、余白は追加されず、出力は要求された用紙サイズと正確に一致します(::renderHonorsExplicitHeightWithoutAutoBuffer が検証します)。

  • ワーカーごとにレンダラーを 1 つ構築し、再利用します。BrowserPool は稼働中のブラウザーを再利用し、100 回のレンダリングの境界で自動的に再起動します。
  • 100 回のレンダリングの境界を待たずに新しい Chrome プロセスを取得したい場合は、ワーカーのシャットダウン時や大きなバッチの合間に close() を呼び出します。
  • デストラクターは close() を呼び出しますが、明示的な close() のほうが確定的であり、長時間稼働するプロセスでは推奨されます。
  • 再起動の通知はレンダリング回数とともに notice レベルでログに記録されます。再起動率の上昇についてはアラートを設定してください。これは、想定より重いドキュメントが処理されていることを示します。

PSR-3 ロガーを注入します。発行されるイベントとそのレベルは次のとおりです。

イベントレベルコンテキスト
レンダリング開始debugsize, width, height
レンダリング完了debugpdfSize, contentHeight
ブラウザー起動infobinary
ブラウザー再起動noticecount
ブラウザークローズdebugrenderCount

HTML、PDF のバイト列、抽出されたテキストはログに記録されません。これは、ペイロードを運用ログに含めないようにという NIST SP 800-92 のガイダンスに沿ったものです。start/complete のペアからレイテンシの SLO を、notice イベントから再起動率のアラートを構築します。

  • サイドカー Chrome: PHP ワーカーと同じコンテナーで Chrome を実行し、chrome_binary を固定します。サンドボックスに対応したコンテナーをプロビジョニングします。詳しくは /integrations/artisan/chrome-renderer-setup/. を参照してください。
  • コンテナーレス / CLI: Artisan には DI コンテナーがありません。CLI ランナーで Premium の電子請求書コントラクトを使用するには EInvoiceServiceFactory を使用します。詳しくは /integrations/artisan/boot-and-discovery/. を参照してください。
  • リソースの制限: render_timeout を、上流のリクエストバジェットおよびホストの cgroup/ulimit. と組み合わせます。脅威モデルについては /integrations/artisan/security-and-operations/. を参照してください。
  • レンダリングの途中で中断されても、Chrome のページは閉じられ(finally)、プールは引き続き使用可能です。
  • 1 つのレンダラーを複数の threads/processes にまたがって再利用することはサポートされていません。1 つのレンダラーは 1 つの Chrome プロセスを所有します。
  • 100 回のレンダリングごとの再起動は固定です。レイテンシのスパイクを予測できるよう、これを考慮してバッチサイズを決めてください。

定常状態でのコストは、入力に対する Chrome のレイアウトと printToPDF であり、ブリッジのオーバーヘッドではありません。起動コストは keepAlive によって分散されます。100 回ごとのレンダリング(プロセスの再起動)でレイテンシのスパイクが発生することを想定してください。これはインシデントとして扱うのではなく、SLO に反映させます。

本番環境のパスは、信頼できない HTML が到達する場所です。/integrations/artisan/security-and-operations/. を再度お読みください。ネットワークの障壁は構成にかかわらず維持されますが、no_sandbox: true は Chrome のプロセス分離を取り除き、入力に対する信頼の要件を高めます。

コンテナーレスのワーカーでは、Premium がインストールされていない場合に EInvoiceServiceFactorynull を返すため、オープンソースのレンダリングパスは変更なく動作します。レンダリング済みドキュメントへの電子請求書の埋め込みと検証を有効にするには、Pro/Enterprise をインストールしてください。

  • /integrations/artisan/quickstart/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/chrome-renderer-setup/
  • /integrations/artisan/troubleshooting/