Docker Stack Traefik

Aus Xinux Wiki
Zur Navigation springen Zur Suche springen

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