1. 部署前的思维准备Kafka 到底是怎么跑起来的很多人在第一次接触 Kafka 安装部署时习惯直接上网搜一条命令复制粘贴结果不是版本对不上就是启动报一堆看不懂的异常。我在最开始踩坑时也这样后来花了很长时间把 Kafka 的运行逻辑梳理清楚再回头看安装过程基本上就顺理成章了。先说一个最容易混淆的点Kafka 本身是用 Scala 写的但它运行在 JVM 上所以安装 Kafka 之前必须先装 JDK。这不只是“装个 Java 环境”那么简单JVM 的参数直接决定 Kafka 的堆内存分配、垃圾回收行为、以及在高吞吐下是否频繁 Full GC。很多安装完跑起来卡顿的问题其实都不是 Kafka 配置错了而是 JDK 版本不对或者堆内存设置太随意。另一个关键点是 Kafka 并不是一个独立运行的二进制它需要配合 ZooKeeper在较新版本中虽然提供了 KRaft 模式但传统部署依旧大量使用 ZooKeeper来管理集群元数据、Broker 注册、Controller 选举等。也就是说部署 Kafka 集群时你实际上要部署两套服务一套是 ZooKeeper 集群一套是 Kafka Broker 集群。理解这个架构后面遇到“连不上”的问题时排查思路会清晰很多。从应用场景来看Kafka 通常被用于日志采集、消息中间件、流式处理、事件驱动架构等等。不同的场景对部署形态的要求不一样如果只是本地开发测试单节点就够了如果是生产环境至少要考虑 3 个 Broker 和 3 个 ZooKeeper 节点还要考虑磁盘、网络、分区副本因子等。这篇文章我不会只给几条命令了事而是把整个安装部署过程中容易忽视的细节、原理和排查经验都讲一遍。适合刚接触 Kafka、准备在 Linux 服务器上部署的读者也适合已经部署过但遇到过蹊跷问题的朋友对照排查。2. 版本选型第一步就决定后面顺不顺2.1 版本号里的大坑Kafka 的版本号看起来比较简单比如 2.8.0、3.2.0但里面有个很容易让人混淆的点Kafka 的源码是 Scala 写的所以官方在发布压缩包时文件名里包含了 Scala 的编译版本比如kafka_2.13-3.2.0.tgz。其中的2.13是 Scala 版本3.2.0才是 Kafka 本身的版本。很多人第一次看到kafka_2.13-3.2.0会以为 Kafka 版本是 2.13然后去查文档发现完全对不上这就是踩坑的开始。另一个容易忽略的是Kafka 从 2.8 版本开始引入了 KRaft 模式也就是不依赖 ZooKeeper 的 KRaftKafka Raft模式。到 3.x 版本后KRaft 逐渐成熟。但很多公司为了稳定仍然在生产环境沿用 ZooKeeper 模式。这导致你在搜索安装教程时看到的配置方式五花八门有人让你配zookeeper.connect有人让你配controller.quorum其实是因为模式不同。所以第一步一定要明确你打算用哪个版本、哪种模式。我的建议是如果是学习、本地测试、或是新项目且团队对 Kafka 熟悉可以选用 3.x 的 ZooKeeper 模式资料最多社区问题解答齐全。如果想尝试新特性可以使用 KRaft但需要做好踩坑准备尤其是权限、ACL、配置迁移这些方面网上可参考的生产案例相对少。如果公司已有历史集群优先跟随现有版本不要贸然升级大版本。2.2 推荐版本组合我在实际部署中比较稳定的组合是JDK 1.8 或 11、ZooKeeper 3.6.x、Kafka 2.8.x 或 3.0.x。这套组合在生产环境运行了较长时间遇到过的坑基本上都能在网上找到解决方案。如果你选择 Kafka 3.x 配合 KRaft那么 JDK 建议用 11 或 17因为新版本对 JDK 版本有要求。可以在官方文档里查看Java版本兼容表但一般来说不要用太老的 JDK 去跑新版本否则可能直接启动失败。版本选型这件事我见过太多人随便下载一个最新版结果跟团队里其他依赖组件不兼容比如 Flume、Spark Streaming、Flink 连接器版本对不上最后被迫重新部署。所以选型时最好把上下游组件的版本一起确认。3. 环境准备别在这些细节上省时间3.1 Linux 服务器的初始化操作我习惯在干净的 CentOS 7 或 Ubuntu 20.04 上部署。拿到一台新服务器后先做几件事更新系统包yum update或apt update。关闭防火墙或放行指定端口Kafka 默认端口是 9092ZooKeeper 是 2181。生产环境建议只放行内网 IP 段不要暴露公网。配置主机名和/etc/hosts尤其搭建集群时节点之间需要互相通过主机名解析。如果直接用 IP 也可以但后续维护时会很痛苦。创建专用的系统用户比如kafka。不要用 root 直接跑 Kafka一方面安全另一方面防止误操作。这里有个容易被忽略的点/etc/hosts必须包含所有节点的 IP 和主机名映射并且 Kafka 的配置文件里尽量使用主机名而不是 IP因为 Kafka 的广告监听地址如果是 IP后续容器化或迁移时会变得非常麻烦。3.2 JDK 安装与 JAVA_HOME 配置JDK 安装不是什么难事但环境变量配置经常出问题。以 CentOS 为例安装 OpenJDK 1.8yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel安装完后找到 JDK 路径update-alternatives --config java然后把JAVA_HOME写进/etc/profile或~/.bashrcexport JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.xxx export PATH$PATH:$JAVA_HOME/bin再执行source /etc/profile生效。检查是否配置成功java -version echo $JAVA_HOME如果JAVA_HOME打印为空说明环境变量没生效常见原因是写在了一个不加载的文件里。这一步虽然基础但很多人就是因为环境变量没配置好导致 Kafka 启动脚本找不到 Java报 “Error: JAVA_HOME is not set” 之类的错误。3.3 ZooKeeper 集群部署虽然是讲 Kafka 安装但 ZooKeeper 部署逃不掉。ZooKeeper 是一个独立服务需要单独下载。提供一个稳定版本的下载方式wget https://archive.apache.org/dist/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz cd apache-zookeeper-3.6.3/conf cp zoo_sample.cfg zoo.cfg修改zoo.cfg核心配置项如下tickTime2000 initLimit10 syncLimit5 dataDir/data/zookeeper clientPort2181 maxClientCnxns60 server.1zk1:2888:3888 server.2zk2:2888:3888 server.3zk3:2888:3888参数含义tickTimeZooKeeper 使用的基本时间单元单位毫秒默认 2000。其他时间参数都以它为基础倍数。initLimitFollower 启动后与 Leader 完成初始同步的最大 tick 数。syncLimitLeader 与 Follower 之间请求响应的最大 tick 数。dataDirZooKeeper 存储快照和事务日志的目录一定提前创建好并且要放在磁盘 IO 较好的路径上不要放在临时目录。clientPort客户端连接端口Kafka 会通过这个端口连接 ZooKeeper。server.N集群节点配置N 是每个节点的 myid。还需要在dataDir目录下创建myid文件内容是对应的编号。比如 zk1 机器的myid文件内容为1zk2 为2以此类推。没写myid文件直接启动ZooKeeper 会报错无法找到身份标识。启动 ZooKeepercd apache-zookeeper-3.6.3/bin ./zkServer.sh start查看状态./zkServer.sh status一个正常启动的 ZooKeeper 节点会输出Mode: leader或Mode: follower。如果三个节点都显示standalone说明集群模式没生效多半是server.N配置有问题或者myid文件不对。4. Kafka Broker 安装与单节点启动全流程4.1 下载与解压Kafka 的二进制包可以通过 Apache 官网镜像下载。比如wget https://archive.apache.org/dist/kafka/3.2.0/kafka_2.13-3.2.0.tgz tar -zxvf kafka_2.13-3.2.0.tgz mv kafka_2.13-3.2.0 /usr/local/kafka cd /usr/local/kafka解压后的目录结构里比较重要的是config/所有配置文件、bin/启动脚本、libs/依赖库。如果你想要自定义目录可以通过--prefix方式指定安装路径但通常/usr/local/kafka就够了。4.2 单节点配置 server.properties 详解先看 Kafka 的核心配置文件config/server.properties。单节点部署时需要关注这几个配置项broker.id0 listenersPLAINTEXT://your_host:9092 advertised.listenersPLAINTEXT://your_host:9092 log.dirs/data/kafka-logs zookeeper.connectyour_zk_host:2181 num.partitions3 default.replication.factor1 offsets.topic.replication.factor1 transaction.state.log.replication.factor1 transaction.state.log.min.isr1解释一下这些参数的作用broker.id每个 Broker 集群内的唯一标识必须是数字。集群中不能重复。listenersBroker 对外提供服务的监听地址。这里有个比较隐蔽的问题如果不配置advertised.listeners客户端拿到的地址可能不是你想让它访问的地址。尤其在使用云服务器或者容器环境时内网 IP 和公网 IP 不一致客户端无法连接。所以最好显式配置advertised.listeners。log.dirsKafka 真正存储消息数据的地方不要放在系统盘最好是独立的挂载盘。多个目录用逗号分隔Kafka 会尽量均匀分配分区目录。zookeeper.connectZooKeeper 连接地址如果是集群用逗号分隔多个节点。可以同时配置 chroot 路径比如zk1:2181,zk2:2181,zk3:2181/kafka这样可以在同一个 ZooKeeper 集群里隔离多个 Kafka 集群或环境非常实用。num.partitions新建 topic 时的默认分区数。单节点测试 3 个够用生产环境需要根据吞吐量和消费者数综合评估。default.replication.factor默认副本因子。单节点只能设为 1集群环境建议至少 2常用 3。offsets.topic.replication.factor消费者 offset 记录主题的副本因子。在只有一个 Broker 时如果保持默认 3 会创建主题失败。还有两个性能相关参数在调优时比较重要log.retention.hours168 log.segment.bytes1073741824 log.retention.check.interval.ms300000log.retention.hours决定消息保留时间默认 7 天。log.segment.bytes是单个日志段文件的大小默认 1GB。如果业务希望消息保存更久或者更短都要去调整。4.3 启动 Kafka Broker先确保 ZooKeeper 已经启动然后执行cd /usr/local/kafka bin/kafka-server-start.sh -daemon config/server.properties-daemon表示后台启动。查看日志确认是否成功tail -f logs/server.log看到类似这样的日志就说明启动成功了[INFO ] Kafka Broker started如果启动失败最常见的报错是Connection refusedKafka 连不上 ZooKeeper。先排查 ZooKeeper 是否启动端口是否可通。Corrupt index或Log segment not found说明log.dirs所指路径存在异常文件可能是之前用过不同版本的 Kafka 写入导致的。启动后创建一个测试主题bin/kafka-topics.sh --create --topic test-topic --partitions 3 --replication-factor 1 --bootstrap-server your_host:9092注意新版 Kafka 推荐使用--bootstrap-server而不是老的--zookeeper。生产消息和消费消息bin/kafka-console-producer.sh --topic test-topic --bootstrap-server your_host:9092 bin/kafka-console-consumer.sh --topic test-topic --from-beginning --bootstrap-server your_host:9092这个流程走通后单节点部署就算完成了。5. 集群部署从单点到高可用的关键一跃5.1 集群规划与端口分配生产环境一般建议 3 个 Broker 起步同时搭配 3 个 ZooKeeper。假设有三台机器kafka1、kafka2、kafka3。每台机器上的配置大体相同只有broker.id不同。端口分配方面ZooKeeper 客户端端口2181ZooKeeper 集群通信端口2888、3888Kafka 客户端端口9092Kafka 内部通信端口9092可以使用同一个端口也可以区分内外部流量但初学时统一 9092 就够了5.2 修改集群配置在每台机器上修改server.properties。比如第一台broker.id1 listenersPLAINTEXT://kafka1:9092 advertised.listenersPLAINTEXT://kafka1:9092 log.dirs/data/kafka-logs zookeeper.connectzk1:2181,zk2:2181,zk3:2181/kafka num.partitions6 default.replication.factor2 offsets.topic.replication.factor3 transaction.state.log.replication.factor3 transaction.state.log.min.isr2第二台、第三台只需要把broker.id改为2、3主机名换成对应的名字。如果所有机器的 ZooKeeper 连接串都相同它们会自动发现彼此组成一个集群。这里要特别强调offsets.topic.replication.factor。这个参数控制__consumer_offsets这个内部主题的副本数。如果只设置default.replication.factor2而忘记设置offsets.topic.replication.factor它依然默认是 3。当你的集群只有 2 台 Broker 时这个主题会因为副本数大于 Broker 数而创建失败消费者提交 offset 时就会报错。这是很多人搭集群时踩得最狠的坑。5.3 启动集群并验证三台机器依次启动 ZooKeeper 和 Kafka然后在任意一台执行bin/kafka-topics.sh --describe --topic test-topic --bootstrap-server kafka1:9092如果集群正常你会看到分区信息里的Replicas和Isr列包含多个 Broker ID。例如Topic: test-topic Partition: 0 Leader: 1 Replicas: 1,2 Isr: 1,2这表示副本已经同步到两个 Broker 上。接下来可以测试故障转移手动停掉一台 Kafka Broker再查看主题的分区 Leader 是否自动切换到其他 Broker。正常情况下几秒内新的 Leader 就会被选举出来这说明高可用生效了。不能只看 Kafka 进程还在就跑掉。还需要检查 JMX 指标是否正常比如活跃控制器数、分区总数、离线分区数等。可以使用bin/kafka-run-class.sh配合 JMX 查询或者接入监控系统这个后面可以单独再聊。5.4 集群部署的常见误区误区一所有 Broker 的broker.id都写成 0。这种集群会互相冲突后启动的 Broker 会一直刷报错日志无法注册。误区二advertised.listeners没配置客户端连接时拿到的是内网 IP从外部访问失败。在云服务器上特别常见。误区三不同机器上的log.dirs指向了同一个 NFS 共享目录。Kafka 官方明确不建议在多 Broker 之间共享同一个数据目录会导致日志段文件互相覆盖。误区四集群中的三个 ZooKeeper 节点只在其中一台配置了server.N另外两台没配置。这样虽然都能启动但无法选出 Leader。6. 配置参数再深挖每个关键选择背后的为什么6.1 为什么需要副本因子但越大越好吗default.replication.factor3是很多生产环境的标配。3 个副本可以容忍一台机器故障而不丢失数据同时 ISRIn-Sync Replicas机制保证 Leader 故障后能从同步副本中选出新 Leader。那是不是副本数越大越好不是。副本数越多磁盘占用越大网络复制开销越高生产性能和写入时延都会受影响。比如分区数为 9、副本因子为 3那么总副本数为 27每台 Broker 上大约有 9 个副本。副本分布还需要考虑 rack 信息最好能在机架维度隔离避免一个机架断电导致所有副本同时丢失。6.2 为什么log.dirs要独立磁盘Kafka 的读写性能受限于磁盘 IO尤其是顺序写和随机读。使用系统盘通常意味着和操作系统日志、其他应用抢 IO。生产环境建议使用独立的 SSD 或高性能云盘并且通过 RAID 或挂载多个数据盘把负载分散开。如果log.dirs配置多个目录Kafka 会按照目录中分区数量的多少做平衡新创建的分区会优先放到分区数最少的目录。6.3num.partitions和消费并行度之间的关系很多人在部署时没有认真思考分区数等到业务上线发现消费速度跟不上才去调大分区数但那时分区数变了会导致消息顺序性无法保证。因为 Kafka 只保证同一个分区内的消息有序。分区数一旦设定扩容分区虽然可行但分区数不能减少。所以部署初期就要结合预期消息量、单分区每秒吞吐量、消费者线程数量去估算。举个例子如果每个分区理论上能扛住 10 MB/s 的写入而业务高峰期需要 60 MB/s那么至少需要 6 个分区。同时要考虑消费者实例数如果只有 3 个消费者但分区数为 6那么每消费者要消费 2 个分区这通常是可行的但如果消费者多于分区数多出来的消费者会处于空闲状态白白占用资源。6.4log.retention.*参数的精确调整保留时间不是越长越安全而是要根据业务需求和数据量来定。Kafka 的保留策略有两种维度时间如log.retention.hours和大小如log.retention.bytes。哪个条件先触发就按哪个清理。如果想保留较长时间但有足够的磁盘空间可以把log.retention.bytes设置得很大只依赖时间清理反之如果磁盘空间有限需要强制按大小清理可以调整log.retention.bytes。注意Kafka 的清理是分 segment 的不会一个消息一个消息地删除所以实际占用空间会略有超过阈值。7. 常见问题与排查技巧实录7.1 启动时报 “Connection to node -1 could not be established before timeout”这是新手最常见的问题。原因通常是客户端到 Broker 的网络不通检查防火墙或安全组。advertised.listeners配置的地址客户端访问不了。比如配成了localhost外部客户端当然连不上。未配置bootstrap-server导致连到了旧的 ZooKeeper 端口。排查时先在客户端机器上执行telnet broker_ip 9092确认端口通不通。如果不通先解决网络问题。如果通就要重点看advertised.listeners的值。7.2 消费者无法提交 offset日志报NotLeaderForPartitionException或UNKNOWN_TOPIC_OR_PARTITION这个往往和内部主题__consumer_offsets的副本因子有关。单节点部署时如果你把offsets.topic.replication.factor保持默认 3而集群只有 1 个 Broker主题创建失败消费者自然无法提交 offset。解决方法是把副本因子改成 1或者先创建一个 3 副本的内部主题。已经发生的话可以修改配置后重启 Kafka删除旧的内部主题不建议在生产环境乱删或者通过kafka-topics.sh --alter手动调整。7.3 分区 Leader 一直在切换或出现NoBrokersAvailable错误这种情况通常说明 Broker 之间无法互相通信特别是使用了不同的监听地址。检查每台 Broker 的listeners和advertised.listeners以及/etc/hosts中的主机名映射。另外如果 ZooKeeper 集群本身就是单点Kafka 集群的 Controller 也会受到牵连所以一定要保证 ZooKeeper 的可靠性。7.4 Kafka 日志清理线程导致 CPU 飙升Kafka 在删除旧日志段时会执行日志段的切分、索引清理和文件删除。如果日志清理的并发度设置过高或者磁盘性能较差会出现 CPU 突然飙升。可以调整log.cleaner.threads和log.cleanup.policy如果业务允许也可以改为默认的delete策略而不是compact减少清理线程的负载。7.5 快速排查工具我习惯准备一个排查命令速查表场景命令查看 Broker 是否注册到 ZooKeeperbin/zookeeper-shell.sh zk_host:2181 ls /brokers/ids查看主题详情和副本分布bin/kafka-topics.sh --describe --topic name --bootstrap-server broker_host:9092查看消费者组 offset 情况bin/kafka-consumer-groups.sh --describe --group name --bootstrap-server broker_host:9092模拟生产测试吞吐bin/kafka-producer-perf-test.sh --topic test --num-records 100000 --record-size 100 --throughput -1 --producer-props bootstrap.serversbroker_host:9092查看节点存活状态bin/kafka-broker-api-versions.sh --bootstrap-server broker_host:9092这些工具在 bin 目录下都有遇到问题时先跑一遍能省很多盲猜时间。8. 部署完成后的自检清单和一点经验分享部署完 Kafka 之后不要急着马上丢给业务方。我习惯做一轮完整的自检相当于给自己留个底ZooKeeper 集群状态三台节点是不是一主两从任何一台挂掉能不能继续服务。Kafka Broker 进程有没有异常退出日志里有没有 WARN 或 ERROR。内部主题__consumer_offsets是否存在副本数是否符合预期。消息发送消费链路从生产端发送一条消息消费端能够正常收到。持久化目录权限dataDir和log.dirs的所有者是不是 kafka 用户重启后会不会因为权限问题起不来。保存一份完整的配置文件副本到版本仓库标注好每台机器的broker.id、主机名和配置差异。这个习惯帮我解决过很多次“当时是怎么配的”的难题。最后分享一个我自己实际遇到过的小问题Kafka 启动脚本默认会读取JMX_PORT环境变量如果没有设置默认使用 9999 端口。在多台服务器同时部署时如果某台机器上别的 Java 应用已经用了 9999 端口Kafka 启动时会显示端口冲突但日志里不一定会直接报错。所以我在启动脚本之前总会显式加上export JMX_PORT9999或改成自定义端口避免无谓的冲突。Kafka 安装部署只是一个开始后续的监控、日志采集、性能调优、安全认证等还有一堆内容。但至少先把地基打牢后面遇到问题才有底气。希望这篇文章的经验能帮你少走一些弯路。