Kompletten PHP-Stack mit Docker und Docker-Compose bereitstellen

Ein typisches PHP-Setup besteht aus drei Hauptkomponenten: einem Webserver, einem relationalen Datenbanksystem und dem PHP-Interpreter. In dieser Anleitung setzen wir eine vollständige PHP-Umgebung mithilfe von Docker auf. Du erfährst, wie du Container für Nginx (Webserver), MySQL (Datenbank) und PHP erstellen und verwalten kannst.

Einfache PHP-Anwendung zur Datenbankabfrage erstellen

Wir entwickeln eine einfache Webanwendung, die eine Liste von Städten aus einer MySQL-Datenbank abruft und auf einer Webseite anzeigt. Dieses Beispiel zeigt, wie ein funktionsfähiges PHP-Projekt implementiert werden kann.

Voraussetzungen für diese Anleitung

Diese Anleitung setzt voraus, dass Docker-CE bereits auf deinem System installiert ist und du über Grundkenntnisse in Docker verfügst.

Projektumgebung einrichten

In der Praxis bestehen Docker-Anwendungen meist aus mehreren Containern. Diese manuell zu verwalten kann schnell unübersichtlich werden. Docker Compose vereinfacht das durch eine YAML-Konfigurationsdatei, mit der mehrere Container kontrolliert werden können.

Docker Compose installieren

curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Projektverzeichnis erstellen

Lege zunächst ein Hauptverzeichnis für deine Projektdateien an und wechsle hinein. Dieses Verzeichnis (nachfolgend als WORKING_DIR bezeichnet) dient künftig als Arbeitsverzeichnis:

mkdir ~/docker
cd ~/docker

Erstelle darin nun drei Unterordner:

Der Ordner php enthält unser eigenes PHP-Image, nginx wird die Nginx-Konfiguration aufnehmen, und app dient als Speicherort für Quellcode und Konfigurationsdateien der Anwendung.

PHP-Container einrichten

Wir verwenden PHP-FPM, um mit dem Nginx-Webserver zu kommunizieren. Unser PHP-Image basiert auf dem offiziellen, schlanken Alpine-Image. Zusätzliche Erweiterungen müssen installiert werden, um auf MySQL-Datenbanken zugreifen zu können. Lege in php eine Datei mit dem Namen Dockerfile an und füge Folgendes ein:

FROM php:7.1-fpm-alpine3.4
RUN apk update --no-cache \
    && apk add --no-cache $PHPIZE_DEPS \
    && apk add --no-cache mysql-dev \
    && docker-php-ext-install pdo pdo_mysql

Das auf Alpine basierende Image benötigt wenig Speicherplatz und ist daher ideal für Container geeignet. Mit dem Befehl docker-php-ext-install, der vom PHP-Image bereitgestellt wird, lassen sich benötigte Erweiterungen einfach aktivieren.

PHP-Docker-Image erstellen

Erstelle das PHP-Image aus dem WORKING_DIR heraus:

docker build -t centron-php php/

docker-compose.yml-Datei erstellen

Mit Docker Compose wird die Container-Konfiguration über die Datei docker-compose.yml definiert und verwaltet. Erstelle sie im Verzeichnis app:

touch app/docker-compose.yml

Füge diesen Inhalt in die Datei ein:

version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app

docker-compose-Konfiguration verstehen

Die erste Zeile version: '2' definiert das Format der Compose-Datei. Unter services werden die Container beschrieben – in unserem Fall nur einer für PHP.

Der Dienst php verweist auf das zuvor erstellte Image. Das Volume-Mapping lautet:

Damit wird das aktuelle Verzeichnis auf /app im Container gemappt. Über working_dir wird dieses Verzeichnis zudem als Standardarbeitsverzeichnis für spätere Kommandos festgelegt.

Container starten

Wechsle in das app-Verzeichnis und starte die Container:

cd ~/docker/app
docker-compose up -d

Prüfe anschließend, ob der PHP-Container läuft:

Befehle in Containern mit Docker Compose ausführen

Im Verzeichnis app kannst du Kommandos innerhalb eines Dienst-Containers mit folgendem Befehl ausführen:

docker-compose exec [service] [command]

Beispielsweise kannst du die installierte PHP-Version im Container prüfen mit:

docker-compose exec php php -v

Die Ausgabe sieht dann etwa so aus:

PHP 7.1.14 (cli) (built: Feb  7 2018 00:40:45) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

Nginx-Container anpassen

Wie beim PHP-Container muss auch für Nginx ein individuelles Setup erstellt werden. Dieses benötigt lediglich eine Konfigurationsdatei für den virtuellen Host. Wechsle dazu in das WORKING_DIR und erstelle ein Dockerfile im Verzeichnis nginx:

cd ~/docker
touch nginx/Dockerfile

Füge diesen Inhalt in das Dockerfile ein:

FROM nginx:1.13.8-alpine
COPY ./default.conf /etc/nginx/conf.d/default.conf

Dieses Setup verwendet das schlanke Nginx-Alpine-Image und kopiert eine benutzerdefinierte Konfigurationsdatei. Erstelle die Datei default.conf im selben Verzeichnis:

Trage anschließend folgenden Inhalt in die Datei ein:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /app;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Die Zeile fastcgi_pass php:9000; verweist auf den PHP-Container über dessen Servicenamen. Docker Compose erstellt ein internes Netzwerk, in dem Dienste über ihren Namen erreichbar sind.

Erstelle nun das Nginx-Image:

docker build -t centron-nginx nginx/

docker-compose.yml um den Webserver erweitern

Ergänze die Datei app/docker-compose.yml um den Nginx-Webdienst:

version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app
  web:
    image: centron-nginx
    volumes:
      - ./:/app
    depends_on:
      - php
    ports:
      - 80:80

Die Option depends_on stellt sicher, dass der PHP-Container zuerst gestartet wird. Mit ports wird Port 80 des Containers auf Port 80 des Hosts weitergeleitet.

Lege eine einfache PHP-Datei im Verzeichnis app an:

Stelle sicher, dass Port 80 durch deine Firewall nicht blockiert wird, und starte anschließend die Container:

cd ~/docker/app
docker-compose up -d
docker ps

Öffne nun deinen Webbrowser und rufe die IP-Adresse deines Servers auf. Um diese herauszufinden, kannst du folgenden Befehl verwenden:

MySQL-Container konfigurieren

Das offizielle MySQL-Image erlaubt die einfache Konfiguration über Umgebungsvariablen. Füge in der Datei docker-compose.yml einen neuen Service für die Datenbank hinzu:

version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app
  web:
    image: centron-nginx
    volumes:
      - ./:/app
    depends_on:
      - php
    ports:
      - 80:80
  mysql:
    image: mysql:5.7.21
    volumes:
      - ./:/app
      - dbdata:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=world
      - MYSQL_ROOT_PASSWORD=root
    working_dir: /app
volumes:
  dbdata:

Dieser neue Service nutzt ein benanntes Volume dbdata, das sicherstellt, dass deine Datenbankdaten beim Entfernen des Containers erhalten bleiben. Dieses Volume muss im oberen Bereich unter volumes: deklariert werden.

Beispieldatenbank herunterladen

Als Datenquelle verwenden wir die offizielle Beispieldatenbank world von MySQL. Lade sie im Verzeichnis app herunter und entpacke sie:

curl -L http://downloads.mysql.com/docs/world.sql.gz -o world.sql.gz
gunzip world.sql.gz

Starte anschließend die Container erneut:

Kontrolliere, ob der MySQL-Container erfolgreich gestartet wurde:

Datenbank importieren

Importiere den Inhalt der world-Datenbank wie folgt in den laufenden MySQL-Container:

docker-compose exec -T mysql mysql -uroot -proot world < world.sql

Überprüfe, ob der Import erfolgreich war, indem du dich mit dem Datenbank-Container verbindest:

docker-compose exec mysql mysql -uroot -proot world

Führe im MySQL-Prompt diesen Testbefehl aus:

select * from city limit 10;

Mit exit beendest du den Datenbankzugang:

PHP-Anwendung implementieren

Ersetze nun den Inhalt der Datei app/index.php durch die folgende Anwendung, die Daten aus der Datenbank abruft und im Browser anzeigt:

<?php

$pdo = new PDO('mysql:host=mysql;dbname=world;charset=utf8', 'root', 'root');

$stmt = $pdo->prepare("
    select city.Name, city.District, country.Name as Country, city.Population
    from city
    left join country on city.CountryCode = country.Code
    order by Population desc
    limit 10
");
$stmt->execute();
$cities = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Städte weltweit</title>
</head>
<body>
    <h2>Die zehn bevölkerungsreichsten Städte</h2>
    <table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Land</th>
            <th>Bezirk</th>
            <th>Einwohner</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach($cities as $city): ?>
            <tr>
                <td><?=$city['Name']?></td>
                <td><?=$city['Country']?></td>
                <td><?=$city['District']?></td>
                <td><?=number_format($city['Population'], 0)?></td>
            </tr>
        <?php endforeach ?>
    </tbody>
    </table>
</body>
</html>

Fazit

In dieser Anleitung hast du Schritt für Schritt gelernt, wie man eine vollständige PHP-Webanwendung mithilfe von Docker erstellt und betreibt. Du hast eigene Container-Images für PHP und Nginx erstellt und mit Docker Compose orchestriert.

Obwohl diese Umgebung einfach gehalten ist, bildet sie typische Szenarien aus der Praxis ab. Für den produktiven Einsatz kannst du deine Images in eine zentrale Docker Registry hochladen, um sie auf mehreren Hosts zu verwenden – sei es Docker Hub oder eine eigene Registry.

Wenn du weitere PHP-Erweiterungen benötigst, kannst du das Dockerfile entsprechend erweitern. Beachte dabei, dass einige Erweiterungen zusätzliche Systempakete benötigen. Konsultiere hierzu die offizielle PHP-Dokumentation.

Für weiterführende Informationen über Docker Compose empfiehlt sich die offizielle Compose-Dokumentation.

This setup uses Alpine Linux for its minimal footprint, ideal for containerized environments. The command docker-php-ext-install provided by the PHP image simplifies enabling required extensions.

Quelle: vultr.com

Jetzt 200€ Guthaben sichern

Registrieren Sie sich jetzt in unserer ccloud³ und erhalten Sie 200€ Startguthaben für Ihr Projekt.

Das könnte Sie auch interessieren:

Moderne Hosting Services mit Cloud Server, Managed Server und skalierbarem Cloud Hosting für professionelle IT-Infrastrukturen

Automate Cypress E2E Tests mit GitHub Actions & Slack

React, Tutorial
CI/CD-Pipelines mit Cypress und GitHub Actions automatisieren CI/CD-Pipelines erleichtern die Softwarebereitstellung, indem sie Code-Integration, Validierung und Deployment automatisieren. Die Einbindung von End-to-End-Tests (E2E) stellt sicher, dass Änderungen wie vorgesehen in…