Container absichern Teil 2 Docker Schwarm

Container absichern Teil 2 Docker Schwarm

English version: Click!

Ich mach einfach da weiter, wo ich vorher aufgehört habe. Dieses Mal also erstelle ich den Schwarm und richte die Überwachung ein: Container absichern Teil 2 Docker Schwarm.

Was ist ein Docker Schwarm?

Falls ihr nicht genau wisst, was ein Schwarm ist – es ist im Prinzip ein Cluster von vorher individuellen Docker Hosts.
Die erstellen was sich ein “Routing Mesh” nennt und anstatt von Containern managed man nun Services.
Aber es ist eigentlich das gleiche Konzept, mit dem Unterschied, dass ich beim Deployment angeben kann, wie viele Instanzen ich von einem Container haben möchte und wo sie hin sollen.
Der Schwarm übernimmt das Erstellen von neuen Instanzen im Fall das ein Host nicht mehr antwortet.

Seit einer Weile ist der Schwarm nun im Basispaket bei Docker integriert, vorher war es ein externes Aden.

Ein Schwarm gibt dir nicht die Flexibilität, Skalierung und vor allem Orchestrierung von Kubernetes, aber auf der anderen Seite erfordert ein Schwarm keine 100 Semester Studienzeit oder das Blut deines Erstgeborenen.

Idealerweise, oder sagen wir im Produktivbetrieb, würde man irgendwo zwischen drei und sieben Manager Knoten einsetzen und multiple Worker Knoten im Cluster. Ich habe gerade nur drei Manager und zwei Worker, und es ist möglich jeder Zeit nach oben oder unten zu skalieren.
Es ist ganz einfach, man fügt Knoten hinzu und kann die nach Belieben befördern oder herunterstufen.

Übrigens, ein Manager ist gleichzeitig auch immer ein Worker. Im Produktivbetrieb würde man da sicherlich exakt die Trennung durchführen und sicherstellen, dass auf Managern keine Services laufen. 
Dazu gibt es das simple “drain” Kommando.

Deployment.

Auf meinem Manager swarm1 starte ich das folgende Kommando:

docker swarm init --advertise-addr 10.0.10.31

Hier ist das Resultat mit gleich mit dem nächsten Schritt:

Ich starte das docker swam join Kommando auf allen Knoten und befördere swarm2 und 3 zu Managern:

docker node promote swarm2
docker node promote swarm3

Ein Test:

Ein Befehl wie docker system info würde noch mehr anzeigen, aber die Zeilen hier reicht zur Bestätigung aus.

Das war’s die Arbeit ist erledigt, wir können nach Hause gehen.
Ist es noch zu früh für ein Bier? Ja, leider, weil ich noch ein paar Dinge erledigen möchte.

Auf zu Orion!

Die Orion Platform erlaubt das Überwachen von Containern seit drei oder vier Jahren und die Dokumentation findet sich hier, aber ich folge einem etwas anderen Ansatz.

Zuerst lege ich alle fünf Knoten im System an und nutze dazu sowohl SNMP als auch den Linux Agent:

Sobald die sich gemeldet haben gehe ich in die Einstellungen, und unter Node & Group Management findet man Manage Container Services.

Im Popup erstellen wir einen kreativen Namen, und wählen Docker Swarm als Umgebung, sowie einen Manager:

Manage Container Service

Nun erstellen wir Credentials – das ist ein Feature das erst im späten 2020 dazu gekommen ist.
Aber es ist ganz einfach, wir benötigen nur einen Namen und drücken auf Generate.

Achtung: Der Name ist unerheblich, aber kopiert euch auf jeden Fall das Token irgendwo hin. Macht das sofort, bevor irgendwo anders hingeblickt wird. Vertraut mir!

Auf “Create Service” klicken erstellt ein paar Befehle, die man auf dem Manager laufen lassen muss. Schauen wir sie uns einmal an.

Der erste zieht ein Image aus der lokalen Orion Plattform. 
Was ich hier interessant finde ist, dass die IP meine aktuelle Orion Maschine ist. In einem meiner Tests habe ich auf die HA VIP gesetzt nur um zu schauen, was passiert, und kann bestätigen, dass es ebenfalls funktioniert. Hallo QA; ihr könnt mir für das Testen dieses Szenarios danken!

curl -o cman-swarm.yaml --insecure --pinnedpubkey sha256//5VZMA0yX8N7itY+J7eEn/0xOWheabqfAW0i/qVtqH6o= https://10.0.20.22:38012/orion/container-management/monitoring/deploymentfile?guid=48f90bbb-6644-4ab2-9b58-28b78b60b9ee

Der zweite Befehl erstellt die tatsächliche Deployment-Datei:

sed -i "s/%HOSTNAME%/$(hostname)/g" cman-swarm.yaml

Es ist ein Deployment-Stack, und eine ganz einfache YAML-Datei die man sich anschauen kann, aber es erledigt den Job von selbst.
Der dritte Schritt ist wichtig, weil hier nach dem Token gefragt wird, das ihr euch hoffentlich kopiert habt:

SCOPE_PASSWORD=$(head -c 32 /dev/urandom | base64 -w 0) && read -sp 'Please enter SolarWinds Token: ' SOLARWINDS_TOKEN && echo -n $SOLARWINDS_TOKEN | base64 -w 0 > ./solarwinds_token_secret && echo -n $SCOPE_PASSWORD >> ./scope_password_secret && echo -n 'BASIC_AUTH_PASSWORD='$SCOPE_PASSWORD >> ./.env_scope && unset SCOPE_PASSWORD && unset SOLARWINDS_TOKEN && echo

In meinem ersten Versuch hatte ich es nicht gespeichert, und ich steckte hier fest und musste den Prozess neu einleiten.
Nun aber klappt es:

sudo docker stack deploy -c cman-swarm.yaml sw

Schliesslich noch ein wenig aufräumen. Erinnert ihr euch noch an IT-OCD?

rm solarwinds_token_secret scope_password_secret .env_scope

Nachdem alles erledigt ist, müssen wir es bestätigen:

Nun warten wir einfach bis die Container hochkommen und sich verbinden.
Das kann eine Weile dauern, also ist jetzt vielleicht Zeit für ein Bier? Meh, die Uhr sagt nein, also überprüfen wir den Fortschritt nach einer Tasse Kaffee:

SolarWinds und Weaveworks sind unsere neuen Container. Aber denkt daran, beim Schwarm haben wir Services, also schauen wir hier auch kurz nach:

Falls irgendetwas nicht funktioniert und man neu deployen muss, finden sich die Schritte dazu hier.
Was die Dokumentation einem nicht sagt ist, dass das Token immer noch aktiv ist und, je nach Szenario, manuell vorab entfernt werden muss. 
Das geht so:

Oh und übrigens, falls man das Token innerhalb der Orion Plattform neu erstellen muss, kann man die alten Credentials unter Settings/API Credentials löschen.

Aber bei mir ist alles schön grün:

Ich sehe auch gut Verkehr auf den vDS meiner ESXi:

Ich habe mir den Verkehr genauer angesehen und es ist hauptsächlich Ceph. Sogar eine ganze Menge.
Aber das ist ja auch klar bei einem verteilten Dateisystem.

Und, das schönste, die Orion Karte:

Jaja, ist rot, aber nur weil swam1 überdramatisch ist und sich Datastore 11 auffüllt. Ich weiss Bescheid, danke.

So weit, so gut. Nun etwas mehr Spannung!

Um mehrere Container/Services einfach zu adressieren, benötigen wir einen Reverse-Proxy.
Da gibt es einige sehr populäre wie Nginx, haproxy usw., aber Traefik ist hauptsächlich für Containerumgebungen gedacht und funktioniert dort wunderbar.

Leide jedoch, abhängig von den gewünschten Features, kann das Deployment etwas komplex sein und kann jegliche Medikation gegen zu niedrigen Blutdruck ersetzen.
Da dies hier mehr ein proof of concept ist und ganz sicher kein Produktiveinsatz, zeige ich einen der simpelsten Wege.

Damit ein Reverse-Proxy wie gewünscht funktioniert, muss man vorab für jeden Container einen DNS-Eintrag erstellen, der auf den Host verweist.

In meinem Beispiel habe ich traefik.sub.domain.tld erstellt mit der gleichen IP wie swarm1.
Wahrscheinlich erkennt ihr gerade ein Problem mit meinem Deployment, aber darum kümmern wir uns später.

Auf swarm1 erstelle ich das folgende Verzeichnis das Dank Ceph repliziert wird:

mkdir -p /var/data/containers/traefik

Ich betreibe Traefik “flach”, in nur einem Ordner. Für meine Konfig ist das egal, aber ich denke nicht, dass das Best Practice im Produktivbetrieb wäre.
Meine Konfig ist sehr einfach gehalten und ignoriert Features wie die automatische Zertifikats-Erstellung und http zu https Umleitungen.

Das kann man immer noch später hinzufügen. Wie beim Kochen – das Salz kommt zuletzt!
Aber Moment, bevor wir irgendwas anstellen, brauchen wir zuerst ein overlay Netzwerk in Docker: 

docker network create --driver=overlay proxy

Jetzt ein gutes, altes Docker compose:

version: '3.3'

services:
  traefik:
    image: traefik:latest
    command:
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.swarmModeRefreshSeconds=30"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=proxy"
      - "--entrypoints.web.address=:80"
      - "--api.dashboard=true"
      - "--api.insecure=true"
    ports:
      - 80:80
        - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
      restart_policy:
        condition: on-failure


networks:
  proxy:
    external: true		
				

Was geht ab?

Eigentlich ganz einfach: Die Datei schnappt sich die aktuelle Version, im Moment 2.4.13. Ich nutze den CLI Ansatz und sende einfache Befehle. Es gibt noch zwei andere Strategien (yaml und toml) aber für mich geht das hier gerade am schnellsten.
Schaut auf “swarmMode=true” und das automatisierte Deployment auf jedem Manager mittels constraints.

Seit docker-compose v3 sind alle Befehle kompatible mit dem Schwarm-Modus, was sehr Angenehmen ist, da man so einfach von einem einzelnen, isolierten Host upgraden kann.

Zum Thema Deployment:

docker stack deploy traefik -c docker-compose.yml

Wie immer, verifizieren:

Sieht gut aus. Das Dashboard ist auch schon da und stehst unter http://fqdn:8080/dashboard.
Na, erinnert ihr euch, warum ich bei Ceph den Port geändert habe?

Noch gibt es nicht viel zu sehen, also setzen wir noch einen anderen Service ein.

Portainer hinzufügen.

Portainer ist ein Management Container der mit sowohl Docker als auch Kubernetes umgehen kann. Das Ding ist recht bekannt und braucht sicher keine große Einführung.

Das Deployment ist super einfach, der Hersteller stellt eine nahezu schlüsselfertige Datei hier zur Verfügung.
Ein klein wenig Anpassung ist doch von Nöten:

version: '3.2'

services:
  agent:
    image: portainer/agent:latest
    environment:
      AGENT_CLUSTER_ADDR: tasks.agent
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/lib/docker/volumes:/var/lib/docker/volumes
    networks:
      - agent_network
    deploy:
      mode: global
      placement:
        constraints: [node.platform.os == linux]

  portainer:
    image: portainer/portainer-ce:latest
    command: -H tcp://tasks.agent:9001 –tlsskipverify
    ports:
      - "9000:9000"
      - "8000:8000"
    volumes:
      - portainer_data:/data
    networks:
      - agent_network
      - proxy
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.portainer.rule=Host(`portainer.sub.domain.tld`)"
        - "traefik.http.routers.portainer.entrypoints=web"
        - "traefik.docker.network=proxy"
        - "traefik.http.services.portainer.loadbalancer.server.port=9000"
        - "traefik.http.routers.portainer.service=portainer"

networks:
  agent_network:
    driver: overlay
    attachable: true
  proxy:
    external: true

volumes:
  portainer_data:

Im Gegensatz zu Traefik ist das hier ein echtes Stack, da zwei Services eingesetzt werden; Agent und Manager.

Schaut auf den Deploy Mode und die Constraints für beide Services, und das Konzept beginnt Sinn zu ergeben.

Hier sind zwei Netze involviert, das erste erstellt sich selbst und dient nur der Portainer Kommunikation wie bei vielen anderen Containern. Das zweite ist das Overlay, welches ich vorher erstellt habe.
Da sind zwei wichtige Informationen drin, hier ist eine:

traefik.http.services.portainer.loadbalancer.server.port=9000

Üblicherweise versucht Traefik, den Datenverkehr von Containern automatisch zu erkennen, aber das funktioniert nicht im Schwarmmodus.
Stattdessen müssen wir ein Label für jeden Container hinzufügen.
Da das Portainer Dashboard an 9000 sitzt, ist das unser Port hier.

Das andere Element ist das Netzwerk Label:

traefik.docker.network=proxy

Ohne dieses würden wir nur eine “bad gateway” msg bekommen da Traefik nicht wüsste, was mit dem Portainer Verkehr zu tun ist.
Zeit zum Deployment:

docker stack deploy portainer -c portainer-agent-stack.yml

Gut:

Eine Sache ist mir aufgefallen, während Portainer richtig flott läuft auf einem einzelnen Host, ist es bei einem Schwarm schon etwas behäbiger, zumindest in meiner Umgebung.

Es dauert eine Weile bis alle Informationen von allen Agents angekommen sind, also springt noch nicht ins Dashboard.

Vielleicht schauen wir vorher kurz bei Traefik rein:

Schon besser. Und etwas später zeigt sich auch Portainer wieder von seiner besten Seite:

Lasst euch nicht verwirren wenn der Schwarm als Down angezeigt wird oder nichts passiert, wenn man irgendwo klickt. Gibt dem Dashboard in dem Fall einfach noch ein paar Minuten.

Sobald die Datensammlung abgeschlossen ist, ist es recht beeindruckend, Container so einfach per GUI zu skalieren:

Okay, das schliesst Teil 2 ab. Was bleibt noch zu tun?

Es gibt immer noch Bedenken mit der Hochverfügbarkeit. Sicher, der Schwarm kümmert sich um die Replikation von Services falls etwas passieren sollte, aber was ist wenn der Manager sich verabschiedet und nicht länger adressiert werden kann?

Dazu kommt es in Teil drei!

More homelab posts:

1 2 3 4

Leave a Comment

Your email address will not be published. Required fields are marked *