OpenReplay 自托管 Kafka Helm Chart 实战:基于 StatefulSet 的 KRaft 模式与可选 TLS 部署指南
发布时间:2026/9/23 1:53:02 锦皓数字建站

OpenReplay 自托管 Kafka Helm Chart 实战基于 StatefulSet 的 KRaft 模式与可选 TLS 部署指南【免费下载链接】openreplaySession replay, cobrowsing and product analytics you can self-host. Best for reproducing issues and iterating on your product.项目地址: https://gitcode.com/gh_mirrors/op/openreplay在 OpenReplay 的自托管架构中Kafka 承担着会话录制数据、事件流与产品分析消息的传输通道是整个数据流水线的核心消息总线。本篇文章基于 OpenReplay 仓库内 scripts/helmcharts/databases/charts/kafka/IMPLEMENTATION_SUMMARY.md 的实现总结结合 Chart 模板、values.yaml 与 参考清单 等仓库资源完整讲解该 Kafka Helm Chart 从样板代码演进为可生产使用的 StatefulSet 部署的全过程。读完本文你将掌握KRaft 模式免 ZooKeeper下的 Kafka 集群编排原理、三类 Service 的拓扑设计、控制器仲裁投票Controller Quorum Voters的动态生成机制、可选 TLS 加密的证书注入流程以及如何将该 Chart 集成进 databases 父 Chart 并完成生产级部署。一、设计背景从样板 Chart 到生产级 Kafka 部署在改造之前charts/kafka目录下的 Chart 只是 Helm 创建命令生成的通用样板一个无状态 Deployment、一个 Ingress、一个 HPA外加一堆占位测试文件——这些结构完全不适合 Kafka 这种有状态、需要稳定网络标识与持久化存储的消息系统。本次实现的目标非常明确依据 IMPLEMENTATION_SUMMARY.md将 Deployment 替换为StatefulSet为每个 Broker 提供稳定的 Pod 身份kafka-0、kafka-1…与专属 PVC全面支持KRaft 模式不再依赖 ZooKeeper提供可选的 TLS 加密能力并向下兼容 PLAINTEXT所有配置项参数化到 values.yaml做到开箱即用、可定制。改造的实现基准来自仓库内的两份原生 K8s 参考清单scripts/dockerfiles/kafka/kube/k8s-kafka-kraft.yaml基础 KRaft 配置scripts/dockerfiles/kafka/kube/k8s-kafka-kraft-tls.yamlTLS 增强配置。Chart 元信息也在 Chart.yaml 中同步更新version: 11.8.6、appVersion: 3对应 Kafka 3.x 镜像描述为 Apache Kafka StatefulSet with KRaft mode support。文件变动一览依据实现摘要类别文件新建templates/statefulset.yaml、templates/service-headless.yaml、templates/service-ssl.yaml、values-tls.yaml、README.md、IMPLEMENTATION_SUMMARY.md修改templates/_helpers.tpl新增自定义函数、templates/service.yaml更新 Kafka 端口、templates/serviceaccount.yaml增加组件标签、templates/NOTES.txtKafka 专属信息、values.yaml全面重写、Chart.yaml移除templates/deployment.yaml由 StatefulSet 取代、templates/ingress.yamlKafka 无需 Ingress、templates/hpa.yaml不适用于 StatefulSet、templates/httproute.yaml、templates/tests/二、整体拓扑三类 Service 一个 StatefulSet该 Chart 的运行时拓扑由 1 个 StatefulSet 和 3 个 Service 构成各司其职Headless Servicerelease-headlessclusterIP: None为 StatefulSet 提供稳定的 DNS 记录kafka-0.release-headless.namespace.svc.cluster.local是 Broker 间通信与客户端直连 Pod 的基础Client Servicerelease标准 ClusterIP暴露 9092 端口PLAINTEXT供集群内客户端如 OpenReplay 的 API、Assets、Assist 等组件统一接入SSL Servicerelease-ssl仅在启用 TLS 时渲染暴露 9094 端口SSL为加密客户端提供专用入口。部署后从集群内访问的端点分别为以db命名空间为例kafka.db.svc.cluster.local:9092 # 客户端 PLAINTEXT kafka-headless.db.svc.cluster.local:9092 # Headless直连 Pod kafka-ssl.db.svc.cluster.local:9094 # SSL启用 TLS 时 kafka-0.kafka-headless.db.svc.cluster.local:9093 # 单 PodINTERNAL 控制器监听这些访问方式与 README.md 中记录的端点完全一致也保留了原始参考清单 k8s-kafka-kraft.yaml 的命名约定kafka-headless、kafka、kafka-ssl。三、核心组件逐项拆解3.1 StatefulSetKRaft 模式的载体statefulset.yaml 是整个 Chart 的心脏关键设计如下podManagementPolicy: Parallel所有 Pod 并行创建加快集群启动速度默认值来自 values.yamlserviceName指向 Headless Service这是 StatefulSet 获得稳定 DNS 身份的前提updateStrategy: RollingUpdate滚动升级 Brokerreplicas可配置默认 2通过--set replicaCount3即可扩容Pod 反亲和anti-affinitypreferredDuringSchedulingIgnoredDuringExecution按kubernetes.io/hostname拓扑键将不同 Kafka Pod 尽量调度到不同节点提升高可用性volumeClaimTemplates每个副本自动申请独立 PVC默认 100Gi、ReadWriteOnce数据目录挂载到/bitnami/kafka。容器环境变量的设计直接继承了参考清单KAFKA_NODE_ID取自 Pod 名保证kafka-0的节点 ID 恒为 0MY_POD_NAME、MY_POD_IP通过 downward API 注入。KRaft 相关变量在kraft.enabledtrue时注入- name: KAFKA_CLUSTER_ID value: Sjg_Rr1iQbO9xpahgDbYpQ - name: KAFKA_PROCESS_ROLES value: broker,controller # 单进程同时承担 broker 与 controller 角色 - name: KAFKA_CONTROLLER_QUORUM_VOTERS value: {{ include kafka.controllerQuorumVoters . | quote }} # 动态生成 - name: KAFKA_CONTROLLER_LISTENER_NAMES value: INTERNAL其中KAFKA_CLUSTER_ID必须是合法的 Base64 UUID可用kafka-storage.sh random-uuid生成processRoles采用broker,controller组合模式即每个 Broker 同时也是控制器投票者无需单独的控制器节点。存储、探针与资源限制同样参数化日志目录为/bitnami/kafka/logsemptyDir 卷livenessProbe与readinessProbe均以 TCP 探测kafka-client端口9092默认参数为 initialDelay 30s/20s、period 10s、failureThreshold 3/6资源请求默认 500m CPU / 1Gi 内存上限 2000m CPU / 2Gi 内存。3.2 Headless ServiceBroker 发现的基石service-headless.yaml 的关键点在于publishNotReadyAddresses: true。在 KRaft 模式下第一个 Broker 启动时会作为控制器参与选主此时 Pod 尚未就绪readiness 未通过但其他 Broker 必须能通过 DNS 找到它来完成集群引导——该开关正是为此而设。该 Service 按需暴露全部三个端口tcp-client: 9092、tcp-internal: 9093、tcp-ssl: 9094targetPort引用容器端口的命名引用kafka-client/kafka-internal/kafka-ssl与参考清单 k8s-kafka-kraft-tls.yaml 中 Headless Service 的端口设计一致。3.3 Client / SSL Serviceservice.yaml类型默认ClusterIP可用service.type覆盖端口取service.client.port9092sessionAffinity: Noneservice-ssl.yaml整体被{{- if .Values.listeners.ssl.enabled }}包裹未启用 TLS 时不会产生任何资源。两个 Service 的 selector 均同时匹配selectorLabels与componentLabels确保与 StatefulSet Pod 精确关联。3.4 ServiceAccount 与标签体系serviceaccount.yaml 支持serviceAccount.create开关、automountServiceAccountToken与 annotations并统一追加app.kubernetes.io/component: kafka组件标签。这套标签体系贯穿所有资源也是 QUICKSTART.md 中kubectl get pods -n db -l app.kubernetes.io/namekafka一类过滤命令得以生效的基础。3.5 Helm 辅助函数动态生成的四个关键模板_helpers.tpl 在标准 name/fullname/labels 函数之外新增了四个与 Kafka 强相关的模板函数kafka.componentLabels输出app.kubernetes.io/component: kafkakafka.controllerQuorumVoters动态生成仲裁投票者列表。对每个副本 i0 起生成节点ID全限定DNS:9093条目节点 ID 为i1DNS 为fullname-i.fullname-headless.namespace.svc.cluster.local最终用逗号连接。默认 2 副本时输出1kafka-0.kafka-headless.db.svc.cluster.local:9093,2kafka-1.kafka-headless.db.svc.cluster.local:9093这与参考清单中的硬编码值完全等价但随replicaCount自动伸缩kafka.advertisedListeners将CLIENT://${MY_POD_NAME}.fullname-headless.namespace.svc.cluster.local:9092及启用 TLS 时的SSL://...:9094组装为通告地址MY_POD_NAME在容器内展开确保客户端拿到的每个 Broker 地址都指向正确的 Podkafka.listeners/kafka.listenerSecurityProtocolMap按listeners.client/internal/ssl.enabled开关组装KAFKA_LISTENERSCLIENT://:9092,INTERNAL://:9093,SSL://:9094与协议映射CLIENT:PLAINTEXT,INTERNAL:PLAINTEXT,SSL:SSL。值得注意的是StatefulSet 中KAFKA_INTER_BROKER_LISTENER_NAME由listeners.client.enabled决定客户端监听启用时使用CLIENT否则回退到INTERNAL——与参考清单中基础版用CLIENT、TLS 版用INTERNAL的做法保持了行为兼容。四、values.yaml 全量配置参考values.yaml 是配置的唯一事实来源主要分段如下4.1 集群与镜像参数默认值说明replicaCount2Broker 副本数生产建议 3image.repositoryghcr.io/openreplay/kafkaKafka 镜像仓库image.tag3Kafka 主版本image.pullPolicyIfNotPresent拉取策略serviceAccount.createtrue是否创建 ServiceAccount4.2 KRaft 配置kraft: enabled: true clusterId: Sjg_Rr1iQbO9xpahgDbYpQ # 必须为合法 Base64 UUID processRoles: broker,controller controllerListenerNames: INTERNAL4.3 Listener 配置参数默认值说明listeners.client.enabled/port/protocoltrue/9092/PLAINTEXT客户端监听listeners.internal.enabled/port/protocoltrue/9093/PLAINTEXT内部broker↔broker、控制器监听listeners.ssl.enabled/port/protocolfalse/9094/SSL可选 SSL 监听4.4 TLS 配置tls: enabled: false secretName: kafka-tls-certs clientAuth: required # 客户端证书认证策略 endpointIdentificationAlgorithm: # 空字符串表示关闭主机名校验Secret 中期望的键为ca-cert.pem、kafka-{0,1,2,...}-cert.pem、kafka-{0,1,2,...}-key.pem即每个副本需持有独立证书。4.5 Kafka 服务端参数kafka:段该段与参考清单 k8s-kafka-kraft.yaml 中的环境变量一一对应均为字符串形式的引号值分组参数默认值说明消息大小messageMaxBytes/replicaFetchMaxBytes31457283MB单条消息与副本拉取上限保留策略logRetentionHours/logRetentionBytes/logSegmentBytes1687 天/10737418241GB/1073741824时间与大小双维度保留刷盘logFlushIntervalMessages/logFlushIntervalMs/logRetentionCheckIntervalMs10000/1000/300000消息数与毫秒触发刷盘副本因子defaultReplicationFactor/offsetsTopicReplicationFactor/transactionStateLogReplicationFactor/transactionStateLogMinIsr均为12 节点集群采用单副本性能numIoThreads/numNetworkThreads/numPartitions/numRecoveryThreadsPerDataDir8/3/1/1线程与分区数网络缓冲socketReceiveBufferBytes/socketRequestMaxBytes/socketSendBufferBytes102400/104857600/102400缓冲区字节数安全autoCreateTopicsEnable/deleteTopicEnable/allowEveryoneIfNoAclFound/superUserstrue/false/true/User:adminACL 与超级用户这些参数在 StatefulSet 中被渲染为KAFKA_*或KAFKA_CFG_*环境变量如KAFKA_MESSAGE_MAX_BYTES、KAFKA_CFG_NUM_IO_THREADS最终由镜像内的启动脚本转换为 Kafka server 配置。4.6 资源、持久化、亲和与探针resources.requests500m CPU / 1Gi 内存resources.limits2000m CPU / 2Gi 内存persistenceenabled: true、storageClass: 空则使用集群默认 StorageClass、accessModes: [ReadWriteOnce]、size: 100Gi、支持annotationsaffinity.podAntiAffinitypreferredDuringSchedulingIgnoredDuringExecutiontopologyKey 为kubernetes.io/hostname权重 1livenessProbe/readinessProbeTCP 探测kafka-client端口podManagementPolicy: Parallel、updateStrategy.type: RollingUpdate扩展钩子extraEnvVars、extraVolumes、extraVolumeMounts可注入自定义配置nodeSelector、tolerations支持调度约束。五、可选 TLS从证书注入到加密通信TLS 能力完全复刻并参数化了参考清单 k8s-kafka-kraft-tls.yaml 的实现。5.1 证书注入的 init container 原理当tls.enabledtrue时StatefulSet 会额外渲染一个setup-certsinit 容器statefulset.yaml 第 47–72 行POD_ID$(echo $POD_NAME | sed s/fullname-//) # 提取 Pod 序号 cp /tls-secret/ca-cert.pem /tls/ca-cert.pem cp /tls-secret/kafka-${POD_ID}-cert.pem /tls/server-cert.pem cp /tls-secret/kafka-${POD_ID}-key.pem /tls/server-key.pem chmod 644 /tls/*.pem chmod 600 /tls/server-key.pem即从名为tls.secretName的 Secret 中按 Pod 序号挑出对应的kafka-N-cert.pem/kafka-N-key.pem连同 CA 证书一起拷贝到共享的 emptyDir 卷/tls主容器以只读方式挂载该卷。Secret 卷以defaultMode: 0644挂载。随后注入 TLS 环境变量KAFKA_SSL_CERT_FILE/tls/server-cert.pem KAFKA_SSL_KEY_FILE/tls/server-key.pem KAFKA_SSL_CA_FILE/tls/ca-cert.pem KAFKA_SSL_CLIENT_AUTHrequired # 由 tls.clientAuth 控制 KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM # 空 关闭主机名校验从镜像侧看scripts/dockerfiles/kafka/Dockerfile 基于 Wolfi 基础镜像安装 Kafka 3、OpenSSL 与 tini入口脚本 start-kafka.sh 会检测到 PEM 证书后自动执行openssl pkcs12 -exportkeytool -importkeystore将 PEM 转换为 JKS keystore/truststore默认密码kafka-ssl-pass可用KAFKA_SSL_KEYSTORE_PASSWORD覆盖从而让 Kafka 原生支持 PEM 输入。5.2 证书生成与 Secret 创建可复用仓库自带的 generate-certs.sh脚本生成有效期 365 天的 CA 证书并为kafka-1、kafka-2即副本 0/1签发带 SANDNS:kafka-N, DNS:localhost, IP:127.0.0.1的证书输出到certs/目录。然后创建 Secretkubectl create secret generic kafka-tls-certs \ --from-fileca-cert.pem./certs/ca-cert.pem \ --from-filekafka-0-cert.pem./certs/kafka-0-cert.pem \ --from-filekafka-0-key.pem./certs/kafka-0-key.pem \ --from-filekafka-1-cert.pem./certs/kafka-1-cert.pem \ --from-filekafka-1-key.pem./certs/kafka-1-key.pem \ -n db5.3 TLS 客户端连接验证在启用 TLS 后客户端需要携带 CA 证书建立信任# 导出 CA 证书 kubectl get secret kafka-tls-certs -n db -o jsonpath{.data.ca-cert\.pem} | base64 -d ca-cert.pem # 客户端属性文件 cat client.properties EOL security.protocolSSL ssl.truststore.locationca-cert.pem ssl.truststore.typePEM EOL # 在测试 Pod 内验证bootstrap 指向 SSL Service kubectl run kafka-client --rm -it --imageconfluentinc/cp-kafka:latest --namespace db -- bash kafka-topics --bootstrap-server kafka-ssl.db.svc.cluster.local:9094 \ --command-config client.properties --list六、部署实战三种典型场景以下命令均以 Chart 目录scripts/helmcharts/databases/charts/kafka为执行上下文默认部署到db命名空间可用--create-namespace自动创建。6.1 基础部署无 TLShelm install kafka . --namespace db --create-namespace # 状态检查 kubectl get statefulset -n db kubectl get pods -n db -l app.kubernetes.io/namekafka kubectl get svc -n db -l app.kubernetes.io/namekafka安装完成后NOTES.txt会打印访问地址与集群概要副本数、KRaft 状态、Cluster ID、TLS 状态。6.2 TLS 部署helm install kafka . -f values-tls.yaml --namespace dbvalues-tls.yaml 是在默认值基础上只做最小改动启用tls.enabled: true、listeners.ssl.enabled: true、tls.clientAuth: required其余保持默认。6.3 自定义配置helm install kafka . \ --set replicaCount3 \ --set persistence.size200Gi \ --set resources.limits.memory4Gi \ --set resources.requests.memory2Gi \ --namespace db6.4 渲染验证与卸载# 仅渲染模板不实际部署 helm template test-kafka . helm template test-kafka . -f values-tls.yaml # 卸载注意不会删除 PVC helm uninstall kafka --namespace db # 如需连数据一起清理 kubectl delete pvc -n db -l app.kubernetes.io/namekafka实现摘要中还记录了从 databases 父 Chart 验证整体渲染的命令cd ../../../ make db-template从 chart 目录回到scripts/helmcharts执行当前仓库的 scripts/helmcharts/Makefile 提供有template渲染 openreplay 全量模板、install、clean等目标可供参考。6.5 扩缩容与监控排查# 扩缩容升级方式 helm upgrade kafka . --namespace db --set replicaCount3 # 监控 kubectl get pods -n db -w -l app.kubernetes.io/namekafka kubectl logs -n db kafka-0 -f kubectl describe pod kafka-0 -n db kubectl get pvc -n db -l app.kubernetes.io/namekafka # 连通性排查 kubectl run test-pod --rm -it --imagebusybox --namespace db -- sh # 容器内 nc -zv kafka.db.svc.cluster.local 9092 nc -zv kafka-0.kafka-headless.db.svc.cluster.local 9092七、与 databases 父 Chart 的集成该 Kafka Chart 已作为依赖挂载到 databases 聚合 Chart 中。在 scripts/helmcharts/databases/Chart.yaml 中dependencies: - name: kafka repository: file://charts/kafka version: 11.8.6 condition: kafka.enabled父 Chart 的 values.yaml 中维护着同构的kafka:配置块默认enabled: false并在 Chart 渲染时透传给子 Chart。这意味着在scripts/helmcharts/databases/values.yaml中将kafka.enabled置为true并按需调整kafka.replicaCount、kafka.persistence.size、kafka.tls等Kafka 配置段与父 Chart 的kafka:段保持字段一一对应通过make db-template或helm template验证整体渲染。此外上游 OpenReplay 应用 Chartscripts/helmcharts/openreplay/templates/job.yaml 第 551–595 行在升级时会启动一个 Kafka 迁移 Job使用kafkaMigration.image即ghcr.io/openreplay/kafka:3通过KAFKA_HOST、KAFKA_PORT、KAFKA_SSL、REPLICATION_FACTOR、RETENTION_TIME等环境变量连接 Kafka 并执行 topic 创建/保留策略等操作这些变量的默认值可在 scripts/helmcharts/vars.yaml 中看到kafkaHost: kafka.db.svc.cluster.local、kafkaPort: 9092、kafkaUseSsl: false、replicationFactor: 2——即默认期望集群部署在db命名空间客户端通过 Client Service 域名接入。八、实现验证与下一步实现摘要中记录的已验证能力包括StatefulSet 创建、三类 Service 创建、环境变量渲染、TLS init container、卷挂载、资源限制、控制器仲裁投票生成——均可通过helm template输出清单逐一核对。如果你要在此基础上继续演进可以从以下几个方面着手生产化加固将replicaCount提升至 3 并相应调高defaultReplicationFactor为persistence.storageClass指定高性能存储类启用 TLS 并为各组件下发 CA 信任监控告警接入 Prometheus/JMX exporter围绕 QUICKSTART.md 的生产清单建立告警与备份策略深度定制借助extraEnvVars注入任意KAFKA_CFG_*变量借助extraVolumes/extraVolumeMounts挂载自定义配置或监控探针。通过本文的源码级拆解可以看到这个 Chart 的价值不在于堆砌功能而在于将两份原生清单中的全部关键行为——KRaft 仲裁、Pod 级通告地址、TLS 证书按 Pod 分发——以 Helm 模板函数和参数化 values 的方式完整保留并做到了随副本数自动伸缩为 OpenReplay 自托管部署提供了开箱即用、可安全加密的消息基础设施。【免费下载链接】openreplaySession replay, cobrowsing and product analytics you can self-host. Best for reproducing issues and iterating on your product.项目地址: https://gitcode.com/gh_mirrors/op/openreplay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。