Kafka KRaft 集群在 Kubernetes 上的部署实践

Kafka KRaft 集群在 Kubernetes 上的部署实践

Kafka 3.6 引入了 KRaft 模式,不再依赖 ZooKeeper,让集群架构更简洁。本文记录在 K8s 上用 bitnami/kafka:3.6.2 部署两套 KRaft 集群的过程——一套用于内部服务通信,另一套带 SASL 认证对外暴露给 ETL 管线消费。


一、两套集群的设计思路

为什么要部署两套?内部集群走 PLAINTEXT 直连,简单高效;ETL 集群面向外部系统,必须走 SASL_PLAINTEXT 认证,保障数据边界安全。两者共用 csjg-kafka 命名空间,但 KAFKA_KRAFT_CLUSTER_ID 不同,互不干扰。

集群 用途 节点数 认证方式 数据目录
Internal 内部服务 kafka01/02/03 PLAINTEXT /home/kafka/data
ETL 外部消费 kafkaetl01/02/03 SASL_PLAINTEXT /home/kafka/data/1/2/3

ETL 集群三个 Pod 跑在同一台机器上(nodeSelector: kafka-server=etl-01),通过不同端口区分(321xx / 322xx / 323xx)。


二、KRaft 核心概念

KRaft 模式下每个节点同时担任 controllerbroker(由 KAFKA_CFG_PROCESS_ROLES 控制),集群通过 KAFKA_CFG_CONTROLLER_QUORUM_VOTERS 互相发现,不再需要独立的 ZooKeeper。

三个关键参数必须全局一致

参数 说明
KAFKA_KRAFT_CLUSTER_ID 集群唯一标识,用 Base64 生成,同一集群内所有节点必须相同
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS Controller 投票成员列表,格式 id@host:port
KAFKA_CFG_PROCESS_ROLES 节点角色,controller,broker 表示兼具两种角色

生成 Cluster ID 的方法:

# 在任意节点上运行
/opt/bitnami/kafka/bin/kafka-storage.sh random-uuid

三、内部集群配置

kafka.yaml 定义了 3 个 StatefulSet,每个对应一个 Kafka 节点,绑定到不同 K8s 节点。

3.1 kafka01 节点配置

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka01
  namespace: csjg-kafka
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka01
  template:
    spec:
      hostNetwork: true
      hostAliases:
      - ip: "<KAFKA_NODE_IP_1>"
        hostnames: ["kafka01"]
      - ip: "<KAFKA_NODE_IP_2>"
        hostnames: ["kafka02"]
      - ip: "<KAFKA_NODE_IP_3>"
        hostnames: ["kafka03"]
      containers:
      - name: kafka01
        env:
        - name: KAFKA_ENABLE_KRAFT
          value: "yes"
        - name: KAFKA_CFG_NODE_ID
          value: "1"
        - name: KAFKA_KRAFT_CLUSTER_ID
          value: "<YOUR_CLUSTER_ID>"
        - name: KAFKA_CFG_PROCESS_ROLES
          value: "controller,broker"
        - name: KAFKA_CFG_CONTROLLER_QUORUM_VOTERS
          value: "1@kafka01:9094,2@kafka02:9094,3@kafka03:9094"
        - name: KAFKA_HEAP_OPTS
          value: "-Xmx4g -Xms4g"
        - name: KAFKA_CFG_LISTENERS
          value: "PLAINTEXT://:9092,CONTROLLER://kafka01:9094,EXTERNAL://:9093"
        - name: KAFKA_CFG_ADVERTISED_LISTENERS
          value: "PLAINTEXT://kafka01:9092,EXTERNAL://kafka-server01:9093"
        - name: KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP
          value: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT"
        - name: KAFKA_CFG_CONTROLLER_LISTENER_NAMES
          value: "CONTROLLER"
        - name: KAFKA_CFG_INTER_BROKER_LISTENER_NAME
          value: "PLAINTEXT"
        - name: KAFKA_CFG_MESSAGE_MAX_BYTES
          value: "10485760"
        - name: KAFKA_CFG_REPLICA_FETCH_MAX_BYTES
          value: "20485760"
        image: <YOUR_REGISTRY>/library/bitnami/kafka:3.6.2
        ports:
        - containerPort: 9091
        - containerPort: 9092
        - containerPort: 9093
        - containerPort: 9094
        volumeMounts:
        - name: kafka-data
          mountPath: /bitnami/kafka/
      volumes:
      - name: kafka-data
        hostPath:
          path: /home/kafka/data/
      nodeSelector:
        kafka-server: "01"

3.2 端口说明

端口 监听器 协议 用途
9091 JMX 监控指标
9092 PLAINTEXT PLAINTEXT 集群内部通信
9093 EXTERNAL PLAINTEXT 对外访问(Service ClusterIP 映射)
9094 CONTROLLER PLAINTEXT KRaft Controller 投票通信

3.3 kafka02 / kafka03 的差异

三个节点配置几乎相同,只需要改以下几处:

差异项 kafka01 kafka02 kafka03
KAFKA_CFG_NODE_ID 1 2 3
KAFKA_CFG_LISTENERS (CONTROLLER) kafka01:9094 kafka02:9094 kafka03:9094
KAFKA_CFG_ADVERTISED_LISTENERS kafka01:9092 kafka02:9092 kafka03:9092
nodeSelector kafka-server: "01" kafka-server: "02" kafka-server: "03"

四、ETL 对外集群配置

ETL 集群面向外部系统,开启了 SASL_PLAINTEXT 认证。三个 Pod 部署在同一台节点上,通过不同端口隔离。

4.1 kafkaetl01 节点配置

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafkaetl01
  namespace: csjg-kafka
spec:
  replicas: 1
  template:
    spec:
      hostNetwork: true
      hostAliases:
      - ip: "<ETL_NODE_IP>"
        hostnames: ["kafkaetl01"]
      - ip: "<ETL_NODE_IP>"
        hostnames: ["kafkaetl02"]
      - ip: "<ETL_NODE_IP>"
        hostnames: ["kafkaetl03"]
      containers:
      - name: kafkaetl01
        env:
        - name: KAFKA_ENABLE_KRAFT
          value: "yes"
        - name: KAFKA_CFG_NODE_ID
          value: "1"
        - name: KAFKA_KRAFT_CLUSTER_ID
          value: "<YOUR_ETL_CLUSTER_ID>"
        - name: KAFKA_CFG_PROCESS_ROLES
          value: "controller,broker"
        - name: KAFKA_CFG_CONTROLLER_QUORUM_VOTERS
          value: "1@kafkaetl01:32193,2@kafkaetl02:32293,3@kafkaetl03:32393"
        - name: KAFKA_HEAP_OPTS
          value: "-Xmx4g -Xms4g"
        - name: KAFKA_CFG_LISTENERS
          value: "PLAINTEXT://:32192,CONTROLLER://kafkaetl01:32193,EXTERNAL://:32194"
        - name: KAFKA_CFG_ADVERTISED_LISTENERS
          value: "PLAINTEXT://kafkaetl01:32192,EXTERNAL://kafkaetl-server01:32194"
        - name: KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP
          value: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:SASL_PLAINTEXT"
        - name: KAFKA_CFG_SASL_ENABLED_MECHANISMS
          value: "PLAIN"
        - name: KAFKA_CLIENT_USERS
          value: "<USER1>,<USER2>,..."
        - name: KAFKA_CLIENT_PASSWORDS
          value: "<PASS1>,<PASS2>,..."
        - name: KAFKA_CFG_LOG_RETENTION_HOURS
          value: "2160"
        image: <YOUR_REGISTRY>/library/bitnami/kafka:3.6.2
        ports:
        - containerPort: 32191
        - containerPort: 32192
        - containerPort: 32193
        - containerPort: 32194
        volumeMounts:
        - name: kafka-data
          mountPath: /bitnami/kafka/
      volumes:
      - name: kafka-data
        hostPath:
          path: /home/kafka/data/1/
      nodeSelector:
        kafka-server: "etl-01"

4.2 ETL 集群端口分配

节点 JMX PLAINTEXT CONTROLLER EXTERNAL (SASL) 数据目录
kafkaetl01 32191 32192 32193 32194 /home/kafka/data/1/
kafkaetl02 32291 32292 32293 32294 /home/kafka/data/2/
kafkaetl03 32391 32392 32393 32394 /home/kafka/data/3/

为什么三个 Pod 在同一台机器? ETL 集群消费量不大,单机部署节省资源。通过不同端口和不同数据目录(data/1/data/2/data/3/)确保数据隔离。

4.3 SASL 认证配置

KAFKA_CLIENT_USERSKAFKA_CLIENT_PASSWORDS 用逗号分隔,一一对应:

users:    localuser, cicc01,  csjg01,  csjg02, ...
passwords: <PASS1>,  <PASS2>, <PASS3>, <PASS4>, ...

客户端连接时需配置 sasl.mechanism=PLAIN 和对应的用户名密码。

4.4 消息保留策略

KAFKA_CFG_LOG_RETENTION_HOURS=2160 即 90 天,适用于 ETL 场景中的历史数据回溯需求。


五、部署步骤

5.1 内部集群

# 为节点打标签
kubectl label nodes <NODE_1> kafka-server=01 kafka-app=true
kubectl label nodes <NODE_2> kafka-server=02 kafka-app=true
kubectl label nodes <NODE_3> kafka-server=03 kafka-app=true

# 创建数据目录
mkdir -p /home/kafka/data
chmod -R 777 /home/kafka/data

# 创建命名空间
kubectl create namespace csjg-kafka

# 部署
kubectl apply -f kafka.yaml

5.2 ETL 集群

# 为 ETL 节点打标签
kubectl label nodes <ETL_NODE> kafka-server=etl-01

# 创建三个数据目录
mkdir -p /home/kafka/data/1 /home/kafka/data/2 /home/kafka/data/3
chmod -R 777 /home/kafka/data

# 部署
kubectl apply -f kafka-etl.yaml

5.3 验证

kubectl get pod -n csjg-kafka -o wide

# 查看某个 Pod 的日志
kubectl -n csjg-kafka logs kafka01-0 --all-containers=true

# 进入 Pod
kubectl exec -it pod/kafka01-0 -n csjg-kafka -- bash

六、常用运维命令

进入 Kafka Pod 后可使用的工具:

# 列出所有 Consumer Group
/opt/bitnami/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka01:9092 --list

# 查看 Consumer Group 详情
/opt/bitnami/kafka/bin/kafka-consumer-groups.sh --describe --group <GROUP_NAME> --bootstrap-server kafka01:9092

# 查看 Topic 详情
/opt/bitnami/kafka/bin/kafka-topics.sh --describe --topic <TOPIC_NAME> --bootstrap-server kafka01:9092

# 重置 Consumer Offset(跳到最新)
/opt/bitnami/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka01:9092 \
  --group <GROUP_NAME> --reset-offsets --all-topics --to-latest --execute

七、注意事项

  1. KAFKA_KRAFT_CLUSTER_ID 必须一致 — 同一集群内所有节点使用相同的 ID,否则无法加入集群
  2. hostAliases 必须完整 — 每个节点都需要知道所有其他节点的主机名映射
  3. hostNetwork 端口冲突 — 确保宿主机上 9091-9094 和 321xx-323xx 端口未被占用
  4. JVM 堆内存 — 建议生产环境 XmsXmx 设为相同值,4g 是最低推荐
  5. 数据目录隔离 — ETL 集群在同一台机器上,必须用不同的 hostPath 子目录
  6. SASL 密码安全KAFKA_CLIENT_PASSWORDS 中包含明文密码,建议通过 K8s Secret 管理
  7. advertised_listeners 准确性 — 客户端通过 advertised.listeners 连接,主机名必须可达

附录:资源清单

资源类型 名称 数量
Namespace csjg-kafka 1
Service kafka01 (ClusterIP :9093) 1
StatefulSet kafka01 / kafka02 / kafka03 3
StatefulSet kafkaetl01 / kafkaetl02 / kafkaetl03 3
hostPath Volume 内部集群 data 每节点 1
hostPath Volume ETL 集群 data/1/2/3 3 (同一节点)

阅读更多

Ghost 邮箱订阅功能在中国大陆的困境:Mailgun 注册受阻实录

Ghost 邮箱订阅功能在中国大陆的困境:Mailgun 注册受阻实录

本文记录了 Ghost 博客邮箱订阅功能因 Mailgun 在中国大陆无法注册而被迫关闭的完整过程,包括注册尝试、工单沟通及最终应对方案。 一、背景:Ghost 与 Mailgun Ghost 博客平台内置了邮箱订阅(Newsletter)功能,读者可以通过输入邮箱地址订阅博客更新,Ghost 会自动将新文章以邮件形式推送给订阅者。这一功能的邮件发送能力依赖于第三方邮件服务——Mailgun。 Ghost 官方推荐使用 Mailgun 作为邮件传输层,配置方式如下: 配置项 说明 示例值 mail.from 发件人地址 newsletter@yourdomain.com mail.transport 传输协议 SMTP mail.options.host SMTP 主机 smtp.mailgun.org mail.options.port

By 菱角
Kubernetes 全栈监控体系:kube-prometheus-stack + 7 大 Exporter 生产实践

Kubernetes 全栈监控体系:kube-prometheus-stack + 7 大 Exporter 生产实践

本文基于 kube-prometheus-stack Helm Chart 在 Kubernetes 上构建完整监控体系的实战,覆盖 Prometheus、Grafana、Alertmanager 及 7 个专用 Exporter 的部署与配置。 一、监控体系全景架构 整套监控以 kube-prometheus-stack 为核心,通过 ServiceMonitor CRD 自动发现并采集 7 大数据源,配合 Alertmanager 分级告警与 Grafana 可视化: 组件 版本 作用 Prometheus v2.45.0 指标采集与存储,10 天数据保留 Alertmanager v0.25.0 告警路由与邮件推送,2 副本 HA

By 菱角
基于 Docker Compose 部署 Apache APISIX:全栈 API 网关实践

基于 Docker Compose 部署 Apache APISIX:全栈 API 网关实践

本文基于 APISIX 3.9.1 + Dashboard 3.0.1 + etcd 3.5.15 + Prometheus + Grafana 的生产级 Docker Compose 部署实践。 一、整体架构 本次部署采用 Docker Compose 编排五大组件,形成从配置管理到流量监控的完整闭环: 组件 版本 作用 端口 APISIX 3.9.1-debian API 网关核心,路由转发、插件执行 9080/9443 Dashboard 3.0.1-alpine 可视化配置管理界面 9000 etcd 3.5.15

By 菱角
在 Kubernetes 上部署 Apache Flink:生产实战指南

在 Kubernetes 上部署 Apache Flink:生产实战指南

本文基于 Flink 1.15.2 + Kubernetes 的生产环境部署实践。 一、架构概览 Flink 在 Kubernetes 上采用经典的 JobManager + TaskManager 主从架构,JobManager 负责作业调度与协调,TaskManager 承载实际的计算任务。 资源分配一览 组件 作用 副本数 Task Slots 内存配置 JobManager 作业调度、Checkpoint 管理、协调 1 — 8G TaskManager 执行计算任务 3 80/Pod 48G 总计 — 4 240 — 二、Namespace 隔离 为 Flink 创建独立 Namespace,

By 菱角