Docker + Traefik

Dieser Artikel befasst sich mit docker + traefik und ist die Grundlage einer Serie von Anleitungen zum Thema docker.

Update

16.05.2023 – IPv6 Beispiel korrigiert
22.05.2023 – Verbesserung der Textqualität; Erklärungen zur traefik Konfiguration
26.06.2023 – IPv6 erneut korrigiert

Vorwort

Viele Jahre arbeite ich schon an und mit Root-Servern und habe auch immer meine eigenen Maschinen am Laufen. Die Setups und Installationen haben dabei immer auch ein hohes Maß an Aufwand erzeugt, natürlich auch ein hohes Maß an Spaß und Freude. Häufig war der Grund etwas Neues zu probieren oder zu evaluieren. Eine neue Version, ein neues Tool, eine neue eigene Cloud usw.

Jetzt gibt es ja schon einige Zeit ein Tool, das Abhilfe schaffen kann: docker. Mit docker konnte ich mich aber laaaange Zeit einfach nicht anfreunden. Vom monolithischen Server, zur Serverfarm mit Cluster, zur Virtualisierung, zu Containern?

„Aber Container … Was soll der Mist? Alte Kamellen in neuen Kleidern?“

Auszug aus dem Vorwort von „Skalierbare Container-Infrastrukturen“ von Oliver Liebel

Diese schlauen (?!) Worte stammen aus dem Buch „skalierbare Container-Infrastrukturen“ von Oliver Liebel. Ein Buch, dass ich in diesem Kontext guten Gewissens empfehlen kann.

A F F I L I A T E

Skalierbare Container-Infrastrukturen

In dem Buch werden viele Aspekte von Docker und der entsprechenden Orchestrierung beschrieben. Für alle die mehr wissen und probieren wollen!

Mit dem Klick und Kauf des Buchs links, unterstützt ihr meine Arbeit!
Ich sage Danke!

Mit ausreichend Distanz und ausreichend Zeit (*hüstel*) habe ich mich mittlerweile mit docker angefreundet, Informationen gesucht, nachgebaut, erweitert, ergänzt und Freundschaft geschlossen.

Ziel

Das Ziel dieser Anleitung ist die Bereitstellung eines docker Servers. D.h. ich installiere auf einem root-Server docker und docker compose. Um die Ressourcen hier entsprechend sinnvoll zu nutzen (Auslastung des Severs), stellt dieser Server dann entsprechende weitere Web-Applikationen zur Verfügung. Auf der Ziellinie kann WordPress, Redmine, Bitwarden, … installiert und betrieben werden. Es bedarf keiner aufwendigen Konfigurationen und dem Handling verschiedener Konfigurationstypen. Wenn wir uns eingespielt haben, kommt (fast) alles im gleichen Format: YML.

Server vorbereiten

Die Basis für diesen und die viele andere Artikel ist ein Root-Server. Ob dieser Bare Metal oder Virtual ist, spielt dabei keine Rolle. Meine Basis ist ein CPX21 bei HETZNER. Damit habe ich die komplette Spielwiese für unter 10 EUR in einem DSGVO-konformen RZ (bei Auswahl der entsprechenden Location).
Auf dem Server läuft Ubuntu 22.04 Minimal.

Vorbereitungen

Um immer auf dem aktuellen Stand zu bleiben source ich docker aus den offiziellen Repository, womit docker immer auf dem aktuellen Stand gehalten werden kann. Hast Du im Nachbau dieser Anleitung Ubuntu neu installiert, sind die nachfolgenden beiden Zeilen nicht notwendig, da nichts davon installiert sein sollte.

sudo apt remove docker docker-engine docker.io containerd runc
sudo rm /etc/apt/sources.list/docker.list
Zsh

Im nächsten Schritt aktualisiere ich die Paketliste und installiere den ersten Schwung notwendiger Pakete. Das Sourcen der docker Repositories (PGP-Keys installieren etc) teilt mir diesen Prozess in mehrere Schritte.

  • Notwendige Pakete installieren
sudo apt update && sudo apt install apt-transport-https ca-certificates curl \
     software-properties-common gnupg lsb-release apache2-utils
Zsh
  • Offizielle Docker GPG Keys einbinden
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor \ 
          -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpgp
Zsh
  • Repository einbinden
echo "deb [arch=$(dpkg --print-architecture) \
     signed-by=/etc/apt/keyrings/docker.gpg] \ 
     https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
     | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Zsh

docker + docker compose installieren

Schlussendlich installiere ich die neueste Docker Engine und Docker Compose ins System

sudo apt update && sudo apt install docker-ce docker-ce-cli \
     containerd.io docker-buildx-plugin docker-compose-plugin
Zsh

docker in Bootprozess einbinden

In wenigen Ausnahmefällen wird man den Schritt weglassen. Aber nach einem reboot sollte schon alles wieder mit starten. Das erledige ich durch nachfolgende Eingabe an der Shell

sudo systemctl enable docker.service && sudo systemctl enable containerd.service

Auf der docker-docs Homepage wird beschrieben, wie die Installation mit hello-world getestet werden kann. Schlecht ist das nicht, denn damit siehst Du recht schnell ob erstmal die Laufzeitumgebung passt. Ich erspare mir das aber und fahre stattdessen gleich mit dem Reverse-Proxy traefik fort. Weiterführende Informationen findet man im Internet, zB die Anleitung auf digitalocean. [Q]

Nacharbeiten

Um auch zielgerichtet in die Zukunft zu blicken, aktivieren wir IPv6 für docker [Q]. Wer Du dich für einen Server bei Hetzner entschieden hast, dann solltest Du dich gleich in die Console einloggen. Hier steht die IPv6, die wir direkt brauchen werden. Mit vi /etc/docker/daemon.yml legen wir die entsprechende Datei an und befüllen diese mit Inhalt.

IPv6

{
  "ipv6": true,
  "fixed-cidr-v6": "2a01:4f9::::1/64"
}
YAML

Bei fixed-cidr-v6 muss deine IPv6 rein, logisch.

Ein kleiner Hinweis. Die letzte Direktive schließt nicht mit einem Komma ab. Wenn Du also nachfolgend beschriebenes Live Restore nutzen möchtest, dann nach der fixed-cidr-v6 Direktive ein Komma ergänzen und nach der live-restore Direktive weglassen.

Live Restore

Live Restore ist auch eine nette Nacharbeit. Dabei bleiben die Container am Start, auch wenn der Daemon herunterfährt, z.B. bei einem Update. Auch dieser Eintrag ergänzt die JSON in der /etc/docker/daemon.yml.


{
  "ipv6": true,
  "fixed-cidr-v6": "2a01:4f9::::1/64",
  "live-restore": true
}
YAML

traefik installieren

Einführung

Traefik ist angetreten Netzwerk-Komplexität abzubauen. In ganz kurzer Zeit hat traefik arrivierten Systemen den Rang abgelaufen. Insbesondere unter dem Aspekt im Umfeld von Microservices verrichtet der Reverse Proxy und/oder Load Balancer herausragende Dienste.

traefik automatic & dynamic routing

Oh mein Gott – Theoretiker schlagen wieder zu!

Einfach gesagt greift Traefik den zielgerichteten Datenverkehr aus dem Internet, via https ab und leitet diesen dann an den eigentlichen Service weiter.

Was sind die Vorteile?

Traefik ist mit allen Cluster Technologien kompatibel (zB Kubernetes, Docker, Docker Swarm, AWS, …). Der Hamster braucht dabei keine spezielle Konfiguration sondern macht dies automatisch. In der Verbindung mit docker, was ja hier Thema ist, schreibt man also eine docker-compose.yml und die Dienste verbinden sich. Die Entwickler geben hier eine ausführliche Einführung.

Arbeitsverzeichnis(se)

Alle Container, die ich auf diesem Server installiere, werden unter /opt/containers, in einem eigenen Unterverzeichnis liegen. Diesem Schema werden auch die weiteren Anleitungen und Artikel folgen.

mkdir -p /opt/containers/traefik/data

SSL/TLS mit Certbot

Die Anleitung hier ist dazu gedacht eine Anwendung über das Internet verfügbar zu machen. Stand der Dinge, der Technik und des guten Geschmacks ist die Absicherung mit SSL/TLS. Ergo erstelle ich im nächsten Schritt die Datei acme_letsencrypt.json im Arbeitsverzeichnis. Diese Datei wird nicht manuell, sondern automatisch befüllt. Die Datei acme_letsencrypt.json dient uns als Zertifikatsspeicher.

vi /opt/containers/traefik/data/acme_letsencrypt.json
chmod 600 /opt/containers/traefik/data/acme_letsencrypt.json

traefik konfigurieren

Dazu wird erneut eine Konfigurationsdatei ins Arbeitsverzeichnis geschrieben. Mit dem Editor deiner Wahl, bei mir vi.

vi /opt/containers/traefik/data/traefik.yml
Zsh

Die einzelnen Abschnitte von oben nach unten…

api:
  dashboard: true

certificatesResolvers:
  http:
    acme:
      email: "sollte-deine-e@mail-se.in"
      storage: "acme_letsencrypt.json"
      httpChallenge:
        entryPoint: http

entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: "https"
          scheme: "https"
  https:
    address: ":443"

global:
  checknewversion: true
  sendanonymoususage: false

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: "proxy"
  file:
    filename: "./dynamic_conf.yml"
    watch: true
  providersThrottleDuration: 10
YAML

In der YAML wird auf die Datei dynamic_conf.yml referenziert. Diese Datei lege ich als Nächstes an. Auch in dieser stehen Anpassungen und Direktiven für traefik, wie die Verschlüsselungsparameter, -algorithmen, Einstellungen zur Middleware, Headern usw.

tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384
      sniStrict: true
http:
  middlewares:
    traefikAuth:
      basicAuth:
        users:
          # gehashten Wert eingeben / enter hashed value
          - "dein-user:dein-passwort-hashwert"
    default:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    secHeaders:
      chain:
        middlewares:
          - default-security-headers
          - gzip

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"
    gzip:
      compress: {}
YAML

In Zeile 21 der Datei musst Du jetzt Anpassungen vornehmen. Dazu führst Du zunächst, an der Kommandozeile den folgenden – mit von Dir verwendetem Benutzernamen und Passwort – Befehl aus:

echo $(htpasswd -nb chef 'omlette-pommes-frites-mit-majo')
Zsh

Den Rückgabewert packst Du dann zusammen mit dem von Dir gewählten Benutzer, in die Zeile 21:

middlewares:
    traefikAuth:
      basicAuth:
        users:
          - "chef:$apr1$BD.QzZIN$6OUwEXt5EtpxHi8gCngf/1"
    default:
YAML

traefik Container > docker-compose.yml

Ich schreibe die Konfiguration hier einfach mal hin. Fertig für TL;DR zum schnellen kopieren in Deine eigene Umgebung. Die Erläuterung reiche ich nach. Daher wird das nachfolgende Kapitel ein „Work in progress“ sein.

version: '3.9'
services:
  traefik:
    container_name: traefik
    image: traefik:latest
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme_letsencrypt.json:/acme_letsencrypt.json
      - ./data/dynamic_conf.yml:/dynamic_conf.yml
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"
      # die passende Domain einsetzen / enter the correct domain
      - "traefik.http.routers.traefik.rule=Host(`traefik.DeineDomain.tld`)"
      - "traefik.http.routers.traefik.middlewares=traefikAuth@file,default@file"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=http"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly=true"
      - "traefik.http.services.traefik.loadbalancer.sticky.cookie.secure=true"
      - "traefik.docker.network=proxy"
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      proxy:
    hostname: traefik
    ports:
      - "80:80"
      - "443:443"

networks:
  proxy:
    name: proxy
    driver: bridge
    attachable: true
YAML

Hier musst Du auf die Domain (Zeile 16) achten. Hinweis: idealerweise zeigt ein A-Record auf die IP des Servers.

    labels:
      - "com.centurylinklabs.watchtower.enable=true"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=https"
      - "traefik.http.routers.traefik.rule=Host(`traefik.pommesbude-mit-majo.tld`)"
      - "traefik.http.routers.traefik.middlewares=traefikAuth@file,default@file"
YAML

traefik Konfiguration verstehen

TL;DR – es folgt die blanke Theorie!

Auf alle Direktiven kann ich nicht eingehen. traefik gilt zwar als einfach, aber …. hahaha … naja, nein. Daher beschränke ich mich in dieser Anleitung, die ja das Ziel hat schnell ein Ergebnis zu liefern, auf die Anweisungen, die ich zum „Funktionieren“ brauche.

docker labels

Die labels: sind die Verbindung zwischen traefik und docker und begegnen Dir in allen Anleitungen, die auf docker + traefik aufsetzen.

Routers

Der Eintrag folgt dem Schema traefik.http.routers.<router_name>.<option>. Die <option> gibt dabei die Option an, die abweichend vom Standard geändert werden soll. Alle Referenzen gibt es in der traefik Dokumentation.

rule: gibt an, für welches spezifische Kriterium die rule (Regel) gültig sein soll (Ref.)

entrypoints: gibt an oder beschränkt den Anwendungsbereich von traefik auf bestimmte Einstiegspunkte (Ref.)

middlewares: mit middlewares werden die Anfragen verändert, bevor diese an den service weitergegeben werden. Dabei können die Anfragen, die Header modifiziert werden. Andere leiten die Anfragen um, oder fügen Authentifizierung hinzu… (Ref.1, Ref.2,)

service: das Ziel für die Anfragen (Ref.)

traefik starten

Ein integraler Bestandteil dieser Anleitung ist es, den Reverse Proxy traefik zu starten.

docker compose -f /opt/containers/traefik/docker-compose.yml up -d
Zsh

Wenn ich alles richtig gemacht habe, dann startet traefik seinen Dienst und wartet auf das „Einhaken“ weiterer Applikationen. Mit den entsprechenden labels versehen werde ich in weiteren Artikeln auch entsprechende Anwendungen einbinden. Die Quittung lass ich direkt ausgeben:

  ~ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                                                                      NAMES
44b8150d0309   traefik:latest   "/entrypoint.sh trae…"   8 minutes ago   Up 8 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   traefik
Zsh

Um jetzt in und mit traefik zu arbeiten, hatte ich die Domain mittels A-Record eingetragen. Wenn diese URL aufgerufen wird, dann öffnet sich ein http-Auth, die ich mit der Eingabe der Benutzer/Kennwort-Kombi von hier hinter mir lasse.

Bei erfolgreicher Einrichtung siehst Du hier das traefik Dashboard

Zusammenfassung

Ich habe in dieser Anleitung die Einrichtung von docker und die Bereitstellung von traefik als Reverse Proxy beschrieben und Dir hier als handliche Bauanleitung zur Verfügung gestellt. Damit steht die Grundlage für weitere Anleitungen rund um docker.

Womit geht es weiter?

Ich habe mittlerweile einige weitere, ergänzende Anleitungen erstellt.

Docker + Redmine
Docker + Bitwarden

Quellen

Install Docker Engine on Ubuntu
How to install and use Docker on Ubuntu 22.04
IPv6 in Docker
goneuland.de
Compose File Reference
traefik Dokumentation

3 Gedanken zu „Docker + Traefik

Schreibe einen Kommentar