Blog

Infrastruktur 9 Min. Lesezeit

Docker für PHP in der Produktion: eine praktische Einrichtungsanleitung für Geschäftsanwendungen

Docker sorgt dafür, dass eine PHP Anwendung auf einem Laptop beim Testen und in der Produktion auf die gleiche Weise ausgeführt wird, und macht Releases und Rollbacks zur Routine statt zum Stress. Außerdem kommen bewegliche Teile hinzu, und ein unachtsamer Aufbau ist schlimmer als ein einfacher Server. Dieser Leitfaden zeigt eine produktionsbereite Basislinie mit echter Konfiguration und zeigt, wann Sie sich nicht darum kümmern sollten.

Titelbild: Docker für PHP in der Produktion: eine praktische Einrichtungsanleitung für Geschäftsanwendungen

Kurz gesagt

  • Docker lohnt sich, wenn Sie identische Umgebungen, schnelle Rollbacks und einfaches Onboarding wünschen. Für eine einzelne kleine Broschürenseite zum Thema Shared Hosting ist dies normalerweise nicht erforderlich.
  • Erstellen Sie ein unveränderliches Image in CI: mehrstufiger Build, nur Produktionsabhängigkeiten, Nicht-Root-Benutzer, OPcache aktiviert, Fehler protokolliert und nie angezeigt.
  • Freigabe nach Image-Tag (dem Git-Commit-Hash), Überprüfung des Zustands nach dem Start und automatisches Zurücksetzen auf das vorherige Tag, wenn die Prüfung fehlschlägt.
  • Container sind wegwerfbar, Daten jedoch nicht: Bewahren Sie die Datenbank und Uploads in Volumes oder verwalteten Diensten auf und sichern Sie sie außerhalb Docker.
  • Die häufigsten Produktionsfehler: Geheimnisse im Bild, veröffentlichte Datenbankports, unbegrenzte Protokolle und PHP-FPM-Limits, die nicht zu Ihrem Speicher passen.

Bei vielen Geschäftsanwendungen sind die tatsächlichen Kosten des Hostings nicht der Server, sondern die Mitarbeiter. Ein Setup, das nur ein Entwickler versteht, eine Bereitstellung per FTP und eine Staging-Umgebung, die sich anders verhält als die Produktion, sind auf lange Sicht teuer. Container lösen dieses Problem, indem sie die Anwendung mit ihrer genauen PHP Version und ihren Erweiterungen packen. Wir betreiben unsere eigenen PHP Sites und Client-Systeme auf einem Docker-basierten Stack, und dieser Leitfaden beschreibt die Basis, von der wir ausgehen.

Wann sich Docker lohnt und wann nicht

  • Es lohnt sich: mehrere Dienste (Web, PHP, Datenbank, Warteschlange), mehr als ein Entwickler, Bedarf an wiederholbaren Releases und schnellen Rollbacks oder der Plan, zwischen Anbietern zu wechseln.
  • Nicht erforderlich: eine einfache Content-Site, die sich beim Managed Hosting selten ändert. Zusätzliche Komplexität muss sich auszahlen.
  • Kein Ersatz für einen guten Betrieb: Sie benötigen weiterhin Patches, Backups und Überwachung. Sehen Sie sich unseren Backup- und Überwachungsleitfaden an.

Die Zielarchitektur

Ein solides, einfaches Setup für eine PHP Anwendung auf einem Server:

  • Reverse-Proxy und TLS am Edge ( Nginx oder Caddy auf dem Host oder ein CDN wie Cloudflare, siehe unsere Cloudflare Einrichtungsanleitung).
  • Webcontainer ( Nginx), der statische Dateien bereitstellt und PHP Anfragen weiterleitet.
  • PHP-FPM Container mit Ihrem Code, integriert in das Image.
  • Datenbank in einem eigenen Container mit einem persistenten Volume, oder besser ein verwalteter Datenbankdienst für kritische Daten.
  • Volumes nur für Daten, die überleben müssen: Datenbankdateien und Benutzer-Uploads.

Eine mehrstufige Docker-Datei

Ein mehrstufiger Build installiert Abhängigkeiten in einem Schritt und kopiert nur das Ergebnis in ein schlankes Laufzeitimage. Das endgültige Image enthält keinen Composer, keine Compiler oder Testtools, was es kleiner macht und Angreifern weniger Möglichkeiten bietet:

# ---- 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

Wählen Sie bewusst die PHP Version und prüfen Sie, ob diese noch unterstützt wird. Unser PHP 8-Upgrade-Leitfaden erklärt, wie Sie das planen. Fixieren Sie Versionen von Basis-Images und aktualisieren Sie sie nach einem Zeitplan, da ein Image, das Sie nie neu erstellen, bekannte Schwachstellen anhäuft. Behalten Sie eine .dockerignore bei, die .git, .env, Tests und lokale Daten ausschließt, damit nichts Sensibles im Image landet.

Produktions PHP und PHP-FPM-Konfiguration

# 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

Das Festlegen opcache.validate_timestamps = 0 bietet die beste Leistung für ein unveränderliches Bild, funktioniert aber nur, wenn Sie niemals Dateien in einem laufenden Container bearbeiten: Sie veröffentlichen stattdessen ein neues Bild. Bemessen Sie den Pool anhand realer Zahlen: Messen Sie den durchschnittlichen Speicher eines PHP Workers unter Last, entscheiden Sie, wie viel Speicher PHP auf dem Server verwenden darf, und teilen Sie ihn auf. Wenn ein Worker 60 MB verwendet und Sie PHP 1 GB geben können, beträgt pm.max_children etwa 16. Wenn Sie den Wert viel höher einstellen, führt dies lediglich dazu, dass der Server ausgetauscht wird oder der Kernel Prozesse während einer Verkehrsspitze beendet. Zu niedrig und Anfragen in der Warteschlange. Überprüfen Sie das PHP-FPM-Protokoll auf „max_children erreicht“-Warnungen.

Eine Compose-Datei mit den richtigen Standardeinstellungen

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:

Das web Image ist ein kleines Nginx Image, in das Ihr public/ Ordner und Ihre Site-Konfiguration kopiert wurden und das in derselben Pipeline wie das PHP Image erstellt wurde. Wichtige Punkte: Veröffentlichte Ports an 127.0.0.1 binden, niemals die Datenbank veröffentlichen, Neustartrichtlinien festlegen, Speicher begrenzen, damit ein Container den Host nicht herunterfahren kann, und Protokollgröße begrenzen. Denken Sie daran, dass von Docker veröffentlichte Ports Host-Firewall-Regeln umgehen können, wie wir im Cloudflare-Leitfaden erklären. Hängen Sie den Docker Socket niemals in einen Anwendungscontainer ein, da dieser Container dadurch die Kontrolle über den Host erhält.

Releases und Rollbacks

Das Beste an einem bildbasierten Setup ist, dass jede Version ein Tag ist und ein Rollback „das vorherige Tag starten“ ist. Kennzeichnen Sie Bilder mit dem Git-Commit-Hash, verlassen Sie sich niemals auf latest und lassen Sie das Release-Skript den Zustand überprüfen:

#!/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

Datenbankänderungen erfordern Sorgfalt: Schreiben Sie Migrationen, damit die vorherige Version des Codes weiterhin mit dem neuen Schema funktioniert (fügen Sie zuerst Spalten hinzu und entfernen Sie sie in einer späteren Version). Das macht einen schnellen Rollback sicher. Erstellen Sie vor jeder Veröffentlichung, die das Schema ändert, eine Datenbanksicherung.

Bau und Versand von CI

Erstellen Sie das Image einmal in CI und stellen Sie genau dieses Artefakt bereit. Ein minimaler GitHub Actions Workflow:

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 }}"

Führen Sie Ihre Tests und statischen Analysen in derselben Pipeline aus, bevor das Image erstellt wird, und verwenden Sie einen Bereitstellungsschlüssel, der nur das Release-Skript ausführen kann. Fügen Sie einen Image-Schwachstellenscan hinzu, wenn Ihr Risikoprofil dies erfordert.

Protokolle, Daten und Sicherheit

  • Protokolle werden an stdout und stderr gesendet, von Docker rotiert und bei wichtigen Ereignissen an einen zentralen Ort gesendet, damit sie im Container überleben.
  • Daten befinden sich außerhalb des Containers. Ein Volume ist kein Backup. Nutzen Sie den Ansatz in unserem Backup-Leitfaden: verschlüsselt, extern, wiederherstellungsgetestet.
  • Geheimnisse werden niemals in Bilder eingebettet. Leiten Sie sie zur Laufzeit über Env-Dateien oder einen Secrets-Manager weiter und halten Sie sie von Git fern.
  • Führen Sie es als Nicht-Root-Benutzer aus, verwenden Sie nach Möglichkeit ein schreibgeschütztes Dateisystem und löschen Sie, was Sie nicht benötigen.
  • Halten Sie die Bilder aktuell. Erstellen Sie regelmäßig neu, um Sicherheitskorrekturen im Basis-Image und in PHP zu übernehmen.
  • Wenden Sie die Kontrollen auf Anwendungsebene aus unserer PHP Sicherheitscheckliste an, da Container keinen unsicheren Code reparieren.

Eine Checkliste für die Produktionsbereitschaft

  1. Mehrstufiges Image nur mit Produktionsabhängigkeiten, angeheftetes Basisimage, Nicht-Root-Benutzer.
  2. OPcache aktiviert, protokollierte Fehler werden nicht angezeigt, PHP-FPM-Größe aus gemessenem Speicher.
  3. Erstellen Sie eine Datei mit Neustartrichtlinien, Integritätsprüfungen, Speicherbeschränkungen, Protokollrotation und keinem öffentlichen Datenbankport.
  4. Veröffentlichungen per Commit-Hash-Tag mit automatischer Integritätsprüfung und Rollback.
  5. Backups und eine getestete Wiederherstellung für die Datenbank und Uploads.
  6. Überwachung und Warnungen zu Zustand, Festplatte, Fehlern und Zertifikaten.

Wie wir helfen können

Wir containerisieren PHP Anwendungen, richten CI-Pipelines und Releases ein und verbinden sie mit Zahlungen, CRM und Messaging. Sehen Sie sich unseren Integrationsservice an oder buchen Sie einen Anruf und wir überprüfen Ihren aktuellen Hosting- und Release-Prozess.

Häufige Fragen

Eignet sich Docker für die Ausführung PHP in der Produktion?

Ja, bei guter Anwendung. Es bietet identische Umgebungen, wiederholbare Releases und schnelle Rollbacks. Es erfordert eine ordnungsgemäße Konfiguration: unveränderliche Bilder, Gesundheitsprüfungen, Ressourcenlimits, Protokollrotation, Secrets-Management und Sicherungen von Daten außerhalb des Containers.

Soll die Datenbank in der Produktion in Docker laufen?

Dies ist für kleine und mittlere Systeme möglich, wenn es ein persistentes Volume verwendet, niemals öffentlich zugänglich gemacht wird und über getestete Backups verfügt. Für kritische Daten bevorzugen viele Teams einen verwalteten Datenbankdienst, der Failover, Updates und Backups für Sie übernimmt.

Wie kann ich eine Docker Version zurücksetzen?

Kennzeichnen Sie jedes Bild mit dem Git-Commit-Hash und behalten Sie das vorherige Tag bei. Um ein Rollback durchzuführen, starten Sie die Container mit dem vorherigen Tag. Schreiben Sie Datenbankmigrationen, damit der ältere Code weiterhin mit dem neueren Schema funktioniert.

Wie viele PHP-FPM-Worker sollte ich konfigurieren?

Messen Sie den durchschnittlichen Speicher eines Workers unter Last, entscheiden Sie, wie viel Speicher PHP verwenden darf, und teilen Sie ihn auf. Wenn der Wert zu hoch eingestellt wird, kommt es zu einem Austausch, während ein zu niedriger Wert dazu führt, dass Anfragen in die Warteschlange gestellt werden. Achten Sie auf max_children-Warnungen im PHP-FPM-Protokoll.

Was ist der größte Docker Sicherheitsfehler bei PHP -Apps?

Geheimnisse in das Image oder Git einfügen und Datenbankports im Internet veröffentlichen. Vermeiden Sie es außerdem, als Root ausgeführt zu werden, den Docker Socket in Container einzubinden und niemals das Basis-Image zu aktualisieren.

Kostenloses Kennenlerngespräch

Unordentlicher Stack oder festgefahrene Integration - Integrono plant den klügsten ersten Schritt.

Sie bekommen eine Senior-Einschätzung zu Budgetfallen, Abhängigkeiten und ob kaufen, kleben oder bauen der richtige Weg ist - bevor die bezahlte Arbeit beginnt. Der CEO bleibt eingebunden.