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:
mkdir php nginx app
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:
volumes:
- ./:/app
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:
docker ps
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:
touch nginx/default.conf
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:
<?php phpinfo();
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:
hostname -I
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:
docker-compose up -d
Kontrolliere, ob der MySQL-Container erfolgreich gestartet wurde:
docker ps
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:
exit
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.