Skip to main content
Dailycoding Notes

traefik als Ingress Controller in einer Single Server Umgebung

Title

Wenn man für ein System mehrere Services deployen muss und diese auch noch von außen über TLS verfügbar sein sollen, stellt man schnell Grenzen von reinen Docker Umgebungen fest. Abhilfe schafft da ein richtig eingereichteter Ingress Controller.

Für eines unserer Produkte habe ich damals entschieden, dass wir die Fachlichkeit der Anwendung in kleinere Services aufteilen. Wir sprechen hier aber noch nicht von Microservices. Wir haben einfach die größeren Aufgabenblöcke in einzelne Docker-Container gepackt und dann getrennt von einander deployed. Das hat den Vorteil, dass die Auswirkungen von Änderungen an der Anwendung lokaler begrenzt bleiben. Außerdem können die einzelnen Services unabhängig voneinander skaliert werden. Das ist aber ein anderes Thema.

Damit die Services von Außen erreichbar sind, müssen die Ports, die Docker bereitstellt, in der Firewall freigegeben werden und jeder Service ein eigenes TLS-Zertifikat verwalten. Das ist natürlich nicht sehr praktikabel. Abhilfe schafft hier ein Ingress Controller. Dieser ist in der Lage, die Anfragen an die richtigen Services weiterzuleiten und auch die TLS-Zertifikate zu verwalten.

Da wir die Services schon mit docker-compose verwalten, fiel die Wahl des Ingress Controllers auf traefik. Dieser ist sehr einfach zu konfigurieren und kann auch mit docker-compose sehr gut umgehen. Außerdem ist es in der Lage, die TLS Zertifikate automatisch zu erneuern. So ist es beispielsweise auch möglich Feature Branches für den Test bereitszustellen und nicht nur den Main-Branch.

Ein weiterer Ingress Controller, den ich mir angesehen habe ist nginx. Dieser hat aber für unseren Anwendungsfall entscheidende Nachteile. Eine Änderung der Konfiguration ist nur mit einem Neustart des Webservers möglich. Die Erneuerung der TLS Zertifikate muss von Hand angestoßen werden. Im besten Fall noch per cron-Job, so wie wir es auf anderen Servern bereits eingerichtet haben.

Entscheidend war aber am Ende, dass ich mit traefik schneller zum Ziel gekommen bin. Die Dokumentation ist sehr gut und ich kann die Ingress Konfiguration über meine docker-compose Files steuern. Das ist für mich ein großer Vorteil, da ich so die Konfiguration der Services und des Ingress Controllers an einer Stelle habe.

Installation

Da wir auf jedem unserer Server schon Docker installiert haben, haben wir traefik ebenfalls als Docker Container gestartet.

version: "3"
services:
  traefik:
    image: "traefik:v2.10"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=ingress"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
    restart: always
    networks:
      - ingress
    ports:
      - "443:443"
      - "80:80"
      - "127.0.0.1:8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

networks:
  ingress:
    external: true

Die Grund-Konfiguration wird über den definierten Command gesteuert, kann aber auch über eine yaml-Datei gesteuert werden. Diese wird in das Verzeichnis /etc/traefik im Container gemountet. Die Datei sieht für das obige Beispiel wie folgt aus:

entryPoints:
  web:
    address: :80
  websecure:
    address: :443
providers:
  docker:
    exposedbydefault: false
    network: ingress
api:
  insecure: true
  dashboard: true

traefik verwendet das Konzept von Entrypoints um die Anfragen entgegenzunehmen. In diesem Beispiel werden die Standard-Ports 80 und 443 verwendet. Der Port 80 wird später für die Weiterleitung auf den Port 443 verwendet, um TLS Verbindungen anbieten zu können. Außerdem wird das Dashboard aktiviert. Da das Dashboard nicht passwortgeschützt ist, ist dieses nur unter http://localhost:8080 erreichbar. Unsere Admins müssen also einen SSH Tunnel auf den Server aufbauen und so das Dashboard auf den eigenen Rechner weiterleiten. Mit den richtigen Shortcuts bleibt das aber weiterhin konfortabel.

traefik lässt sich auch in Kubernetes Umgebungen installieren. Dafür gibt es eine eigene Dokumentation. Die Installation erfolgt recht einfach über Helm Charts.

Provider

Um die Services im Endpunkt anbieten zu können, muss traefik natürlich wissen, welche Services denn überhaupt aktiv sind und welche er bereitstellen soll.

Dazu bedient sich traefik sogenannter Provider. Diese können verschiedene Quellen sein. In unserem Fall ist es der Docker Provider. Dieser liest die Labels aus den Docker Containern aus und konfiguriert sich so selbst. Das ist sehr praktisch, da wir so die Konfiguration der Services und des Ingress Controllers an einer Stelle haben.

Service Detection

traefik ist in der Lage über die Provider die entsprechenden Services selbständig zu finden und einzurichten.

Dazu muss sich der Service allerdings im selben Docker Netzwerk wie traefik befinden. Wir haben ein eigenes Ingress Network erstellt, in dem alle Services laufen, die von außen erreichbar sein sollen. Dieses Netzwerk wird dann in der docker-compose.yml Datei mit dem ingress Label versehen.

networks:
  ingress:
    external: true

Da docker-compose externe Netzwerke nicht selbst anlegt, muss man das Network einmal manuell mit dem Docker Command Line Tool anlegen.

docker network create ingress

Jetzt is es an der Zeit den ersten Service zu deployen. Um das Beispiel einfach zu halten, verwende ich den von traefik in den Beispielen genutzten whoami Service. Dieser gibt bei einem Aufruf die IP Adresse des Servers zurück. Das ist sehr praktisch, um zu sehen, ob die Anfragen auch wirklich ankommen.

version: "3"
services:
  whoami:
    image: "traefik/whoami"
    container_name: "whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"
    networks:
      - ingress
networks:
    ingress:
        external: true

Wichtig ist wieder, dass der Service mindestens mit dem Netzwerk von traefik verbunden ist. Bei meinen ersten Gehversuchen habe ich das vergessen und mich gewundert, warum der Service nicht erreichbar ist bzw. erst gar nicht in traefik angezeigt wurde.

Wenn wir den whoami Service jetzt starten sollte der Service nach kurzer Zeit auch im Dashboard von traefik auftauchen. Außerdem sollte der Service unter http://whoami.localhost erreichbar sein.

traefik Dashboard mit deploytem whoami Container

TLS

Anfangs habe ich ja schon erwähnt, dass traefik automatisch die TLS Zertifikate erneuern und verwalten kann. traefik verwendet standardmäßig Let's-Encrypt als Zertifikats-Aussteller. Das funktioniert natürlich nur, wenn die Domains auch auf den Server zeigen. Die vorherige korrekte DNS-Konfiguration ist also hier auch ein wichtiger Punkt.

Ohne eingerichtetes Let's-Encrypt verwendet traefik ein eigenes Zertifikat für jeden Host und jeden Service. Die Verschlüsselung ist zwar gegeben, aber der Browser wird dennoch eine Warnung anzeigen, da das Zertifikat nicht von einer vertrauenswürdigen Stelle ausgestellt wurde.

Um von Let's-Encrypt Zertifikate abrufen zu können, muss ein sogenannter ACME Provider konfiguriert werden. Dieser ist in der Lage, die Zertifikate automatisch zu erneuern. Dazu muss der Provider wissen, welche Domains er verwalten soll. Das kann über die traefik.yml Datei konfiguriert werden.

Unsere ursprüngliche docker-compose Datei muss nun um den Zertifikatsresolver erweitert werden.

version: "3"
services:
  traefik:
    image: "traefik:v2.10"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=ingress"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.acmeresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.acmeresolver.acme.email=noreply@dailcoding.de"
      - "--certificatesresolvers.acmeresolver.acme.storage=/letsencrypt/acme.json"
    restart: always
    networks:
      - ingress
    ports:
      - "443:443"
      - "80:80"
      - "127.0.0.1:8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

networks:
  ingress:
    external: true

Es können druchaus mehrere Reoslver registriert werden, die sich um verschiedene Services kümmern. In unserem Fall ist es nur ein Resolver, der sich um alle Services kümmert. Mehrere Resolver können dann nützlich sein, wenn sich unterschiedliche Teams um die Services kümmern und bei Problemen mit den Zertifikaten (bspw. Ablauf oder Rückruf) unterschiedliche Personen informiert werden sollen.

Der Service muss dann noch definieren, über welchen Resolver er sein Zertifikat abgerufen haben möchte.

whoami:
  image: "traefik/whoami"
  container_name: "whoami"
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
    - "traefik.http.routers.whoami.entrypoints=websecure"
    - "traefik.http.routers.whoami.tls.certresolver=acmeresolver"
  networks:
    - ingress

Fazit

traefik ist ein sehr mächtiger Ingress Controller, der sich sehr gut in Docker Umgebungen einsetzen lässt. Die Konfiguration ist sehr einfach und kann über die docker-compose Dateien gesteuert werden. Das ist für mich ein großer Vorteil, da ich so die Konfiguration der Services und des Ingress Controllers an einer Stelle habe und diese sehr gut versionieren kann.

Für High Availability Umgebungen ist traefik in der Konfiguration wie wir sie benutzen nicht geeignet. Die Konfiguration wird nicht zwischen den Instanzen synchronisiert. Auch die Beschaffung der TLS Zertifikate ist nicht auf mehrere Instanzen verteilt. So kann es zu Problemen bei der Aktualisierung der Zertifiakte kommen. Für HA Umgebeungen eignet sich sowieso ein Kubernetes Cluster besser. Dort kann traefik dann auch als Ingress Controller eingesetzt werden.

Für uns läuft diese Konfiguration schon seit über einem Jahr sehr stabil. Wir haben bisher keine Probleme mit der Konfiguration gehabt. Die Zertifikate werden automatisch erneuert und die Services sind immer über eine TLS Verbindung erreichbar. Wenn neue Services dazukommen oder ein neues Feature getestet werden soll, ist das sehr einfach möglich. Wir müssen nur die entsprechenden Labels in der docker-compose.yml Datei hinzufügen und schon ist der Service erreichbar.

Diese Art der Bereitstellung unserer Services, außerhalb eines Kubernetes Clusters, hat uns früher einiges an Kopfzerbrechen bereitet, heute kann ich darüber nur lächeln.

Also, Happy Coding!