Fáze požadavku: 11. FrankenPHP & Produkční běh
Modul 10 · Lekce 1

Anatomie našeho Dockerfile: Jeden obraz pro vývoj (dev) i produkci (prod) s FrankenPHP

💡 Shrnutí lekce

Ponořte se do multi-stage Dockerfile pro PHP 8.4 a moderní aplikační server FrankenPHP. Zjistěte, jak oddělit sestavení závislostí a assetů pro maximální výkon a minimální velikost produkčního obrazu.

Éra FrankenPHP a moderní aplikační servery

Dlouhá léta byla standardem pro provozování PHP v Dockeru kombinace Nginx + PHP-FPM (buď jako dva samostatné kontejnery propojené přes TCP, nebo přes supervisor v jednom kontejneru). Tato konfigurace s sebou nesla režii meziprocesové komunikace přes protokol FastCGI, dvojí konfiguraci a zbytečnou složitost.

FrankenPHP přináší revoluci. Jde o moderní aplikační server napsaný v Go, postavený na špičkovém webserveru Caddy, do kterého je PHP runtime přímo vložen jako CGO knihovna.

  • Žádné FastCGI: Webserver komunikuje s PHP enginem přímo v paměti procesu.
  • Vysoký výkon a HTTP/3: Nativní podpora HTTP/2, HTTP/3, Brotli a Zstandard komprese.
  • Worker Mode: Možnost udržovat celou Laravel aplikaci načtenou v RAM (podobně jako u Laravel Octane).

Multi-Stage Dockerfile: Čistý a bezpečný produkční obraz

V našem souboru docker/Dockerfile využíváme technologii Multi-Stage Build. To nám umožňuje mít jediný deklarativní soubor pro lokální vývoj i finální produkci:

  1. base: Vychází z dunglas/frankenphp:1-php8.4. Nainstaluje systémové knihovny a PHP extenze (pdo_pgsql, opcache, pcntl), zkopíruje konfiguraci docker/php.ini, docker/Caddyfile a startovací skript entrypoint.sh.
  2. dev: Cíl pro lokální vývoj na počítači programátora. Doinstaluje Xdebug, nastaví ne-rootovského uživatele (UID/GID 1000) pro bezproblémová práva na soubory na Linuxu a umožňuje živé mountování složky projektu (volumes: .:/app).
  3. vendor-prod: Spustí composer install --no-dev --optimize-autoloader a připraví čisté produkční závislosti bez vývojářských nástrojů (žádný Pest, PHPUnit, Mockery).
  4. assets: Využívá lehký obraz node:22-alpine. Nainstaluje npm balíčky a zkompiluje produkční JS/CSS assety přes Vite (npm run build) do složky public/build.
  5. prod: Finální cílový obraz. Přebírá PHP prostředí z vendor-prod a zkopíruje zkompilované soubory z fáze assets. V produkčním obrazu vůbec není nainstalován Node.js ani npm!

Výsledkem je kompaktní, bezpečný a bleskurychlý obraz s minimální útočnou plochou.


Konfigurace OPcache (docker/php.ini) a Caddyfile

V souboru docker/php.ini si všimněte řádku:

opcache.validate_timestamps = ${PHP_OPCACHE_VALIDATE_TIMESTAMPS}

V lokálním vývoji máme proměnnou nastavenu na 1, aby se změny v PHP souborech okamžitě projevily. V produkci (compose.prod.yaml) předáváme hodnotu 0. PHP tak kompletně ignoruje kontrolu času modifikace souborů na disku a spouští předkompilovaný bytecode výhradně z paměti RAM.

Soubor docker/Caddyfile zajišťuje moderní kompresi (encode zstd br gzip), bezpečnostní HTTP hlavičky (X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN) a předává požadavky vestavěnému PHP workeru.

💻 Klíčová konstrukce z praxe
# docker/Dockerfile — Ukázka multi-stage architektury
FROM dunglas/frankenphp:1-php8.4 AS base

# Instalace PostgreSQL, OPcache, PCNTL a dalších PHP extenzí
RUN apt-get update && apt-get install -y --no-install-recommends \
    git unzip curl \
    && install-php-extensions pdo_pgsql pgsql pdo_sqlite sqlite3 intl gd zip pcntl bcmath opcache \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY docker/php.ini /usr/local/etc/php/conf.d/zz-app.ini
COPY docker/Caddyfile /etc/frankenphp/Caddyfile
COPY docker/entrypoint.sh /usr/local/bin/entrypoint
RUN chmod 755 /usr/local/bin/entrypoint

# --- Fáze pro instalaci produkčních Composer závislostí ---
FROM base AS vendor-prod
WORKDIR /app
COPY . /app
RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader

# --- Fáze pro kompilaci frontendových assetů ve Vite ---
FROM node:22-alpine AS assets
WORKDIR /app
COPY --from=vendor-prod /app /app
RUN npm install --no-audit --no-fund && npm run build

# --- Finální lehký produkční obraz ---
FROM vendor-prod AS prod
WORKDIR /app
COPY --from=assets /app/public/build /app/public/build
RUN mkdir -p storage bootstrap/cache \
    && chown -R www-data:www-data storage bootstrap/cache \
    && chmod -R 775 storage bootstrap/cache

🎯 Ověření znalostí z lekce

Kvíz otestuje pochopení architektury a reálného kódu probíraného v této lekci.

Otázka 1 z 3

Jaká je hlavní architektonická přednost FrankenPHP oproti tradiční konfiguraci Nginx + PHP-FPM?

Otázka 2 z 3

Proč v multi-stage Dockerfile kompilujeme assety v izolované fázi (node:22-alpine)?

Otázka 3 z 3

Jaký význam má v produkčním běhu nastavení opcache.validate_timestamps=0?

Zodpovězeno: 0 / 3
🔬 Živá anatomie v této aplikaci
3 soubory
Na co se v kódu zaměřit: V souboru docker/Dockerfile prozkoumej definici fází base, dev, vendor-prod, assets a prod. Všimni si, že produkční obraz dědí z base a kopíruje pouze hotové assety a produkční vendor bez dev balíčků.
cesta: docker/Dockerfile
89 řádků · 2.2 KB
Multi-stage Dockerfile založený na dunglas/frankenphp:1-php8.4 s dev a prod cíli a instalací pgsql/opcache rozšíření.
1 FROM dunglas/frankenphp:1-php8.4 AS base
2
3 # System dependencies & PHP extensions
4 RUN apt-get update && apt-get install -y --no-install-recommends \
5 git \
6 unzip \
7 curl \
8 && install-php-extensions \
9 pdo_pgsql \
10 pgsql \
11 pdo_sqlite \
12 sqlite3 \
13 intl \
14 gd \
15 zip \
16 pcntl \
17 bcmath \
18 opcache \
19 && apt-get clean && rm -rf /var/lib/apt/lists/*
20
21 # Composer
22 COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
23
24 # Safe git directory
25 RUN git config --system --add safe.directory '*'
26
27 # Configuration & Entrypoint
28 COPY docker/php.ini /usr/local/etc/php/conf.d/zz-app.ini
29 COPY docker/Caddyfile /etc/frankenphp/Caddyfile
30 COPY docker/entrypoint.sh /usr/local/bin/entrypoint
31 RUN chmod 755 /usr/local/bin/entrypoint
32
33 WORKDIR /app
34
35 ENV SERVER_NAME=":80" \
36 COMPOSER_ALLOW_SUPERUSER=1
37
38 ENTRYPOINT ["entrypoint"]
39 CMD ["frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile"]
40
41 # --- Dev Stage ---
42 FROM base AS dev
43
44 ARG UID=1000
45 ARG GID=1000
46 ARG USER=app
47
48 RUN apt-get update && apt-get install -y --no-install-recommends \
49 libcap2-bin \
50 && install-php-extensions xdebug \
51 && echo "xdebug.mode=off" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
52 && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp \
53 && (groupadd -g ${GID} ${USER} || true) \
54 && (useradd -u ${UID} -g ${GID} -m -s /bin/bash ${USER} || true) \
55 && mkdir -p /config /data /etc/frankenphp /app \
56 && chown -R ${USER}:${USER} /config /data /etc/frankenphp /app \
57 && chmod -R 775 /config /data \
58 && apt-get clean && rm -rf /var/lib/apt/lists/*
59
60 # --- Composer Prod Stage ---
61 FROM base AS vendor-prod
62
63 WORKDIR /app
64
65 COPY . /app
66
67 RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
68
69 # --- Assets Stage ---
70 FROM node:22-alpine AS assets
71
72 WORKDIR /app
73
74 COPY --from=vendor-prod /app /app
75
76 RUN npm install --no-audit --no-fund && npm run build
77
78 # --- Prod Stage ---
79 FROM vendor-prod AS prod
80
81 WORKDIR /app
82
83 COPY --from=assets /app/public/build /app/public/build
84
85 RUN mkdir -p storage bootstrap/cache \
86 && chown -R www-data:www-data storage bootstrap/cache \
87 && chmod -R 775 storage bootstrap/cache
88
89 ENV APP_ROOT=/app
90
cesta: docker/php.ini
14 řádků · 0.3 KB
Soubor aplikační architektury v modulu DevOps & Docker.
1 [PHP]
2 memory_limit = 512M
3 upload_max_filesize = 32M
4 post_max_size = 40M
5 max_execution_time = 60
6 date.timezone = Europe/Prague
7 expose_php = Off
8
9 [opcache]
10 opcache.enable = 1
11 opcache.enable_cli = 1
12 opcache.memory_consumption = 192
13 opcache.max_accelerated_files = 20000
14 opcache.validate_timestamps = ${PHP_OPCACHE_VALIDATE_TIMESTAMPS}
15
cesta: docker/Caddyfile
20 řádků · 0.3 KB
Konfigurace Caddy webserveru a FrankenPHP worker módu pro bleskovou obsluhu požadavků bez opakovaného bootu.
1 {
2 frankenphp
3 order php_server before file_server
4 }
5
6 {$SERVER_NAME::80}
7
8 root * {$APP_ROOT:/app}/public
9 encode zstd br gzip
10
11 header {
12 X-Content-Type-Options nosniff
13 X-Frame-Options SAMEORIGIN
14 Referrer-Policy strict-origin-when-cross-origin
15 -Server
16 }
17
18 php_server {
19 env HTTPS on
20 }
21
Zdroj: skutečné soubory této aplikace Plný průzkumník anatomie →