Blog

Infrastruktura 9 min čtení

Docker pro PHP ve výrobě: praktický průvodce nastavením pro podnikové aplikace

Docker umožňuje, aby aplikace PHP běžela stejným způsobem na přenosném počítači, při testování i v produkci, a namísto stresu je vydávání a navracení rutinou. Přidává také pohyblivé části a neopatrné nastavení je horší než obyčejný server. Tato příručka ukazuje výchozí stav připravený k výrobě se skutečnou konfigurací a situace, kdy byste se neměli obtěžovat.

Ilustrace článku: Docker pro PHP ve výrobě: praktický průvodce nastavením pro podnikové aplikace

Ve zkratce

  • Docker stojí za to, když chcete stejná prostředí, rychlé vrácení zpět a snadné začlenění. Pro jednu malou brožuru na sdíleném hostingu je to obvykle zbytečné.
  • Vytvořte neměnný obraz v CI: vícestupňové sestavení, pouze produkční závislosti, uživatel bez oprávnění root, zapnutá OPcache, chyby se zaznamenávají a nikdy se nezobrazují.
  • Uvolněte pomocí značky obrázku (hash commit Git), po spuštění zkontrolujte stav a v případě selhání kontroly se automaticky vraťte k předchozí značce.
  • Kontejnery jsou na jedno použití, data nikoli: databázi a nahrané položky uchovávejte ve svazcích nebo spravovaných službách a zálohujte je mimo Docker.
  • Nejčastější produkční chyby: tajemství v obraze, publikované databázové porty, neomezené logy a limity php-fpm, které neodpovídají paměti, kterou máte.

U mnoha podnikových aplikací není skutečnými náklady na hosting server, ale lidé. Nastavení, kterému rozumí pouze jeden vývojář, nasazení prováděné ručně přes FTP a pracovní prostředí, které se chová odlišně od produkčního, jsou z dlouhodobého hlediska drahé. Kontejnery to řeší zabalením aplikace s její přesnou verzí PHP a rozšířeními. Své vlastní stránky PHP a klientské systémy provozujeme na zásobníku založeném na Dockeru a tato příručka popisuje základní linii, ze které vycházíme.

Kdy se Docker vyplatí a kdy ne

  • Vyplatí se: několik služeb (web, PHP, databáze, fronta), více než jeden vývojář, potřeba opakovatelných verzí a rychlých návratů nebo plán na přesun mezi poskytovateli.
  • Není nutné: jednoduchý obsahový web, který se na spravovaném hostingu mění jen zřídka. Přidaná složitost vyžaduje odplatu.
  • Není náhradou za dobré operace: stále potřebujete opravy, zálohování a monitorování. Podívejte se na našeho průvodce zálohováním a monitorováním.

Cílová architektura

Solidní a jednoduché nastavení pro aplikaci PHP na jednom serveru:

  • Reverzní proxy a TLS na okraji ( Nginx nebo Caddy na hostiteli nebo CDN, jako je Cloudflare, viz průvodce nastavením Cloudflare).
  • Webový kontejner ( Nginx), který obsluhuje statické soubory a předává PHP požadavky dál.
  • PHP-FPM kontejner s vaším kódem, zabudovaným do obrazu.
  • Databáze ve vlastním kontejneru s trvalým svazkem, nebo lépe spravovaná databázová služba pro kritická data.
  • Svazky pouze pro data, která musí přežít: databázové soubory a nahrání uživatelů.

Vícefázový Dockerfile

Vícestupňové sestavení nainstaluje závislosti v jedné fázi a zkopíruje pouze výsledek do tenkého runtime obrazu. Finální obrázek neobsahuje žádný Composer, kompilátory ani testovací nástroje, což jej zmenšuje a útočníkům umožňuje méně použití:

# ---- stage 1: PHP dependencies ----
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --no-interaction --optimize-autoloader

# ---- stage 2: runtime ----
FROM php:8.3-fpm-alpine

RUN apk add --no-cache icu-libs libzip \
 && apk add --no-cache --virtual .build icu-dev libzip-dev $PHPIZE_DEPS \
 && docker-php-ext-install -j"$(nproc)" pdo_mysql intl zip opcache \
 && apk del .build

COPY docker/php.ini  /usr/local/etc/php/conf.d/zz-app.ini
COPY docker/www.conf /usr/local/etc/php-fpm.d/zz-www.conf

WORKDIR /var/www/app
COPY --from=vendor /app/vendor ./vendor
COPY --chown=www-data:www-data . .

USER www-data
EXPOSE 9000

Záměrně vyberte verzi PHP a zkontrolujte, zda je stále podporována. Náš průvodce upgradem PHP 8 vysvětluje, jak to naplánovat. Připněte si verze základních obrazů a aktualizujte je podle plánu, protože obraz, který nikdy neobnovíte, hromadí známé chyby zabezpečení. Udržujte .dockerignore, který vylučuje .git, .env, testy a místní data, takže v obrazu neskončí nic citlivého.

Produkční konfigurace PHP a php-fpm

# docker/php.ini
expose_php = 0
display_errors = 0
log_errors = 1
memory_limit = 256M
upload_max_filesize = 10M
post_max_size = 12M

opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0   ; the code in an image never changes while it runs

# docker/www.conf
[www]
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
request_terminate_timeout = 30s
catch_workers_output = yes
decorate_workers_output = no

Nastavení opcache.validate_timestamps = 0 poskytuje nejlepší výkon pro neměnný obraz, ale funguje pouze tehdy, pokud nikdy neupravujete soubory uvnitř běžícího kontejneru: místo toho uvolníte nový obraz. Určete velikost fondu z reálných čísel: změřte průměrnou paměť jednoho pracovníka PHP při zatížení, rozhodněte, kolik paměti může PHP na serveru použít, a rozdělte. Pokud pracovník používá 60 MB a vy můžete dát PHP 1 GB, pm.max_children je asi 16. Nastavení mnohem vyšší způsobí, že proces swapování serveru nebo zabíjení jádra během špičky provozu. Příliš nízká a fronta požadavků. Zkontrolujte protokol php-fpm, zda neobsahuje varování „max_children reach“.

Vytvořit soubor se správnými výchozími hodnotami

x-logging: &default-logging
  driver: json-file
  options: { max-size: "10m", max-file: "5" }     # logs cannot fill the disk

services:
  web:
    image: ghcr.io/your-org/app-web:${TAG}
    ports: ["127.0.0.1:8080:80"]                  # only the local reverse proxy can reach it
    depends_on: [php]
    restart: unless-stopped
    logging: *default-logging
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/health.php"]
      interval: 15s
      timeout: 3s
      retries: 3

  php:
    image: ghcr.io/your-org/app:${TAG}
    env_file: .env                                 # secrets stay on the server, not in the image
    restart: unless-stopped
    read_only: true
    tmpfs: [/tmp]
    mem_limit: 512m
    volumes: [uploads:/var/www/app/storage/uploads]
    logging: *default-logging

  db:
    image: mysql:8.4
    env_file: .env.db
    restart: unless-stopped
    volumes: [db_data:/var/lib/mysql]              # no "ports:" - the database is not public
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
      interval: 15s
      retries: 5
    logging: *default-logging

volumes:
  uploads:
  db_data:

web obrázek je malý obrázek Nginx se zkopírovanou vaší public/ složkou a konfigurací webu, zabudovaný ve stejném kanálu jako obrázek PHP. Klíčové body: svázat publikované porty na 127.0.0.1, nikdy nepublikovat databázi, nastavit zásady restartování, omezit paměť, aby jeden kontejner nemohl odstranit hostitele, a omezit velikost protokolu. Pamatujte, že porty publikované Docker mohou obejít pravidla brány firewall hostitele, jak vysvětlujeme v příručce Cloudflare. Nikdy nepřipojujte soket Docker do aplikačního kontejneru, protože tomuto kontejneru dává kontrolu nad hostitelem.

Vydání a vrácení zpět

Nejlepší funkcí nastavení založeného na obrázcích je, že každé vydání je značka a vrácení zpět je „zahájit předchozí značku“. Označte obrázky pomocí hash commitu Git, nikdy se nespoléhejte na latest a nechte skript vydání ověřit stav:

#!/usr/bin/env bash
# /srv/app/release.sh <git-sha>
set -euo pipefail
TAG="${1:?usage: release.sh <git-sha>}"
cd /srv/app
PREV="$(cat .current_tag 2>/dev/null || true)"

export TAG
docker compose pull php web
docker compose up -d --no-deps php web

for i in $(seq 1 30); do
  if curl -fsS http://127.0.0.1:8080/health.php > /dev/null; then
    echo "$TAG" > .current_tag
    echo "healthy: $TAG"; exit 0
  fi
  sleep 2
done

echo "unhealthy after release, rolling back to ${PREV:-none}" >&2
if [ -n "$PREV" ]; then TAG="$PREV" docker compose up -d --no-deps php web; fi
exit 1

Změny databáze vyžadují péči: zapište migrace, aby předchozí verze kódu stále fungovala s novým schématem (nejprve přidejte sloupce, v pozdější verzi je odstraňte). To je to, co dělá rychlý návrat bezpečným. Proveďte zálohu databáze před vydáním, které změní schéma.

Stavba a expedice od CI

Vytvořte obraz jednou v CI a nasaďte přesně tento artefakt. Minimální pracovní postup GitHub Actions:

name: build-and-release
on:
  push:
    branches: [main]
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/your-org/app:${{ github.sha }}
      - name: Release over SSH
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
        run: |
          install -m 600 /dev/null key && echo "$DEPLOY_KEY" > key
          ssh -i key -o StrictHostKeyChecking=accept-new deploy@your-server "/srv/app/release.sh ${{ github.sha }}"

Před vytvořením obrazu spusťte testy a statickou analýzu ve stejném kanálu a použijte klíč nasazení, který může spustit pouze skript vydání. Přidejte skenování zranitelnosti obrázků, pokud to váš rizikový profil vyžaduje.

Protokoly, data a zabezpečení

  • Protokoly jdou do stdout a stderr, jsou rotovány Docker a pro cokoliv důležitého jsou odeslány na centrální místo, aby přežily kontejner.
  • Data žijí mimo kontejner. Svazek není záloha. Použijte přístup v našem průvodci zálohováním: šifrované, off-site, testováno obnovením.
  • Tajemství se nikdy nezapečou do obrazů. Předejte je za běhu prostřednictvím souborů env nebo správce tajemství a udržujte je mimo Git.
  • Spusťte jako uživatel bez oprávnění root, používejte souborový systém pouze pro čtení, kde je to možné, a zahoďte to, co nepotřebujete.
  • Udržujte snímky čerstvé. Pravidelně přestavujte, abyste získali opravy zabezpečení v základním obrazu a PHP.
  • Použijte ovládací prvky na úrovni aplikace z našeho kontrolního seznamu zabezpečení PHP, protože kontejnery neopravují nezabezpečený kód.

Kontrolní seznam připravenosti výroby

  1. Vícestupňový obraz pouze s produkčními závislostmi, připnutý základní obraz, uživatel bez oprávnění root.
  2. OPcache povolena, chyby se nezobrazují, velikost php-fpm z naměřené paměti.
  3. Vytvořte soubor se zásadami restartování, zdravotními kontrolami, limity paměti, rotací protokolů a žádným veřejným databázovým portem.
  4. Vydání pomocí tagu commit-hash s automatickou kontrolou stavu a vrácením zpět.
  5. Zálohy a testovaná obnova databáze a nahrávání.
  6. Monitorování a upozornění na stav, disk, chyby a certifikáty.

Jak můžeme pomoci

Kontejnerizujeme aplikace PHP, nastavujeme kanály a verze CI a propojujeme je s platbami, CRM a zasíláním zpráv. Podívejte se na naši integrační službu nebo si zavolejte a my zkontrolujeme váš aktuální proces hostování a vydávání.

Časté otázky

Je Docker dobrý pro spuštění PHP v produkci?

Ano, když se dobře používá. Poskytuje identická prostředí, opakovatelné verze a rychlé vrácení zpět. Potřebuje správnou konfiguraci: neměnné obrazy, kontroly stavu, limity zdrojů, rotaci protokolů, správu tajemství a zálohování dat mimo kontejner.

Měla by databáze běžet v Docker v produkci?

U malých a středních systémů může, pokud používá trvalý svazek, není nikdy veřejně přístupný a má otestované zálohy. Pro kritická data mnoho týmů upřednostňuje spravovanou databázovou službu, která se postará o převzetí služeb při selhání, aktualizace a zálohy za vás.

Jak vrátím vydání Docker ?

Označte každý obrázek hashem Git commit a ponechte předchozí značku. Chcete-li se vrátit zpět, začněte kontejnery s předchozí značkou. Napište migrace databáze, aby starší kód stále fungoval s novějším schématem.

Kolik pracovníků php-fpm bych měl nakonfigurovat?

Změřte průměrnou paměť jednoho pracovníka při zatížení, rozhodněte, kolik paměti může PHP používat, a rozdělte. Nastavení příliš vysoké hodnoty způsobí odkládání, zatímco příliš nízké způsobí požadavky ve frontě. Podívejte se na php-fpm log pro max_children varování.

Jaká je největší bezpečnostní chyba Docker s aplikacemi PHP ?

Vkládání tajemství do obrazu nebo Git a publikování databázových portů na internet. Také se vyhněte spouštění jako root, připojování soketu Docker do kontejnerů a nikdy neaktualizujte základní obraz.

Volání bezplatného zjišťování

Přineste chaotický stack nebo zaseknutou integraci - Integrono namapuje nejchytřejší první krok.

Dostanete seniorský pohled na rozpočtové pasti, závislosti a na to, zda koupit, slepit nebo postavit - než začne placená práce. CEO zůstává v okruhu.