Kafka KRaft 模式 Docker Compose 部署手册apache/kafka 官方镜像目标用Apache 官方镜像apache/kafka:4.3.1以Docker Compose方式部署KRaft无 ZooKeeperKafka。随本手册交付 4 个可直接使用的文件文件内容适用场景docker-compose.env.example变量模板镜像、集群 ID、对外地址、端口所有编排文件共用先复制为.envdocker-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_FACTOR1三节点 combined33容 1 节点broker 与 controller 同进程最简单的高可用形态角色分离63controller 容 1、broker 容 1controller 不受业务流量影响可分别扩缩容三个编排文件互不冲突项目名分别为kafka-single/kafka-cluster/kafka-isolated但同一端口不能同时占用单节点用 9092三节点用 29092/39092/49092按需启动其中一个即可。二、前置条件Docker ≥ 20.10.4必须。低于该版本时容器创建/opt/kafka/config等目录的权限不正确启动会直接报错退出 Configuring …之后跟Running in KRaft mode… /opt/kafka/config/ file not writable。Docker Compose v2docker compose子命令形式。建议宿主机单节点 2C2G 起三节点 4C8G 起数据盘按业务量预留Kafka 吃磁盘顺序写SSD 最佳。端口占用检查单节点9092三节点29092 / 39092 / 49092角色分离再加 broker 的同样三个端口controller 不暴露端口。宿主机时钟同步KRaft 对时钟敏感。三、关键设计说明为什么这么写1. 镜像与启动流程官方镜像的启动命令是/etc/kafka/docker/runDockerfile 中由CMD指定。它依次做三件事configureDefaults为未设置的变量填默认值包括CLUSTER_ID镜像内置了一个默认集群 IDconfigure校验必需变量CLUSTER_ID必填、controller-only 节点不允许设置KAFKA_ADVERTISED_LISTENERS等launch调用kafka.docker.KafkaDockerWrapper setup把「默认配置 挂载配置 KAFKA_*环境变量」合并写入/opt/kafka/config/server.properties并在数据目录未格式化时自动格式化已格式化则跳过并打印already formatted最后启动 broker。也就是说不需要手动执行kafka-storage.sh format镜像会自己处理。2. 环境变量命名规则配置项 → 环境变量的转换规则.→_、_→__、-→___再统一加前缀KAFKA_。配置项环境变量node.idKAFKA_NODE_IDlog.dirsKAFKA_LOG_DIRSoffsets.topic.replication.factorKAFKA_OFFSETS_TOPIC_REPLICATION_FACTORabc-defKAFKA_ABC___DEFabc_defKAFKA_ABC__DEFKAFKA_HEAP_OPTS、KAFKA_OPTS、KAFKA_JMX_*、KAFKA_LOG4J_*属于脚本特殊处理的变量不遵循上述映射。3. 三种配置注入方式优先级从低到高内置默认配置镜像自带的单节点 combined 配置挂载配置文件把*.properties挂到容器/mnt/shared/config会覆盖默认配置环境变量优先级最高覆盖同名的文件配置。注意即使用挂载文件方式CLUSTER_ID、KAFKA_NODE_ID、KAFKA_LISTENERS、KAFKA_CONTROLLER_QUORUM_VOTERS这类被启动脚本直接读取的项仍需用环境变量提供。4. 监听器模型Compose 部署最容易踩的坑容器内用三个监听器把「集群内部」「控制器」「宿主机客户端」彻底分开监听器用途是否映射到宿主机是否出现在 advertised 列表PLAINTEXTbroker 之间、容器之间通信否是用容器名如kafka-1:19092CONTROLLERKRaft 控制器通信否否控制器不是客户端入口PLAINTEXT_HOST宿主机/外部客户端是是用宿主机 IP/域名 映射端口advertised.listeners里的地址必须是客户端真正能连到的地址。客户端第一次连上 bootstrap 后会拿到 broker 自报的地址并直连所以客户端在本机 →localhost客户端在局域网其他机器 → 宿主机内网 IP客户端在公网 → 公网 IP/域名。填错的典型症状是「能连上 bootstrap随后超时/连接被拒」。本手册已把这一项抽成.env里的HOST_ADVERTISED_HOST改一处即可。5. 数据持久化与权限镜像内建用户appuseruid1000 / gid1000进程以该用户运行。官方示例把KAFKA_LOG_DIRS设为/tmp/kraft-combined-logs该路径在容器可写层内容器一删数据即丢。本手册统一改为/var/lib/kafka/data并挂载数据卷镜像已预建该目录并声明为VOLUME。用命名卷named volumeDocker 会按镜像内的属主初始化开箱即用无需处理权限。用宿主机目录bind mount必须先改属主否则启动报权限错误sudomkdir-p/data/kafkasudochown-R1000:1000 /data/kafka6. 容器内路径速查路径用途/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生成集群 IDdockerrun--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 brokerdockercompose-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、MaxFollowerLag0/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-partitions2. 宿主机/其他容器验证# 另起一个临时客户端容器接入同一网络网络名 项目名 _defaultdockerrun--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--list3. 故障演练三节点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_HOSTlocalhost同一 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--tail200kafka重启与停止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 不是 votercontroller 数量建议保持奇数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镜像的启动脚本支持通过环境变量开启 JMXenvironment:KAFKA_JMX_PORT:9099KAFKA_JMX_HOSTNAME:${HOST_ADVERTISED_HOST:-localhost}ports:-9099:9099需要指标接入 Prometheus 时可用KAFKA_OPTS挂 JMX Exporter 的 javaagent或部署独立的 kafka-exporter 容器。八、生产加固清单副本与一致性KAFKA_DEFAULT_REPLICATION_FACTOR3、KAFKA_MIN_INSYNC_REPLICAS2生产者acksallunclean.leader.election.enablefalse。关闭自动建 TopicKAFKA_AUTO_CREATE_TOPICS_ENABLEfalse清单已设置避免误建单副本 topic。资源与 JVMKAFKA_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升级 Dockerbind 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--tail100kafkadockercompose-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、集群 IDhttps://github.com/apache/kafka/blob/trunk/docker/examples/README.md官方 Compose 示例单节点 / combined 集群 / isolated 集群https://github.com/apache/kafka/tree/trunk/docker/examples/docker-compose-filesApache 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角色分离服务数1363 controller 3 brokernode.id11/2/3controller 1/2/3broker 4/5/6process.rolesbroker,controllerbroker,controllercontroller 或 broker宿主机端口909229092/39092/4909229092/39092/49092controller 不暴露内部端口190921909219092控制器端口2909390939093副本因子133数据卷kafka-datakafka-1/2/3-datacontroller-1/2/3-data kafka-1/2/3-data
阅读完成 · 觉得有帮助?