Traefik und Portainer
Container Management mit Portainer hinter dem Reverse-Proxy Traefik mit automatischer Zertifikatsvergabe von Let's Encrypt.
Traefik
Traefik ist ein moderner HTTP-Reverse-Proxy und Load-Balancer, der die Bereitstellung von Containern in verschiedenen Infrastrukturen wie Docker, Docker Swarm und Kubernetes vereinfacht. Dabei wird Traefik selbst als Container installiert und über statische und dynamische Konfigurationen eingerichtet. Traefik verwaltet als Reverse-Proxy automatisch die Zertifikate für die angebotenen Dienste über Zertifizierungsstellen, wie z.B. von Let's Encrypt.

Portainer
Portainer ist eine Web-GUI für Docker, welche selbst in Docker Containern läuft. Dabei unterstützt Portainer nicht nur Docker als Plattform, sondern auch Docker Swarm und Kubernetes. Während Docker auch über die Kommandozeile verwaltet werden kann, ist es besonders bei komplexeren Befehlen, die über mehrere Zeilen gehen, schwierig den Überblick zu behalten und Fehler zu vermeiden. Aus diesem Grund kann eine grafische Benutzeroberfläche wie Portainer eine große Hilfe sein, um die Verwaltung von Docker Containern zu vereinfachen und auch weniger erfahrenen Anwendern den Umgang damit zu erleichtern.
Portainer ist in zwei Versionen verfügbar - Community Edition (CE) und Business Edition (EE). Die Business Edition ist zur Zeit für drei Nodes und für ein Jahr als kostenlose Lizenz zu bekommen.
Einrichtung von Domainnamen
Für den Einsatz von Traefik als Reverse-Proxy werden Domainnamen zur Zuordnung der einzelnen Dienste über den HTTP-Host-Header benötigt. Für mein HomeLab nutze ich das kostenlose Angebot der Firma Digineo aus Bremen, um sowohl ipv4, als auch ipv6 Adressen dynamisch zu registrieren.
Installation von Traefik mit Portainer
Für die Einrichtung meiner HomeLab Umgebung verwende ich "docker compose" zur einfachen Bereitstellung der Basisumgebung. Als Basis dient dazu mein Post zu "Docker unter Debian 12".
Für die Umgebung richte ich einen Basis Ordner für die Docker Konfigurationsdatei "docker-compose.yml" und die Datei mit den Umgebungsvariablen ".env" für "docker compose" ein. Zudem einen Ordner "traefik" für die Speicherung der Zertifikate in der Datei "acme.json" und einen Unterordner "rules" für spätere dynamische Konfigurationsdateien von Traefik. Diese Ordnerstruktur erstelle ich wie folgt im Dateisystem unter "/opt".
$ mkdir -p /opt/containers/basis/traefik/rules
$ touch /opt/containers/basis/traefik/acme.json
$ chmod 600 /opt/containers/basis/traefik/acme.json
In den Umgebungsvariablen sind die Domainnamen für Traefik und Portainer anzugeben. Das Dashboard von Traefik sichere ich mit der Basisauthentifizierung ab, wozu Traefik eine Zeichenkette aus Benutzername und Passwordhash benötigt. Diese Zeichenkette kann mit Hilfe von "htpasswd" erstellt werden.
$ echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
Zur Nutzung von Let's Encrypt über das ACME Protokoll erwartet der Dienst eine gültige E-Mail Adresse, an die auch Meldungen über ablaufende Zertifikate verschickt werden.
Beispielhafter Inhalt der Datei /opt/containers/basis/.env
TRAEFIK_DASHBOARD_DOMAINNAME=traefik.example.com
TRAEFIK_DASHBOARD_AUTH=view:$$hjezstfkjgldp$$qdVT/l3SI.GtFiqoi13cf1
TRAEFIK_ACME_EMAIL=webmaster@example.com
PORTAINER_FRONTEND_DOMAINNAME=portainer.example.com
PORTAINER_EDGE_DOMAINNAME=edge.example.com
Für Traefik wird das externe Docker Netzwerk "proxy" definiert. Traefik soll alle Anfragen auf den Ports 80 (web) und 443 (websecure) und jeden eingerichteten Domainnamen im HTTP-Host-Header ein Zertifikat von Let's Encrypt ("leresolver") beziehen. Dabei werden alle Anfragen ("http-catchall") auf Port 80 nach Port 443 umgeleitet ("redirect-to-https").
Unter der Adresse "https://traefik.example.com/dashboard/" bietet Traefik nach der Benutzeranmeldung einen Überblick der Konfiguration im Web-Dashboard an.

Portainer soll seine Daten in einem Docker Volume "portainer_data" speichern. Die Anwendung hört intern auf den Port 9000, ist aber über Traefik und dem Domainnamen "https://portainer.example.com" mit einem Let's Encrypt Zertifikat von außen erreichbar. Als erster Schritt muss für Portainer ein Administratorkonto vergeben werden.

Wird die Business Edition verwendet, ist zusätzlich der per E-Mail versandte Lizenzschlüssel anzugeben.
Um Traefik und Portainer zu installieren, werden durch folgenden Docker Befehl gemäß der "docker-compose.yml" die Images geladen und gestartet. Mit der Option "-d" laufen die Anwendungen im Hintergrund, zeigen die Debug Meldungen aber nicht interaktiv an.
$ docker compose up -d
Inhalt der Datei /opt/containers/basis/docker-compose.yml
version: "3.3"
services:
traefik:
image: "traefik:latest"
restart: unless-stopped
command:
# Globals
- --global.sendAnonymousUsage=false
- --log.level=DEBUG
- --api.dashboard=true
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Providers
- --providers.docker
- --providers.docker.exposedbydefault=false
- --providers.file.directory=/etc/traefik/rules
- --providers.file.watch=true
# LetsEncrypt
- --certificatesresolvers.leresolver.acme.httpchallenge=true
- --certificatesresolvers.leresolver.acme.email=${TRAEFIK_ACME_EMAIL}
- --certificatesresolvers.leresolver.acme.storage=/etc/traefik/acme.json
- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
networks:
- proxy
ports:
- 80:80
- 443:443
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/etc/localtime:/etc/localtime:ro"
- "./traefik/acme.json:/etc/traefik/acme.json"
- "./traefik/rules:/etc/traefik/rules"
labels:
# Dashboard
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_DASHBOARD_DOMAINNAME}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls.certresolver=leresolver"
- "traefik.http.routers.dashboard.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_DASHBOARD_AUTH}"
# Containers
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
portainer:
image: portainer/portainer-ee:latest
restart: unless-stopped
command: -H unix:///var/run/docker.sock
networks:
- proxy
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/etc/localtime:/etc/localtime:ro"
- portainer_data:/data
labels:
# Frontend
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`${PORTAINER_FRONTEND_DOMAINNAME}`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.services.frontend.loadbalancer.server.port=9000"
- "traefik.http.routers.frontend.service=frontend"
- "traefik.http.routers.frontend.tls.certresolver=leresolver"
# Edge
- "traefik.http.routers.edge.rule=Host(`${PORTAINER_EDGE_DOMAINNAME}`)"
- "traefik.http.routers.edge.entrypoints=websecure"
- "traefik.http.services.edge.loadbalancer.server.port=8000"
- "traefik.http.routers.edge.service=edge"
- "traefik.http.routers.edge.tls.certresolver=leresolver"
volumes:
portainer_data:
networks:
proxy:
name: proxy
