Skip to content

Commit 19029df

Browse files
Matthieuclaude
andcommitted
[kafka] Migrate from ZooKeeper to KRaft mode with automatic migration support
Replace ZooKeeper-based Kafka deployments with KRaft (Kafka Raft) metadata mode. Strimzi 0.45 is the last version supporting ZooKeeper — this change prepares for the transition before ZooKeeper support is removed entirely. Changes: - New instances deploy directly in KRaft mode using combined KafkaNodePool - Existing ZooKeeper instances migrate automatically via Strimzi's built-in ZK-to-KRaft migration state machine (triggered by annotation) - Broker pool data is preserved during and after migration via lookup-based pool layout detection - Kafka version is now user-configurable (v3.9 default, v3.8 available) - ZooKeeper removed from dashboard UI (ApplicationDefinition) - Strimzi operator updated from 0.45.1-rc1 to 0.45.0 (stable) - WorkloadMonitor added for KRaft controllers in separate-pool layout - 44 Helm unit tests added across 6 test suites - E2E test updated for KRaft mode - Fix: ZooKeeper metricsConfig referenced wrong ConfigMap key - Fix: ZooKeeper storageClass used kafka.storageClass instead of its own Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> Signed-off-by: Matthieu <[email protected]>
1 parent 38624b7 commit 19029df

31 files changed

Lines changed: 855 additions & 183 deletions

‎api/apps/v1alpha1/kafka/types.go‎

Lines changed: 18 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎hack/e2e-apps/kafka.bats‎

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,14 @@ metadata:
1010
name: $name
1111
namespace: tenant-test
1212
spec:
13+
version: "v3.9"
1314
external: false
1415
kafka:
1516
size: 10Gi
1617
replicas: 2
1718
storageClass: ""
1819
resources: {}
1920
resourcesPreset: "nano"
20-
zookeeper:
21-
size: 5Gi
22-
replicas: 2
23-
storageClass: ""
24-
resources:
25-
resourcesPreset: "nano"
2621
topics:
2722
- name: testResults
2823
partitions: 1
@@ -41,12 +36,6 @@ EOF
4136
sleep 5
4237
kubectl -n tenant-test wait hr kafka-$name --timeout=30s --for=condition=ready
4338
kubectl wait kafkas -n tenant-test test --timeout=60s --for=condition=ready
44-
timeout 60 sh -ec "until kubectl -n tenant-test get pvc data-kafka-$name-zookeeper-0; do sleep 10; done"
45-
kubectl -n tenant-test wait pvc data-kafka-$name-zookeeper-0 --timeout=50s --for=jsonpath='{.status.phase}'=Bound
46-
timeout 40 sh -ec "until kubectl -n tenant-test get svc kafka-$name-zookeeper-client -o jsonpath='{.spec.ports[0].port}' | grep -q '2181'; do sleep 10; done"
47-
timeout 40 sh -ec "until kubectl -n tenant-test get svc kafka-$name-zookeeper-nodes -o jsonpath='{.spec.ports[*].port}' | grep -q '2181 2888 3888'; do sleep 10; done"
48-
timeout 80 sh -ec "until kubectl -n tenant-test get endpoints kafka-$name-zookeeper-nodes -o jsonpath='{.subsets[*].addresses[0].ip}' | grep -q '[0-9]'; do sleep 10; done"
39+
timeout 80 sh -ec "until kubectl -n tenant-test get endpoints kafka-$name-kafka-bootstrap -o jsonpath='{.subsets[*].addresses[0].ip}' | grep -q '[0-9]'; do sleep 10; done"
4940
kubectl -n tenant-test delete kafka.apps.cozystack.io $name
50-
kubectl -n tenant-test delete pvc data-kafka-$name-zookeeper-0
51-
kubectl -n tenant-test delete pvc data-kafka-$name-zookeeper-1
5241
}

‎packages/apps/kafka/Chart.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ description: Managed Kafka service
44
icon: /logos/kafka.svg
55
type: application
66
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
7-
appVersion: "3.7.0"
7+
appVersion: "3.9.0"

‎packages/apps/kafka/README.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
### Common parameters
66

7-
| Name | Description | Type | Value |
8-
| ---------- | ------------------------------------------------ | ------ | ------- |
9-
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
7+
| Name | Description | Type | Value |
8+
| ---------- | ------------------------------------------------ | -------- | ------- |
9+
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
10+
| `version` | Kafka version to deploy. | `string` | `v3.9` |
1011

1112

1213
### Application-specific parameters
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"v3.9": "3.9.0"
2+
"v3.8": "3.8.1"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{- define "kafka.versionMap" }}
2+
{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }}
3+
{{- if not (hasKey $versionMap .Values.version) }}
4+
{{- printf `Kafka version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }}
5+
{{- end }}
6+
{{- index $versionMap .Values.version }}
7+
{{- end }}

‎packages/apps/kafka/templates/dashboard-resourcemap.yaml‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
{{- $existingKafka := lookup "kafka.strimzi.io/v1beta2" "Kafka" .Release.Namespace .Release.Name }}
2+
{{- $isExisting := not (empty $existingKafka) }}
3+
{{- $metadataState := "" }}
4+
{{- if $isExisting }}
5+
{{- $metadataState = dig "status" "kafkaMetadataState" "ZooKeeper" $existingKafka }}
6+
{{- end }}
7+
{{- $isKRaft := or (not $isExisting) (eq $metadataState "KRaft") }}
8+
{{- $existingBrokerPool := lookup "kafka.strimzi.io/v1beta2" "KafkaNodePool" .Release.Namespace (printf "%s-broker" .Release.Name) }}
9+
{{- $useCombinedPool := empty $existingBrokerPool }}
110
apiVersion: rbac.authorization.k8s.io/v1
211
kind: Role
312
metadata:
@@ -23,7 +32,12 @@ rules:
2332
- workloadmonitors
2433
resourceNames:
2534
- {{ .Release.Name }}
26-
- {{ $.Release.Name }}-zookeeper
35+
{{- if not $useCombinedPool }}
36+
- {{ .Release.Name }}-controller
37+
{{- end }}
38+
{{- if not $isKRaft }}
39+
- {{ .Release.Name }}-zookeeper
40+
{{- end }}
2741
verbs: ["get", "list", "watch"]
2842
---
2943
kind: RoleBinding

‎packages/apps/kafka/templates/kafka.yaml‎

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1+
{{- $kafkaVersion := include "kafka.versionMap" . }}
2+
{{- $existingKafka := lookup "kafka.strimzi.io/v1beta2" "Kafka" .Release.Namespace .Release.Name }}
3+
{{- $isExisting := not (empty $existingKafka) }}
4+
{{- $metadataState := "" }}
5+
{{- if $isExisting }}
6+
{{- $metadataState = dig "status" "kafkaMetadataState" "ZooKeeper" $existingKafka }}
7+
{{- end }}
8+
{{- $isKRaft := or (not $isExisting) (eq $metadataState "KRaft") }}
19
apiVersion: kafka.strimzi.io/v1beta2
210
kind: Kafka
311
metadata:
412
name: {{ .Release.Name }}
513
labels:
614
app.kubernetes.io/instance: {{ .Release.Name }}
715
app.kubernetes.io/managed-by: {{ .Release.Service }}
16+
annotations:
17+
strimzi.io/node-pools: enabled
18+
{{- if $isKRaft }}
19+
strimzi.io/kraft: enabled
20+
{{- else }}
21+
strimzi.io/kraft: migration
22+
{{- end }}
823
spec:
924
kafka:
10-
replicas: {{ .Values.kafka.replicas }}
11-
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.kafka.resourcesPreset .Values.kafka.resources $) | nindent 6 }}
25+
version: {{ $kafkaVersion }}
1226
listeners:
1327
- name: plain
1428
port: 9092
@@ -46,24 +60,13 @@ spec:
4660
default.replication.factor: 3
4761
min.insync.replicas: 2
4862
{{- end }}
49-
storage:
50-
type: jbod
51-
volumes:
52-
- id: 0
53-
type: persistent-claim
54-
{{- with .Values.kafka.size }}
55-
size: {{ . }}
56-
{{- end }}
57-
{{- with .Values.kafka.storageClass }}
58-
class: {{ . }}
59-
{{- end }}
60-
deleteClaim: true
6163
metricsConfig:
6264
type: jmxPrometheusExporter
6365
valueFrom:
6466
configMapKeyRef:
6567
name: {{ .Release.Name }}-metrics
6668
key: kafka-metrics-config.yml
69+
{{- if not $isKRaft }}
6770
zookeeper:
6871
replicas: {{ .Values.zookeeper.replicas }}
6972
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.zookeeper.resourcesPreset .Values.zookeeper.resources $) | nindent 6 }}
@@ -72,7 +75,7 @@ spec:
7275
{{- with .Values.zookeeper.size }}
7376
size: {{ . }}
7477
{{- end }}
75-
{{- with .Values.kafka.storageClass }}
78+
{{- with .Values.zookeeper.storageClass }}
7679
class: {{ . }}
7780
{{- end }}
7881
deleteClaim: false
@@ -81,7 +84,8 @@ spec:
8184
valueFrom:
8285
configMapKeyRef:
8386
name: {{ .Release.Name }}-metrics
84-
key: kafka-metrics-config.yml
87+
key: zookeeper-metrics-config.yml
88+
{{- end }}
8589
entityOperator:
8690
topicOperator: {}
8791
userOperator: {}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{{- /* Detect pool layout: if a broker pool already exists, keep separate pools to preserve data */ -}}
2+
{{- $existingBrokerPool := lookup "kafka.strimzi.io/v1beta2" "KafkaNodePool" .Release.Namespace (printf "%s-broker" .Release.Name) }}
3+
{{- $useCombinedPool := empty $existingBrokerPool }}
4+
{{- /* Controller replicas: use odd number for quorum (1 for single-node, 3 otherwise) */ -}}
5+
{{- $controllerReplicas := 3 }}
6+
{{- if le (int .Values.kafka.replicas) 1 }}
7+
{{- $controllerReplicas = 1 }}
8+
{{- end }}
9+
10+
{{- if $useCombinedPool }}
11+
---
12+
apiVersion: kafka.strimzi.io/v1beta2
13+
kind: KafkaNodePool
14+
metadata:
15+
name: {{ .Release.Name }}-combined
16+
labels:
17+
strimzi.io/cluster: {{ .Release.Name }}
18+
spec:
19+
replicas: {{ .Values.kafka.replicas }}
20+
roles:
21+
- controller
22+
- broker
23+
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.kafka.resourcesPreset .Values.kafka.resources $) | nindent 4 }}
24+
storage:
25+
type: jbod
26+
volumes:
27+
- id: 0
28+
type: persistent-claim
29+
{{- with .Values.kafka.size }}
30+
size: {{ . }}
31+
{{- end }}
32+
{{- with .Values.kafka.storageClass }}
33+
class: {{ . }}
34+
{{- end }}
35+
kraftMetadata: shared
36+
deleteClaim: true
37+
38+
{{- else }}
39+
---
40+
apiVersion: kafka.strimzi.io/v1beta2
41+
kind: KafkaNodePool
42+
metadata:
43+
name: {{ .Release.Name }}-broker
44+
labels:
45+
strimzi.io/cluster: {{ .Release.Name }}
46+
spec:
47+
replicas: {{ .Values.kafka.replicas }}
48+
roles:
49+
- broker
50+
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.kafka.resourcesPreset .Values.kafka.resources $) | nindent 4 }}
51+
storage:
52+
type: jbod
53+
volumes:
54+
- id: 0
55+
type: persistent-claim
56+
{{- with .Values.kafka.size }}
57+
size: {{ . }}
58+
{{- end }}
59+
{{- with .Values.kafka.storageClass }}
60+
class: {{ . }}
61+
{{- end }}
62+
deleteClaim: true
63+
---
64+
apiVersion: kafka.strimzi.io/v1beta2
65+
kind: KafkaNodePool
66+
metadata:
67+
name: {{ .Release.Name }}-controller
68+
labels:
69+
strimzi.io/cluster: {{ .Release.Name }}
70+
spec:
71+
replicas: {{ $controllerReplicas }}
72+
roles:
73+
- controller
74+
storage:
75+
type: jbod
76+
volumes:
77+
- id: 0
78+
type: persistent-claim
79+
size: 5Gi
80+
{{- with .Values.kafka.storageClass }}
81+
class: {{ . }}
82+
{{- end }}
83+
kraftMetadata: shared
84+
deleteClaim: true
85+
{{- end }}

‎packages/apps/kafka/templates/metrics-configmap.yaml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
{{- $existingKafka := lookup "kafka.strimzi.io/v1beta2" "Kafka" .Release.Namespace .Release.Name }}
2+
{{- $isExisting := not (empty $existingKafka) }}
3+
{{- $metadataState := "" }}
4+
{{- if $isExisting }}
5+
{{- $metadataState = dig "status" "kafkaMetadataState" "ZooKeeper" $existingKafka }}
6+
{{- end }}
7+
{{- $isKRaft := or (not $isExisting) (eq $metadataState "KRaft") }}
18
kind: ConfigMap
29
apiVersion: v1
310
metadata:
@@ -165,6 +172,7 @@ data:
165172
- pattern: "kafka.server<type=broker-metadata-metrics><>(.+):"
166173
name: kafka_server_brokermetadatametrics_$1
167174
type: GAUGE
175+
{{- if not $isKRaft }}
168176
zookeeper-metrics-config.yml: |
169177
# See https://github.com/prometheus/jmx_exporter for more info about JMX Prometheus Exporter metrics
170178
lowercaseOutputName: true
@@ -196,3 +204,4 @@ data:
196204
labels:
197205
replicaId: "$2"
198206
memberType: "$3"
207+
{{- end }}

0 commit comments

Comments
 (0)