
Ce guide déploie Keycloak sur Kubernetes avec l'Operator officiel. Vous installez l'Operator et ses CRD, provisionnez la base PostgreSQL qu'il n'embarque pas, déclarez une instance via la ressource Keycloak, importez un realm de façon déclarative, et diagnostiquez les pannes de démarrage. Il s'adresse aux profils intermédiaires à avancés qui connaissent déjà kubectl. Toutes les commandes et sorties viennent d'un cluster réel sous Keycloak 26.7.0.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer l'Operator Keycloak et ses quatre CRD sans casser les permissions.
- Déployer une instance avec PostgreSQL et vérifier ses conditions de statut.
- Importer un realm en déclaratif, et connaître la limite qui piège tout le monde.
- Diagnostiquer un pod bloqué : hostname, port des health checks, mémoire.
- Choisir entre l'Operator et un chart Helm en connaissance de cause.
Prérequis
Section intitulée « Prérequis »- un cluster Kubernetes fonctionnel et
kubectlconfiguré (un cluster local type k3d suffit) ; - environ 2 Go de RAM disponibles pour le pod Keycloak, qui demande 1700 Mio par défaut ;
- des notions de Keycloak : si vous découvrez l'outil, commencez par l'installation avec Docker, bien plus simple.
Comment fonctionne l'Operator
Section intitulée « Comment fonctionne l'Operator »Un Operator est un contrôleur qui étend l'API Kubernetes avec vos propres types d'objets. Au lieu d'écrire vous-même un StatefulSet, un Service et une douzaine de variables d'environnement, vous déclarez une ressource Keycloak, et l'Operator fabrique et surveille tout le reste. Si le concept est neuf pour vous, la page sur les opérateurs Kubernetes pose les bases.
L'Operator Keycloak 26 installe quatre CRD dans le groupe k8s.keycloak.org :
| CRD | Rôle |
|---|---|
Keycloak | L'instance elle-même (réplicas, base, hostname, TLS) |
KeycloakRealmImport | Créer un realm à partir d'un manifeste |
KeycloakOIDCClient | Déclarer un client OIDC (nouveauté 26) |
KeycloakSAMLClient | Déclarer un client SAML (nouveauté 26) |
Retenez ce chiffre de quatre, il explique la première panne du guide.
Installer l'Operator
Section intitulée « Installer l'Operator »L'installation se fait avec Kustomize, en épinglant la version. C'est la méthode officielle, et ce n'est pas un détail de confort.
kubectl create namespace keycloakkubectl apply -k 'github.com/keycloak/keycloak-k8s-resources/kubernetes?ref=26.7.0'Vérifiez que les quatre CRD sont bien enregistrées, puis que l'Operator tourne :
kubectl get crd | grep keycloak# keycloakoidcclients.k8s.keycloak.org# keycloakrealmimports.k8s.keycloak.org# keycloaks.k8s.keycloak.org# keycloaksamlclients.k8s.keycloak.org
kubectl get pods -n keycloak# NAME READY STATUS RESTARTS AGE# keycloak-operator-849dc9ddcd-xttb2 1/1 Running 0 11sProvisionner PostgreSQL
Section intitulée « Provisionner PostgreSQL »L'Operator ne gère aucune base de données. C'est écrit noir sur blanc dans la documentation, et c'est la première surprise : Keycloak tourne en mode production sous l'Operator, il lui faut donc une base externe. À vous de la fournir.
Pour un lab, un PostgreSQL éphémère suffit. En production, utilisez une base gérée ou un operator dédié.
apiVersion: v1kind: Secretmetadata: name: keycloak-db-secret namespace: keycloaktype: OpaquestringData: username: keycloak password: keycloak-lab-password---apiVersion: apps/v1kind: StatefulSetmetadata: name: postgres namespace: keycloakspec: serviceName: postgres replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: postgres:17-alpine ports: - containerPort: 5432 env: - name: POSTGRES_DB value: keycloak - name: POSTGRES_USER valueFrom: secretKeyRef: name: keycloak-db-secret key: username - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: keycloak-db-secret key: password - name: PGDATA value: /var/lib/postgresql/data/pgdata volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumes: - name: data emptyDir: {}---apiVersion: v1kind: Servicemetadata: name: postgres namespace: keycloakspec: selector: app: postgres ports: - port: 5432kubectl apply -f postgres.yamlkubectl wait --for=condition=ready pod -l app=postgres -n keycloak --timeout=180s# pod/postgres-0 condition metDéclarer l'instance Keycloak
Section intitulée « Déclarer l'instance Keycloak »Voici le manifeste minimal qui fonctionne pour un lab. Chaque champ compte.
apiVersion: k8s.keycloak.org/v2beta1kind: Keycloakmetadata: name: keycloak-lab namespace: keycloakspec: instances: 1 db: vendor: postgres host: postgres database: keycloak usernameSecret: name: keycloak-db-secret key: username passwordSecret: name: keycloak-db-secret key: password http: httpEnabled: true hostname: strict: false ingress: enabled: falseDétaillons les choix :
apiVersion: k8s.keycloak.org/v2beta1. Attention, la documentation officielle utilise encorev2alpha1dans plusieurs exemples. Depuis Keycloak 26, la version de stockage estv2beta1: c'est celle qu'il faut écrire.db.host: postgrespointe le Service PostgreSQL. Les identifiants sont lus dans le Secret, jamais en clair.http.httpEnabled: trueactive l'écouteur HTTP en clair. Sans cela, Keycloak sert du HTTPS avec un certificat auto-signé, ce qui complique inutilement un lab.hostname.strict: falsedésactive la validation stricte du hostname. Keycloak 26 est intraitable sur ce point : en mode production, il exige soit un hostname explicite, soit cette désactivation, sinon il refuse de démarrer.ingress.enabled: falseévite que l'Operator crée un Ingress dont nous n'avons pas besoin ici.
Appliquez, puis surveillez les conditions de la ressource :
kubectl apply -f keycloak.yaml
kubectl get keycloaks/keycloak-lab -n keycloak \ -o go-template='{{range .status.conditions}}{{.type}}={{.status}}{{"\n"}}{{end}}'# Ready=True# HasErrors=False# RollingUpdate=FalseComptez environ 70 secondes avant Ready=True : Keycloak est lent à démarrer. L'Operator a créé un StatefulSet et deux Services :
kubectl get statefulset,svc -n keycloak# statefulset.apps/keycloak-lab 1/1# service/keycloak-lab-service ClusterIP 8080/TCP,9000/TCP# service/keycloak-lab-discovery ClusterIP None 7800/TCPNotez le Service -discovery, headless, sur le port 7800 : c'est par lui que les instances se découvrent pour former le cluster de cache Infinispan quand vous montez instances au-delà de 1.
Se connecter à la console
Section intitulée « Se connecter à la console »L'Operator génère un compte administrateur temporaire dans un Secret nommé d'après votre ressource :
kubectl get secret keycloak-lab-initial-admin -n keycloak \ -o jsonpath='{.data.username}' | base64 -d# temp-admin
kubectl get secret keycloak-lab-initial-admin -n keycloak \ -o jsonpath='{.data.password}' | base64 -dOuvrez ensuite un tunnel vers le Service :
kubectl port-forward -n keycloak service/keycloak-lab-service 8080:8080La console est sur http://localhost:8080. Si le port 8080 est déjà pris sur votre machine (par une autre instance Keycloak en Docker, par exemple), changez le port local : kubectl port-forward ... 8081:8080.
Importer un realm en déclaratif
Section intitulée « Importer un realm en déclaratif »La ressource KeycloakRealmImport crée un realm à partir d'un manifeste. Le champ realm accepte une RealmRepresentation complète, c'est-à-dire le même objet JSON que produit un export de realm.
apiVersion: k8s.keycloak.org/v2beta1kind: KeycloakRealmImportmetadata: name: intranet-realm namespace: keycloakspec: keycloakCRName: keycloak-lab realm: id: intranet realm: intranet displayName: Intranet enabled: trueL'Operator lance un Job qui pousse le realm dans l'instance :
kubectl apply -f realm.yaml
kubectl get jobs -n keycloak# NAME STATUS COMPLETIONS DURATION# intranet-realm Complete 1/1 19s
kubectl get keycloakrealmimport intranet-realm -n keycloak \ -o go-template='{{range .status.conditions}}{{.type}}={{.status}} {{end}}'# Done=True Started=False HasErrors=FalseDépannage
Section intitulée « Dépannage »Keycloak sur Kubernetes échoue presque toujours pour l'une de ces cinq raisons. Le tableau donne le symptôme tel que vous le verrez, pas le message d'erreur théorique.
| Symptôme | Cause | Correctif |
|---|---|---|
Operator en CrashLoopBackOff, pile Java illisible | Manifestes posés en apply -f : CRD manquantes, RoleBindings cassés | Réinstaller avec apply -k |
| Pod Keycloak refuse de démarrer, log sur le hostname | Keycloak 26 exige un hostname explicite en mode production | hostname.strict: false (lab) ou hostname.hostname (prod) |
Pod jamais Ready, probe en connection refused | Les health checks ont migré sur le port 9000 | Sonder :9000, ouvrir ce port dans vos NetworkPolicy |
Pod tué au démarrage, OOMKilled | Limite mémoire rabotée « pour un lab » | Laisser les 1700 Mio de requête, ou monter la limite |
Boucle de redirection, Invalid parameter: redirect_uri | Keycloak ne fait pas confiance aux en-têtes du reverse proxy | proxy.headers: xforwarded |
Le port 9000, la panne qui suit tous les upgrades
Section intitulée « Le port 9000, la panne qui suit tous les upgrades »Depuis Keycloak 25, les endpoints de santé ont quitté le port applicatif pour un port de management dédié, le 9000. C'est vérifiable en une commande :
# Sur le port 9000 : la santé répondcurl -s http://keycloak-lab-service:9000/health/ready# {"status": "UP", "checks": [ ...
# Sur le port 8080 : riencurl -s -o /dev/null -w "%{http_code}\n" http://keycloak-lab-service:8080/health/ready# 404Toute sonde, tout Ingress ou toute NetworkPolicy qui interroge encore 8080/health/ready marquera le pod comme défaillant alors qu'il fonctionne parfaitement. C'est la cause numéro un des pods « jamais Ready » après une montée de version.
Le mythe de la probe qui tue Keycloak
Section intitulée « Le mythe de la probe qui tue Keycloak »On lit souvent que la sonde de vivacité abattrait Keycloak avant qu'il ait fini de démarrer. Avec l'Operator, c'est faux : la startupProbe par défaut laisse dix minutes de grâce. Si votre pod meurt au démarrage, cherchez plutôt du côté de la mémoire (OOMKilled) ou du hostname. Réduire la limite mémoire à 512 Mio « parce que c'est un lab » est le meilleur moyen de se créer ce problème.
Le piège du reverse proxy
Section intitulée « Le piège du reverse proxy »Derrière un Ingress qui termine le TLS, Keycloak se croit en HTTP simple et fabrique des URL de redirection en http://. Résultat : boucle de redirection ou Invalid parameter: redirect_uri. Le correctif tient en une ligne dans la ressource :
spec: proxy: headers: xforwardedMéfiez-vous des tutoriels encore en ligne qui recommandent proxy: edge ou KC_PROXY=edge : cette option n'existe plus depuis Keycloak 24. Sa présence dans un guide est le meilleur indicateur qu'il est périmé.
Passer en production
Section intitulée « Passer en production »Ce lab tient sur une instance. Trois points changent la donne dès que vous en voulez plusieurs.
Les sessions vivent dans le cache. Keycloak stocke les sessions d'authentification dans un cache Infinispan local à chaque pod. Sans configuration, un utilisateur routé vers un autre pod perd sa session. Le Service -discovery headless sert justement à ce que les instances se trouvent.
Les sticky sessions ont un piège. Coller la session sur le cookie AUTH_SESSION_ID provoque une boucle de redirection infinie. C'est un classique, il faut coller sur un autre cookie.
Un upgrade coupe le service. L'Operator fait du rolling update quand la configuration change, mais bascule en recréation avec coupure dès que l'image change, car les migrations de schéma exigent qu'une seule instance touche la base. Trois réplicas ne vous garantissent donc pas une montée de version sans interruption.
Sur le durcissement (TLS, en-têtes, journaux d'audit), les principes valent pour toute installation : voyez la page sécurité opérationnelle.
À retenir
Section intitulée « À retenir »- Aucun chart Helm officiel n'existe et il n'en existera pas : l'Operator est la voie soutenue.
- Installez avec
kubectl apply -k ...?ref=26.7.0. Poser les manifestes à la main casse les CRD et les permissions. - L'Operator ne fournit pas de base de données : PostgreSQL est à provisionner à part.
- L'
apiVersioncourante estk8s.keycloak.org/v2beta1, malgré ce qu'écrivent encore certains exemples officiels. - Aucun champ du manifeste n'est obligatoire : un CR invalide est accepté puis échoue. Surveillez la condition
HasErrors. - Les health checks sont sur le port 9000, plus sur 8080. C'est la panne qui suit tous les upgrades.
KeycloakRealmImportne met jamais à jour un realm existant, et le fait sans le dire. Pour du GitOps, passez par keycloak-config-cli ou Terraform.