In diesem Beitrag erkläre ich die Installation und den Betrieb von Docker Containern zusammen mit der Reverse Proxy Traefik 2.0

Motivation

Als ein aktueller Trend zeichnet sich immer weiter die Containerisierung von Diensten und Unterteilung in Microservices in der IT-Landschaft ab. Die Verwendung von Container ist nach kurzer Einarbeitung sehr simpel und übersichtlich und bietet im Vergleich zu Bare-Metal-Lösungen ein hohes Maß an Flexibilität.
Nahezu jede Software-Lösung auf dem Markt wird mittlerweile als Docker Container angeboten, oder durch Dritte bereitgestellt. Darüber hinaus lassen sich auch fehlende Container einfach selber erstellen.

Über Traefik

Traefik ist eine in Golang programmierte Reverse Proxy welche selber als Docker Container auf dem Zielserver deployed wird. Über die Docker API und die Verbindung zum Docker Socket erhält der Service jederzeit Informationen über aktuelle und neue Container.
In der Infrastruktur stellt die Reverse Proxy das Bindeglied zwischen den einzelnen Containern und den aus dem Internet erreichbaren Domains und Subdomains dar.

Flexibilität

Besonders an Traefik ist die Funktionsweise in der die Reverse Proxy neue Services erkennt. Ja richtig gehört, Traefik erkennt selber über den Docker Socket neue Container und erstellt dabei alle notwendigen Einstellungen um den Container aus dem Internet zu erreichen.
Daraus resultiert, dass das gesamte System in einem hohen Maße dynamisch agiert und keine lästige Konfiguration benötigt.

Das Setup

In diesem Beitrag verwende ich einen frischen Debian 10 Server, den ich für diesen Guide komplett neu installiert habe.
Welches Server Betriebsystem gewählt wird, ist im Endeffekt egal. Docker bietet für nahezu jede Distribution eine entsprechende Installationsanleitung, die hier gefunden werden kann.

Zusätzlich zu der Docker Engine, die bei einigen Distributionen übrigens schon vorinstalliert sind, verwende ich hier das Command-Line Tool docker-compose welches ebenfalls auf der Seite von Docker installiert werden kann.

Ordnerstruktur

mkdir -p /opt/containers/{traefik,portainer}
mkdir -p /opt/containers/traefik/data

touch /opt/containers/traefik/data/acme.json
chmod 600 /opt/containers/traefik/data/acme.json

touch /opt/containers/traefik/data/traefik.yml

Zu Beginn erstelle ich die Ordner für alle Daten von Container die ich später deployen werden.
Für diesen Zweck hat sich in der Vergangenheit der Ordner /opt immer sehr bewährt.

Die acme.json Datei wird von Traefik dafür verwendet um die SSL Zertifikate über Let’s Encrypt abzuspeichern.
Damit die Datei später korrekt verwendet werden kann und es keine Fehler gibt, müssen die Rechte auf 600 gesetzt werden.

Neben der Installation von Traefik werde ich als ersten Container portainer deployen, welcher ein UI für die Verwaltung von Containern bereitstellt.

Traefik-Konfiguration

nano /opt/containers/traefik/data/traefik.yml

api:
  dashboard: true
entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  http:
    acme:
      email: [email protected]
      storage: acme.json
      httpChallenge:
        entryPoint: http

In der Konfiguration aktiviere ich das Traefik eigene Dashoard, definiere die Standardports, sowie die Verbindung zum Docker.
Damit Traefik die Subdomains direkt mit SSL Zertifikaten ausstattet, wird im Anschluss noch der Zertifikat Resolver definiert.
Hier sollte bei email eine E-Mail-Adresse angegeben werden, an die Let’s Encrypt bei Fehlern zur Verlängerung Emails schickt.

Zugangsdaten fürs Dashboard

Um das Traefik Dashboard vor fremden Blicken zu schützen, sichere ich dieses per htpasswd ab.

Hierfür bietet sich das Command-Line Tool htpasswd von apache2-utils an.
In Abhängigkeit vom gewählten Betriebssystem des Servers muss dieses noch per apt install apache2-utils installiert werden.

echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g

Die Anmeldung erfolgt dann über ein htpassword Fenster im Browser mit den Daten user und password. Diese sollte man natürlich ändern!

Traefik Dockerfile

nano /opt/containers/traefik/docker-compose.yml

version: '3'

services:
  traefik:
    image: traefik:v2.0
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=user:passwordhash"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=http"
      - "[email protected]"

networks:
  proxy:
    external: true

In diesem Dockerfile erstelle ich den Traefik Container mit den Port und Volume Mappings, sodass dieser korrekt auf die Konfiguration zugreifen kann.
traefik.example.com ist hierbei die Subdomain auf dem das Dashboard später erreichbar ist.
user:passwordhash muss durch die Ausgabe des Befehls von oben ersetzt werden.

Docker Netzwerk anlegen

docker network create proxy

Mit dem Befehl erstelle ich ein neues Docker Netzwerk, in welchem später alle anderen Container kommunizieren.

Traefik Container starten

Wichtig! Bevor Traefik nun gestartet wird, muss die Subdomain/Domain per A / AAAA Record auf die IP-Adresse des Docker-Host zeigen!

cd /opt/containers/traefik

docker-compose up -d

Mit Hilfe des Composer CLI wird nun der Container heruntergeladen und im Hintergrund erstellt.

Der erste Container

Als ersten Container verwende ich nun portainer, eine Web-Oberfläche zum Verwalten von Containern auf dem Server. Ab diesem Punkt kann aber auch jeder anderer Container erstellt werden, der über Ports verfügt, welche per Subdomain erreichbar sind.
Die docker-compose Konfiguration von portainer kann im Endeffekt mit jedem anderen Container ersetzt werden.

nano /opt/containers/portainer/docker-compose.yml

version: '3'

services:
  portainer:
    image: portainer/portainer:latest
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.entrypoints=http"
      - "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)"
      - "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.portainer.middlewares=portainer-https-redirect"
      - "traefik.http.routers.portainer-secure.entrypoints=https"
      - "traefik.http.routers.portainer-secure.rule=Host(`portainer.example.com`)"
      - "traefik.http.routers.portainer-secure.tls=true"
      - "traefik.http.routers.portainer-secure.tls.certresolver=http"
      - "traefik.http.routers.portainer-secure.service=portainer"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

Gestartet wird dieser Container auch wie schon Traefik über docker-compose.

Wichtig! Bevor Portainer nun gestartet wird, muss die Subdomain/Domain per A / AAAA Record auf die IP-Adresse des Docker-Host zeigen!

cd /opt/containers/portainer

docker-compose up -d

Alle weiteren Container

Für alle weiteren Container gelten im Endeffekt die selben Regel wie für Portainer.
Entscheidend ist, dass sich der Container im Netzwerk proxy befindet und die Labels sich an dem Container befinden.
Unabhängig von Portainer sind die Labels wie folgt.

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.<SERVICE-NAME>.entrypoints=http"
  - "traefik.http.routers.<SERVICE-NAME>.rule=Host(`<SUBDOMAIN>`)"
  - "traefik.http.middlewares.<SERVICE-NAME>-https-redirect.redirectscheme.scheme=https"
  - "traefik.http.routers.<SERVICE-NAME>.middlewares=<SERVICE-NAME>-https-redirect"
  - "traefik.http.routers.<SERVICE-NAME>-secure.entrypoints=https"
  - "traefik.http.routers.<SERVICE-NAME>-secure.rule=Host(`<SUBDOMAIN>`)"
  - "traefik.http.routers.<SERVICE-NAME>-secure.tls=true"
  - "traefik.http.routers.<SERVICE-NAME>-secure.tls.certresolver=http"
  - "traefik.http.routers.<SERVICE-NAME>-secure.service=<SERVICE-NAME>"
  - "traefik.http.services.<SERVICE-NAME>.loadbalancer.server.port=<PORT>"
  - "traefik.docker.network=proxy"

<SERVICE-NAME> muss ein eindeutiger Name für den Container sein
<SUBDOMAIN> ist die Domain oder Subdomain unter der der Container dann erreichbar ist. Mehrere durch Komma getrennt sind möglich.
<PORT> ist der Port von dem Dienst im Container. Dieser ist oft im GitHub / DockerHub des Containers definiert.

Wichtig! Bevor der Container nun gestartet wird, muss die Subdomain/Domain per A / AAAA Record auf die IP-Adresse des Docker-Host zeigen!

Weitere Konfiguration

Die Labels die hier nun verwendet wurden sind nur der Anfang von den schier unendlichen Möglichkeiten die Traefik bietet.
Neben der Reverse Proxy Funktionalität bietet Traefik Loadbalancing von mehreren Containern, weitere Middlewares und viele weitere Möglichkeiten die über Label am Container konfiguriert werden.

Alle möglichen Optionen sind in der sehr ausführlichen Dokumentation von Traefik 2.0 dokumentiert.

I am text block. Click edit button to change this text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

Alle Dateien zu diesem Beitrag inkl. docker-compose.yml findest du hier.