Kafka on Kubernetes 有状态应用部署文档(KRaft 模式)
适用版本:Kafka 3.9.0(KRaft 模式,无需 Zookeeper)
部署方式:StatefulSet + Headless Service(贴合有状态应用特性)
适用环境:测试/开发环境(单副本),生产环境可基于此优化为3副本高可用
命名空间:test-kafka
一、部署说明
-
Kafka 作为典型有状态应用,需通过 StatefulSet 保证 Pod 名称、网络标识、存储的稳定性,避免随机变化导致服务异常。
-
采用 KRaft 模式,无需依赖 Zookeeper,简化部署架构,降低维护成本。
-
当前为单副本部署,生产环境建议调整为3副本,提升服务可用性。
-
核心依赖 Headless Service(无头服务),为 Kafka Pod 提供固定域名解析,确保集群内通信稳定。
二、环境前置条件
-
Kubernetes 集群版本 ≥ 1.21,确保 StatefulSet、KRaft 相关特性支持。
-
已配置 kubectl 命令行工具,且能正常访问 K8s 集群(具备集群操作权限)。
-
提前创建命名空间 test-kafka(若未创建,可执行部署命令中的命名空间创建指令)。
三、核心部署文件(kafka-sts.yaml)
该文件包含 Kafka 有状态部署的全部核心资源(Headless Service + StatefulSet),无需拆分其他文件,直接部署即可。
yaml
---
# Kafka 无头服务(StatefulSet 必需,提供固定域名解析,不分配集群IP)
apiVersion: v1
kind: Service
metadata:
name: kafka-headless
namespace: test-kafka
labels:
app: kafka
spec:
selector:
app: kafka
ports:
- name: client
port: 9092
- name: internal
port: 19092
- name: controller
port: 29093
- name: external
port: 9094
clusterIP: None
---
# Kafka StatefulSet(有状态应用核心,保证 Pod 稳定运行)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
namespace: test-kafka
spec:
serviceName: kafka-headless # 关联无头服务,必需配置,保证域名生成规则固定
replicas: 1 # 单副本部署,生产环境建议改为3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
securityContext:
runAsUser: 1000
fsGroup: 1000
containers:
- name: kafka
image: apache/kafka:3.9.0 # Kafka 镜像版本,可按需升级
ports:
- containerPort: 9092
name: client
- containerPort: 19092
name: internal
- containerPort: 29093
name: controller
- containerPort: 9094
name: external
securityContext:
privileged: true
runAsUser: 1000
runAsGroup: 1000
env:
- name: TZ
value: "Asia/Shanghai" # 时区配置,避免时间异常
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: PODIP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: KAFKA_NODE_ID
value: "0" # 节点ID,单副本为0,多副本依次为0、1、2
- name: KAFKA_PROCESS_ROLES
value: "broker,controller" # 单节点同时作为 broker 和 controller
- name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
value: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT"
- name: KAFKA_LISTENERS
value: "CONTROLLER://:29093,PLAINTEXT://:9092,INTERNAL://:19092,EXTERNAL://:9094"
- name: KAFKA_ADVERTISED_LISTENERS
value: "PLAINTEXT://$(PODIP):9092,INTERNAL://$(HOSTNAME).kafka-headless.test-kafka.svc.cluster.local:19092,EXTERNAL://192.168.133.200:30094"
- name: KAFKA_CONTROLLER_QUORUM_VOTERS
value: "0@kafka-0.kafka-headless.test-kafka.svc.cluster.local:29093" # 单副本投票节点
- name: KAFKA_INTER_BROKER_LISTENER_NAME
value: "INTERNAL" # 集群内 broker 通信监听器
- name: KAFKA_CONTROLLER_LISTENER_NAMES
value: "CONTROLLER"
- name: CLUSTER_ID
value: "test-kafka" # 集群ID,自定义即可
- name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
value: "1" # 偏移量主题副本因子,单副本为1,多副本为3
- name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR
value: "1"
- name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR
value: "1"
- name: KAFKA_DEFAULT_REPLICATION_FACTOR
value: "1" # 新创建主题默认副本因子
- name: KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS
value: "3000" # 初始重平衡延迟,避免频繁触发
- name: KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR
value: "1"
- name: KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR
value: "1"
- name: KAFKA_NUM_PARTITIONS
value: "2" # 新主题默认分区数,提升并行处理能力
- name: KAFKA_LOG_RETENTION_HOURS
value: "168" # 日志保留7天,按需调整
- name: KAFKA_LOG_SEGMENT_BYTES
value: "1073741824" # 单个日志段1GB,避免段文件过多
- name: KAFKA_LOG_RETENTION_CHECK_INTERVAL_MS
value: "3600000" # 每1小时检查日志清理
- name: KAFKA_LOG_SEGMENT_DELETE_DELAY_MS
value: "60000" # 日志段标记删除后延迟1分钟物理删除
- name: KAFKA_AUTO_CREATE_TOPICS_ENABLE
value: "true" # 允许自动创建主题
- name: KAFKA_HEAP_OPTS
value: "-Xms1024m -Xmx1024m" # JVM堆内存,按需调整
- name: KAFKA_LOG_DIRS
value: "/var/lib/kafka/data/kafka-logs" # 日志存储路径
volumeMounts:
- name: kafka-data
mountPath: /var/lib/kafka/data # 挂载存储卷
livenessProbe: # 存活探针,检测服务是否正常运行
tcpSocket:
port: 9092
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
readinessProbe: # 就绪探针,检测服务是否可提供服务
tcpSocket:
port: 9092
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
resources: # 资源限制,按需调整
limits:
cpu: 4000m
memory: 4Gi
requests:
cpu: 500m
memory: 1Gi
# 存储配置:测试用emptyDir(Pod删除后数据丢失),生产需替换为PVC
volumes:
- name: kafka-data
emptyDir: {}
---
# 可选:NodePort服务(外部访问用,不影响有状态核心部署)
apiVersion: v1
kind: Service
metadata:
labels:
app: kafka
name: kafka-nodeport
namespace: test-kafka
spec:
ports:
- name: client
nodePort: 30094
port: 9094
protocol: TCP
targetPort: 9094
selector:
app: kafka
sessionAffinity: None
type: NodePort
四、一键部署与验证命令
复制以下命令,在终端执行,即可完成部署并验证状态,无需手动分步操作。
bash
# 1. 创建命名空间(若未创建)
kubectl create namespace test-kafka
# 2. 部署 Kafka 有状态应用(核心命令)
kubectl apply -f kafka-sts.yaml
# 3. 验证部署状态(查看StatefulSet、Pod、Service)
kubectl get sts kafka -n test-kafka
kubectl get pods -n test-kafka -l app=kafka
kubectl get svc -n test-kafka | grep kafka
验证标准:StatefulSet 状态 READY 为 1/1,Pod 状态为 Running,两个 Service(kafka-headless、kafka-nodeport)正常存在。
五、访问方式
5.1 集群内访问(推荐,适用于集群内其他服务调用)
-
服务访问:kafka-headless.test-kafka.svc.cluster.local:9092
-
固定Pod域名访问(有状态特性):kafka-0.kafka-headless.test-kafka.svc.cluster.local:19092
5.2 外部访问(可选,适用于本地客户端测试)
通过 NodePort 服务访问:192.168.133.200:30094(确保节点3009端口可被外部访问)
六、功能测试(验证 Kafka 可用性)
bash
# 1. 进入 Kafka Pod 内部
kubectl exec -it kafka-0 -n test-kafka -- /bin/bash
# 2. 创建测试主题(名称:test-topic,分区数2,副本数1)
kafka-topics.sh --create --topic test-topic --bootstrap-server localhost:9092 --partitions 2 --replication-factor 1
# 3. 发送测试消息
kafka-console-producer.sh --topic test-topic --bootstrap-server localhost:9092
# 输入消息后按回车发送,例如:hello kafka on k8s
# 4. 新开终端,消费测试消息(从开头消费)
kubectl exec -it kafka-0 -n test-kafka -- /bin/bash
kafka-console-consumer.sh --topic test-topic --bootstrap-server localhost:9092 --from-beginning
七、生产环境优化建议
- 数据持久化:将 emptyDir 替换为 volumeClaimTemplates(PVC模板),确保Pod删除/重启后数据不丢失,示例:
bash
volumeClaimTemplates:
- metadata:
name: kafka-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi # 按需调整存储大小
storageClassName: "your-storage-class" # 替换为集群可用的存
-
高可用部署:
-
将 replicas 改为3,确保多节点冗余。
-
调整 KAFKA_NODE_ID 为0、1、2(每个节点唯一)。
-
更新 KAFKA_CONTROLLER_QUORUM_VOTERS:0@kafka-0.kafka-headless.test-kafka.svc.cluster.local:29093,1@kafka-1.kafka-headless.test-kafka.svc.cluster.local:29093,2@kafka-2.kafka-headless.test-kafka.svc.cluster.local:29093
-
将所有副本因子相关配置(如 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR)改为3。
-
-
资源调整:根据业务压力,调整 resources.limits 和 resources.requests,避免资源不足或浪费。
-
安全优化:生产环境建议关闭 KAFKA_AUTO_CREATE_TOPICS_ENABLE,手动创建主题;替换 PLAINTEXT 协议为加密协议(如 SSL)。
八、常见问题排查
-
Pod启动失败:执行 kubectl describe pod kafka-0 -n test-kafka,查看事件日志,排查镜像拉取失败、资源不足、配置错误等问题。
-
域名解析失败:确认 StatefulSet 的 serviceName 与无头服务名称一致;检查集群 CoreDNS 组件是否正常运行(kubectl get pods -n kube-system | grep coredns)。
-
数据丢失:确认是否使用了 emptyDir 存储,生产环境需立即替换为 PVC。
-
外部访问失败:检查 NodePort 端口是否被防火墙拦截;确认节点IP(192.168.133.200)是否正确,且集群节点可被外部访问。
九、文档说明
-
部署文件 kafka-sts.yaml 可直接复制保存,无需修改即可用于测试环境部署,生产环境需按优化建议调整配置。
-
若无需外部访问,可删除文件中 NodePort 服务相关配置,不影响 Kafka 核心功能。
十、kafka可视化管理(kafka-ui)
bash
version: '3.3'
services:
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
restart: always
ports:
- "8080:8080" # 宿主机端口:容器端口,可自定义宿主机端口
environment:
# 配置Kafka集群(支持多个集群,用逗号分隔)
- KAFKA_CLUSTERS_0_NAME=kafka-cluster # 集群名称(自定义)
# #- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka-broker1:9092,kafka-broker2:9092 # Kafka Broker地址列表
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=10.0.12.10:9092 # 替换成kafka实际的连接地址
# 可选:若Kafka启用了SASL认证(如PLAIN/SCRAM),添加以下配置
# - KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL=SASL_PLAINTEXT
# - KAFKA_CLUSTERS_0_PROPERTIES_SASL_MECHANISM=PLAIN
# - KAFKA_CLUSTERS_0_PROPERTIES_SASL_JAAS_CONFIG=org.apache.kafka.common.security.plain.PlainLoginModule required username="admin" password="admin-secret";
# 可选:配置ZooKeeper地址(用于查看Broker元数据,非必须)
# - KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper-host1:2181,zookeeper-host2:2181
# 可选:设置默认主题创建策略(分区数、副本数)
# - KAFKA_CLUSTERS_0_TOPIC_CREATION_DEFAULTREPLICATIONFACTOR=3
# - KAFKA_CLUSTERS_0_TOPIC_CREATION_DEFAULTPARTITIONS=12


十一、docker-compose 快速部署kafka
bash
version: '3.3'
services:
broker:
image: apache/kafka:3.9.0
hostname: broker
container_name: broker
ports:
- '9092:9092'
# 新增:允许容器访问公网,且绑定所有网卡
network_mode: host
environment:
KAFKA_NODE_ID: 1
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT'
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT_HOST://10.0.12.10:9092,PLAINTEXT://localhost:19092'
KAFKA_PROCESS_ROLES: 'broker,controller'
KAFKA_CONTROLLER_QUORUM_VOTERS: '1@localhost:29093'
KAFKA_LISTENERS: 'CONTROLLER://localhost:29093,PLAINTEXT_HOST://0.0.0.0:9092,PLAINTEXT://localhost:19092'
KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT'
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
CLUSTER_ID: 'prod-kafka'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR: 1
KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR: 1
KAFKA_LOG_DIRS: '/var/lib/kafka/data'
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"