Wer Keycloak in einem Kubernetes-Cluster betreibt, kennt die Anforderungen: Container-Deployments müssen schnell, berechenbar und ressourcenschonend sein. Schickt man Keycloak allerdings einfach mit dem offiziellen Standard-Image in Produktion, erlebt man beim ersten Neustart oft eine böse Überraschung. Zähe Bootvorgänge, CPU-Spitzen und fehlschlagende Health-Checks können automatische Rollouts und Skalierungen massiv ausbremsen.
Die Ursache dafür liegt in der Architektur des zugrundeliegenden Quarkus-Frameworks. Schauen wir uns an, warum der Standard-Startansatz in produktiven Umgebungen hakt und wie man mit einem vorkonfigurierten Custom Build ein schlankes, sekundenschnell startendes Image baut.
Warum der Standardbetrieb oft hakt
Startet das offizielle Keycloak-Image ohne vorherige Build-Schritte, analysiert der Server bei jedem einzelnen Bootvorgang seine komplette Umgebung. Hinter den Kulissen durchsucht Quarkus den Classpath nach Providern, parst Konfigurationsdateien, initialisiert Caches und registriert Datenbanktreiber – alles live während des Hochfahrens.
Was in der lokalen Testumgebung kaum auffällt, führt im produktiven Betrieb zu handfesten Problemen:
- Lange Startzeiten: Es dauert oft 30 bis 60 Sekunden (oder sogar länger), bis ein unvorbereiteter Container überhaupt Anfragen annehmen kann. Bei Rolling Updates oder über den HPA (Horizontal Pod Autoscaler) skalierenden Pods entstehen so unnötige Wartezeiten.
- Hohe Last beim Booten: Der Analyse- und Initialisierungsprozess frisst deutlich mehr CPU-Ressourcen als ein optimierter Start. Fahren mehrere Instanzen gleichzeitig hoch (etwa nach einem Node-Failover), entstehen Lastspitzen, die das gesamte Cluster beeinträchtigen können.
- Fehlende Immutability: Das Image ist kein in sich geschlossenes Artefakt. Erst Umgebungsvariablen entscheiden zur Laufzeit darüber, welche Komponenten überhaupt aktiv sind. Das macht Deployments fehleranfällig. Eigentlich sollten Container alle Ressourcen in einer bestimmten Version einfrieren, damit man sauber und vorhersehbar zwischen diesen Versionen wechseln kann.
Die Lösung: Ein optimierter Custom Build
Die Lösung ist im Grunde simpel: Wir verlagern diese ressourcenhungrigen Vorbereitungen dorthin, wo sie hingehören – in die CI/CD-Pipeline oder den Docker-Build. Mit dem Befehl kc.sh build berechnet Keycloak die interne Struktur vorab und friert sie fest im Image ein.
Startet man den Container anschließend mit dem Parameter --optimized, löst das die typischen Betriebsprobleme auf einen Schlag:
- Sekundenschneller Start: Da der interne Build bereits abgeschlossen ist, entfällt die Klassen- und Treiberanalyse komplett. Der Container fährt hoch und ist in wenigen Sekunden Ready.
- Geringe Boot-Last: Die CPU-Spitzen beim Starten schrumpfen enorm, da der Server nur noch seine Runtime-Konfiguration einliest und die Datenbankverbindung herstellt.
- Deterministische Deployments: Das resultierende Image ist absolut unveränderlich. Es enthält exakt die benötigten Datenbanktreiber, Extensions und Feature-Flags – nicht mehr und nicht weniger. Unerwartete Konfigurationsänderungen zur Laufzeit sind damit ausgeschlossen.
Der Schlüssel: Build-Time vs. Runtime strikt trennen
Damit das Vorkompilieren reibungslos klappt, muss klar sein, was fest ins Image gebacken wird und was dynamisch bleibt.
Build-Time-Parameter formen das Server-Artefakt während des kc.sh build und werfen ungenutzten Code raus:
- Datenbank-Typ (
KC_DB): Legt den Vendor fest (z. B.postgres). Nicht benötigte Treiber fliegen aus dem Image. - Features (
KC_FEATURES): Aktiviert Erweiterungen wie Token Exchange oder Organizations. - Monitoring (
KC_HEALTH_ENABLED,KC_METRICS_ENABLED): Schaltet Health-Checks und Prometheus-Metriken ein. - Erweiterungen: Eigene JARs (SPIs) und Themes müssen beim Build physisch im Image liegen, damit Keycloak sie registriert.
Runtime-Parameter umfassen alles, was je nach Umgebung variiert oder sensibel ist. Keycloak liest diese erst beim Container-Start aus:
- Datenbank-Zugriff: URL, Port, Benutzer und Passwort (
KC_DB_URL,KC_DB_USERNAME,KC_DB_PASSWORD). - Netzwerk: Hostname (
KC_HOSTNAME) und Proxy-Einstellungen (KC_PROXY_HEADERS). - Betrieb: Log-Level (
KC_LOG_LEVEL) und dynamische Cluster-Konfigurationen.
Tipp: In der offiziellen Keycloak-Dokumentation sind praktischerweise alle Parameter, die zwingend zur Build-Time angegeben werden müssen, mit einem Schraubenschlüssel-Symbol markiert.
Das Dockerfile: Multi-Stage Build in der Praxis
Um ein wirklich schlankes und sicheres Image zu erzeugen, empfiehlt sich ein Multi-Stage Build.
Warum der Aufwand mit zwei Stages?
Auf den ersten Blick wirkt es vielleicht redundant, das Basis-Image zweimal zu laden. Der Multi-Stage-Ansatz hat hier aber gute Gründe:
- In Stage 1 (Builder) definieren wir alle Build-Time-Parameter und kopieren unsere statischen Artefakte (Extensions, Themes) in das Dateisystem. Danach führt
kc.sh builddie Optimierung durch und registriert alle Erweiterungen fest im System. Dabei entstehen aber temporäre Caches und Build-Reste. - In Stage 2 (Runtime) holen wir uns per
COPY --from=builderausschließlich das final vorbereitete Keycloak-Verzeichnis in ein frisches Basis-Image. Das fertige Image enthält unsere Anpassungen also fest „eingebrannt“, verzichtet aber komplett auf den Build-Ballast. Das hält den Container schlank, gut auditierbar und minimiert die Angriffsfläche.
Das finale CMD ["start", "--optimized"] sorgt übrigens dafür, dass der Container standardmäßig immer im optimierten Modus hochfährt. Man muss den Parameter also nicht mehr extra im Kubernetes-Deployment-Manifest mitschleifen.
Automatisierung: Build und Push via GitHub Actions
Um das Ganze bei Code-Änderungen automatisch zu bauen, reicht ein kompakter GitHub-Actions-Workflow im Verzeichnis .github/workflows/build-keycloak.yml.
Die Pipeline nutzt das automatisch bereitgestellte GITHUB_TOKEN für den Login bei der GitHub Container Registry (GHCR) – es müssen also keine manuellen Secrets angelegt werden. Ein netter Bonus: Dank des integrierten GitHub-Actions-Caches (cache-from: type=gha) spart man bei unveränderten Layern massiv Build-Zeit.
Fazit
Wer Keycloak professionell betreiben will, kommt um ein vorkompiliertes Custom Image kaum herum. Die strikte Trennung von Build- und Laufzeit sorgt für stabile Cluster-Performance, vorhersehbare Startzeiten und wirklich reproduzierbare Umgebungen. Mit einem kleinen Dockerfile und einer schlanken CI/CD-Pipeline wird aus einer Standard-Installation ein wartungsarmer, passgenauer Baustein für die eigene Plattform.
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Blog-Autor*in
Stefan Gries
IAM & IT Consultant, Software Engineer
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.