Docker Stack Traefik: Unterschied zwischen den Versionen

Aus Xinux Wiki
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 kompletten Docker-Compose-Stack mit '''Traefik''' als Reverse-Proxy, einer gemeinsamen '''MariaDB''' und den beiden Webanwendungen '''Nextcloud''' und '''MediaWiki'''. Traefik erkennt die Container automatisch über Labels und terminiert TLS mit einem statischen Wildcard-Zertifikat.
+
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.
  
; Eine Datenbank für beide Apps
+
; Pro App eine eigene Datenbank
* Ein gemeinsamer MariaDB-Container hält zwei getrennte Datenbanken (<code>nextcloud</code> und <code>mediawiki</code>) mit jeweils eigenem Benutzer.
+
* 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/
+
└── traefik/
│  └── dynamic/
+
    └── dynamic/
│      └── tls.yml
+
        └── tls.yml
└── db-init/
 
    └── 01-init.sql
 
 
</pre>
 
</pre>
  
Zeile 54: Zeile 65:
 
       - proxy
 
       - proxy
  
   # MariaDB - gemeinsame DB fuer Nextcloud + MediaWiki
+
   # 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:
       - db_data:/var/lib/mysql
+
       - nextcloud_db:/var/lib/mysql
      - ./db-init:/docker-entrypoint-initdb.d:ro
 
 
     networks:
 
     networks:
       - backend
+
       - nextcloud-back
  
  # Nextcloud
 
 
   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
       - backend
+
       - 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
       - backend
+
       - mediawiki-back
 
     labels:
 
     labels:
 
       - "traefik.enable=true"
 
       - "traefik.enable=true"
Zeile 123: Zeile 150:
 
networks:
 
networks:
 
   proxy:
 
   proxy:
   backend:
+
   nextcloud-back:
 +
  mediawiki-back:
  
 
volumes:
 
volumes:
   db_data:
+
   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.
  
===MariaDB===
+
===Datenbanken===
  
; Gemeinsame Datenbank
+
; Anlage über Umgebungsvariablen
* Ein Container, zwei Datenbanken. Die Anlage übernimmt das Init-Skript (siehe unten), das beim ersten Start automatisch ausgeführt wird.
+
* 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.
  
; Transaction-Isolation
+
; Getrennte Volumes
* <code>READ-COMMITTED</code> und <code>binlog-format=ROW</code> sind die von Nextcloud empfohlenen Einstellungen.
+
* <code>nextcloud_db</code> und <code>mediawiki_db</code> sind voneinander unabhängige Volumes. Jede DB hat ihren eigenen Datenbestand.
 
 
===Netze===
 
 
 
; Zwei Netze trennen Front und Back
 
* <code>proxy</code> verbindet Traefik mit den Webanwendungen. <code>backend</code> verbindet die Webanwendungen mit der Datenbank. Die DB hängt '''nicht''' am Proxy-Netz und ist damit von außen nicht erreichbar.
 
  
 
==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.
 
==Datenbank-Init (01-init.sql)==
 
 
Pfad: <code>./db-init/01-init.sql</code> – wird beim '''ersten''' Start von MariaDB automatisch ausgeführt.
 
 
<syntaxhighlight lang="sql">
 
CREATE DATABASE IF NOT EXISTS nextcloud
 
  CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
 
CREATE USER IF NOT EXISTS 'nextcloud'@'%' IDENTIFIED BY 'nextcloudpass';
 
GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'%';
 
 
CREATE DATABASE IF NOT EXISTS mediawiki
 
  CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
 
CREATE USER IF NOT EXISTS 'mediawiki'@'%' IDENTIFIED BY 'mediawikipass';
 
GRANT ALL PRIVILEGES ON mediawiki.* TO 'mediawiki'@'%';
 
 
FLUSH PRIVILEGES;
 
</syntaxhighlight>
 
 
; Nur beim ersten Start
 
* Das Init-Verzeichnis wird nur ausgeführt, solange das Datenverzeichnis <code>db_data</code> leer ist. Bei einem späteren Lauf muss man die Datenbanken von Hand anlegen oder das Volume löschen.
 
  
 
==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.crt und /etc/ssl/own.key 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

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=false sorgt dafür, dass nur Container mit traefik.enable=true verö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=true lä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_USER und MARIADB_PASSWORD. Ein separates Init-Skript ist nicht nötig.
Getrennte Volumes
  • nextcloud_db und mediawiki_db sind 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 einfaches tls=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.crt und /etc/ssl/own.key vorhanden 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.php mit dem Web-Installer. Als Datenbank-Host mediawiki-db, Datenbank mediawiki, Benutzer mediawiki angeben.
LocalSettings.php einhängen
  • Nach dem Setup wird die generierte LocalSettings.php heruntergeladen, neben das Compose-File gelegt und der auskommentierte Volume-Mount im mediawiki-Service aktiviert. Danach Container neu starten.
  • docker compose up -d mediawiki