This is an automated email from the ASF dual-hosted git repository.
lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-helm-chart.git
The following commit(s) were added to refs/heads/master by this push:
new 57e2aec Add separate headless service for ZooKeeper statefulset (#649)
57e2aec is described below
commit 57e2aec5aaf1822afeac97bc9b0041cd300b08c6
Author: Lari Hotari <[email protected]>
AuthorDate: Sat Feb 21 00:19:21 2026 +0200
Add separate headless service for ZooKeeper statefulset (#649)
---
README.md | 6 ++
charts/pulsar/templates/_zookeeper.tpl | 9 +-
.../templates/bookkeeper-cluster-initialize.yaml | 2 +-
.../templates/pulsar-cluster-initialize.yaml | 2 +-
...ervice.yaml => zookeeper-headless-service.yaml} | 6 +-
charts/pulsar/templates/zookeeper-service.yaml | 7 --
.../templates/zookeeper-statefulset-upgrade.yaml | 115 +++++++++++++++++++++
charts/pulsar/templates/zookeeper-statefulset.yaml | 2 +-
charts/pulsar/values.yaml | 11 ++
9 files changed, 145 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index c32f15a..7047af1 100644
--- a/README.md
+++ b/README.md
@@ -418,6 +418,12 @@ helm upgrade -n <namespace> -f values.yaml
<pulsar-release-name> apachepulsar/pu
For more detailed information, see our
[Upgrading](http://pulsar.apache.org/docs/helm-upgrade/) guide.
+## Upgrading to Helm chart version 4.6.0 (upcoming release)
+
+The ZooKeeper StatefulSet has been modified to use a separate headless service
and a separate ClusterIP service.
+The StatefulSet will be deleted and re-created during an upgrade. Deleting the
StatefulSet will not delete data. The pods will
+remain running until the upgrade has replaced them. The deletion is handled
using a Helm pre-upgrade hook, which runs a Kubernetes job using a container
that contains `kubectl`. The image is `alpine/k8s` by default and is
configurable under the `images.kubectl` key in values.yaml.
+
## Upgrading to Helm chart version 4.2.0
### TLS configuration for ZooKeeper has changed
diff --git a/charts/pulsar/templates/_zookeeper.tpl
b/charts/pulsar/templates/_zookeeper.tpl
index dc86d40..b3bdcd9 100644
--- a/charts/pulsar/templates/_zookeeper.tpl
+++ b/charts/pulsar/templates/_zookeeper.tpl
@@ -18,12 +18,19 @@ under the License.
*/}}
{{/*
-Define the pulsar zookeeper
+Define the pulsar zookeeper service (ordinary ClusterIP, used by
brokers/bookies)
*/}}
{{- define "pulsar.zookeeper.service" -}}
{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}
{{- end }}
+{{/*
+Define the pulsar zookeeper headless service (used as the StatefulSet
serviceName for pod DNS)
+*/}}
+{{- define "pulsar.zookeeper.service.headless" -}}
+{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}-headless
+{{- end }}
+
{{/*
Define the pulsar zookeeper
*/}}
diff --git a/charts/pulsar/templates/bookkeeper-cluster-initialize.yaml
b/charts/pulsar/templates/bookkeeper-cluster-initialize.yaml
index 77fbac2..3269cc6 100755
--- a/charts/pulsar/templates/bookkeeper-cluster-initialize.yaml
+++ b/charts/pulsar/templates/bookkeeper-cluster-initialize.yaml
@@ -75,7 +75,7 @@ spec:
echo "user provided zookeepers {{ $zk }} are unreachable...
check in 3 seconds ..." && sleep 3;
done;
{{ else }}
- until nslookup {{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1
}}.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{
template "pulsar.namespace" . }}; do
+ until nslookup {{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1
}}.{{ template "pulsar.zookeeper.service.headless" . }}.{{ template
"pulsar.namespace" . }}; do
sleep 3;
done;
{{- end}}
diff --git a/charts/pulsar/templates/pulsar-cluster-initialize.yaml
b/charts/pulsar/templates/pulsar-cluster-initialize.yaml
index 158a22d..3099856 100755
--- a/charts/pulsar/templates/pulsar-cluster-initialize.yaml
+++ b/charts/pulsar/templates/pulsar-cluster-initialize.yaml
@@ -83,7 +83,7 @@ spec:
echo "user provided zookeepers {{ $zk }} are unreachable...
check in 3 seconds ..." && sleep 3;
done;
{{ else if .Values.components.zookeeper }}
- until nslookup {{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1
}}.{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}.{{
template "pulsar.namespace" . }}; do
+ until nslookup {{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}-{{ add (.Values.zookeeper.replicaCount | int) -1
}}.{{ template "pulsar.zookeeper.service.headless" . }}.{{ template
"pulsar.namespace" . }}; do
sleep 3;
done;
{{- end }}
diff --git a/charts/pulsar/templates/zookeeper-service.yaml
b/charts/pulsar/templates/zookeeper-headless-service.yaml
similarity index 91%
copy from charts/pulsar/templates/zookeeper-service.yaml
copy to charts/pulsar/templates/zookeeper-headless-service.yaml
index a28f479..8c99fa6 100644
--- a/charts/pulsar/templates/zookeeper-service.yaml
+++ b/charts/pulsar/templates/zookeeper-headless-service.yaml
@@ -22,19 +22,17 @@
apiVersion: v1
kind: Service
metadata:
- name: "{{ template "pulsar.fullname" . }}-{{ .Values.zookeeper.component }}"
+ name: "{{ template "pulsar.zookeeper.service.headless" . }}"
namespace: {{ template "pulsar.namespace" . }}
labels:
{{- include "pulsar.standardLabels" . | nindent 4 }}
component: {{ .Values.zookeeper.component }}
- annotations:
-{{- with .Values.zookeeper.service.annotations }}
+{{- with .Values.zookeeper.headlessService.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
spec:
ports:
- # prometheus needs to access /metrics endpoint
- name: http
port: {{ .Values.zookeeper.ports.http }}
- name: "{{ .Values.tcpPrefix }}follower"
diff --git a/charts/pulsar/templates/zookeeper-service.yaml
b/charts/pulsar/templates/zookeeper-service.yaml
index a28f479..da3a02e 100644
--- a/charts/pulsar/templates/zookeeper-service.yaml
+++ b/charts/pulsar/templates/zookeeper-service.yaml
@@ -27,7 +27,6 @@ metadata:
labels:
{{- include "pulsar.standardLabels" . | nindent 4 }}
component: {{ .Values.zookeeper.component }}
- annotations:
{{- with .Values.zookeeper.service.annotations }}
annotations:
{{ toYaml . | indent 4 }}
@@ -37,10 +36,6 @@ spec:
# prometheus needs to access /metrics endpoint
- name: http
port: {{ .Values.zookeeper.ports.http }}
- - name: "{{ .Values.tcpPrefix }}follower"
- port: {{ .Values.zookeeper.ports.follower }}
- - name: "{{ .Values.tcpPrefix }}leader-election"
- port: {{ .Values.zookeeper.ports.leaderElection }}
- name: "{{ .Values.tcpPrefix }}client"
port: {{ .Values.zookeeper.ports.client }}
{{- if .Values.zookeeper.ports.admin }}
@@ -51,8 +46,6 @@ spec:
- name: "{{ .Values.tlsPrefix }}client-tls"
port: {{ .Values.zookeeper.ports.clientTls }}
{{- end }}
- clusterIP: None
- publishNotReadyAddresses: true
selector:
{{- include "pulsar.matchLabels" . | nindent 4 }}
component: {{ .Values.zookeeper.component }}
diff --git a/charts/pulsar/templates/zookeeper-statefulset-upgrade.yaml
b/charts/pulsar/templates/zookeeper-statefulset-upgrade.yaml
new file mode 100644
index 0000000..fa70df1
--- /dev/null
+++ b/charts/pulsar/templates/zookeeper-statefulset-upgrade.yaml
@@ -0,0 +1,115 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# only when `components.zookeeper` is true and
`zookeeper.statefulsetUpgrade.enabled` is true,
+# this pre-upgrade hook job will be created to clean up the old zookeeper
statefulset if the existing statefulset is created
+# by a chart older than 4.6.0, which has a different headless service name and
will cause issue if not deleted before
+# the new statefulset is created.
+{{- if and .Values.components.zookeeper
.Values.zookeeper.statefulsetUpgrade.enabled }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ .Release.Name }}-sts-cleanup
+ namespace: {{ template "pulsar.namespace" . }}
+ labels:
+ {{- include "pulsar.standardLabels" . | nindent 4 }}
+ component: {{ .Values.zookeeper.component }}-sts-cleanup
+ annotations:
+ "helm.sh/hook": pre-upgrade
+ "helm.sh/hook-weight": "-10"
+ "helm.sh/hook-delete-policy": hook-succeeded
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: {{ .Release.Name }}-sts-cleanup
+ namespace: {{ template "pulsar.namespace" . }}
+ labels:
+ {{- include "pulsar.standardLabels" . | nindent 4 }}
+ component: {{ .Values.zookeeper.component }}-sts-cleanup
+ annotations:
+ "helm.sh/hook": pre-upgrade
+ "helm.sh/hook-weight": "-10"
+ "helm.sh/hook-delete-policy": hook-succeeded
+rules:
+ - apiGroups: ["apps"]
+ resources: ["statefulsets"]
+ verbs: ["list", "get", "delete"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: {{ .Release.Name }}-sts-cleanup
+ namespace: {{ template "pulsar.namespace" . }}
+ labels:
+ {{- include "pulsar.standardLabels" . | nindent 4 }}
+ component: {{ .Values.zookeeper.component }}-sts-cleanup
+ annotations:
+ "helm.sh/hook": pre-upgrade
+ "helm.sh/hook-weight": "-10"
+ "helm.sh/hook-delete-policy": hook-succeeded
+subjects:
+ - kind: ServiceAccount
+ name: {{ .Release.Name }}-sts-cleanup
+roleRef:
+ kind: Role
+ name: {{ .Release.Name }}-sts-cleanup
+ apiGroup: rbac.authorization.k8s.io
+---
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: {{ .Release.Name }}-sts-cleanup
+ namespace: {{ template "pulsar.namespace" . }}
+ labels:
+ {{- include "pulsar.standardLabels" . | nindent 4 }}
+ component: {{ .Values.zookeeper.component }}-sts-cleanup
+ annotations:
+ "helm.sh/hook": pre-upgrade
+ "helm.sh/hook-weight": "0"
+ "helm.sh/hook-delete-policy": hook-succeeded
+spec:
+ backoffLimit: 1
+ template:
+ spec:
+ serviceAccountName: {{ .Release.Name }}-sts-cleanup
+ restartPolicy: Never
+ containers:
+ - name: sts-cleanup
+ image: "{{ template "pulsar.imageFullName" (dict "image"
.Values.images.kubectl "root" .) }}"
+ command:
+ - sh
+ - -c
+ - |
+ STS="{{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}"
+ CHART_LABEL=$(kubectl get statefulset "$STS" -o
jsonpath='{.metadata.labels.chart}' 2>/dev/null || true)
+ if [ -z "$CHART_LABEL" ]; then
+ echo "StatefulSet $STS not found or has no chart label,
skipping delete"
+ exit 0
+ fi
+ VERSION="${CHART_LABEL#pulsar-}"
+ MAJOR="$(echo "$VERSION" | cut -d. -f1)"
+ MINOR="$(echo "$VERSION" | cut -d. -f2)"
+ if [ "$MAJOR" -lt 4 ] || { [ "$MAJOR" -eq 4 ] && [ "$MINOR" -lt
6 ]; }; then
+ echo "Chart version $CHART_LABEL is older than pulsar-4.6.0,
deleting StatefulSet $STS"
+ kubectl delete statefulset "$STS" --cascade=orphan
--ignore-not-found
+ else
+ echo "Chart version $CHART_LABEL is 4.6.0 or newer, skipping
delete"
+ fi
+{{- end }}
\ No newline at end of file
diff --git a/charts/pulsar/templates/zookeeper-statefulset.yaml
b/charts/pulsar/templates/zookeeper-statefulset.yaml
index 1834f4a..cf962be 100755
--- a/charts/pulsar/templates/zookeeper-statefulset.yaml
+++ b/charts/pulsar/templates/zookeeper-statefulset.yaml
@@ -29,7 +29,7 @@ metadata:
{{- include "pulsar.standardLabels" . | nindent 4 }}
component: {{ .Values.zookeeper.component }}
spec:
- serviceName: "{{ template "pulsar.fullname" . }}-{{
.Values.zookeeper.component }}"
+ serviceName: "{{ template "pulsar.zookeeper.service.headless" . }}"
replicas: {{ .Values.zookeeper.replicaCount }}
selector:
matchLabels:
diff --git a/charts/pulsar/values.yaml b/charts/pulsar/values.yaml
index b1ed888..9f8620b 100755
--- a/charts/pulsar/values.yaml
+++ b/charts/pulsar/values.yaml
@@ -219,6 +219,9 @@ images:
repository: oxia/oxia
tag: 0.15.3
pullPolicy:
+ kubectl:
+ repository: alpine/k8s
+ tag: 1.32.12
## TLS
## templates/tls-certs.yaml
@@ -572,6 +575,14 @@ zookeeper:
##
service:
annotations: {}
+ ## Zookeeper headless service
+ ## templates/zookeeper-headless-service.yaml
+ ##
+ headlessService:
+ annotations: {}
+ ## Zookeeper statefulset upgrade job
+ statefulsetUpgrade:
+ enabled: true
## Zookeeper PodDisruptionBudget
## templates/zookeeper-pdb.yaml
##