Docker Stack Traefik: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
(Die Seite wurde neu angelegt: „Dieser Artikel beschreibt einen kompletten Docker-Compose-Stack mit '''Traefik''' als Reverse-Proxy, einer gemeinsamen '''MariaDB''' und den beiden Webanwendun…“) |
|||
| Zeile 1: | Zeile 1: | ||
| − | Dieser Artikel beschreibt einen | + | Dieser Artikel beschreibt einen Docker-Compose-Stack mit '''Traefik''' als Reverse-Proxy und den beiden Webanwendungen '''Nextcloud''' und '''MediaWiki''', die jeweils eine '''eigene, getrennte''' MariaDB erhalten. Traefik erkennt die Container automatisch über Labels und terminiert TLS mit einem statischen Wildcard-Zertifikat. |
==Konzept== | ==Konzept== | ||
| Zeile 9: | Zeile 9: | ||
* Im Lab gibt es kein ACME/Let's-Encrypt nach außen. Das Wildcard-Zertifikat liegt unter <code>/etc/ssl/own.crt</code> und <code>/etc/ssl/own.key</code> und wird per File-Provider als Default registriert. | * Im Lab gibt es kein ACME/Let's-Encrypt nach außen. Das Wildcard-Zertifikat liegt unter <code>/etc/ssl/own.crt</code> und <code>/etc/ssl/own.key</code> und wird per File-Provider als Default registriert. | ||
| − | ; | + | ; Pro App eine eigene Datenbank |
| − | * | + | * Jede Anwendung bekommt einen eigenen MariaDB-Container in einem eigenen Backend-Netz. Es gibt '''keine''' gemeinsame Datenbank. Eine kompromittierte Anwendung kann damit nicht über eine geteilte DB an die Daten der anderen Anwendung gelangen. |
| + | |||
| + | ==Netz-Trennung== | ||
| + | |||
| + | ; proxy | ||
| + | * Verbindet Traefik mit den Webanwendungen. | ||
| + | |||
| + | ; nextcloud-back | ||
| + | * Verbindet '''nur''' Nextcloud mit seiner Datenbank. | ||
| + | |||
| + | ; mediawiki-back | ||
| + | * Verbindet '''nur''' MediaWiki mit seiner Datenbank. | ||
| + | |||
| + | Die beiden DB-Container hängen jeweils nur an ihrem eigenen Backend-Netz und sind weder von außen noch von der jeweils anderen Anwendung erreichbar. | ||
==Verzeichnisstruktur== | ==Verzeichnisstruktur== | ||
| Zeile 17: | Zeile 30: | ||
docker-stack/ | docker-stack/ | ||
├── docker-compose.yml | ├── docker-compose.yml | ||
| − | + | └── traefik/ | |
| − | + | └── dynamic/ | |
| − | + | └── tls.yml | |
| − | |||
| − | |||
</pre> | </pre> | ||
| Zeile 54: | Zeile 65: | ||
- proxy | - proxy | ||
| − | # | + | # Nextcloud + eigene DB |
| − | db: | + | nextcloud-db: |
image: mariadb:11 | image: mariadb:11 | ||
| − | container_name: db | + | container_name: nextcloud-db |
restart: unless-stopped | restart: unless-stopped | ||
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW | command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW | ||
environment: | environment: | ||
| − | MARIADB_ROOT_PASSWORD: rootpass | + | MARIADB_ROOT_PASSWORD: nc-rootpass |
| + | MARIADB_DATABASE: nextcloud | ||
| + | MARIADB_USER: nextcloud | ||
| + | MARIADB_PASSWORD: nextcloudpass | ||
MARIADB_AUTO_UPGRADE: "1" | MARIADB_AUTO_UPGRADE: "1" | ||
volumes: | volumes: | ||
| − | - | + | - nextcloud_db:/var/lib/mysql |
| − | |||
networks: | networks: | ||
| − | - | + | - nextcloud-back |
| − | |||
nextcloud: | nextcloud: | ||
image: nextcloud:30-apache | image: nextcloud:30-apache | ||
| Zeile 75: | Zeile 87: | ||
restart: unless-stopped | restart: unless-stopped | ||
depends_on: | depends_on: | ||
| − | - db | + | - nextcloud-db |
environment: | environment: | ||
| − | MYSQL_HOST: db | + | MYSQL_HOST: nextcloud-db |
MYSQL_DATABASE: nextcloud | MYSQL_DATABASE: nextcloud | ||
MYSQL_USER: nextcloud | MYSQL_USER: nextcloud | ||
| Zeile 89: | Zeile 101: | ||
networks: | networks: | ||
- proxy | - proxy | ||
| − | - | + | - nextcloud-back |
labels: | labels: | ||
- "traefik.enable=true" | - "traefik.enable=true" | ||
| Zeile 100: | Zeile 112: | ||
- "traefik.http.routers.nextcloud.middlewares=nc-dav" | - "traefik.http.routers.nextcloud.middlewares=nc-dav" | ||
| − | # MediaWiki | + | # MediaWiki + eigene DB |
| + | mediawiki-db: | ||
| + | image: mariadb:11 | ||
| + | container_name: mediawiki-db | ||
| + | restart: unless-stopped | ||
| + | environment: | ||
| + | MARIADB_ROOT_PASSWORD: mw-rootpass | ||
| + | MARIADB_DATABASE: mediawiki | ||
| + | MARIADB_USER: mediawiki | ||
| + | MARIADB_PASSWORD: mediawikipass | ||
| + | MARIADB_AUTO_UPGRADE: "1" | ||
| + | volumes: | ||
| + | - mediawiki_db:/var/lib/mysql | ||
| + | networks: | ||
| + | - mediawiki-back | ||
| + | |||
mediawiki: | mediawiki: | ||
image: mediawiki:1.43 | image: mediawiki:1.43 | ||
| Zeile 106: | Zeile 133: | ||
restart: unless-stopped | restart: unless-stopped | ||
depends_on: | depends_on: | ||
| − | - db | + | - mediawiki-db |
volumes: | volumes: | ||
- mediawiki_data:/var/www/html/images | - mediawiki_data:/var/www/html/images | ||
| Zeile 113: | Zeile 140: | ||
networks: | networks: | ||
- proxy | - proxy | ||
| − | - | + | - mediawiki-back |
labels: | labels: | ||
- "traefik.enable=true" | - "traefik.enable=true" | ||
| Zeile 123: | Zeile 150: | ||
networks: | networks: | ||
proxy: | proxy: | ||
| − | + | nextcloud-back: | |
| + | mediawiki-back: | ||
volumes: | volumes: | ||
| − | + | nextcloud_db: | |
nextcloud_data: | nextcloud_data: | ||
| + | mediawiki_db: | ||
mediawiki_data: | mediawiki_data: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| Zeile 147: | Zeile 176: | ||
* Traefik liest die Container-Labels über den Docker-Socket. Der wird '''read-only''' gemountet. | * Traefik liest die Container-Labels über den Docker-Socket. Der wird '''read-only''' gemountet. | ||
| − | === | + | ===Datenbanken=== |
| − | ; | + | ; Anlage über Umgebungsvariablen |
| − | * | + | * Jeder MariaDB-Container legt Datenbank und Benutzer beim ersten Start selbst an, gesteuert über <code>MARIADB_DATABASE</code>, <code>MARIADB_USER</code> und <code>MARIADB_PASSWORD</code>. Ein separates Init-Skript ist nicht nötig. |
| − | ; | + | ; Getrennte Volumes |
| − | * <code> | + | * <code>nextcloud_db</code> und <code>mediawiki_db</code> sind voneinander unabhängige Volumes. Jede DB hat ihren eigenen Datenbestand. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
==Traefik Dynamic Config (tls.yml)== | ==Traefik Dynamic Config (tls.yml)== | ||
| Zeile 178: | Zeile 202: | ||
; Default-Zertifikat | ; Default-Zertifikat | ||
* Durch das <code>default</code>-Store reicht an jedem Router ein einfaches <code>tls=true</code>. Traefik greift automatisch auf dieses Zertifikat zurück, ein eigener Cert-Verweis pro Router entfällt. | * Durch das <code>default</code>-Store reicht an jedem Router ein einfaches <code>tls=true</code>. Traefik greift automatisch auf dieses Zertifikat zurück, ein eigener Cert-Verweis pro Router entfällt. | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
==Inbetriebnahme== | ==Inbetriebnahme== | ||
| Zeile 228: | Zeile 231: | ||
; Erstaufruf im Browser | ; Erstaufruf im Browser | ||
| − | * MediaWiki startet ohne <code>LocalSettings.php</code> mit dem Web-Installer. Als Datenbank-Host <code>db</code>, Datenbank <code>mediawiki</code>, Benutzer <code>mediawiki</code> angeben. | + | * MediaWiki startet ohne <code>LocalSettings.php</code> mit dem Web-Installer. Als Datenbank-Host <code>mediawiki-db</code>, Datenbank <code>mediawiki</code>, Benutzer <code>mediawiki</code> angeben. |
; LocalSettings.php einhängen | ; LocalSettings.php einhängen | ||
* Nach dem Setup wird die generierte <code>LocalSettings.php</code> heruntergeladen, neben das Compose-File gelegt und der auskommentierte Volume-Mount im <code>mediawiki</code>-Service aktiviert. Danach Container neu starten. | * Nach dem Setup wird die generierte <code>LocalSettings.php</code> heruntergeladen, neben das Compose-File gelegt und der auskommentierte Volume-Mount im <code>mediawiki</code>-Service aktiviert. Danach Container neu starten. | ||
* <code>docker compose up -d mediawiki</code> | * <code>docker compose up -d mediawiki</code> | ||
Version vom 27. Juni 2026, 16:13 Uhr
Dieser Artikel beschreibt einen Docker-Compose-Stack mit Traefik als Reverse-Proxy und den beiden Webanwendungen Nextcloud und MediaWiki, die jeweils eine eigene, getrennte MariaDB erhalten. Traefik erkennt die Container automatisch über Labels und terminiert TLS mit einem statischen Wildcard-Zertifikat.
Konzept
- Traefik als Reverse-Proxy
- Im Gegensatz zu HAProxy werden die Backends nicht von Hand konfiguriert. Traefik liest die Docker-Labels der Container und erzeugt Router und Services dynamisch.
- Statisches Zertifikat
- Im Lab gibt es kein ACME/Let's-Encrypt nach außen. Das Wildcard-Zertifikat liegt unter
/etc/ssl/own.crtund/etc/ssl/own.keyund wird per File-Provider als Default registriert.
- Pro App eine eigene Datenbank
- Jede Anwendung bekommt einen eigenen MariaDB-Container in einem eigenen Backend-Netz. Es gibt keine gemeinsame Datenbank. Eine kompromittierte Anwendung kann damit nicht über eine geteilte DB an die Daten der anderen Anwendung gelangen.
Netz-Trennung
- proxy
- Verbindet Traefik mit den Webanwendungen.
- nextcloud-back
- Verbindet nur Nextcloud mit seiner Datenbank.
- mediawiki-back
- Verbindet nur MediaWiki mit seiner Datenbank.
Die beiden DB-Container hängen jeweils nur an ihrem eigenen Backend-Netz und sind weder von außen noch von der jeweils anderen Anwendung erreichbar.
Verzeichnisstruktur
docker-stack/
├── docker-compose.yml
└── traefik/
└── dynamic/
└── tls.yml
docker-compose.yml
services:
# Traefik - Reverse Proxy (statisches Wildcard-Cert)
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik/dynamic:/etc/traefik/dynamic:ro"
- "/etc/ssl/own.crt:/etc/ssl/own.crt:ro"
- "/etc/ssl/own.key:/etc/ssl/own.key:ro"
networks:
- proxy
# Nextcloud + eigene DB
nextcloud-db:
image: mariadb:11
container_name: nextcloud-db
restart: unless-stopped
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
environment:
MARIADB_ROOT_PASSWORD: nc-rootpass
MARIADB_DATABASE: nextcloud
MARIADB_USER: nextcloud
MARIADB_PASSWORD: nextcloudpass
MARIADB_AUTO_UPGRADE: "1"
volumes:
- nextcloud_db:/var/lib/mysql
networks:
- nextcloud-back
nextcloud:
image: nextcloud:30-apache
container_name: nextcloud
restart: unless-stopped
depends_on:
- nextcloud-db
environment:
MYSQL_HOST: nextcloud-db
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_PASSWORD: nextcloudpass
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: adminpass
NEXTCLOUD_TRUSTED_DOMAINS: cloud.it2XX.xinmen.de.int
OVERWRITEPROTOCOL: https
volumes:
- nextcloud_data:/var/www/html
networks:
- proxy
- nextcloud-back
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.rule=Host(`cloud.it2XX.xinmen.de.int`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls=true"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
- "traefik.http.middlewares.nc-dav.redirectregex.regex=https://([^/]*)/.well-known/(card|cal)dav"
- "traefik.http.middlewares.nc-dav.redirectregex.replacement=https://$${1}/remote.php/dav/"
- "traefik.http.routers.nextcloud.middlewares=nc-dav"
# MediaWiki + eigene DB
mediawiki-db:
image: mariadb:11
container_name: mediawiki-db
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: mw-rootpass
MARIADB_DATABASE: mediawiki
MARIADB_USER: mediawiki
MARIADB_PASSWORD: mediawikipass
MARIADB_AUTO_UPGRADE: "1"
volumes:
- mediawiki_db:/var/lib/mysql
networks:
- mediawiki-back
mediawiki:
image: mediawiki:1.43
container_name: mediawiki
restart: unless-stopped
depends_on:
- mediawiki-db
volumes:
- mediawiki_data:/var/www/html/images
# LocalSettings.php nach dem Setup einhaengen:
# - ./LocalSettings.php:/var/www/html/LocalSettings.php:ro
networks:
- proxy
- mediawiki-back
labels:
- "traefik.enable=true"
- "traefik.http.routers.mediawiki.rule=Host(`wiki.it2XX.xinmen.de.int`)"
- "traefik.http.routers.mediawiki.entrypoints=websecure"
- "traefik.http.routers.mediawiki.tls=true"
- "traefik.http.services.mediawiki.loadbalancer.server.port=80"
networks:
proxy:
nextcloud-back:
mediawiki-back:
volumes:
nextcloud_db:
nextcloud_data:
mediawiki_db:
mediawiki_data:
Erklärung der Bausteine
Traefik
- Docker-Provider mit Opt-in
exposedbydefault=falsesorgt dafür, dass nur Container mittraefik.enable=trueveröffentlicht werden. Alles andere bleibt unsichtbar.
- File-Provider für TLS
- Das statische Zertifikat wird nicht über Labels, sondern über eine Datei eingebunden (siehe
tls.yml).watch=truelädt Änderungen ohne Neustart.
- HTTP→HTTPS-Redirect global
- Die beiden
redirections-Zeilen leiten den gesamten Port-80-Verkehr auf HTTPS um. Das muss kein Container selbst regeln.
- Docker-Socket read-only
- Traefik liest die Container-Labels über den Docker-Socket. Der wird read-only gemountet.
Datenbanken
- Anlage über Umgebungsvariablen
- Jeder MariaDB-Container legt Datenbank und Benutzer beim ersten Start selbst an, gesteuert über
MARIADB_DATABASE,MARIADB_USERundMARIADB_PASSWORD. Ein separates Init-Skript ist nicht nötig.
- Getrennte Volumes
nextcloud_dbundmediawiki_dbsind voneinander unabhängige Volumes. Jede DB hat ihren eigenen Datenbestand.
Traefik Dynamic Config (tls.yml)
Pfad im Container: /etc/traefik/dynamic/tls.yml
tls:
certificates:
- certFile: /etc/ssl/own.crt
keyFile: /etc/ssl/own.key
stores:
default:
defaultCertificate:
certFile: /etc/ssl/own.crt
keyFile: /etc/ssl/own.key
- Default-Zertifikat
- Durch das
default-Store reicht an jedem Router ein einfachestls=true. Traefik greift automatisch auf dieses Zertifikat zurück, ein eigener Cert-Verweis pro Router entfällt.
Inbetriebnahme
- Zertifikat ablegen
- Das Wildcard-Zertifikat zur Domain mit dem vorhandenen Skript ziehen, sodass
/etc/ssl/own.crtund/etc/ssl/own.keyvorhanden sind. ./get-cert.sh
- Stack starten
docker compose up -d
- Status prüfen
docker compose ps
- Logs verfolgen
docker compose logs -f traefik
DNS
Die beiden Hostnamen müssen auf den Docker-Host zeigen:
- Nextcloud
cloud.it2XX.xinmen.de.int
- MediaWiki
wiki.it2XX.xinmen.de.int
MediaWiki-Setup abschließen
- Erstaufruf im Browser
- MediaWiki startet ohne
LocalSettings.phpmit dem Web-Installer. Als Datenbank-Hostmediawiki-db, Datenbankmediawiki, Benutzermediawikiangeben.
- LocalSettings.php einhängen
- Nach dem Setup wird die generierte
LocalSettings.phpheruntergeladen, neben das Compose-File gelegt und der auskommentierte Volume-Mount immediawiki-Service aktiviert. Danach Container neu starten. docker compose up -d mediawiki