Skip to content

Commit 3381af8

Browse files
committed
feat(keycloakx): automate minor/major upgrade process in cluster mode
Signed-off-by: Mohamed Amine AROUS <mohamed.amine.arous93@gmail.com>
1 parent 15f09f3 commit 3381af8

6 files changed

Lines changed: 160 additions & 2 deletions

File tree

charts/keycloakx/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
apiVersion: v2
22
name: keycloakx
3-
version: 7.1.11
3+
version: 7.1.12
44
appVersion: 26.5.6
55
description: Keycloak.X - Open Source Identity and Access Management for Modern Applications and Services
66
keywords:

charts/keycloakx/README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,15 @@ The following table lists the configurable parameters of the Keycloak-X chart an
238238
| `test.image.pullPolicy` | The image pull policy for the test Pod image | `IfNotPresent` |
239239
| `test.podSecurityContext` | SecurityContext for the entire test Pod | `{"fsGroup":1000}` |
240240
| `test.securityContext` | SecurityContext for the test container | `{"runAsNonRoot":true,"runAsUser":1000}` |
241-
| `test.deletionPolicy` | `helm.sh/hook-delete-policy` for the test Pod | `before-hook-creation` | | `before-hook-creation` |
241+
| `test.deletionPolicy` | `helm.sh/hook-delete-policy` for the test Pod | `before-hook-creation` |
242+
| `updateHook.enabled` | If `true`, enables the update hook that runs before statefulset updates | `true` |
243+
| `updateHook.image` | The image used for the update hook | `docker.io/curlimages/curl` |
244+
| `updateHook.podSecurityContext` | SecurityContext for the update hook Pod | `{"fsGroup":1000,"runAsNonRoot":true,"seccompProfile":{"type":"RuntimeDefault"}} |
245+
| `updateHook.securityContext` | SecurityContext for the update hook container | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsUser":1000} |
246+
| `updateHook.resources` | Resource requests and limits for the update hook container | `{"limits":{"cpu":"20m","memory":"32Mi"},"requests":{"cpu":"20m","memory":"32Mi"}}` |
247+
| `updateHook.kubernetesApi.url` | Kubernetes API URL for the update hook (Required if updateHook is enabled) | `""` |
248+
| `updateHook.kubernetesApi.port` | Kubernetes API port for the update hook (Required if updateHook is enabled) | `""` |
249+
| `updateHook.kubernetesApi.cidr` | Kubernetes API CIDR for the update hook (Required if updateHook and networkpolicy are enabled) | `""` |
242250

243251
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example:
244252

@@ -372,6 +380,25 @@ extraEnv: |
372380
For high availability, Keycloak must be run with multiple replicas (`replicas > 1`).
373381
The chart has a helper template (`keycloak.serviceDnsName`) that creates the DNS name based on the headless service.
374382

383+
#### Updating Minor and Major Versions in Cluster Mode
384+
385+
Keycloak does **not** support minor or major version upgrades while running in cluster mode. Only patch updates are supported.
386+
(Refer to the official Keycloak documentation for more details.)
387+
388+
If you attempt such an upgrade in cluster mode, you may encounter errors due to JGroups version mismatches, for example:
389+
390+
> WARN [org.jgroups.protocols.TCP] (TcpServer.Acceptor[7800]-1,keycloakx-1-2180(v=16.0.8)) JGRP000006: 10.151.254.47:7800: failed accepting connection from peer SSLSocket[hostname=127.0.0.6, port=51749, Session(1776862509121|TLS_AES_256_GCM_SHA384)]: java.io.IOException: 10.151.254.47:7800: readPeerAddress(): packet from /127.0.0.6:51749 has different version (5.3.16) from ours (5.5.1); discarding it
391+
392+
Because of this limitation, **downtime is required** to perform minor or major version upgrades.
393+
394+
**Recommended upgrade procedure:**
395+
396+
1. Scale down the StatefulSet to a single replica to disable cluster mode.
397+
2. Perform the version upgrade.
398+
3. Once the upgrade is complete, scale the StatefulSet back to its original number of replicas.
399+
400+
To simplify this process, the chart includes an `updateHook` parameter that automates these steps.
401+
375402
### Default Cache Stack
376403

377404
The default cache stack is now using `jdbc-ping` which leverages a table called `jgroups_ping` in the keycloak database to store the cache and significantly reduces network complexity. Keycloak has set this [transport stack](https://www.keycloak.org/server/caching#_transport_stacks) as the default starting in 26.1.0 and it is backwards compatible with all 26.X releases.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{{- if and .Values.updateHook.enabled (gt (int .Values.replicas) 1) .Values.networkPolicy.enabled }}
2+
apiVersion: networking.k8s.io/v1
3+
kind: NetworkPolicy
4+
metadata:
5+
name: "{{ include "keycloak.fullname" . }}-scale-down"
6+
annotations:
7+
"helm.sh/hook": pre-upgrade
8+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
9+
"helm.sh/hook-weight": "-15"
10+
spec:
11+
podSelector:
12+
matchLabels:
13+
job-name: "{{ include "keycloak.fullname" . }}-scale-down"
14+
policyTypes:
15+
- Egress
16+
egress:
17+
- to:
18+
- ipBlock:
19+
cidr: {{ .Values.updateHook.kubernetesApi.cidr | quote }}
20+
ports:
21+
- protocol: TCP
22+
port: {{ .Values.updateHook.kubernetesApi.port }}
23+
{{- end }}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{{- if and .Values.updateHook.enabled (gt (int .Values.replicas) 1) }}
2+
apiVersion: v1
3+
kind: ServiceAccount
4+
metadata:
5+
name: "{{ include "keycloak.fullname" . }}-scale-down"
6+
annotations:
7+
"helm.sh/hook": pre-upgrade
8+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
9+
"helm.sh/hook-weight": "-15"
10+
---
11+
apiVersion: rbac.authorization.k8s.io/v1
12+
kind: Role
13+
metadata:
14+
name: "{{ include "keycloak.fullname" . }}-scale-down"
15+
annotations:
16+
"helm.sh/hook": pre-upgrade
17+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
18+
"helm.sh/hook-weight": "-15"
19+
rules:
20+
- apiGroups: ["apps"]
21+
resources: ["statefulsets", "statefulsets/scale"]
22+
verbs: ["get", "patch", "update"]
23+
---
24+
apiVersion: rbac.authorization.k8s.io/v1
25+
kind: RoleBinding
26+
metadata:
27+
name: "{{ include "keycloak.fullname" . }}-scale-down"
28+
annotations:
29+
"helm.sh/hook": pre-upgrade
30+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
31+
"helm.sh/hook-weight": "-15"
32+
subjects:
33+
- kind: ServiceAccount
34+
name: "{{ include "keycloak.fullname" . }}-scale-down"
35+
roleRef:
36+
apiGroup: rbac.authorization.k8s.io
37+
kind: Role
38+
name: "{{ include "keycloak.fullname" . }}-scale-down"
39+
{{- end }}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{{- if and .Values.updateHook.enabled (gt (int .Values.replicas) 1) }}
2+
apiVersion: batch/v1
3+
kind: Job
4+
metadata:
5+
name: "{{ include "keycloak.fullname" . }}-scale-down"
6+
annotations:
7+
"helm.sh/hook": pre-upgrade
8+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
9+
"helm.sh/hook-weight": "-10"
10+
spec:
11+
template:
12+
spec:
13+
serviceAccountName: "{{ include "keycloak.fullname" . }}-scale-down"
14+
restartPolicy: Never
15+
securityContext:
16+
{{- toYaml .Values.updateHook.podSecurityContext | nindent 8 }}
17+
containers:
18+
- name: kubectl
19+
image: "{{ .Values.updateHook.image }}"
20+
securityContext:
21+
{{- toYaml .Values.updateHook.securityContext | nindent 12 }}
22+
command:
23+
- /bin/sh
24+
- -ec
25+
- |
26+
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
27+
NAMESPACE="$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)"
28+
CACERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
29+
KUBERNETES_API="{{ .Values.updateHook.kubernetesApi.url }}:{{ .Values.updateHook.kubernetesApi.port }}"
30+
31+
curl --fail --silent --show-error \
32+
--cacert "${CACERT}" \
33+
-H "Authorization: Bearer ${TOKEN}" \
34+
-H "Content-Type: application/merge-patch+json" \
35+
-X PATCH \
36+
"${KUBERNETES_API}/apis/apps/v1/namespaces/${NAMESPACE}/statefulsets/{{ include "keycloak.fullname" . }}/scale" \
37+
--data '{"spec":{"replicas":1}}'
38+
resources:
39+
{{- toYaml .Values.updateHook.resources | nindent 12 }}
40+
backoffLimit: 0
41+
{{- end }}

charts/keycloakx/values.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,3 +646,31 @@ extraManifests: []
646646
# name: "{{ include \"keycloak.fullname\" . }}-tpl"
647647
# data:
648648
# foo: bar
649+
650+
updateHook:
651+
enabled: true
652+
image: docker.io/curlimages/curl
653+
podSecurityContext:
654+
fsGroup: 1000
655+
runAsNonRoot: true
656+
seccompProfile:
657+
type: RuntimeDefault
658+
securityContext:
659+
allowPrivilegeEscalation: false
660+
readOnlyRootFilesystem: true
661+
capabilities:
662+
drop:
663+
- ALL
664+
runAsGroup: 1000
665+
runAsUser: 1000
666+
resources:
667+
requests:
668+
cpu: "20m"
669+
memory: "32Mi"
670+
limits:
671+
cpu: "20m"
672+
memory: "32Mi"
673+
# kubernetesApi:
674+
# url: https://kubernetes-api.example.com
675+
# port: 8443
676+
# cidr: 10.20.30.40/32

0 commit comments

Comments
 (0)