ทำคอนเทนเนอร์ให้แอปพลิเคชัน NextPDF
ภาพรวมโดยสรุป
หัวข้อที่มีชื่อว่า “ภาพรวมโดยสรุป”คุณต้องการ Docker image ขนาดเล็กที่ทำซ้ำได้ซึ่งรันเอนจิน NextPDF core แบบเนทีฟ
ภายในกระบวนการ — composer require nextpdf/core สร้าง PDF ภายในกระบวนการ PHP
ของคุณ หน้านี้สร้างสิ่งนั้นพอดี: image php:8.4 ที่มีเฉพาะ extension ที่เอนจิน
ต้องการจริง ไม่มี development dependency ในเลเยอร์สุดท้าย ฟอนต์ที่บันเดิล ผู้ใช้
runtime แบบ non-root, opcache ที่ปรับแต่งสำหรับการใช้งานจริง และขั้นตอนการตรวจสอบ
ที่ทำให้ build ล้มเหลวหากมีสิ่งใดขาดหายไป
หน้านี้ เฉพาะ สำหรับเอนจินเนทีฟ Chrome bridge
(writeHtmlChrome ผ่าน nextpdf/artisan) และเซิร์ฟเวอร์ Connect เป็น runtime
แยกต่างหากที่มี image ของตัวเองซึ่งหนักกว่า — การติดตั้ง Chromium แบบ headless
สำหรับ bridge บริการที่ทำงานยาวนานสำหรับ Connect อย่าเพิ่มเบราว์เซอร์หรือเซิร์ฟเวอร์
ลงใน image นี้ เอนจินเนทีฟไม่ต้องการทั้งสองอย่าง
ก่อนเริ่ม ให้ยืนยันว่าชิ้นส่วนเหล่านี้พร้อมแล้ว
- แอปพลิเคชันของคุณมี
composer.jsonและcomposer.lockที่ commit แล้ว พร้อมnextpdf/coreเป็น dependency - คุณมีไฟล์ฟอนต์ที่ตั้งใจจะฝัง และคุณได้รับอนุญาตให้ฝังมัน
- คุณสามารถรัน
docker buildกับไดเรกทอรีแอปพลิเคชันของคุณ
นี่คือ how-to ด้านการปฏิบัติการ แทบไม่มี PHP ที่นี่ งานคือ Dockerfile และการตั้งค่า สภาพแวดล้อมไม่กี่อย่าง
สิ่งที่เอนจินต้องการจริง
หัวข้อที่มีชื่อว่า “สิ่งที่เอนจินต้องการจริง”image ต้องตอบสนองข้อกำหนดแพลตฟอร์มจริงของเอนจิน ไม่มากไปกว่านั้น เมื่ออ่านมัน
ตรงจากแพ็กเกจ nextpdf/core ต้องการ php: >=8.4 <9.0 และ extension PHP เหล่านี้
| Extension | ทำไมเอนจินจึงต้องการมัน |
|---|---|
ext-mbstring | การจัดการสตริง multi-byte สำหรับข้อความและการเข้ารหัส |
ext-intl | การรองรับ Unicode, locale และ internationalization |
ext-gd | การถอดรหัสและประมวลผลภาพ raster |
ext-openssl | การเข้ารหัสสำหรับการลงนามและการแฮชที่ปลอดภัย |
ext-zlib | การบีบอัด stream (Flate) ของวัตถุ PDF |
ext-curl | HTTP client สำหรับการเรียกออกของเอนจิน |
จับคู่สิ่งเหล่านั้นกับ image php:8.4 ทางการ openssl, curl และ zlib
ถูกคอมไพล์เข้าไปใน image PHP ทางการแล้ว ดังนั้นคุณจึง ไม่ ต้อง
docker-php-ext-install มัน mbstring, gd และ intl ไม่ได้ บันเดิลและ
ต้องติดตั้ง และแต่ละตัวต้องการ development header ของระบบที่มีอยู่ก่อน —
mbstring ยังต้องการ build dependency libonig-dev (Oniguruma) เพิ่มเติม
อย่าเพิ่ม extension ของเอนจินที่แพ็กเกจไม่ได้ระบุ — docker-php-ext-install ที่
เกินมาทุกตัวคือเวลา build และพื้นผิวการโจมตีที่คุณไม่ต้องการ extension ที่ไม่ใช่
ของเอนจินตัวเดียวที่ image นี้ติดตั้งคือ opcache: มันเป็น extension
ประสิทธิภาพ runtime ไม่ได้บันเดิลเปิดใช้งานบน image ทางการ และการปรับแต่ง opcache
ด้านล่างขึ้นกับการที่มันมีอยู่ (ดู “Opcache สำหรับการใช้งานจริง”)
Dockerfile สำหรับการใช้งานจริง
หัวข้อที่มีชื่อว่า “Dockerfile สำหรับการใช้งานจริง”นี่คือ build แบบสองสเตจ สเตจแรกติดตั้ง dependency ของ Composer โดยไม่รวมแพ็กเกจ development สเตจที่สองคือ runtime image แบบบางที่ส่งออกไป
ก่อนอื่นเพิ่ม .dockerignore ข้าง Dockerfile หน้าที่หลักของมันคือการเก็บ
สภาพแวดล้อมของโฮสต์ — vendor/ ที่ build บนโฮสต์ ไฟล์ความลับในเครื่อง และ build
cache — ให้พ้นจาก build context ทั้งหมด เพื่อให้ COPY . /var/www/app ส่งเฉพาะ
สิ่งที่คุณตั้งใจ: build ที่เล็กกว่า เร็วกว่า และปลอดภัยกว่าซึ่งรั่วความลับ .env
ในเครื่องไม่ได้ หรือไม่นำ vendor/ ของโฮสต์ขนาดหลายเมกะไบต์เข้าไปใน image
การยกเว้น vendor/ ยังสำคัญเพราะ COPY ไดเรกทอรีเป็นการ merge ไม่ใช่การแทนที่
Dockerfile ด้านล่างรัน RUN rm -rf /var/www/app/vendor ก่อน
COPY --from=vendor ... /var/www/app/vendor ดังนั้นใน image นี้ vendor/
ของโฮสต์จึงไม่มีวันอยู่รอดใต้แผนผัง dependency ที่สะอาด แต่หากคุณลบ
safeguard rm -rf นั้นออก vendor/ ที่ build บนโฮสต์ใน context จะลงมาก่อนและ
การคัดลอกจากสเตจ vendor จะเขียนทับเฉพาะพาธที่แผนผังที่สะอาดมี — ไฟล์ของโฮสต์ที่
เกินมา (แพ็กเกจที่ stale หรือติดตั้งแบบ dev, คลาสกำพร้า) จะอยู่รอดข้างใต้ การเก็บ
vendor/ ให้พ้นจาก context ปิดช่องโหว่นั้นโดยไม่คำนึงถึง rm -rf
# .dockerignore — keep the host environment out of the build context.vendor/.git/.env.env.local.env.*.localvar/cache/storage/node_modules/*.logยกเว้นไฟล์ความลับในเครื่องจริง (.env, .env.local, .env.*.local) ไม่ใช่
.env.* แบบครอบคลุม — wildcard นั้นยังตัด template ที่ไม่ใช่ความลับเช่น
.env.example ที่คุณ ต้องการ ส่งไปด้วยเพื่อให้ image นำพา baseline การกำหนดค่า
ที่บันทึกไว้ เก็บ template env ที่ commit แล้วซึ่งไม่ใช่ความลับไว้ใน context
ยกเว้นเฉพาะไฟล์ที่ถือความลับในเครื่องจริง
# syntax=docker/dockerfile:1
# ---- Stage 1: dependencies (no dev) ---------------------------------------FROM composer:2 AS vendor
WORKDIR /appCOPY composer.json composer.lock ./
# Install production dependencies only. --no-dev excludes phpunit, phpstan,# infection, and the other require-dev tooling from the shipped image.# --optimize-autoloader builds a class map for the *vendor* tree here; the# application's own classes are not present in this stage yet, so they are# optimized after the source copy in the runtime stage (see below).RUN composer install \ --no-dev \ --no-interaction \ --no-progress \ --prefer-dist \ --optimize-autoloader \ --no-scripts
# ---- Stage 2: runtime ------------------------------------------------------FROM php:8.4-cli AS runtime
# System headers for the gd, intl, and mbstring extensions that need compiling.# The PHP image already provides openssl, curl, and zlib, so those are NOT# listed; gd, intl, and mbstring are installed below. opcache has no system# headers and is installed in the same step. mbstring is built against# Oniguruma, so libonig-dev is in the *-dev set and its runtime lib (libonig5)# is preserved by the same detection below.## Build the *-dev headers (which pull in the runtime libs), compile the# extensions, then mark only the runtime shared libraries the extensions# actually link against so they survive the --auto-remove purge of the headers.# Removing libicu / libpng / libjpeg / libfreetype / libonig here would unlink# intl.so, gd.so, or mbstring.so at runtime ("undefined symbol" / "cannot open# shared object file").RUN set -eux; \ savedAptMark="$(apt-mark showmanual)"; \ apt-get update; \ apt-get install -y --no-install-recommends \ libicu-dev \ libpng-dev \ libjpeg62-turbo-dev \ libfreetype6-dev \ libonig-dev; \ docker-php-ext-configure gd --with-freetype --with-jpeg; \ docker-php-ext-install -j"$(nproc)" gd intl mbstring opcache; \ # Detect the runtime .so dependencies of the just-built extensions and # mark them manual so --auto-remove keeps them while dropping the headers. apt-mark auto '.*' > /dev/null; \ apt-mark manual $savedAptMark > /dev/null; \ find /usr/local/lib/php/extensions -type f -name '*.so' -exec \ sh -c 'ldd "$1" 2>/dev/null \ | awk "/=>/ { print \$3 }" \ | grep -E "^/" \ | xargs -r dpkg-query -S 2>/dev/null \ | cut -d: -f1 \ | sort -u \ | xargs -r apt-mark manual' _ {} \; ; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ rm -rf /var/lib/apt/lists/*
# Production opcache settings (see the opcache section below). The opcache# extension is installed above (docker-php-ext-install opcache); this file only# tunes it.COPY docker/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# A static Composer binary for the one optimized-autoloader rebuild below. It is# copied into the build but the final stage runs no Composer at request time.COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/app
# Application code, then the vendor tree from the dependency stage. The .dockerignore# should already keep a host vendor/ out of the context; the rm here is a second line# of defense so a stale host-built vendor/ can never merge under the clean one (a# directory COPY merges, it does not replace).COPY . /var/www/appRUN rm -rf /var/www/app/vendorCOPY --from=vendor /app/vendor /var/www/app/vendor
# Now that the application source is present, regenerate the optimized class map# so the APP's own classes are in the optimized autoloader, not just the vendor# packages. --no-dev keeps require-dev out; --no-scripts avoids running# application hooks during the image build.RUN composer dump-autoload \ --optimize \ --no-dev \ --no-interaction \ --no-scripts \ && rm -f /usr/bin/composer
# The bundled fonts live at /var/www/app/resources/fonts. The native engine does# NOT read any font-path environment variable — the entrypoint registers that# directory in PHP (see "Bundle fonts into the image" below). There is no ENV# line for fonts here.
# Run as a non-root user (see the non-root section below).RUN useradd --system --no-create-home --uid 10001 appuser \ && chown -R appuser:appuser /var/www/appUSER appuser
CMD ["php", "bin/generate.php"]สเตจ dependency รันด้วย --no-scripts เพื่อไม่ให้ application post-install hook
รันกับแผนผังที่ไม่สมบูรณ์ รันขั้นตอน build ของแอปพลิเคชันใดๆ (การคอมไพล์ asset
การ warm cache) ในสเตจถัดไปหลังจากคัดลอกโค้ดแล้ว
การติดตั้ง Composer แบบ multi-stage (ไม่มี dev dependency)
หัวข้อที่มีชื่อว่า “การติดตั้ง Composer แบบ multi-stage (ไม่มี dev dependency)”image ที่ส่งออกต้องไม่มีเครื่องมือ development แฟล็ก --no-dev บน
composer install คือบรรทัดที่สำคัญที่สุด: มันข้ามทุกอย่างภายใต้ require-dev ใน
nextpdf/core และแอปพลิเคชันของคุณ — test runner, static analyzer และเครื่องมือ
mutation — ซึ่งไม่มีตัวใดมีที่ในการใช้งานจริง จับคู่มันกับ --optimize-autoloader
เพื่อให้ autoloader เป็น class map ที่สร้างขึ้นแทนที่จะเป็นการสแกนระบบไฟล์ในทุกคำขอ
คัดลอก composer.json และ composer.lock ก่อน ส่วนที่เหลือของ source เพื่อ
ให้ Docker แคชเลเยอร์ dependency และแก้ไขใหม่เฉพาะเมื่อ lock file เปลี่ยน เพราะ
การติดตั้งครั้งแรกนั้นรันกับ lock file เพียงอย่างเดียว — โดยไม่มี application
source — --optimize-autoloader ที่นั่นสร้าง class map สำหรับแผนผัง vendor
เท่านั้น คลาสของแอปพลิเคชันของคุณเองยังไม่มี นั่นคือเหตุผลที่สเตจ runtime รัน
composer dump-autoload --optimize --no-dev --no-scripts หนึ่งครั้ง หลัง
การคัดลอก source: มันพับคลาสของแอปลงใน class map ที่ optimize เดียวกัน อย่ารัน
composer dump-autoload แยกต่างหากในแผนผังที่คุณยังพัฒนาอยู่ (มันจะ commit class
map ของการใช้งานจริงลงในแผนผัง dev) การ rebuild อยู่ใน image หลังการคัดลอก source
ดังที่แสดงด้านบน
บันเดิลฟอนต์ลงใน image
หัวข้อที่มีชื่อว่า “บันเดิลฟอนต์ลงใน image”เอนจินเนทีฟแก้ฟอนต์จาก ไฟล์ฟอนต์ ที่มันอ่านได้ ไม่ใช่จากฟอนต์ที่ติดตั้งของ OS
การติดตั้งแพ็กเกจ fonts-* หรือการรัน fc-cache ไม่ทำอะไรที่เส้นทางเนทีฟเห็นได้
ดังนั้น image นี้จึงไม่ติดตั้งฟอนต์ของระบบ บันเดิลไฟล์ .ttf / .otf ของคุณใต้
resources/fonts/ คำสั่ง COPY . /var/www/app ด้านบนนำมันเข้าไปใน image อยู่แล้ว
การนำไฟล์เข้าไปใน image เป็นเพียงครึ่งหนึ่งของงาน เอนจินเนทีฟเปล่าๆ อ่านตัวแปร
สภาพแวดล้อมค้นหาฟอนต์ ไม่ ได้ — NEXTPDF_FONTS_PATH คือค่าเริ่มต้นของคีย์
config fonts_path ของแพ็กเกจ nextpdf/laravel
(env('NEXTPDF_FONTS_PATH', resource_path('fonts'))) และถูกใช้เฉพาะโดย
integration ของเฟรมเวิร์กนั้น ไม่ใช่โดย nextpdf/core entrypoint
php bin/generate.php ธรรมดาที่ตั้งเฉพาะตัวแปรนั้นไม่ลงทะเบียนฟอนต์ใดและเรนเดอร์
tofu แบบเดียวกับที่ image นี้มีอยู่เพื่อป้องกัน entrypoint ต้องลงทะเบียนไดเรกทอรี
ที่บันเดิลใน PHP
use NextPDF\Typography\FontRegistry;use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;
// Register the directory the Dockerfile bundled the fonts into.$registry = new FontRegistry('/var/www/app/resources/fonts');// (equivalently, $registry->addFontDirectory('/var/www/app/resources/fonts');)
$factory = new DocumentFactory($registry, new ImageRegistry(maxCacheBytes: 0));$doc = $factory->create();นั่นคือทั้งหมดที่เกี่ยวข้องกับ Docker สำหรับฟอนต์ กฎการตั้งชื่อไฟล์ API ของ registry รูปแบบ warmup-and-lock และการจัดการระบบไฟล์อ่านอย่างเดียวอยู่ในหน้า เฉพาะทั้งหมด — อย่าทำซ้ำที่นี่ อ่าน จัดเตรียมฟอนต์สำหรับเอนจินเนทีฟในการใช้งานจริง สำหรับรูปแบบเต็ม และลงทะเบียนไดเรกทอรีเดียวกับที่คุณบันเดิล
รันเป็นผู้ใช้แบบ non-root
หัวข้อที่มีชื่อว่า “รันเป็นผู้ใช้แบบ non-root”image PHP ทางการรันเป็น root โดยค่าเริ่มต้น ตัวสร้าง PDF ไม่ต้องการ root ดังนั้น
ให้สร้างผู้ใช้ที่ไม่มีสิทธิพิเศษและสลับไปใช้มัน Dockerfile ด้านบนเพิ่มผู้ใช้ระบบ
appuser พร้อม UID สูงที่กำหนดไว้ตายตัว (10001) ให้กรรมสิทธิ์แผนผังแอปพลิเคชัน
แก่มัน และจบด้วย USER appuser เพื่อให้ทุกกระบวนการที่คอนเทนเนอร์เริ่มไม่มีสิทธิพิเศษ
รักษาแอปพลิเคชันให้อ่านอย่างเดียว ณ runtime ที่ใดทำได้ เอนจินอ่านไฟล์ฟอนต์ของมัน
และเขียนเฉพาะเอาต์พุตและ cache ของฟอนต์ที่แยกวิเคราะห์แล้วที่เป็นทางเลือก ดังนั้น
คอนเทนเนอร์ readOnlyRootFilesystem จึงทำงานได้ตราบเท่าที่พาธเอาต์พุตและ
ไดเรกทอรี cache ใดๆ เป็น writable mount ผสมสิ่งนี้กับ Linux capability ที่ drop
และแฟล็ก no-new-privileges ใน orchestrator ของคุณเพื่อการป้องกันแบบ defense
in depth
Opcache สำหรับการใช้งานจริง
หัวข้อที่มีชื่อว่า “Opcache สำหรับการใช้งานจริง”Opcache คุ้มค่าสำหรับ PHP worker ที่มีอายุยาว — FPM pool หรือกระบวนการ
Apache mod_php ที่ให้บริการหลายคำขอจากกระบวนการที่ warm หนึ่งตัว กระบวนการ
เหล่านั้นคอมไพล์คลาสของคุณหนึ่งครั้งแล้วไม่เคย stat ไฟล์ source บนเส้นทางที่ร้อน
อีก ซึ่งเป็นสิ่งที่ opcache.validate_timestamps=0 ให้คุณพอดี Opcache ไม่
เปิดใช้งานทันทีบน image php:8.4 ทางการ ดังนั้น Dockerfile ด้านบนติดตั้งมันด้วย
docker-php-ext-install opcache (คุณสามารถ docker-php-ext-enable opcache
เทียบเท่าได้หาก extension ถูกคอมไพล์แล้ว) ไฟล์ conf.d ด้านล่างคือการปรับแต่ง
ไม่ใช่ขั้นตอนการเปิดใช้งาน — มันไม่ทำอะไรจนกว่า extension จะถูกโหลด ส่งมันเป็น
conf.d include (docker/opcache.ini คัดลอกใน Dockerfile)
opcache.enable=1opcache.enable_cli=0opcache.memory_consumption=192opcache.interned_strings_buffer=16opcache.max_accelerated_files=20000opcache.validate_timestamps=0opcache.validate_timestamps=0 หมายความว่า cache ไม่เคยตรวจสอบไฟล์ source ซ้ำ —
ถูกต้องสำหรับ image ที่เปลี่ยนแปลงไม่ได้ เนื่องจากวิธีเดียวที่โค้ดเปลี่ยนคือ image
ใหม่ ปรับแต่ง memory_consumption และ max_accelerated_files ให้เข้ากับจำนวน
คลาสของแอปพลิเคชันของคุณ
CMD ที่แสดงเป็นตัวสร้าง CLI แบบ one-shot และ opcache.enable_cli=0 ถูกต้อง
สำหรับมัน กระบวนการ php bin/generate.php ที่มีอายุสั้นเริ่มต้น คอมไพล์ เรนเดอร์
หนึ่งครั้ง และออก ดังนั้น opcode cache ที่มันแชร์กับคำขอถัดไปไม่ได้จึงไม่ให้
ประโยชน์ — ปล่อย CLI opcache ปิดไว้และไม่จ่ายต้นทุนหน่วยความจำใดๆ Opcache คุ้มค่า
เฉพาะที่กระบวนการถูกใช้ซ้ำ: FPM/Apache SAPI หรือ CLI worker ที่ทำงานยาวนานจริง
(queue consumer หรือเซิร์ฟเวอร์แบบ RoadRunner) เฉพาะ CLI worker แบบ resident เช่น
นั้นที่จะตั้ง opcache.enable_cli=1 สำหรับตัวสร้าง one-shot ที่นี่ ให้คงไว้ที่ 0
หากคุณรันการตั้งค่าที่ใช้ opcache preloading (FPM worker ที่มีอายุยาวพร้อม
สคริปต์ opcache.preload) ให้ตั้ง opcache.preload=/path/to/preload.php และ
เพิ่ม opcache.preload_user=appuser เพื่อให้ preload รันเป็นผู้ใช้ที่ไม่มีสิทธิพิเศษ
หากไม่มีสคริปต์ opcache.preload จริง opcache.preload_user ไม่ทำอะไร ซึ่งเป็น
เหตุผลที่มันไม่อยู่ในการกำหนดค่า baseline ด้านบน — อย่าเพิ่มมันเว้นแต่คุณจะตั้ง
opcache.preload ด้วย
ตรวจสอบ image
หัวข้อที่มีชื่อว่า “ตรวจสอบ image”เพิ่มขั้นตอนการตรวจสอบเพื่อให้ image ที่ build ผิดล้มเหลวอย่างชัดเจนแทนที่จะผลิต
tofu หรือ fatal ที่คำขอแรก NextPDF มาพร้อม CLI ที่คำสั่ง doctor ตรวจสอบ
สภาพแวดล้อม PHP ที่กำลังรันและรายงานเกี่ยวกับ extension ที่เอนจินใส่ใจพอดี —
openssl, zlib, mbstring, gd, curl และ intl แพ็กเกจประกาศ
"bin": ["bin/nextpdf"] ดังนั้นในแอปพลิเคชันที่ใช้งาน Composer ติดตั้ง
executable ที่ vendor/bin/nextpdf (ไม่ใช่ bin/nextpdf ซึ่งเป็นพาธภายใน
แพ็กเกจ nextpdf/core เอง) รันมันภายใน image ที่ build แล้ว
docker run --rm your-app:latest php vendor/bin/nextpdf doctorผลลัพธ์ที่แข็งแรงยืนยัน PHP 8.4 และ extension ที่จำเป็นทุกตัวถูกโหลด เชื่อมการ เรียกเดียวกันเข้ากับ build (หรืองาน smoke ใน CI) เพื่อให้ extension ที่หายไปหยุด ไปป์ไลน์
# Fail the pipeline if the engine's environment is not healthy.docker run --rm your-app:latest php vendor/bin/nextpdf doctor || exit 1สำหรับการตรวจสอบแบบ end-to-end ให้เรนเดอร์หนึ่งหน้าผ่าน entrypoint ของคุณเองและ ยืนยันบนเอาต์พุต ดังที่หน้าฟอนต์อธิบายสำหรับการตรวจสอบ smoke ของฟอนต์
กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”php:8.4-fpmหรือ-apacheแทน-cliใช้ SAPI ที่แอปของคุณให้บริการจริง รายการ extension เหมือนกัน ต่างเฉพาะ base tag และCMD/entrypoint สำหรับ queue worker หรืองาน batch CLI-cliถูกต้อง- Alpine (
php:8.4-alpine) ต้องการชื่อแพ็กเกจที่แตกต่าง บรรทัดapt-getด้านบนเป็นสำหรับ image เริ่มต้นที่ใช้ Debian บน Alpine ให้ติดตั้ง header*-devเป็น virtual build group (apk add --no-cache --virtual .build-deps icu-dev libpng-dev freetype-dev libjpeg-turbo-dev oniguruma-dev) และหลังจากขั้นตอนdocker-php-ext-install gd intl mbstring opcacheapk del .build-deps— แต่ก่อนอื่นapk add --no-cachelibrary runtime ที่ extension link ถึง (icu-libs,libpng,freetype,libjpeg-turbo,oniguruma) เพื่อให้การลบ build group ไม่ unlinkintl.so/gd.so/mbstring.soนี่คือกฎการเก็บ runtime lib เดียวกับที่ บล็อก Debian บังคับด้วยapt-mark - อย่าติดตั้งแพ็กเกจ
fonts-*มันมองไม่เห็นโดยเอนจินเนทีฟ บันเดิลไฟล์ฟอนต์ แทน — ดูหน้าฟอนต์ที่ลิงก์ไว้ด้านบน - Premium และ ionCube เป็นเรื่องของ image คนละเรื่อง build NextPDF Pro / Enterprise ที่เข้ารหัสด้วย ionCube ต้องการ ionCube Loader ที่ติดตั้งใน image และตรงกับ build PHP ที่แน่นอนของคอนเทนเนอร์ (8.4, NTS เทียบกับ ZTS) นั่นอยู่นอก ขอบเขตของ image core หากคุณ deploy premium ให้ทำตามส่วน Docker ของ การตั้งค่า ionCube Loader
- เก็บ
vendor/ของโฮสต์ให้พ้นจาก build context.dockerignore(ยกเว้นvendor/,.git/และ cache ในเครื่อง) เก็บแผนผังโฮสต์ให้พ้นจาก context ทั้งหมด — นั่นคือสิ่งที่ทำให้ build เล็ก เร็ว และปลอดความลับในเครื่อง ที่รั่ว มันยังป้องกันกรณี directory-merge:COPYไดเรกทอรีเป็นการ merge ไม่ใช่ การแทนที่ ดังนั้นvendor/ที่ build บนโฮสต์ที่ไปถึง context จะลงมาก่อนและCOPY --from=vendor /app/vendor /var/www/app/vendorจะเขียนทับเฉพาะพาธที่ แผนผัง dependency ที่สะอาดมี ใน Dockerfile นี้RUN rm -rf /var/www/app/vendorก่อนการคัดลอก vendor ลบไดเรกทอรีเช่นนั้นแล้ว ดังนั้นสิ่งตกค้างนั้นจึงเกิดที่นี่ ไม่ได้ ความเสี่ยงการ merge กลับมาก็ต่อเมื่อคุณลบ safeguardrm -rfนั้น ซึ่ง เป็นเหตุผลที่การยกเว้นใน.dockerignoreเป็นการแก้ไขที่ทนทาน
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”- อย่าส่ง dev dependency
--no-devเก็บเครื่องมือ test และ analysis และ แพ็กเกจที่ส่งต่อของมัน ให้พ้นจาก runtime image และพื้นผิวการโจมตีของมัน - รันแบบไม่มีสิทธิพิเศษ
USER appuserสุดท้ายรับประกันว่าไม่มีกระบวนการ คอนเทนเนอร์ใดรันเป็น root ผสมมันกับระบบไฟล์ root อ่านอย่างเดียวและ capability ที่ drop ใน orchestrator ของคุณ - ตรึง base image ตรึง
php:8.4ไปยัง digest ในการใช้งานจริงเพื่อให้การ rebuild ไม่สามารถดึง base ที่เปลี่ยนแปลงเงียบๆ และ rebuild ตามรอบเพื่อรับ security patch อย่างจงใจ - เก็บฟอนต์และใบอนุญาตให้พ้นจากเลเยอร์สาธารณะ บันเดิลเฉพาะฟอนต์ที่คุณได้รับ อนุญาตให้ฝัง และอย่าฝังไฟล์ใบอนุญาต premium ลงใน image ที่ push สาธารณะ — mount มัน ณ runtime แทน
ความสอดคล้องตามมาตรฐาน
หัวข้อที่มีชื่อว่า “ความสอดคล้องตามมาตรฐาน”คู่มือนี้ไม่อ้างเรื่องมาตรฐานเชิงบรรทัดฐาน ข้อเท็จจริงเกี่ยวกับแพลตฟอร์มอ่านตรงจาก
แพ็กเกจ nextpdf/core: ข้อจำกัด php: >=8.4 <9.0 และ extension ที่จำเป็น
ext-mbstring, ext-intl, ext-gd, ext-openssl,
ext-zlib และ ext-curl คำสั่งการตรวจสอบคือ handler doctor ของ CLI
nextpdf จริง — ประกาศเป็น "bin": ["bin/nextpdf"] ใน nextpdf/core และจึง
ติดตั้งที่ vendor/bin/nextpdf ในแอปที่ใช้งาน — ซึ่งรายงานเกี่ยวกับชุด extension
เดียวกัน เอนจินเนทีฟลงทะเบียนฟอนต์ผ่าน NextPDF\Typography\FontRegistry
(argument constructor ของไดเรกทอรี / addFontDirectory()) ที่เชื่อมผ่าน
NextPDF\Core\DocumentFactory NEXTPDF_FONTS_PATH คือคีย์ config fonts_path
ของแพ็กเกจ nextpdf/laravel (env('NEXTPDF_FONTS_PATH', resource_path('fonts'))) ไม่ใช่ตัวแปรที่ nextpdf/core อ่าน พฤติกรรมของ
registry บันทึกไว้ในหน้าฟอนต์ที่ลิงก์ไว้ใต้ดูเพิ่มเติม
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- จัดเตรียมฟอนต์สำหรับเอนจินเนทีฟในการใช้งานจริง: การตั้งชื่อไฟล์ฟอนต์ API ของ registry และรูปแบบ warmup-and-lock ที่ image นี้พึ่งพา
- สตรีม PDF ขนาดใหญ่ที่สร้างขึ้นเป็น HTTP response: โมเดลหน่วยความจำสำหรับการให้บริการเอกสารที่สร้างจาก controller ของเฟรมเวิร์ก
- เรนเดอร์ที่ edge ด้วย Cloudflare: เมื่อคอนเทนเนอร์ภายในกระบวนการไม่ใช่ runtime ที่เหมาะสม
- การตั้งค่า ionCube Loader: เรื่องของ image ที่แยกต่างหากสำหรับ build premium ที่เข้ารหัสด้วย ionCube