Service-Registration wieder aktivieren
Summary: Schritt-für-Schritt-Anleitung, um eine zuvor auskommentierte service_registration "kubernetes" {}-Stanza im Helm-Chart sauber wieder einzuschalten. Reihenfolge ist wichtig — erst die Voraussetzungen (NetworkPolicy-Egress, RBAC, Downward API) verifizieren, dann die Stanza setzen, dann das Rollout staffeln und Active-/Standby-Services prüfen.
Sources: raw/docs/configuration/service-registration/kubernetes.md, raw/docs/configuration/service-registration/index.md, raw/docs/platform/k8s/helm/terraform.md, raw/docs/platform/k8s/helm/run.md, raw/docs/concepts/ha.md.
Last updated: 2026-05-20
0 — Wo wir herkommen
Die Stanza service_registration "kubernetes" {} war im Chart aktiv, hat aber beim Start einen TLS-Handshake-Hang ausgelöst. Als Workaround wurde sie auskommentiert. Konsequenz (siehe kubernetes-service-registration):
- Pods werden nicht mehr mit
openbao-active,openbao-sealed,openbao-initialized,openbao-versiongelabelt. - Die Selector-basierten Services
*-activeund*-standbyhaben leere Endpoints. - Routing läuft heute über den Headless-Service bzw. den ClusterIP-Service round-robin — Writes auf Followern werden über das Request-Forwarding bzw. einen
307-Redirect zurapi_addrdes Leaders aufgelöst (source:raw/docs/concepts/ha.md). Das funktioniert, ist aber ein Hop teurer und ohne Sealed-Schutz. - Das Chart-Feature
ui.activeOpenbaoPodOnlyund chirurgische Upgrades anhand der Labels sind nicht nutzbar.
Ziel der Reaktivierung: Pod-Labels wieder schreiben lassen, ohne dass der Start hängt.
1 — Warum die Reihenfolge so ist
Sobald die Stanza aktiv ist, ruft OpenBao beim Start die Kubernetes-API auf (PATCH /api/v1/namespaces/<ns>/pods/<pod>). Hängt dieser Call, hängt der Pod-Start. Genau das war das ursprüngliche Symptom. Drei Voraussetzungen müssen deshalb vor dem Aktivieren der Stanza nachweisbar erfüllt sein:
- Netzwerk: Egress vom OpenBao-Pod zur Kubernetes-API (
kubernetes.default.svc:443) ist erlaubt. - Identität: Der ServiceAccount der OpenBao-Pods darf
get/update/patchauf das eigene Pod-Objekt (source:raw/docs/configuration/service-registration/kubernetes.md). - Selbst-Identifikation: Die Pods kennen ihren eigenen Namespace und Pod-Namen über
BAO_K8S_NAMESPACEundBAO_K8S_POD_NAMEaus der Downward API (source:raw/docs/configuration/service-registration/kubernetes.md).
Erst danach wird die Stanza eingeschaltet und ein gestaffeltes Rollout durchgezogen.
2 — Schritt 1: NetworkPolicy-Egress zur K8s-API freigeben
Verdachtsdiagnose zuerst aus einem laufenden Pod bestätigen, bevor irgendetwas geändert wird:
kubectl exec -n openbao-tm openbao-tm-0 -- \
sh -c 'wget -O- -T 5 --no-check-certificate https://kubernetes.default.svc/version 2>&1 | head -20'
Erwartet bei funktionierendem Egress: ein JSON mit gitVersion. Hängt der Call oder kommt connection timed out, blockiert eine NetworkPolicy (oder CNI-Layer-Policy wie Cilium CiliumNetworkPolicy) den Egress.
Die NetworkPolicy braucht einen Egress-Regel-Eintrag zur K8s-API. Bei CiliumNetworkPolicy typischerweise über das kube-apiserver-Entity:
egress:
- toEntities:
- kube-apiserver
Bei vanilla NetworkPolicy muss man den API-Server-Endpoint via ipBlock adressieren (die K8s-API ist kein normales Pod-Target — podSelector/namespaceSelector matcht sie nicht). Die konkrete CIDR ergibt sich aus kubectl get endpoints kubernetes -n default:
egress:
- to:
- ipBlock:
cidr: <api-server-ip>/32
ports:
- protocol: TCP
port: 443
Nach Apply den Test aus dem Pod wiederholen — die Antwort muss innerhalb von 1–2 Sekunden kommen. Ist sie da, ist die Hauptursache des TLS-Hangs beseitigt.
Hängt der Call obwohl die NetPol passt, ist die nächstwahrscheinliche Ursache eine falsche CA für die K8s-API im Pod (
/var/run/secrets/kubernetes.io/serviceaccount/ca.crtmuss zum API-Server passen — bei selbst gebauten Images leicht zu übersehen).
3 — Schritt 2: RBAC prüfen
ServiceAccount des StatefulSets feststellen:
kubectl get statefulset openbao-tm -n openbao-tm -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}'
Berechtigung explizit prüfen — das ist der zuverlässigste Test, weil kubectl auth can-i direkt das RBAC-Subsystem fragt:
SA=$(kubectl get sts openbao-tm -n openbao-tm -o jsonpath='{.spec.template.spec.serviceAccountName}')
kubectl auth can-i patch pods --as=system:serviceaccount:openbao-tm:$SA -n openbao-tm
kubectl auth can-i get pods --as=system:serviceaccount:openbao-tm:$SA -n openbao-tm
kubectl auth can-i update pods --as=system:serviceaccount:openbao-tm:$SA -n openbao-tm
Alle drei müssen yes antworten. Falls nicht, fehlt die Role oder die RoleBinding. Sollwert (source: raw/docs/configuration/service-registration/kubernetes.md):
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: openbao-tm
name: openbao-tm
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "update", "patch"]
Das offizielle Helm-Chart legt diese Role automatisch an, wenn server.serviceAccount.create=true und HA aktiv ist (source: raw/docs/platform/k8s/helm/terraform.md). Wer den ServiceAccount manuell pflegt, muss die Role manuell mitliefern.
4 — Schritt 3: Downward API verifizieren
Aus einem laufenden Pod:
kubectl exec -n openbao-tm openbao-tm-0 -- sh -c 'echo "ns=$BAO_K8S_NAMESPACE pod=$BAO_K8S_POD_NAME"'
Erwartet: ns=openbao-tm pod=openbao-tm-0. Sind die Variablen leer, fehlt der entsprechende env-Block im StatefulSet-Template. Beim offiziellen Chart ist das im HA-Mode Default; wenn die values-tm.yaml aber server.standalone.config oder ein eigenes Pod-Template setzt, kann das Mapping fehlen. Sollwert:
env:
- name: BAO_K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: BAO_K8S_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
Alternativ — falls die Downward API in diesem Setup nicht nachrüstbar ist — kann man die Werte explizit in die Stanza schreiben. Das geht im StatefulSet aber nur über einen pro-Pod-Generator-Workaround und ist nicht empfehlenswert.
5 — Schritt 4: Stanza in der Helm-Values reaktivieren
Erst jetzt — wenn Egress, RBAC und Downward API alle bestätigt sind — wird die Stanza wieder eingeschaltet. In values-tm.yaml:
storage "raft" {
path = "/openbao/data"
# retry_join … explizit, wie bisher
}
service_registration "kubernetes" {}
Die leere {}-Form genügt: Namespace und Pod-Name kommen aus der Downward API (source: raw/docs/configuration/service-registration/kubernetes.md).
helm upgrade ausführen — noch nicht die Pods neu starten. Erst die rendered ConfigMap kontrollieren:
helm upgrade openbao-tm openbao/openbao -f values-tm.yaml -n openbao-tm
kubectl get configmap -n openbao-tm openbao-tm-config -o jsonpath='{.data.extraconfig-from-values\.hcl}' | grep -A1 service_registration
Erwartet: service_registration "kubernetes" {} ist im gerenderten Output enthalten.
6 — Schritt 5: Gestaffeltes Rollout
Update-Strategy des StatefulSets auf OnDelete lassen (oder kurzfristig setzen) und die Pods einzeln neu starten — Standbys zuerst, aktiver Pod zuletzt. Welcher Pod aktuell Leader ist, weiß noch nicht das Label (das gibt es ja gerade noch nicht), sondern die API:
for i in 0 1 2; do
echo -n "openbao-tm-$i: "
kubectl exec -n openbao-tm openbao-tm-$i -- bao status 2>/dev/null | grep -E 'HA Mode'
done
Zuerst die beiden Standbys neu erzeugen:
kubectl delete pod -n openbao-tm openbao-tm-1
# warten bis Ready und unsealed:
kubectl wait pod openbao-tm-1 -n openbao-tm --for=condition=Ready --timeout=120s
kubectl exec -n openbao-tm openbao-tm-1 -- bao status
kubectl delete pod -n openbao-tm openbao-tm-2
kubectl wait pod openbao-tm-2 -n openbao-tm --for=condition=Ready --timeout=120s
Wenn beide Standbys mit gesetztem openbao-active=false-Label hochkommen, ist klar: der Patch-Call funktioniert. Erst dann den Leader zerstören:
kubectl delete pod -n openbao-tm openbao-tm-0
Ein Standby übernimmt; der neu hochkommende openbao-tm-0 wird Standby.
Verifikation:
kubectl get pods -n openbao-tm -L openbao-active,openbao-sealed,openbao-version
Erwartet: genau ein Pod hat openbao-active=true, alle drei haben openbao-sealed=false und eine gesetzte Versions-Spalte.
7 — Schritt 6: Active- und Standby-Services testen
Die Selector-Services bekommen jetzt automatisch Endpoints:
kubectl get endpoints -n openbao-tm openbao-tm-active openbao-tm-standby
Erwartet:
openbao-tm-active: genau eine Pod-IP — die des aktuellen Leaders.openbao-tm-standby: zwei Pod-IPs — die der beiden Followers.
Funktionscheck aus einem Test-Pod:
kubectl run curl --rm -it --image=curlimages/curl -n openbao-tm -- \
curl -s -k https://openbao-tm-active:8200/v1/sys/health | jq .
Im Response muss "standby": false stehen — wir landen tatsächlich beim Leader.
Failover-Test (optional, aber empfohlen, wenn das Setup heiß ist):
LEADER=$(kubectl get pods -n openbao-tm -l openbao-active=true -o name)
kubectl delete -n openbao-tm $LEADER
# warten — neuer Leader übernimmt
sleep 15
kubectl get pods -n openbao-tm -L openbao-active
kubectl get endpoints -n openbao-tm openbao-tm-active
Erwartet: ein anderer Pod ist jetzt openbao-active=true, und das einzige Endpoint im *-active-Service zeigt auf den neuen Leader.
8 — Rollback-Plan
Falls beim Re-Aktivieren wieder ein Hang auftritt:
- Stanza in
values-tm.yamlwieder auskommentieren. helm upgrade …mit alten Values.kubectl rollout restart statefulset/openbao-tm -n openbao-tm.- Diagnose aufnehmen — typischerweise war einer der drei Voraussetzungs-Checks (Netzwerk, RBAC, Downward API) doch nicht erfüllt, hat aber im statischen Test gepasst, weil z. B. die NetworkPolicy nur auf bestimmte Pods matched.
Wichtig: Beim Rollback bleibt das Pod-Labelset stale, bis die Pods rotieren. Die Selector-Services haben in der Zwischenzeit weiter Endpoints, die aber nicht mehr aktuell sind. Deshalb beim Rollback immer auch die Pods neu starten, damit die alten Labels verschwinden.
9 — Was bewusst nicht geändert wird
retry_join-Block instorage "raft"bleibt mit expliziten Peer-Adressen stehen.retry_joinist ein DNS-/HTTP-Discovery-Mechanismus von Raft selbst und hat nichts mit Pod-Labels oder der Kubernetes-API zu tun (source:raw/docs/configuration/storage/raft.md, raft § “retry_join”). Service Registration ersetzt es nicht. Der Block ist mit drei explizit benannten Peer-DNS-Namen so robust, wie es geht — ihn anzufassen, nur weil Service Registration zurückkommt, würde ohne Gewinn Komplexität entfernen.- TLS-Konfiguration (
listener "tcp" { tls_… }) undapi_addrbleiben unverändert. Service Registration hängt nicht davon ab.
10 — Antipatterns
- Stanza vor NetPol-Fix reaktivieren: führt direkt zurück in den ursprünglichen Hang.
- Alle drei Pods gleichzeitig neu starten: kein Quorum-Schutz, Leader-Übergabe potentiell mehrfach, Endpoints flackern. Immer staffeln.
publishNotReadyAddresses: trueam Active-Service stehen lassen: hebelt den Sealed-Schutz aus, den Service Registration gerade liefern soll. Defaultfalselassen (siehe kubernetes-service-registration § 10).- Auto-Unseal vergessen: Nach Pod-Restart bleiben die Pods sealed,
openbao-activekann nicht auftruegehen, der Active-Service hat keine Endpoints. In K8s ist Auto-Unseal für service registration de facto Pflicht. - RBAC nur am ClusterRole-Level prüfen: Die benötigte Role ist namespaced auf die eigene Namespace — eine globale ClusterRole erfüllt das zwar auch, aber die Mehrheit der Setups arbeitet mit
Role+RoleBinding.kubectl auth can-iist die zuverlässige Probe.
Related pages
- kubernetes-service-registration — was die Stanza tut, welche Labels gesetzt werden, RBAC im Detail
- k8s-ha-setup — das Gesamt-Setup, in dem diese Reaktivierung stattfindet
- high-availability — Request-Forwarding und der
307-Redirect-Fallback, der heute das Routing trägt - raft —
retry_joinund warum es vom Service-Registration-Pfad getrennt bleibt - seal-unseal — warum Auto-Unseal hier praktisch verpflichtend ist
- configuration — die
service_registration-Stanza im Kontext der gesamten Server-Konfiguration