Anatomie našeho Dockerfile: Jeden obraz pro vývoj (dev) i produkci (prod) s FrankenPHP
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:
base: Vychází zdunglas/frankenphp:1-php8.4. Nainstaluje systémové knihovny a PHP extenze (pdo_pgsql,opcache,pcntl), zkopíruje konfiguracidocker/php.ini,docker/Caddyfilea startovací skriptentrypoint.sh.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).vendor-prod: Spustícomposer install --no-dev --optimize-autoloadera připraví čisté produkční závislosti bez vývojářských nástrojů (žádný Pest, PHPUnit, Mockery).assets: Využívá lehký obraznode:22-alpine. Nainstaluje npm balíčky a zkompiluje produkční JS/CSS assety přes Vite (npm run build) do složkypublic/build.prod: Finální cílový obraz. Přebírá PHP prostředí zvendor-proda zkopíruje zkompilované soubory z fázeassets. 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.
# 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.
Jaká je hlavní architektonická přednost FrankenPHP oproti tradiční konfiguraci Nginx + PHP-FPM?
Proč v multi-stage Dockerfile kompilujeme assety v izolované fázi (node:22-alpine)?
Jaký význam má v produkčním běhu nastavení opcache.validate_timestamps=0?
| 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 | |