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

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

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


一、两套集群的设计思路

为什么要部署两套?内部集群走 PLAINTEXT 直连,简单高效;ETL 集群面向外部系统,必须走 SASL_PLAINTEXT 认证,保障数据边界安全。两者共用 rtghj-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: rtghj-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: rtghj-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,  rtghj01,  rtghj02, ...
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 rtghj-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 rtghj-kafka -o wide

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

# 进入 Pod
kubectl exec -it pod/kafka01-0 -n rtghj-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 rtghj-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 (同一节点)

阅读更多

Skills系统:可扩展AI能力设计

Skills系统:可扩展AI能力设计

概述 Skills系统是AI-Native架构中的重要组件,它允许通过声明式配置扩展AI的能力。本文将介绍Skills系统的设计与实现,让大模型能够像人类专家一样具备特定领域的能力。 什么是Skills系统 概念 Skills(技能)是一种声明式的AI能力扩展机制,类似于人类的"专业技能": 与Function Calling的区别 特性 Skills Function Calling 目的 改变AI的"思维"方式 扩展AI的"工具"能力 实现 系统提示词注入 API调用 持久性 会话级别 单次调用 复杂度 简单(配置) 复杂(开发) 灵活性 高(声明式) 低(编程式) 系统设计 架构 SKILL.

By 菱角
插件化架构设计模式

插件化架构设计模式

概述 插件化架构是一种将核心功能与扩展功能分离的设计模式,允许系统在运行时动态加载和卸载功能模块。本文将介绍如何在微服务平台中设计和实现插件化架构。 为什么需要插件化 插件化优势 1. 模块化:功能独立,边界清晰 2. 可扩展:按需加载,动态增删 3. 隔离性:插件间互不干扰 4. 可维护:独立开发、测试、部署 5. 可定制:用户按需选择功能 核心设计 架构概览 核心组件实现 1. 插件接口定义 // core/plugin.interface.ts // 插件接口 export interface IPlugin { // 插件名称 readonly name: string // 插件版本 readonly version: string // 插件配置 getConfig(): PluginConfig // 插件清单

By 菱角
gRPC服务通信设计与实践

gRPC服务通信设计与实践

概述 在微服务架构中,服务间通信是关键环节。相比REST API,gRPC提供了更高的性能和更强的类型安全。本文将介绍如何在微服务平台中设计和实现gRPC服务通信。 为什么选择gRPC gRPC vs REST对比 特性 gRPC REST 协议 HTTP/2 HTTP/1.1 序列化 Protocol Buffers (二进制) JSON (文本) 性能 高(二进制+压缩) 中(文本开销) 类型安全 强(代码生成) 弱(运行时检查) 流式通信 原生支持(双向流) 需额外实现(SSE/WebSocket) 代码生成 自动生成 手动编写 浏览器支持 需gRPC-Web 原生支持 调试难度

By 菱角
多语言微服务架构:Node.js与Python协作

多语言微服务架构:Node.js与Python协作

概述 在微服务架构中,根据场景选择最适合的编程语言是最佳实践。本文将介绍如何在微服务平台中实现Node.js与Python的协作,发挥各自技术优势。 技术选型策略 为什么混合使用 服务划分 Node.js服务(7个) 服务 功能 选择Node.js的原因 llm.api 大模型服务 高并发SSE流式响应 ucenter.api 用户中心 RESTful API标准实践 doc.api 文件服务 流式上传下载处理 resource.api 资源管理 gRPC高性能通信 rag.api 知识库服务 MongoDB集成便利 statistic.api 统计分析 事件驱动架构 pptonline.api PPT服务 与前端技术栈统一 Python服务(1个) 服务 功能 选择Python的原因

By 菱角