Skip to content

Thiết lập Docker

Chạy Artisan trong Docker yêu cầu binary Chrome hoặc Chromium trong container. Hướng dẫn này bao gồm pattern Dockerfile sẵn sàng production, cấu hình Compose, cân nhắc bảo mật và hỗ trợ font.

Dockerfile

Tối thiểu (dựa trên Debian)

dockerfile
FROM php:8.3-cli

# Cài Chromium và thư viện cần thiết
RUN apt-get update && apt-get install -y --no-install-recommends \
    chromium \
    fonts-liberation \
    libappindicator3-1 \
    libasound2 \
    libatk-bridge2.0-0 \
    libatk1.0-0 \
    libcups2 \
    libdbus-1-3 \
    libdrm2 \
    libgbm1 \
    libnspr4 \
    libnss3 \
    libx11-xcb1 \
    libxcomposite1 \
    libxdamage1 \
    libxrandr2 \
    xdg-utils \
    && rm -rf /var/lib/apt/lists/*

# Đặt đường dẫn Chrome cho Artisan tự phát hiện
ENV CHROME_PATH=/usr/bin/chromium

# Cài dependency Composer
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY . .

Dựa trên Alpine (Image nhỏ hơn)

dockerfile
FROM php:8.3-cli-alpine

RUN apk add --no-cache \
    chromium \
    nss \
    freetype \
    harfbuzz \
    ca-certificates \
    ttf-freefont \
    font-noto-cjk

ENV CHROME_PATH=/usr/bin/chromium-browser

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY . .

Docker Compose

yaml
services:
  app:
    build: .
    volumes:
      - ./output:/app/output
    environment:
      CHROME_PATH: /usr/bin/chromium
    deploy:
      resources:
        limits:
          memory: 1G
        reservations:
          memory: 512M
    # Cần thiết cho Chrome sandboxing trong Docker
    security_opt:
      - seccomp=unconfined
    cap_add:
      - SYS_ADMIN

Chrome Flag cho Docker

Chrome trong container cần flag cụ thể để chạy ổn định. Truyền chúng khi tạo renderer.

php
use Yeeefang\TcpdfNext\Artisan\HtmlRenderer;

$renderer = HtmlRenderer::create(
    chromeFlags: [
        '--no-sandbox',              // bắt buộc trừ khi bạn cấu hình seccomp
        '--disable-setuid-sandbox',  // bỏ qua sandbox phụ
        '--disable-gpu',             // không GPU trong container
        '--disable-dev-shm-usage',   // ghi vào /tmp thay vì /dev/shm
        '--disable-software-rasterizer',
        '--single-process',          // giảm bộ nhớ cho render đơn giản
    ],
);

WARNING

Flag --no-sandbox tắt process sandbox của Chrome. Trong production, ưu tiên giữ sandbox bật và cấp quyền SYS_ADMIN cho container hoặc dùng seccomp profile tùy chỉnh.

Cân nhắc bảo mật

Tùy chọn A: Giữ Sandbox (Khuyên dùng)

Thêm SYS_ADMIN vào container và bỏ --no-sandbox.

yaml
services:
  app:
    cap_add:
      - SYS_ADMIN
    security_opt:
      - seccomp=chrome-seccomp.json

Bạn có thể tìm seccomp profile tối thiểu cho Chrome trong tài liệu dự án Chromium.

Tùy chọn B: User Non-Root

Chạy Chrome dạng user không đặc quyền bên trong container.

dockerfile
RUN groupadd -r artisan && useradd -r -g artisan -G audio,video artisan \
    && mkdir -p /home/artisan/Downloads \
    && chown -R artisan:artisan /home/artisan

USER artisan

Tùy chọn C: Filesystem Read-Only

Mount root filesystem dạng read-only và cung cấp tmpfs ghi được cho Chrome.

yaml
services:
  app:
    read_only: true
    tmpfs:
      - /tmp
      - /home/artisan/.config

Giới hạn bộ nhớ

Chrome có thể tiêu thụ bộ nhớ đáng kể, đặc biệt khi render trang phức tạp. Đặt giới hạn container và giám sát sử dụng.

Độ phức tạp trangBộ nhớ khuyên dùng
Text đơn giản (1--5 trang)256 MB
Bảng và hình ảnh (5--20 trang)512 MB
Layout phức tạp, biểu đồ, JS (20+ trang)1 GB+

Nếu Chrome hết bộ nhớ, nó thoát với code 137 (OOM killed). RenderException của Artisan bọc lỗi này với thông báo mô tả.

php
// Fail nhanh với timeout chặt để tránh tốn bộ nhớ
$options = RenderOptions::create()->setTimeout(15000);

Cài font

Docker image đi kèm font tối thiểu. Cài thêm font để render đúng chữ viết phi Latin và typography thương hiệu.

Font hệ thống

dockerfile
# Font CJK (Trung, Nhật, Hàn)
RUN apt-get update && apt-get install -y \
    fonts-noto-cjk \
    fonts-noto-cjk-extra

# Arabic, Hebrew, Devanagari
RUN apt-get install -y \
    fonts-noto-core \
    fonts-noto-extra

# Google Font (vd: Inter, Roboto)
RUN apt-get install -y fonts-inter

Font tùy chỉnh

Sao chép file font vào container và đăng ký với fontconfig.

dockerfile
COPY ./fonts/*.ttf /usr/share/fonts/custom/
RUN fc-cache -fv

Web Font

Chrome trong Artisan có thể tải @font-face web font tại thời điểm render, giống trình duyệt thường. Không cần cấu hình thêm, nhưng đảm bảo container có quyền truy cập mạng đến font CDN.

css
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');

body {
    font-family: 'Inter', sans-serif;
}

Health Check

Thêm health check xác minh Chrome hoạt động.

dockerfile
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD chromium --headless=new --disable-gpu --no-sandbox \
        --dump-dom about:blank > /dev/null 2>&1 || exit 1

Ví dụ Production đầy đủ

dockerfile
FROM php:8.3-cli-bookworm AS base

# System dependency + Chromium
RUN apt-get update && apt-get install -y --no-install-recommends \
    chromium \
    fonts-liberation \
    fonts-noto-cjk \
    libnss3 libgbm1 libatk-bridge2.0-0 \
    && rm -rf /var/lib/apt/lists/*

ENV CHROME_PATH=/usr/bin/chromium

# User non-root
RUN groupadd -r artisan && useradd -r -g artisan artisan \
    && mkdir -p /home/artisan && chown artisan:artisan /home/artisan

WORKDIR /app

COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

COPY --chown=artisan:artisan . .

USER artisan

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD chromium --headless=new --no-sandbox --dump-dom about:blank > /dev/null 2>&1

CMD ["php", "artisan", "render:process"]

Bước tiếp theo

Phân phối theo giấy phép LGPL-3.0-or-later.