Docker Ambassador mit HAProxy und etcd

Keine Kommentare

In einem vorherigen Artikel habe ich über den allgemeinen Aufbau des Ambassador in einem gemeinsamen Projekt der LeanIX GmbH mit der codecentric AG geschrieben. In diesem Artikel werde ich weiter auf die technischen Details des Ambassador Containers eingehen.

Die Funktion des Ambassadors wird durch HAProxy realisiert. Auf Grund von Beschränkungen durch Docker und die Anforderung, die Konfiguration des HAProxy dynamisch und ohne Neustart des Containers ändern zu können, sind noch einige weitere Komponenten nötig:

supervisord

supervisord betreiben wir als root-Prozess im Ambassador Docker Container. Dies ist nötig, da der HAProxy für die Aktualisierung der Konfiguration neue Prozesse mit der neuen Konfiguration startet. Sollte der HAProxy als root-Prozess des Docker-Containers laufen, würde sich dieser beim Aktualisieren der Konfiguration beenden.

etcd

etcd ist ein verteilter Key Value Store. In unserem Fall speichern wir in etcd Parameter für die Konfiguration des HAProxy, genauer gesagt, welche Backends HAProxy für einen Dienst zur Verfügung stehen.

confd

confd dient dazu, Variablen aus einem Backend zu lesen und vorhandene Templates mit deren Werten zu füllen und ggf. Befehle auszuführen. In unserem Projekt überwacht confd bestimmte Einträge in etcd, überträgt diese in ein Template einer HAProxy-Konfigurationsdatei und gibt bei Änderungen dem HAProxy den Befehl seine Konfiguration neu zu laden.

Drei dieser vier Komponenten werden in einem Docker Container betrieben:

  • supervisord
  • confd
  • HAProxy

Diese Komponenten machen unseren Ambassador aus. Es gibt noch eine Diskussion darüber, ob wirklich mehrere Prozesse in einem Docker-Container laufen sollten. Eine Sichtweise dazu kommt aus dem Phusion-Projekt und wird in diesem Ambassador umgesetzt.

Der etcd ist nicht Teil des Ambassador Containers, sondern kann in beliebiger Form, auch als Docker-Container, an beliebiger Stelle laufen. Er muss lediglich über ein Netzwerk erreichbar sein.

Implementierung

Zuerst einmal das Grundgerüst für alles, das Dockerfile:

# Dockerfile for haproxy
# Version 1.0
FROM ubuntu:14.04
 
MAINTAINER Christian Zunker <christian.zunker@codecentric.de>
 
RUN apt-get update && apt-get install -y haproxy supervisor netcat
 
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/ambassador.conf
 
ADD confd-0.7.1-linux-amd64 /usr/local/bin/confd
RUN chmod +x /usr/local/bin/confd
ADD haproxy.toml /etc/confd/conf.d/haproxy.toml
ADD haproxy.cfg.tmpl /etc/confd/templates/haproxy.cfg.tmpl
 
CMD ["/usr/bin/supervisord"]

Im Dockerfile ist festgelegt, dass supervisord gestartet werden soll. Dieser ist aber nur ein Mittel zum Zweck. Er startet die weiteren Dienste:

[supervisord]
nodaemon=true
 
[program:confd]
command=/usr/local/bin/confd -interval %(ENV_CONFD_INTERVAL)s -node %(ENV_ETCD_SERVER)s -config-file /etc/confd/conf.d/haproxy.toml
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=18
 
[program:haproxy]
command=haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=19

Der supervisord startet unter anderem auch den confd. Dieser erhält über Umgebungsvariablen die Verbindungsdaten zum etcd und eine Konfigurationsdatei, in der sein Verhalten beschrieben wird:

[template]
src = "haproxy.cfg.tmpl"
dest = "/etc/haproxy/haproxy.cfg"
keys = [
  "/backends",
  "/vhosts"
]
reload_cmd = "haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)"

In der confd-Konfigurationsdatei wird auf das Template für die HAProxy-Konfiguration verwiesen und mit welchem Befehl der Reload des HAProxy durchzuführen ist. Die Informationen aus den Verzeichnissen /backends und /vhosts des etcd schreibt confd in die Template-Datei:

{{ $service_port := printf "%s" (getenv "SERVICE_PORT") }}
{{ $etcd_prefix := printf "%s" (getenv "ETCD_PREFIX") }}
{{ $service_type := printf "%s" (getenv "SERVICE_TYPE") }}
 
frontend {{ $service_type }}
    bind *:{{ $service_port }}
    mode tcp
    use_backend backend-{{ $service_type }} if TRUE
 
backend backend-{{ $service_type }}
    mode tcp
    balance roundrobin
    {{ $service_path := printf "%s/*" $etcd_prefix }}
    {{ range gets $service_path }}
    server {{base .Key}} {{.Value}} check
    {{ end }}

Damit man eine bessere Vorstellung davon bekommt, welche Daten confd in das Template schreibt, hier ein möglicher Aufbau der Daten in etcd:

/vhosts/leanix.acme.com/services/export/server1: 192.168.110.11:9100
/backends/solr/solr1: 192.168.110.11:8983
/backends/db/db1: 192.168.110.11:3306
/backends/mail/mail1: 192.168.110.11:25

Die Daten im etcd können während der Laufzeit geändert werden. confd prüft in regelmäßigen Abständen, ob es Änderungen gegeben hat. Ist das der Fall, passt confd die Konfiguration des HAProxy an und lässt diesen damit neu starten. So können im laufenden Betrieb neue Backends hinzugenommen oder auch wieder entfernt werden. Für den Docker-Container, der das Backend eigentlich nutzen möchte, sind die Änderungen transparent, da er seine Anfragen nur an den Ambassador stellt.

  • Seite
  • 1
  • 2
Christian Zunker

Christian Zunker arbeitet als System Engineer im Bereich Hosting der codecentric AG. Dort beschäftigt er sich mit der Entwicklung und dem Betrieb von verteilten Systemen.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.