Neuigkeiten von trion.
Immer gut informiert.

Canary Deployment mit traefik

traefik Proxy

In vielen Kundenprojekten ist der Wunsch zu beobachten, von klassisch betriebenen Anwendungen sofort in die Cloud oder zumindest nach Kubernetes zu migrieren. Vielleicht schwingt dabei der Wunsch mit, Zeit zu sparen, indem Zwischenschritte ausgelassen werden. Oder man ist sich sehr wohl bewußt, dass man in Gewissen Bereichen versäumt hat, Know-How aufzubauen und in Modernisierung zu investieren.

Wir empfehlen regelmäßig zumindest kleine Zwischenschritte einzuplanen, um Erfahrungen mit der Erstellung aber auch dem Design von Anwendungen für Container- und Cloudumgebungen zu sammeln. Das gilt um so mehr, wenn das Unternehmen sich nicht ganze Teams, die sich nur um Infrastruktur und Support kümmern können, leisten möchte.

Eine gute Möglichkeit zum Start stellt der Einsatz von Docker Containern ohne automatischen Orchestrator wie Kubernetes, Mesos oder Docker-Swarm dar. Dabei wählt man typischerweise eine Anwendung aus, die nicht absolut essentiell ist, und optimalerweise bereits von einem Team mit modernen Technologien und vor allem Mindset entwickelt und betreut wird.

Mit verhältnismäßig wenig Infrastruktur können dann auch bereits Patterns aus der Cloud-Welt verprobt werden und entsprechende Erfahrungen mit den notwendigen Umsystemen und Prozessen gewonnen werden. Wichtig ist dabei, dass man den Schwenk auf fertige Lösungen vornimmt, und nicht mit eigenen Mitteln Dinge nachbaut und wartet, die ein Orchestrator mitliefert.

Hat man sich für traefik als Reverseproxy und Loadbalancer entschieden, um Container verfügbar zu machen, kann man bereits von vielen Vorzügen profitieren.

Ein Kunde wünschte sich Canary-Deployments auszuprobieren, und das allein mit traefik. Wie so ein Canary oder A/B Deployment mit traefik umgesetzt werden kann, zeigt der folgende Beitrag.

Vorweg der Hinweise: Das Verfahren bezieht sich stets auf einen einzelnen Server. Natürlich kann der Server jedoch in einem Verbund betrieben werden.

traefik Proxy

Das besondere an traefik ist, dass er durch eine Verbindung zum Docker Daemon über Events benachrichtigt wird, wenn sich an den laufenden Containern etwas ändert. Darüber ist eine sehr dynamische Konfiguration möglich, die keine manuellen Eingriffe erforderlich macht. Auch über eine Konfigurationsdatei im TOML oder YAML Format kann traefik - ohne Neustart - umkonfiguriert werden.

Zunächst muss traefik selbst als container bereitgestellt werden. Dazu bietet sich docker-compose an, der folgende Ausschnitt zeigt ein traefik Deployment incl. dem traefik Dashboard auf http://traefik.localhost.

traefik mit docker-compose
version: "3.9"

networks:
  traefik:
    driver: bridge
    name: traefik

services:
  traefik:
    image: "traefik:v2.4"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--providers.file.filename=/opt/traefik/config.yaml"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./config:/opt/traefik"
    labels:
      - traefik.enable=true
      # Dashboard
      - traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
      - traefik.http.routers.traefik.service=api@internal
      - traefik.http.services.traefik.loadbalancer.server.port=8080
    networks:
      - traefik

Weiterhin ist das Verzeichnis config als Volume nach /opt/traefik eingebunden und traefik wird angewiesen zusätzlich die config.yaml als Konfigurationsquelle zu verwenden.

Das Netzwerk traefik wird unter dem Namen bereitgestellt, damit weitere Container später nachgestartet werden können, die sich in exakt dies Netzwerk integrieren. Die typische Isolation von docker-compose für Netzwerke wird daher gezielt aufgehoben.

Anwendungscontainer

Als Beispielanwendung soll hier ein nginx Webserver dienen, der lediglich eine Datei ausliefert, in der der Buchstabe A steht. Durch Label am Container erkennt traefik, dass der Container bereitgestellt weden soll, und wie er angesprochen werden soll. In diesem Fall durch den Hostnamen example.localhost.

nginx Container
app:
  image: nginx:alpine
  labels:
    - traefik.enable=true
    - traefik.http.routers.app_prod.rule=Host(`example.localhost`)
    - traefik.docker.network=traefik
  volumes:
    - ./app/a.html:/usr/share/nginx/html/index.html
  networks:
    - traefik

Canary Deployment

Unter einem Canary Deployment versteht man, dass ein bestimmter Anteil von Anfragen auf eine neue Version einer Anwendung geht. Gibt es dabei Probleme, so betrifft das nur einen geringen Anteil der Nutzer, und die Änderung kann zurückgenommen werden. Die Canary Instanz kann z.B. durch bestimmte Regeln explizit angesprochen werden, dann verwendet man einen speziellen Hostnamen, zusätzliche Header oder einen Cookie. In traefik würde man dazu die rule anpassen.

Möchte man eine bestimmte Menge von Anfragen automatisch an die Canary Anwendung delegieren, kann traefik dies über ein Gewicht beim Loadbalancing vornehmen. Da traefik jedoch nur für Server bzw. Services ein Gewicht erlaubt, macht man sich für das Canary Szenario einen Trick zu Nutze: Man definiert einen separaten Service, der dann wiederum an die Container weiterleitet.

Diese Konfiguration ist über eine Konfigurationsdatei möglich, wie im folgenden zu sehen. Dabei wird eine Verteilung von 40:10 auf die produktive und Canary Instanz definiert.

traefik Konfiguration in Datei config.yaml
http:
  routers:
    router-canary:
      service: app_weighted
      rule: "Host(`example.localhost`)"
  services:
    app_weighted:
      weighted:
        services:
          - name: app_prod@docker
            weight: 40

          - name: app_canary@docker
            weight: 10

Die einzelnen Docker Container app_prod und app_canary müssen dann über entsprechende Label für traefik definiert werden. Wichtig ist hier, dass kein Router definiert wird, sondern lediglich die Services, auf die traefik dann die Requests verteilen soll.

nginx Container als traefik Services
app:
  image: nginx:alpine
  labels:
    - traefik.enable=true
    - traefik.http.services.app_prod.loadbalancer.server.port=80
    - traefik.docker.network=traefik
  volumes:
    - ./app/a.html:/usr/share/nginx/html/index.html
  networks:
    - traefik

canary:
  image: nginx:alpine
  labels:
    - traefik.enable=true
    - traefik.http.services.app_canary.loadbalancer.server.port=80
    - traefik.docker.network=traefik
  volumes:
    - ./app/b.html:/usr/share/nginx/html/index.html
  networks:
    - traefik

Die Datei kann jederzeit angepasst werden, und traefik konfiguriert sich automatisch um. Damit kann dann auch einfach zwischen einem reinen produktiven Deployment und einem Canary Deployment umgestellt werden.

Im traefik Dashboard sieht die Konfiguration anschließend so aus:

traefik Canary Deployment

Canary Test

Das Canary Setup mit traefik soll natürlich auch getestet werden. Mit einem einfachen Shellscript können Requests generiert werden und anschließend die Anzahl der Antworten mit A und B gezählt werden.

Shell Script zur quantitativen Auswertung des Canary Deployments
#!/bin/bash

for i in {1..100}; do curl -s example.localhost:80 ; done | sort | uniq -c

Das Ergebnis ist in der folgenden Ausgabe zu sehen:

Ergebnis der Auswertung
$ ./check.sh
     80 A
     20 B

traefik Canary Deployment Fazit

Das gezeigte Setup ist gut zur Veranschaulichung geeignet und kann auch für Anwendungsfälle wie A/B-Tests erweitert werden. Für einfache Szenarien handelt es sich um eine funktionale Lösung mit wenig Komplexität und geringem Ressourcenbedarf.

Jedoch ist das ganze keinesfalls mit der Robustheit einer mit Kubernetes umgesetzten Lösung vergleichbar. Fällt hier ein Container vollständig aus, findet traefik den Service nicht mehr, und alle Requests resultieren in einem Fehler.
Dazu kommt, dass manuelle Schritte nötig sind, um die Konfigurationsdatei von traefik anzupassen. Fängt man an der Stelle an, über Automatisierung nachzudenken, ist auch der Moment erreicht, in dem man über eine umfassendere Lösung, wie Kubernetes, nachdenken sollte.




Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.