HTTP Tests im Cluster

Jeder kennt es, es ist ein neuer Cluster aufgesetzt und ggf. sind restriktive Firewall Regeln ein möglicher Grund für ein Delpoyment. Oder es wird anderweitig mal auf die Schnelle ein WebContent benötigt. Z.B. eine WebComponent die integriert werden soll.

Natürlich kann man schnell einen NginX spawnen, aber mir ist das zu schwergewichtig. Daher möchte ich euch eine leichtgewichtige Methode zeigen. In der man unter einer Minute Content aus dem aktuellen Verzeichnis ausliefern kann.

k8sdebug

Ich hatte ja bereit k8sdebug vorgestellt. Ein Helferlein, um schnell einen Container zu spawnen. Wir nutzen hier in dem Beitrag k8sdebug, um Alpine Linux Shell zu öffnen.

Python3 to the rescue

Ok, dann legen wir mal los. Zunächst erstellen wir einen Namespace, damit wir ggf. anderen Anwendungen nicht stören.

k create namespace www

Starten wir nun die Shell in Alpine:

k8sdebug alpine www

Jetzt brauchen wir Python3. Das ist schnell installiert.

/ # apk add python3
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/10) Installing libbz2 (1.0.8-r1)
(2/10) Installing expat (2.2.10-r1)
(3/10) Installing libffi (3.3-r2)
(4/10) Installing gdbm (1.19-r0)
(5/10) Installing xz-libs (5.2.5-r0)
(6/10) Installing ncurses-terminfo-base (6.2_p20210109-r0)
(7/10) Installing ncurses-libs (6.2_p20210109-r0)
(8/10) Installing readline (8.1.0-r0)
(9/10) Installing sqlite-libs (3.34.1-r0)
(10/10) Installing python3 (3.8.10-r0)

Erstellen wir unsere Demo Content der ausgeliefert werden soll. Es ist kein echtes HTML, aber das spielt hier auch keine Rolle.

echo "Hello World!!!" > index.html

Jetzt haben wir auch schon alle Zutaten die wir benötigen. Starten wir nun den WebServer

python3 -m http.server

Das war es nun kann das Verzeichnis in bzw. die index.html auf dem Port 8000 des POD aufgerufen werden

Alternative kann auch mit Port-Forwarding der Inhalt von außen im Browser betrachtet werden.

k portforward -n www debug 8000

Im Browser wird nun der Inhalt des Demo Content angezeigt.

Hinweis: Da die Datei index.html heißt wird diese auch bei / ausgelifert. Möchte man im Dateisystem Browsen, dann muss man die Datei einfach umbennen, dann kann man im Browser das Verzeichnis durchsuchen und sich die Dateien anzeigen lassen.

kubectl cp

Einzelne Dateien kann man bekannterweise mit kubectl cp in einen POD kopieren. Das wird umständlich, wenn man mehrere Dateien bzw. den Inhalt eines Verzeichnis kopieren möchte. Dieser Artikel soll eine Möglichkeit zeigen, die sehr schlank und ohne Abhängigkeiten auskommt. KSync verfolgt hier einen anderen Ansatz und verwendet unter der Haube Syncthing, um die Verzeichnisse bidirektional zu synchronisieren.

Beispiel Liferay

Die Portalsoftware Liferay basiert auf OSGi Technologie und bietet mit der Autodeployfunktion ein HotReload von Bundles an. Die Entwicklung der Bundles findet lokal statt und soll im Cluster getestet werden. In diesem Beispiel verwende ich einen lokalen K3D Cluster, um das Demo zu zeigen.

In dem Beispiel betreiben wir Liferay in einen Pod im Kubernetes Cluster und möchten mehrere Bundle Dateien aus einem Verzeichnis in den Containern in das Autodeploy Verzeichnis kopieren.

Dazu kann folgendes Skript als Grundlage dienen, um Dateien aus einem Verzeichnis zu kopieren.

#!/bin/bash

# Destination
NAMESPACE=liferay
LIFERAY_HOME=/opt/liferay
LIFERAY_DEPLOY=$LIFERAY_HOME/deploy

# Source
SRC=/dest

# get pod name
POD=`kubectl get pod -n $NAMESPACE -l app=liferay --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'`

# copy files
tar -cvz -C $SRC . | kubectl exec -ti -n $NAMESPACE $POD -- sh -c "cd $LIFERAY_DEPLOY && tar -xzv"

Zunächst wird der POD Name ermittelt. Der Liferay POD ist mit app=liferay gelabelt, sodass der POD Name einfach über den Selektor zu ermitteln ist.

Dann wird das SRC mit allen Dateien als tar.gz gepackt und dann per sh Kommando wieder im Container entpackt.

Cilium

Mit Cilium steht Kubernetes ein mit von Google vorangetriebenes CNI zur Verfügung. Das Projekt Cilium nutzt eBPF Filter im Linux Kernel, um Kubernetes Network Policies effizient und performant umzusetzen. Durch die Verwendung von eBPF ergibt sich ein weiterer Vorteil. Es lassen sich alle Verbindungungen im Cluster tracken. Dieses kann mit Hubble UI im Browser visualisiert werden. Dadurch kann man schnell einen Überblick über die bestehenden Verbindungen erlangen und ggf. eingreifen, wenn unerwünschter Traffic erkannt wird.

Hubble

Für die Visualisierung der Verbidnungen im Browser kommt Hubble UI zu Einsatz. Zunächst muss man den gewünschten Namespace wählen und schon wird ein Graph gezeichnet, der die Verbindungen im Browser anzeigt. Es wird ein gerichteter Graph mit den Abhängigkeiten der Pods erzeugt und auch eine Tabellarische übersicht geboten. Mit klick auf einen Pod kann eine Filterung gesetzt werden. So lässt sich schnell der angezeigte Traffic filtern und analysieren.

Setup K3D Cluster

Für das Beispiel verwende ich K3D, um einen Demo Cluster zu erstellen. Dieses geht wie immer schnell von der Hand.

Konfiguration

Zunächst definieren wir den Clusternamen, die Anzahl der Worker und den API Port als Environment Variablen, um sie später zu nutzen:

export CLUSTERNAME=cilium-demo
export CLUSTERWORKER=2
export CLUSTER_API_PORT=6443

Cluster anlegen

Nun können wir nach der Konfiguration den Cluster anlegen.

k3d cluster create $CLUSTERNAME \
-a $CLUSTERWORKER \
--api-port=$CLUSTER_API_PORT \
--k3s-server-arg "--disable=servicelb" \
--k3s-server-arg "--disable=traefik"  \
--no-lb \
--k3s-server-arg "--disable-network-policy" \
--k3s-server-arg "--flannel-backend=none" \
--image rancher/k3s:v1.20.5-alpha1-k3s2

Hinweis: Um Cilium zu nutzen müssen der LoadBalancer und Traefik nicht disabled werden. Die Komponenten habe ich nur aus Performancegründen ausgeschaltet, damit der Cluster schneller hochfährt. Sie sind für die Demo nicht relevant. Wichtig ist, dass mit –flannel-backend=none kein CNI Plugin automatisch installiert und das über –disable-network-policy der Defautl Network Policy Controller nicht geladen wird.

Korrektur der Initialisierung von Cilium

Da K3S keine Bash unter /bin/bash vorhält, funktioniert die Initialisierung von Cilium nicht korrekt. Daher muss hier manuell eingegriffen werden, um BPF Filter zu kompilieren.

for AGENT in $(seq 0 ${CLUSTERWORKER+1} 1)
do
  docker exec -it k3d-$CLUSTERNAME-agent-$AGENT mount bpffs /sys/fs/bpf -t bpf
  docker exec -it k3d-$CLUSTERNAME-agent-$AGENT mount --make-shared /sys/fs/bpf
done

docker exec -it k3d-$CLUSTERNAME-server-0 mount bpffs /sys/fs/bpf -t bpf
docker exec -it k3d-$CLUSTERNAME-server-0 mount --make-shared /sys/fs/bpf

Die shared mount müssen auf allen Nodes (agents) und Server ausgeführt werden. Dazu muss ggf. der Server Block angepasst werden. Je nach dem wieviele Agents und Server ihr beim Erstellen des Clusters angegeben habt.

Cilium und Hubble installieren

Die Installation von Cilium und Hubble ist eigentlich trivial.

helm repo add cilium https://helm.cilium.io/

helm install cilium cilium/cilium --version 1.9.5 \
   --namespace kube-system \
   --set kubeProxyReplacement=disabled \
   --set debug.enabled=true \
   --set hostServices.enabled=false \
   --set externalIPs.enabled=true \
   --set nodePort.enabled=true \
   --set hostPort.enabled=true \
   --set bpf.masquerade=false \
   --set image.pullPolicy=IfNotPresent \
   --set ipam.mode=kubernetes

helm upgrade cilium cilium/cilium --version 1.9.5 \
   --namespace kube-system \
   --reuse-values \
   --set hubble.listenAddress=":4244" \
   --set hubble.relay.enabled=true \
   --set hubble.ui.enabled=true

Nun watch warten bis alle Pods up and running sind

watch kubectl get pods -n kube-system

Hinweise zum Parameter KubeProxyReplacement

In den Blogeinträgen die man zum Thema Cilium und K3D im Internewt findet beschreiben immer das der Parameter kubeProxyReplacement auf partial stehen soll. Damit lies sich der Cluster aber nicht zum Laufen bewegen. Nicht einmal CoreDNS konnte mit der Einstellung deployt werden. Mit der Einstellung –set kubeProxyReplacement=disabled hingegen fährt der Cluster hoch und alles funtkioniert.

Port-Forwarding

Um Hubble im Browser zu öffnen muss noch ein Port-Forwarding eingerichtet werden. Dann kann unter http://localhost:12000/kube-system aufgerufen werden.

kubectl port-forward -n kube-system svc/hubble-ui --address 0.0.0.0 --address :: 12000:80    

Demo Applications installieren

Das Projekt Cilium bietet eine Demoanwendung, mit der man schnell erste Tests durchführen kann. Die Anwendungen sind einfach deployt…

# create namespace
kubectl create namespace cilium-test

# deploy demo apps
kubectl apply -n cilium-test -f https://raw.githubusercontent.com/cilium/cilium/v1.9/examples/kubernetes/connectivity-check/connectivity-check.yaml

Jetzt kann man in der UI zuschauen, wie sich der Traffic ändert und die verschiedenen Verbindungen in der UI analysieren.

Network Policies überprüfen

Wenn man eine neue Regel für Kubernetes Network Policies erstellt hat, dann taucht bestimmt die Frage der Wirksamkeit auf. Auch dieses lässt sich schön über die Oberfläche der Hubble UI darstellen. Dazu muss man oben in der Toolbar

die Auswahl auf Dropped ändern.

Pull through Cache

In diesem Artikel zeige ich wie man einfach einen Pull through Cache für K3D nutzt, um auch beim Löschen eines Clusters vom Caching von größen Images zu profitieren.

K3D

Ein Kubernetes Cluster der mit K3D/K3S aufgebaut ist, der hat ein integriertes Caching für Docker Images die aus einer Registry (docker.io oder quay.io) geladen werden. Solange also der Cluster steht und nicht mit delete gelöscht wird, hält K3D die Images innerhalb der Docker Container weiter vor. Dadurch wird der Netzwerkverkehr gesenkt und auch das erneute Deployen von Anwendungen erfolgt deutlich schneller.

K3D Registries

Seit kurzer Zeit besteht eine komfortable Möglichkeit eine Registry in K3D zu registrieren. Dazu muss man nur eine Registry Konfigurationsdatei angeben. In dieser werden die Parameter für die Konfiguration der Registry übergeben. Der Parameter –registry-config spezifiziert die Konfigurationsdatei.

Der Default registriert K3D den Hostname host.k3d.internal automatisch mit der IP des Hosts, sodass aus dem Cluster dieser hierüber angesprochen werden kann. In dem Beispiel möchte ich die docker.io und quay.io cachen. Trage wir dazu 2 Registries als Mirror in die Konfiguration ein:

mirrors:
  docker.io:
    endpoint:
      - "http://host.k3d.internal:5001"
      - "https://docker.io"
  quay.io:
    endpoint:
      - "http://host.k3d.internal:5002"
      - "https://quay.io"

Wir werden auf den lokalen Ports 5001 und 5002 des Hosts jeweils einen Pull through Cache installieren, sodass diese als Registries innerhalb des Clusters verwendet werden.

Docker Registry V2 und UI

Kommen wir nun zur Installation des Caches. Docker bietet mit dem Registry v2 Image selber eine Lösung hierfür an. Wichtig ist hierbei die Environment Variable REGISTRY_PROXY_REMOTEURL. Über diese kann eine Registry die angegeben werden, diese wird dann geproxied und das ist genau das wir für den Anwendungsfall benötigen.

Als UI kommt die https://github.com/Joxit/docker-registry-ui zum Einsatz. Diese bietet eine einfache UI, um den Inhalt der Registry anzuzeigen. Was in unserem Fall aber vollkommen ausreichend ist. Die UI wird über die Ports 6001 und 6002 analog zu den Registries bereitgestellt. Somit ergibt sich das folgende Dockerfile:

version: '2.0'
services:

#
# docker.io
#
  registry_1:
    image: registry:2.6.2
    ports:
      - 5001:5000
    volumes:
      - ./registry-data_1:/var/lib/registry
    networks:
      - registry-ui-net
    environment:
      REGISTRY_PROXY_REMOTEURL: https://registry-1.docker.io

  ui_1:
    image: joxit/docker-registry-ui:static
    ports:
      - 6001:80
    environment:
      - REGISTRY_TITLE=My Private Docker docker.io Registry
      - REGISTRY_URL=http://registry_1:5000
    depends_on:
      - registry_1
    networks:
      - registry-ui-net

#
# quay.io
#
  registry_2:
    image: registry:2.6.2
    ports:
      - 5002:5000
    volumes:
      - ./registry-data_2:/var/lib/registry
    networks:
      - registry-ui-net
    environment:
      REGISTRY_PROXY_REMOTEURL: https://quay.io

  ui_2:
    image: joxit/docker-registry-ui:static
    ports:
      - 6002:80
    environment:
      - REGISTRY_TITLE=My Private Docker Quay.io Registry Cache
      - REGISTRY_URL=http://registry_2:5000
    depends_on:
      - registry_2
    networks:
      - registry-ui-net

networks:
  registry-ui-net:

Registry und UI starten

Der Start ist unspektakulär. Es genügt

docker-compose up -d

um die Container zu starten. Nun steht K3D der Pull through Cache zur Verfügung.

Ein Git Repository in einen Kubernetes Container mounten

Es gibt in Kubernetes https://kubernetes.io/docs/concepts/storage/volumes/#gitrepo um ein Git Repository einzufügen. Diese Storage Art ist aber bereits als deprecated markiert und wird nicht mehr empfohlen.

Git Repos lassen sich dennoch recht einfach als Volume in einen Container einbinden. Man muss dafür einen InitContainer verwenden, der das Repository auscheckt und in einem Verzeichnir bereitstellt. Diese mounted man per emptyDir{} in den Container. Nun stehen die zuvor ausgecheckten Dateien aus dem Repository im Container bereit.

Beispiel

# Beispiel wie man InitContainers für die Bereitstellung
# eines GIT Repository nutzt. Die GitRepo Volumes sind
# deprecated und sollten nicht mehr verwendet werden. Dieses
# Beispiel nutzt den Ansatz das Git klonen in einem
# InitContainer auszuführen.
apiVersion: v1
kind: Pod
metadata:
  name: git-repository-demo
  annotations:
    seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
spec:
  initContainers:
    # Dieser InitContainer klont das gewünschte Repository
    # in das EmptyDir Volume Verzeichnis
    - name: git-clone
      image: alpine/git # Alpine Linux mit Git
      args:
        - clone
        - --single-branch
        - --
        - https://github.com/test/testrepo # Das Repository
        - /repo # Put it in the volume
      securityContext:
        runAsUser: 1 # Hier ggf. Anpassungen vornehmen
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
      volumeMounts:
        - name: git-repo
          mountPath: /repo
  containers:
    # Hier muss das richtige Image angegeben werden
    # das genutzt werden soll. Bsp. BusyBox
    - name:  busybox
      image: busybox
      args: ['tail','-f','/dev/null'] # Demo tue nichts
      volumeMounts:
        - name: git-repo
          mountPath: /repo
  volumes:
    - name: git-repo
      emptyDir: {}

ttyd

Das Projekt https://github.com/tsl0922/ttyd erlaubt den Zugriff auf ein Linux über einen Web Browser. In diesem Beispiel verwende ich die Alpine Version des Images von Docker Hub. Es ist mit rund 4MB sehr klein und ist daher sehr schnell erstellt.

Cluster erstellen

Für das Demo nutze ich K3D. Der Cluster ist mit

k3d cluster create demo -p80:80@loadbalancer

auch schon fertig.

Installation ttyd

Mit kubectl ist ttyd per run zu starten. Per default lauscht ttyd auf dem Port 7681. Das heißt damit man auf die WebShell zugreifen kann muss der Port Forward mit ausgeführt werden.

kubectl run --image=tsl0922/ttyd:alpine --port=7681 ttyd
kubectl port-forward ttyd 7681:7681 --address=0.0.0.0&

Midnight Commander

Als Beispiel für die volle funtkionstüchtigkeit habe ich per apk add mc den MC installiert.

K3D Traefik

Wenn man mit K3D einen neuen Cluster anlegt, dann wird Treafik V1 per Default über Helm anstatt Nginx ausgerollt. Das ist oft nicht von Relevanz, aber einige Anwendung setzen für den Betrieb den NginX als Ingress Controler voraus.

K3D ohne Traefik

Man kann K3S –k3s-server-arg ‘–no-deploy=traefik’ nun K3S veranlassen Traefik nicht auszurollen, um NginX als Ingress Controller zu installieren.

k3d create cluster demo --k3s-server-arg '--no-deploy=traefik' 

Bug in K3D

Es befindet sich in Bug in K3D, sodass der Parameter –no-deploy=traefik an K3S nicht korrekt übergeben wird.

Fehler wurde behoben mit K3D >=4.1.1 siehe https://github.com/rancher/k3d/issues/482

Installation NginX Ingress Controller

K3S schaut beim Start in dem Verzeichnis /var/lib/rancher/k3s/server/manifests/ nach Manifesten. Man kann also hier das CRD für NginX ablegen und das führt. Mehr dazu unter https://rancher.com/docs/k3s/latest/en/helm/.

apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: ingress-controller-nginx
  namespace: kube-system
spec:
  repo: https://kubernetes.github.io/ingress-nginx
  chart: ingress-nginx
  version: 3.7.1
  targetNamespace: kube-system

Volume mappen

Jetzt muss man beim Start von dem K3D Cluster den Parameter volume übergeben, sodass die Datei helm-ingress-nginx.yaml in den Container in den Pfad gemappt wird.

volume "$(pwd)/helm-ingress-nginx.yaml:/var/lib/rancher/k3s/server/manifests/helm-ingress-nginx.yaml"

Damit wird beim Start des Clusters nun NginX als Ingress Controller automatisch deployt.

Kubernetes Fehlersuche

Die Fehlersuche in Kubernetes ist manchmal aufgrund der restriktiven Container Images nicht ganz trivial, da wichtige Tools für das Debugging fehlen.

Am einfachsten ist es einen weiteren POD zu Starten, um von dort die Fehlersuche durchzuführen.

Kubernetes “run” – to the Rescue!

Kubernetes besitzt mit run wie bei Docker auch einen Container zu starten. Dieses nutzen wir nun, um einen weiteren temporären Container zu starten. Sehr leichtgewichtige Images sind Busybox und Alpine, was ein schnelles hochfahren ermöglicht.

Bash Funktion

# open debug shell
k8sdebug() {
  case "$#" in
  "2")
    kubectl run -i --tty --rm debug --image=$1 -n $2 --restart=Never -- sh
    ;;
  "1")
    kubectl run -i --tty --rm debug --image=busybox -n $1 --restart=Never -- sh
    ;;
  "0")
    echo "Synopsis: k8sdebug [imagename] namespace"
    echo "if no image is specified busybox is used"
    echo "e.g.: k8sdebug alpine liferay-dev to start an alpine linux in namespace liferay-dev"
  esac
}

Beispiele

Hier noch ein paar Anregungen für den Einsatz der Debug-Shell.

Wget

Wer nur wget benötigt, der kann auf Busybox Image starten. Es muss nur der Namespace angegeben werden.

[sascha@vbox ~]$ k8sdebug liferay
If you don't see a command prompt, try pressing enter.
/ # wget
BusyBox v1.32.1 (2021-01-12 00:38:40 UTC) multi-call binary.

Usage: wget [-c|--continue] [--spider] [-q|--quiet] [-O|--output-document FILE]
        [-o|--output-file FILE] [--header 'header: value'] [-Y|--proxy on/off]
        [--no-check-certificate]
        [-P DIR] [-S|--server-response] [-U|--user-agent AGENT] [-T SEC] URL...

Retrieve files via HTTP or FTP

        --spider        Only check URL existence: $? is 0 if exists
        --no-check-certificate  Don't validate the server's certificate
        -c              Continue retrieval of aborted transfer
        -q              Quiet
        -P DIR          Save to DIR (default .)
        -S              Show server response
        -T SEC          Network read timeout is SEC seconds
        -O FILE         Save to FILE ('-' for stdout)
        -o FILE         Log messages to FILE
        -U STR          Use STR for User-Agent header
        -Y on/off       Use proxy
/ #

Curl

Möchte man lieber ein echtes Curl verwenden, dann ist alpine linux die beste Wahl.

[sascha@vbox ~]$ k8sdebug alpine liferay
If you don't see a command prompt, try pressing enter.
/ # apk add curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20191127-r5)
(2/5) Installing brotli-libs (1.0.9-r3)
(3/5) Installing nghttp2-libs (1.42.0-r1)
(4/5) Installing libcurl (7.74.0-r0)
(5/5) Installing curl (7.74.0-r0)
Executing busybox-1.32.1-r0.trigger
Executing ca-certificates-20191127-r5.trigger
OK: 8 MiB in 19 packages
/ # curl svc-or-ip

NC – NetCat

NC ist bestandteil des busybox Images. Hier abruf einer Seite von Liferay auf dem Port 8080.

[sascha@vbox ~]$ k8sdebug liferay
If you don't see a command prompt, try pressing enter.
/ # nc liferay-cluster:8080
GET /index.html

K3D

In der aktuellen Version von K3D wird in K3S CoreDNS für die Namesauflösung verwendet. Leider ist der Upstream Nameserver auf 8.8.8.8 voreingestellt, sodass es aus den Containern zu Problemen kommen kann, wenn man einen eigenen DNS hat und den für die Namensauflösung der Ingresses in dem Cluster sorgt.

So kann ein Ingress aus einem anderen POD heraus nicht über die Ingress URL aufgerufen werden.

Konfiguration des CoreDNS

Die Konfiguration erfogt über die Datei Corefile im Namespace kube-system.

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        hosts /etc/coredns/NodeHosts {
          ttl 60
          reload 15s
          fallthrough
        }
        prometheus :9153
        forward . 192.168.2.1
        cache 30
        loop
        reload
        loadbalance
    }

Hier ist die Zeile mit forward . 192.168.2.1 Interessant. Diese bestimmt den Upstream DNS Server für CoreDNS. Hier muss also der lokale DNS eingetragen werden, dann kann man das Manifest anwenden und der DNS ist umkonfiguriert. Dann sollte die Namensauflösung in den Containern funktionieren.

Daten aus KeyCloak exportieren

Die Exportfunktion in dem KeyCloak, also über die Web UI, kann leider keinen Datenbank Dump durchführen. D.h. es werden keine Secrets exportiert, was einen vollständigen Import beim Starten eines Clusters nicht möglich macht.

Dennoch gibt es von der Konsole aus die Möglichkeit einen Export durchzuführen. Dazu muss man das Binary standalone.sh mit der Aktion keycloak.migration.action=export aufrufen. Dann lässt sich ein Dump durchführen. In diesem Artikel beschreibe ich folgende Situation. Es soll aus einem laufenden KeyCloak Container die vorgenommenen Einstellungen gesichert werden, sodass diese bei einem Neustart wiederhergestellt werden. Ich setze hier einen K3D Kubernetes Cluster mit KeyCloak ein.

Auf die Einstellungen des REALMs gehe ich hier jetzt nicht weiter ein und spielt auch keine Rolle welche REALMs vorhanden sind.

Das Vorgehen ist wie folgt: Es muss der Name des PODs ermittelt werden. In dem Beispiel gehe ich davon aus, dass der KeyCloak in einem eigenen Namespane keycloak läuft und das mit dem Label app=keycloak versehen ist. Haben wir die Namen des PODs ermittelt, dann können wir eine zweite Instanz in dem Container von außen starten und den Export anstoßen. Leider beendet der KeyCloak sich nicht nach dem Export, sodass wir eine CTRL+C Handler installieren müssen. Die vollständige Dokumentation für die KeyCloak Im-/Export-Funktion finden sie hier.

Wichtig ist für den Start der neuen Instanz, dass ein beliebiger freier Port gewählt wird, da sonst der KeyCloak nicht starten würde. Dieses gilt für den HTTP und den Management Port.

Ist der KeyCloak hochgefahren, dann kann er mit STRG+C beendet werden und der Export wird aus dem Container in lokale Verzeichnis kopiert.

Das Skript

Zusammengefasst ergibt sich folgendes export.sh Skript:

#!/bin/bash

#
# to copy export file from container stop standalone.sh with CTRL+c
#


# retrieve POD name
POD_NAME=`kubectl get pod -n keycloak -lapp=keycloak --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}'`

# add CTRL-C Handler
trap trap_catch INT


function trap_catch() {
  # copy exported config from container to local storage
  kubectl cp keycloak/$POD_NAME:/tmp/keycloak-export.json test.json
}

# start new server 
kubectl exec -n keycloak $POD_NAME bash -- /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=/tmp/keycloak-export.json -Djboss.http.port=8888 -Djboss.https.port=9999 -Djboss.management.http.port=7777