การตั้งค่า Chrome renderer สำหรับ NextPDF Artisan
ภาพรวมโดยสรุป
หัวข้อที่มีชื่อว่า “ภาพรวมโดยสรุป”บริดจ์จะเปิดและควบคุมกระบวนการ Chrome/Chromium ภายในเครื่องผ่าน chrome-php/chrome ใช้หน้านี้เพื่อตั้งค่ารันไทม์ดังกล่าวให้เรนเดอร์ Portable Document Format (PDF) ได้สำเร็จ และช่วยตัดสินใจเรื่องคอนเทนเนอร์กับแซนด์บ็อกซ์ได้อย่างถูกต้อง
วิธีที่บริดจ์สื่อสารกับ Chrome
หัวข้อที่มีชื่อว่า “วิธีที่บริดจ์สื่อสารกับ Chrome”BrowserPool จะสร้าง chrome-php/chromeBrowserFactory (สามารถระบุพาธไบนารีอย่างชัดเจนได้) และเปิด Chrome ด้วยชุดแฟล็กตายตัว ได้แก่ headless: true, keepAlive: true, windowSize: [1200, 800], sendSyncDefaultTimeout: renderTimeout * 1000 และแฟล็กกำหนดเองที่ระบุไว้ในหน้า /integrations/artisan/configuration/ จากนั้นบริดจ์จะควบคุมกระบวนการที่เปิดอยู่ผ่าน Chrome DevTools Protocol (CDP) บริดจ์จะไม่เชื่อมต่อกับกระบวนการ Chrome แยกต่างหากผ่านพอร์ตดีบักระยะไกล จึงไม่มีปลายทางเครือข่ายที่ต้องเปิดเผยหรือยืนยันตัวตน Chrome ทำงานเป็นกระบวนการลูกของ PHP worker การทดสอบ tests/Unit/Artisan/BrowserPoolTest.php::getBrowserCreatesAndReusesInstanceWithExpectedOptions ยืนยันตัวเลือกการเปิดใช้งานเหล่านี้ไว้อย่างแม่นยำ
จัดเตรียมไบนารี
หัวข้อที่มีชื่อว่า “จัดเตรียมไบนารี”ติดตั้งบิลด์ Chrome หรือ Chromium ที่ผู้ใช้ worker สามารถรันได้:
# Debian / Ubuntuapt-get install -y chromium
# RHEL / Fedoradnf install -y chromium
# Alpine (containers)apk add --no-cache chromium nss freetype harfbuzz ttf-freefontตรวจสอบว่าไบนารีสามารถรันแบบ headless ในฐานะผู้ใช้ worker ได้:
chromium --headless --dump-dom about:blankโค้ดออก 0 พร้อม Document Object Model (DOM) ว่างเปล่า หมายความว่าไบนารีและไลบรารีที่ใช้ร่วมกันมีครบถ้วน โค้ดออกที่ไม่ใช่ศูนย์คือความล้มเหลวแบบเดียวกับที่บริดจ์จะแสดงเป็น ChromeRenderException ให้แก้ไขที่จุดนี้ก่อน
ชี้บริดจ์ไปยังไบนารี
หัวข้อที่มีชื่อว่า “ชี้บริดจ์ไปยังไบนารี”การตรวจหาอัตโนมัติ (ค่าเริ่มต้นของ chrome-php/chrome) ใช้งานได้เมื่อไบนารีอยู่ในพาธมาตรฐาน แต่เพื่อให้พฤติกรรมในการใช้งานจริงคาดเดาได้ ควรกำหนดพาธอย่างชัดเจน:
$config = new ChromeRendererConfig( chromeBinaryPath: '/usr/bin/chromium',);หรือผ่านการกำหนดค่าแบบอาร์เรย์:
$config = ChromeRendererConfig::fromArray([ 'chrome_binary' => '/usr/bin/chromium',]);การจัดเตรียมคอนเทนเนอร์และการตัดสินใจเรื่องแซนด์บ็อกซ์
หัวข้อที่มีชื่อว่า “การจัดเตรียมคอนเทนเนอร์และการตัดสินใจเรื่องแซนด์บ็อกซ์”ในคอนเทนเนอร์ แซนด์บ็อกซ์ระดับระบบปฏิบัติการของ Chrome มักเริ่มทำงานในฐานะ root / process identifier (PID) 1 ไม่ได้หากไม่มี kernel capability เพิ่มเติม มีสองแนวทางดังนี้:
- คงแซนด์บ็อกซ์ไว้ (แนะนำ) รัน worker ในฐานะผู้ใช้ที่ไม่ใช่ root และมอบ capability ที่แซนด์บ็อกซ์ของ Chrome ต้องการให้คอนเทนเนอร์ (โดยทั่วไปคือ
SYS_ADMINหรือโปรไฟล์ seccomp ที่อนุญาตให้สร้าง user-namespace ได้) วิธีนี้ช่วยให้การแยกกระบวนการของ Chrome ยังคงสมบูรณ์ - ปิดใช้งานแซนด์บ็อกซ์ ตั้งค่า
no_sandbox: trueChrome จะเปิดด้วย--no-sandboxการตั้งค่านี้เป็นการถอดแซนด์บ็อกซ์สำหรับแยกกระบวนการของ Chrome ออกจริง จึงลดการกักกันลงจริง ไม่ใช่เพียงแฟล็กที่ไม่มีผลในทางปฏิบัติ ใช้วิธีนี้เฉพาะเมื่อไม่สามารถเปิดใช้งานแซนด์บ็อกซ์ได้ ให้รัน Chrome ในฐานะผู้ใช้ที่ไม่ใช่ root ภายในคอนเทนเนอร์ที่จำกัด และถือว่าการนำไปใช้งานรูปแบบนี้ต้องอาศัยความเชื่อถือในอินพุตที่สูงขึ้น เครื่องกั้นเครือข่ายของบริดจ์ ได้แก่ Content Security Policy (CSP) และการปิดกั้น CDP ยังคงมีผลบังคับใช้ในทุกกรณี แต่สิ่งเหล่านี้ใช้แทนการแยกกระบวนการไม่ได้ แนวทางนี้สอดคล้องกับคำแนะนำเรื่องสิทธิ์ขั้นต่ำ (least-privilege) และการแยกส่วน (isolation) ของ OWASP ASVS สำหรับการเรนเดอร์เนื้อหาที่ไม่น่าเชื่อถือ
คำชี้แจงขอบเขตฉบับเต็ม รวมถึงสิ่งที่แซนด์บ็อกซ์ปกป้องและไม่ปกป้อง อยู่ในหน้า /integrations/artisan/security-and-operations/ หน้านี้ไม่ได้อ้างว่าการปิดใช้งานแซนด์บ็อกซ์ปลอดภัย
รูปแบบคอนเทนเนอร์อ้างอิง
หัวข้อที่มีชื่อว่า “รูปแบบคอนเทนเนอร์อ้างอิง”FROM php:8.4-cliRUN apt-get update && apt-get install -y --no-install-recommends \ chromium fonts-liberation \ && rm -rf /var/lib/apt/lists/*RUN useradd -m -u 10001 workerUSER workerENV CHROME_BINARY=/usr/bin/chromium# Set CHROME_NO_SANDBOX=1 only if the sandbox cannot be enabled in your runtime.รัน worker ในฐานะ worker (user ID 10001) ไม่ใช่ root บริดจ์ใช้แฟล็ก --disable-dev-shm-usage อยู่แล้ว ซึ่งช่วยหลีกเลี่ยงการแครชจาก /dev/shm ที่มีขนาดเล็ก ปัญหานี้พบได้บ่อยในคอนเทนเนอร์ที่ไม่ได้ปรับแต่งเพิ่มเติม
บริดจ์จะปิดกั้นการดึงฟอนต์จากระยะไกล (--disable-remote-fonts และ CSP) ให้ติดตั้งฟอนต์ที่ต้องการในระดับระบบปฏิบัติการ หรือฝังฟอนต์เป็นแหล่ง data: Uniform Resource Identifier (URI) ของ @font-face ภายใน defaultCss หรือ Hypertext Markup Language (HTML) ผลลัพธ์ภาษาจีน ญี่ปุ่น และเกาหลี (CJK) ต้องมีแพ็กเกจฟอนต์ CJK (เช่น fonts-noto-cjk) ติดตั้งอยู่ในอิมเมจ
การตรวจสอบสถานะ (health probe)
หัวข้อที่มีชื่อว่า “การตรวจสอบสถานะ (health probe)”ใช้ probe แบบสแตนด์อโลนนี้เพื่อทดสอบเส้นทางบริดจ์ครบทั้งสายโดยไม่ต้องใช้แอปพลิเคชันโฮสต์:
<?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 ยืนยันการเปิดใช้งาน การเรนเดอร์ และการนำเข้า exception ที่ถูกโยนออกมาคือความล้มเหลวจริง เทียบเคียงกับข้อมูลในหน้า /integrations/artisan/troubleshooting/ เชื่อมต่อ probe นี้เป็นการตรวจสอบความพร้อม (readiness check) ในการนำไปใช้งานที่มีการกำกับการทำงาน (orchestrated deployment)
หมายเหตุเกี่ยวกับการแยกทรัพยากร
หัวข้อที่มีชื่อว่า “หมายเหตุเกี่ยวกับการแยกทรัพยากร”- รัน Chrome ในฐานะผู้ใช้เฉพาะที่ไม่ใช่ root
- กำหนดขีดจำกัดหน่วยความจำของโฮสต์ บริดจ์จำกัดการเติบโตด้วยการรีสตาร์ททุก 100 การเรนเดอร์ แต่ยังคงต้องมีเพดานในระดับโฮสต์
- จับคู่
render_timeoutกับงบประมาณคำขอในระดับต้นทาง (upstream) บนทุกเส้นทางที่อินพุตที่ไม่น่าเชื่อถือเข้าถึงได้ - อย่าเปิดเผยพอร์ตดีบักระยะไกลของ Chrome บริดจ์ไม่ได้ใช้พอร์ตดังกล่าว และพอร์ต CDP ที่เปิดอยู่คือช่องทางควบคุมที่ไม่ผ่านการยืนยันตัวตน
รูปแบบความล้มเหลวและการแก้ไขปัญหา
หัวข้อที่มีชื่อว่า “รูปแบบความล้มเหลวและการแก้ไขปัญหา”| อาการ | สาเหตุที่เป็นไปได้ | ดูที่ใด |
|---|---|---|
ChromeNotAvailableException | chrome-php/chrome ไม่ได้ติดตั้ง | /integrations/artisan/install/ (การติดตั้ง) |
ChromeRenderException ในการเรนเดอร์ครั้งแรก | ไม่มีไบนารี / แซนด์บ็อกซ์เริ่มต้นใช้งานไม่ได้ | หน้านี้; /integrations/artisan/troubleshooting/ |
| PDF ว่างเปล่า | ไม่มีกล่องที่มองเห็นได้ / Chrome แครช | /integrations/artisan/troubleshooting/ (การแก้ปัญหา) |
| รูปภาพระยะไกลว่างเปล่า | เครือข่ายถูกปิดกั้นโดยการออกแบบ | /integrations/artisan/security-and-operations/ (ความปลอดภัยและการดำเนินงาน) |
| ความหน่วงพุ่งสูงเป็นระยะ | การรีสตาร์ททุก 100 การเรนเดอร์ | /integrations/artisan/production-usage/ (การใช้งานจริง) |
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- /integrations/artisan/install/ (การติดตั้ง)
- /integrations/artisan/configuration/ (การกำหนดค่า)
- /integrations/artisan/security-and-operations/ (ความปลอดภัยและการดำเนินงาน)
- /integrations/artisan/troubleshooting/ (การแก้ปัญหา)
- /integrations/artisan/production-usage/ (การใช้งานจริง)