ข้ามไปยังเนื้อหา
getnextpdf.com

ทำคอนเทนเนอร์ให้แอปพลิเคชัน 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-curlHTTP 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 สำหรับการใช้งานจริง”)

นี่คือ 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.*.local
var/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 /app
COPY 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/app
RUN rm -rf /var/www/app/vendor
COPY --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/app
USER appuser
CMD ["php", "bin/generate.php"]

สเตจ dependency รันด้วย --no-scripts เพื่อไม่ให้ application post-install hook รันกับแผนผังที่ไม่สมบูรณ์ รันขั้นตอน build ของแอปพลิเคชันใดๆ (การคอมไพล์ asset การ warm cache) ในสเตจถัดไปหลังจากคัดลอกโค้ดแล้ว

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 ดังที่แสดงด้านบน

เอนจินเนทีฟแก้ฟอนต์จาก ไฟล์ฟอนต์ ที่มันอ่านได้ ไม่ใช่จากฟอนต์ที่ติดตั้งของ 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 และการจัดการระบบไฟล์อ่านอย่างเดียวอยู่ในหน้า เฉพาะทั้งหมด — อย่าทำซ้ำที่นี่ อ่าน จัดเตรียมฟอนต์สำหรับเอนจินเนทีฟในการใช้งานจริง สำหรับรูปแบบเต็ม และลงทะเบียนไดเรกทอรีเดียวกับที่คุณบันเดิล

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 คุ้มค่าสำหรับ 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=1
opcache.enable_cli=0
opcache.memory_consumption=192
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0

opcache.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 ที่ 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 แล้ว

Terminal window
docker run --rm your-app:latest php vendor/bin/nextpdf doctor

ผลลัพธ์ที่แข็งแรงยืนยัน PHP 8.4 และ extension ที่จำเป็นทุกตัวถูกโหลด เชื่อมการ เรียกเดียวกันเข้ากับ build (หรืองาน smoke ใน CI) เพื่อให้ extension ที่หายไปหยุด ไปป์ไลน์

Terminal window
# 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 opcache apk del .build-deps — แต่ก่อนอื่น apk add --no-cache library runtime ที่ extension link ถึง (icu-libs, libpng, freetype, libjpeg-turbo, oniguruma) เพื่อให้การลบ build group ไม่ unlink intl.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 กลับมาก็ต่อเมื่อคุณลบ safeguard rm -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 บันทึกไว้ในหน้าฟอนต์ที่ลิงก์ไว้ใต้ดูเพิ่มเติม