拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kafka KRaft 模式 Docker Compose 部署手册——筑梦之路

Kafka KRaft 模式 Docker Compose 部署手册——筑梦之路

Kafka KRaft 模式 Docker Compose 部署手册(apache/kafka 官方镜像)

目标:用Apache 官方镜像apache/kafka:4.3.1以Docker Compose方式部署KRaft(无 ZooKeeper)Kafka。
随本手册交付 4 个可直接使用的文件:

文件内容适用场景
docker-compose.env.example变量模板(镜像、集群 ID、对外地址、端口)所有编排文件共用,先复制为.env
docker-compose-single-node.yml单容器(broker + controller 合一)本地开发、功能测试、单机小规模
docker-compose-cluster-3node.yml3 容器 combined 集群,可容 1 节点故障小规模生产、预发、联调
docker-compose-isolated.yml3 controller + 3 broker,角色分离生产取向,可独立扩缩容

一、方案概览

拓扑容器数副本因子容错备注
单节点11无开发测试;KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1
三节点 combined33容 1 节点broker 与 controller 同进程,最简单的高可用形态
角色分离63controller 容 1、broker 容 1controller 不受业务流量影响,可分别扩缩容

三个编排文件互不冲突(项目名分别为kafka-single/kafka-cluster/kafka-isolated),但同一端口不能同时占用(单节点用 9092,三节点用 29092/39092/49092),按需启动其中一个即可。


二、前置条件

  1. Docker ≥ 20.10.4(必须)。低于该版本时容器创建/opt/kafka/config等目录的权限不正确,启动会直接报错退出:
    ===> Configuring …之后跟Running in KRaft mode… /opt/kafka/config/ file not writable。
  2. Docker Compose v2(docker compose子命令形式)。
  3. 建议宿主机:单节点 2C2G 起;三节点 4C8G 起;数据盘按业务量预留(Kafka 吃磁盘顺序写,SSD 最佳)。
  4. 端口占用检查:单节点9092;三节点29092 / 39092 / 49092;角色分离再加 broker 的同样三个端口(controller 不暴露端口)。
  5. 宿主机时钟同步(KRaft 对时钟敏感)。

三、关键设计说明(为什么这么写)

1. 镜像与启动流程

官方镜像的启动命令是/etc/kafka/docker/run(Dockerfile 中由CMD指定)。它依次做三件事:

  1. configureDefaults:为未设置的变量填默认值(包括CLUSTER_ID,镜像内置了一个默认集群 ID);
  2. configure:校验必需变量(CLUSTER_ID必填、controller-only 节点不允许设置KAFKA_ADVERTISED_LISTENERS等);
  3. launch:调用kafka.docker.KafkaDockerWrapper setup把「默认配置 + 挂载配置 +KAFKA_*环境变量」合并写入/opt/kafka/config/server.properties,并在数据目录未格式化时自动格式化(已格式化则跳过并打印already formatted),最后启动 broker。

也就是说:不需要手动执行kafka-storage.sh format,镜像会自己处理。

2. 环境变量命名规则

配置项 → 环境变量的转换规则:.→_、_→__、-→___,再统一加前缀KAFKA_。

配置项环境变量
node.idKAFKA_NODE_ID
log.dirsKAFKA_LOG_DIRS
offsets.topic.replication.factorKAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
abc-defKAFKA_ABC___DEF
abc_defKAFKA_ABC__DEF

KAFKA_HEAP_OPTS、KAFKA_OPTS、KAFKA_JMX_*、KAFKA_LOG4J_*属于脚本特殊处理的变量,不遵循上述映射。

3. 三种配置注入方式(优先级从低到高)

  1. 内置默认配置(镜像自带的单节点 combined 配置);
  2. 挂载配置文件:把*.properties挂到容器/mnt/shared/config,会覆盖默认配置;
  3. 环境变量:优先级最高,覆盖同名的文件配置。

注意:即使用挂载文件方式,CLUSTER_ID、KAFKA_NODE_ID、KAFKA_LISTENERS、KAFKA_CONTROLLER_QUORUM_VOTERS这类被启动脚本直接读取的项仍需用环境变量提供。

4. 监听器模型(Compose 部署最容易踩的坑)

容器内用三个监听器把「集群内部」「控制器」「宿主机客户端」彻底分开:

监听器用途是否映射到宿主机是否出现在 advertised 列表
PLAINTEXTbroker 之间、容器之间通信否是(用容器名,如kafka-1:19092)
CONTROLLERKRaft 控制器通信否否(控制器不是客户端入口)
PLAINTEXT_HOST宿主机/外部客户端是是(用宿主机 IP/域名 + 映射端口)

advertised.listeners里的地址必须是客户端真正能连到的地址。客户端第一次连上 bootstrap 后,会拿到 broker 自报的地址并直连,所以:

  • 客户端在本机 →localhost;
  • 客户端在局域网其他机器 → 宿主机内网 IP;
  • 客户端在公网 → 公网 IP/域名。

填错的典型症状是「能连上 bootstrap,随后超时/连接被拒」。本手册已把这一项抽成.env里的HOST_ADVERTISED_HOST,改一处即可。

5. 数据持久化与权限

  • 镜像内建用户appuser(uid=1000 / gid=1000),进程以该用户运行。
  • 官方示例把KAFKA_LOG_DIRS设为/tmp/kraft-combined-logs,该路径在容器可写层内,容器一删数据即丢。本手册统一改为/var/lib/kafka/data并挂载数据卷(镜像已预建该目录并声明为VOLUME)。
  • 用命名卷(named volume):Docker 会按镜像内的属主初始化,开箱即用,无需处理权限。
  • 用宿主机目录(bind mount):必须先改属主,否则启动报权限错误:
    sudomkdir-p/data/kafka&&sudochown-R1000:1000 /data/kafka

6. 容器内路径速查

路径用途
/opt/kafka/bin/所有 Kafka 命令行工具(kafka-topics.sh、kafka-metadata-quorum.sh等)
/opt/kafka/config/server.properties启动脚本合并生成的最终配置(排查配置问题时看这里)
/etc/kafka/docker/镜像自带的默认配置与启动脚本
/mnt/shared/config/用户挂载的配置文件目录(覆盖默认配置)
/etc/kafka/secrets/证书、JAAS 等敏感文件目录(SSL/SASL 用)
/var/lib/kafka/data数据目录(KAFKA_LOG_DIRS)

四、部署步骤

第 1 步:准备变量文件

cpdocker-compose.env.example .env# 至少修改两处:# CLUSTER_ID —— 用下面命令生成# HOST_ADVERTISED_HOST —— 客户端所在机器能访问到的主机名/IP

生成集群 ID:

dockerrun--rmapache/kafka:4.3.1 /opt/kafka/bin/kafka-storage.sh random-uuid

输出形如4L6g3nShT-eMCtK--X86sw,填进.env。同一集群的所有节点必须一致;集群 ID 只在首次格式化时写入,后续更换必须清空数据卷。

第 2 步:校验编排文件(可选但推荐)

dockercompose-fdocker-compose-single-node.yml config

该命令只做解析与变量替换,能提前发现语法/变量问题,不会启动容器。

第 3 步:启动

# 单节点dockercompose-fdocker-compose-single-node.yml up-d# 三节点 combined 集群dockercompose-fdocker-compose-cluster-3node.yml up-d# 角色分离(3 controller + 3 broker)dockercompose-fdocker-compose-isolated.yml up-d

第 4 步:等待就绪

dockercompose-fdocker-compose-cluster-3node.ymlps# 期望 STATUS 为 healthydockercompose-fdocker-compose-cluster-3node.yml logs-fkafka-1

日志中出现Kafka Server started即为启动成功;首次启动还会看到格式化数据目录的相关输出。


五、验证

1. 容器内自检(推荐,最省事)

dockercompose-fdocker-compose-cluster-3node.ymlexeckafka-1bash

容器内依次执行:

# 1) KRaft 元数据 quorum 状态:应看到 LeaderId、3 个 voter、MaxFollowerLag=0/opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server localhost:19092 describe--status# 2) 复制明细:3 个节点 LogEndOffset 应一致、Lag 全为 0/opt/kafka/bin/kafka-metadata-quorum.sh --bootstrap-server localhost:19092 describe--replication# 3) 建 topic(三节点用 3 副本,单节点用 1)/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:19092\--create--topicdemo--partitions3--replication-factor3# 4) 确认分区分布/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:19092--describe--topicdemo# 5) 生产 / 消费/opt/kafka/bin/kafka-console-producer.sh --bootstrap-server localhost:19092--topicdemo /opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:19092\--topicdemo --from-beginning --max-messages5# 6) 数据面健康检查:两条都应无输出/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:19092--describe--under-replicated-partitions /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:19092--describe--unavailable-partitions

2. 宿主机/其他容器验证

# 另起一个临时客户端容器,接入同一网络(网络名 = 项目名 + _default)dockerrun--rm-it--networkkafka-cluster_default apache/kafka:4.3.1\/opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka-1:19092--list# 宿主机上若装有 Kafka CLI,可直接用映射端口kafka-topics.sh --bootstrap-server localhost:29092--list

3. 故障演练(三节点)

dockercompose-fdocker-compose-cluster-3node.yml stop kafka-2# 观察 quorum 仍正常、topic 仍可读写dockercompose-fdocker-compose-cluster-3node.yml start kafka-2

六、客户端接入

客户端位置bootstrap 地址前提
宿主机上的进程localhost:9092(单节点)/localhost:29092(三节点任一).env中HOST_ADVERTISED_HOST=localhost
同一 Compose 网络内的容器kafka:19092/kafka-1:19092加入同一网络,且 advertised 的容器名可解析
局域网其他机器<宿主机IP>:29092.env中HOST_ADVERTISED_HOST=宿主机IP,且防火墙放行
公网客户端<公网IP或域名>:29092需要公网映射 +务必启用 SASL_SSL

安全提醒:PLAINTEXT暴露到公网等于无认证无加密。生产环境请改用SASL_SSL:
把证书与 JAAS 文件挂载到/etc/kafka/secrets,用KAFKA_OPTS=-Djava.security.auth.login.config=/etc/kafka/secrets/<jaas文件>指定 JAAS,
再用KAFKA_SSL_KEYSTORE_FILENAME、KAFKA_SSL_KEYSTORE_CREDENTIALS、KAFKA_SSL_TRUSTSTORE_FILENAME等变量提供证书(镜像脚本会自动补全路径与密码)。


七、日常运维

查看日志

dockercompose-fdocker-compose-single-node.yml logs-fkafkadockercompose-fdocker-compose-single-node.yml logs--tail=200kafka

重启与停止

dockercompose-fdocker-compose-single-node.yml restart kafka# 重启单个服务dockercompose-fdocker-compose-single-node.yml stop# 停止(保留容器与数据卷)dockercompose-fdocker-compose-single-node.yml down# 删除容器与网络(数据卷保留)dockercompose-fdocker-compose-single-node.yml down-v# 连数据卷一起删(数据全丢,慎用)

升级镜像版本

三节点/角色分离场景不要一次性up -d全量重建,应逐个滚动:

# 1) 改 .env 中的 KAFKA_IMAGE# 2) 逐个重建并等待健康,再处理下一个dockercompose-fdocker-compose-cluster-3node.yml pull kafka-1dockercompose-fdocker-compose-cluster-3node.yml up-d--no-deps --force-recreate kafka-1dockercompose-fdocker-compose-cluster-3node.ymlpskafka-1# 等到 healthy 再继续# 依次对 kafka-2、kafka-3 重复

扩缩容

  • 角色分离模式:broker 层直接加服务即可(新 broker 用新的node.id与端口,KAFKA_CONTROLLER_QUORUM_VOTERS不用改,因为 broker 不是 voter);controller 数量建议保持奇数(3 或 5)。
  • combined 模式:加节点等于同时加一个 controller,必须把所有节点的KAFKA_CONTROLLER_QUORUM_VOTERS一起更新为新列表再逐个重建,期间集群会有短暂不可用。
  • 缩容:先迁移分区副本(kafka-reassign-partitions.sh),再停容器;数据卷不会自动删除,需手动清理。

备份与恢复

# 逻辑备份(推荐):用 MirrorMaker 2 复制到另一个集群# 物理备份:停容器后打包数据卷dockercompose-fdocker-compose-single-node.yml stop kafkadockerrun--rm-vkafka-single_kafka-data:/data-v"$PWD":/backup alpine\tarczf /backup/kafka-data-$(date+%F).tar.gz-C/data.dockercompose-fdocker-compose-single-node.yml start kafka

监控(JMX)

镜像的启动脚本支持通过环境变量开启 JMX:

environment:KAFKA_JMX_PORT:9099KAFKA_JMX_HOSTNAME:${HOST_ADVERTISED_HOST:-localhost}ports:-"9099:9099"

需要指标接入 Prometheus 时,可用KAFKA_OPTS挂 JMX Exporter 的 javaagent,或部署独立的 kafka-exporter 容器。


八、生产加固清单

  • 副本与一致性:KAFKA_DEFAULT_REPLICATION_FACTOR=3、KAFKA_MIN_INSYNC_REPLICAS=2;生产者acks=all;unclean.leader.election.enable=false。
  • 关闭自动建 Topic:KAFKA_AUTO_CREATE_TOPICS_ENABLE=false(清单已设置),避免误建单副本 topic。
  • 资源与 JVM:KAFKA_HEAP_OPTS取容器内存上限的约 1/2,其余留给页缓存;内存 limits 过小会被 OOM Kill。
  • 文件句柄:ulimits.nofile调到 65536 以上(清单已设置)。
  • 重启策略:restart: unless-stopped(清单已设置);宿主机重启后自动恢复。
  • 优雅退出:stop_grace_period: 60s(清单已设置),保证关停前完成落盘。
  • 存储:SSD/独立数据盘;配置磁盘使用率告警,磁盘写满会导致 broker 不可用。
  • 安全:启用 SASL/SSL;/etc/kafka/secrets只读挂载;对外只暴露必要端口。
  • 监控:JMX 或 kafka-exporter;重点看 UnderReplicatedPartitions、OfflinePartitionsCount、ActiveControllerCount、请求延迟、磁盘使用率。
  • 日志与保留:按业务量调整KAFKA_LOG_RETENTION_HOURS、log.segment.bytes;可用KAFKA_LOG4J_ROOT_LOGLEVEL调整日志级别。
  • 备份容灾:跨集群用 MirrorMaker 2;关键 topic 单独设置保留策略。
  • 配置版本化:.env与 compose 文件纳入 Git 管理(注意不要把密钥提交进仓库)。

九、常见问题排查

现象原因处理
客户端能连上 bootstrap,随后超时/连接被拒advertised.listeners里的地址客户端不可达(如填了localhost)改.env的HOST_ADVERTISED_HOST为客户端可达地址后重建容器
启动即退出,日志KAFKA_ADVERTISED_LISTENERS is not supported on a KRaft controller.controller-only 节点设置了KAFKA_ADVERTISED_LISTENERS从该服务删除此变量(本手册 isolated 文件已规避)
日志/opt/kafka/config/ file not writableDocker 版本 < 20.10.4升级 Docker
bind mount 后报Permission denied写数据目录宿主机目录属主不是 uid 1000sudo chown -R 1000:1000 /data/kafka
容器重建后数据全没了没设KAFKA_LOG_DIRS到挂载卷,用了默认的/tmp/kraft-combined-logs设置KAFKA_LOG_DIRS=/var/lib/kafka/data并挂卷
改了CLUSTER_ID后启动失败 / 集群 ID 不一致数据目录已用旧集群 ID 格式化过清空数据卷后重新启动
端口被占用(bind: address already in use)宿主机端口冲突改.env的HOST_KAFKA_PORT或 compose 里的映射端口
三节点中某个 broker 起不来KAFKA_CONTROLLER_QUORUM_VOTERS与实际hostname/node.id不匹配逐项核对三个服务的 hostname、KAFKA_NODE_ID、voters 列表
容器状态一直unhealthy但业务正常健康检查每次要启动一个 JVM,首次启动或负载高时超时调大healthcheck.timeout/retries/start_period
改了.env但配置没生效容器未重建(环境变量只在创建时注入)docker compose up -d --force-recreate <service>

常用排查命令:

dockercompose-fdocker-compose-single-node.ymlpsdockercompose-fdocker-compose-single-node.yml logs--tail=100kafkadockercompose-fdocker-compose-single-node.ymlexeckafkacat/opt/kafka/config/server.propertiesdockercompose-fdocker-compose-single-node.ymlexeckafkals-l/var/lib/kafka/datadockerinspect kafka--format'{{json .State.Health}}'dockervolumels|grepkafka

十、参考来源

  • Apache Kafka 官方 Docker 页面(镜像与版本):https://kafka.apache.org/43/getting-started/docker/
  • Apache Kafka 官方 Docker 镜像使用指南(三种配置方式、环境变量命名规则、SASL/SSL、集群 ID):https://github.com/apache/kafka/blob/trunk/docker/examples/README.md
  • 官方 Compose 示例(单节点 / combined 集群 / isolated 集群):https://github.com/apache/kafka/tree/trunk/docker/examples/docker-compose-files
  • Apache Kafka 4.3.1 发布公告:https://kafka.apache.org/blog/2026/06/25/apache-kafka-4.3.1-release-announcement/
  • KRaft 运维文档(quorum 状态查看、controller 增删):https://kafka.apache.org/43/operations/kraft/

附:三个编排文件的关键差异速查

项单节点三节点 combined角色分离
服务数136(3 controller + 3 broker)
node.id11/2/3controller 1/2/3,broker 4/5/6
process.rolesbroker,controllerbroker,controllercontroller 或 broker
宿主机端口909229092/39092/4909229092/39092/49092(controller 不暴露)
内部端口190921909219092
控制器端口2909390939093
副本因子133
数据卷kafka-datakafka-1/2/3-datacontroller-1/2/3-data + kafka-1/2/3-data
返回列表