ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Kafka与ZooKeeper集群搭建实战:从0到1的完整指南

Kafka与ZooKeeper集群搭建实战:从0到1的完整指南 如果你手头正好有几台空闲的服务器领导丢给你一句“把Kafka集群搭起来”然后就没有下文了那你现在的心情我特别懂。我在这种场景下踩过的坑比看文档时以为的多一倍版本之间的坑、ZK和Kafka互相依赖的坑、配置文件里一个参数没对齐导致的连锁故障……这篇就把从0到1搭建Kafka和ZooKeeper集群的路完整走一遍。这是一篇面向实操的记录适合已经有Linux基础、对消息队列有概念但没真正部署过的人。跟着这篇文章走完你得到的是一套三节点集群三台ZooKeeper、三台Kafka能扛单机故障能正常生产消费并且留好了后续监控和扩容的入口。我会把“为什么这么做”也讲清楚而不只是甩给你一份能跑的配置。每台机器什么角色、每个参数什么含义、启动之后怎么验证都会落到具体命令和输出上方便你照着抄也方便出问题时回头查。1. 搭建前先理清架构Kafka和ZooKeeper是怎么配合的1.1 各管一摊元数据与消息数据的分工先说一个很多人最初理解偏了的地方Kafka和ZooKeeper不是“一套软件装一起”这么简单的关系。Kafka的Broker节点本身专注做消息存储和读写消息落盘、副本复制、消费组拉取这些事情都由Broker自己处理。但分布式系统最麻烦的问题是“谁说了算”——哪台Broker是Controller、某个分区的Leader副本在哪台机器上、某个消费组现在消费到哪个offset这些元数据必须有个大家都能访问的协调者ZooKeeper就是干这个的。打个比方Kafka的Broker像是市场里的一个个摊位ZooKeeper就是市场管理处。摊主出摊了要去管理处报到撤摊了要注销摊位之间起了纠纷也去管理处协调。如果管理处挂了买卖双方都会心里没底整个市场虽然还摆着摊但已经乱了套。所以在老版本架构下ZooKeeper不是可选项而是Kafka集群的基础设施。这里有一个关键点ZooKeeper只负责协调和元数据不碰消息数据本身。消息数据永远在Kafka Broker的本地磁盘上。理解这个分工你后面排查问题就会有一个方向感当“消费者找不到分区”“Broker注册不上”这类问题时往ZK这边想当“消息写不进去”“磁盘IO打满”时往Broker这边想。两者之间有明确的边界不要一上来就全盘怀疑。1.2 集群规模为什么要奇数台ZooKeeper集群的规模不是随便拍的。它采用Zab协议做数据一致性写入一条元数据要得到过半节点确认才算成功。所谓过半就是“n/2 1”这个概念3台机器要2台确认5台要3台确认。因此奇数台能在“可用性”和“成本”之间取得平衡——2台和1台效果一样4台和3台容错能力一样但多花了钱。所以ZK的常见规模就是1、3、5、7。生产环境我建议至少3台如果条件允许单独用3台机器装ZK不要和Kafka Broker混用。混装在节约机器的时候能跑但ZK和Kafka都是对资源有要求的服务尤其是IO和内存混装容易互相踩脚。Kafka的Broker数量则按业务流量来算和ZK的数量没有必须相等的关系三节点起步主要为了让副本机制能真正发挥作用——如果只有1台Kafka副本因子配3也没有意义。1.3 版本选型不要一上来就追新版本这块是新手最容易踩坑的地方。Kafka从3.3开始逐步引入KRaft模式可以脱离ZooKeeper运行但直到3.5.xZK模式依然是存量企业用得最多的生产方式相关资料也最全。你在生产环境遇到问题要快速查资料、找解决方案时用稳定且资料多的版本一定更舒服。我这次选的是Kafka 3.3.2加上ZooKeeper 3.7.1配OpenJDK 11。这个组合是我在生产上验证过的稳定坑少。Kafka 3.x的官方包里其实自带一个ZooKeeper但那个ZK主要是给快速体验用集群模式建议单独下载ZooKeeper发行版这样版本可控出问题也好定位。选版本还有一个原则Kafka和ZK的版本兼容性以官方Release Notes为准不要拿最新版ZK去配旧版Kafka否则可能出现序列化协议不匹配之类的怪问题。2. 环境准备从JDK到系统参数的完整核对清单2.1 JDK版本对齐装之前先把三台机器的JDK统一好这个步骤很多人跳过去后面就吃大亏。Kafka 3.3.x官方支持Java 8、11、17我推荐用11ZooKeeper 3.7.x要求Java 8以上。三台机器如果JDK版本不一致轻则某个节点起不来重则集群出现奇怪的时间同步和序列化问题排查起来非常头疼。安装方式不用太纠结OpenJDK直接解压或者用包管理器装都行关键是配好JAVA_HOME。我习惯把JDK放在/opt/jdk目录然后写进/etc/profile.d/java.sh确保所有用户都能读到。装完之后依次执行java -version和echo $JAVA_HOME确认三台输出完全一致再往下走。这一步别嫌麻烦集群环境最怕“看起来一样实际有差异”的隐性问题。2.2 文件句柄、swap和时间同步这三个系统参数是我在集群出过问题之后才真正重视起来的。Kafka数据目录下会生成大量segment文件每个文件都要占一个文件句柄生产环境并发起来默认的1024根本不够。需要修改/etc/security/limits.conf给运行用户设置nofile和nproc比如nofile设为65535或更高改完要重新登录会话才生效。很多人改完limits发现不生效就是因为没有重新登录。swap的问题在于Linux内核默认会把不常用的内存页换到磁盘但Kafka这种低延迟系统最怕换页抖动一旦发生swap消息写入延迟会突然飙高。把vm.swappiness调成1核心业务期间基本不会主动换页。时间同步同样重要Kafka日志时间戳和副本复制重试都依赖机器时间三台机器时间偏差太大会出现日志错乱务必在每台机器上跑通ntp或chrony并且确认用的时间源一致。2.3 数据目录规划与多磁盘挂载数据目录的规划决定了集群能跑多久不出磁盘问题。ZooKeeper的数据量不大但它的事务日志对写延迟很敏感强烈建议把dataDir和dataLogDir分开dataLogDir放独立的物理盘或者SSD上。Kafka的日志目录更讲究如果机器上有两块以上数据盘可以用逗号把多个路径配给log.dirs让Kafka自动做跨目录负载均衡而不是所有分区都压在系统盘上。我习惯的布局是系统盘放程序数据盘挂到/data下ZooKeeper用/data/zookeeper/data和/data/zookeeper/logsKafka用/data/kafka/logs。挂载的时候给上noatime参数减少不必要的磁盘写操作。这一步看着小但对集群长期性能影响很大尤其是Broker数量上去了以后磁盘布局不合理的机器会最先暴露瓶颈。3. 手把手搭ZooKeeper集群3台机器的完整过程3.1 下载解压与基础目录先去Apache官网下载zookeeper-3.7.1.tar.gz下载完成记得核对sha512校验值下载包被篡改的情况虽然少见但核对一下成本很低。把安装包传到三台机器上统一解压到/opt/zookeeper。然后创建数据目录和日志目录这一步不要跳过目录规划后面myid文件、事务日志和快照都会放进去目录结构越清晰出了问题越容易定位。创建完目录还需要给目录设置好属主。我一般创建一个专用系统用户比如useradd -r zookeeper把/opt/zookeeper和/data/zookeeper的属主改成这个用户。用专用用户跑服务的好处是权限可控即使某个Web应用被攻破了拿到低权限shell也不至于直接碰到ZK的数据目录。这个习惯在Kafka那边同样适用后面我会再提。3.2 核心配置文件zoo.cfg逐项说明ZooKeeper的配置集中在conf/zoo.cfg。从模板复制一份然后重点说明几个关键参数。tickTime2000是ZK最基本的时间单位单位毫秒心跳等很多超时都基于它。initLimit10表示Follower启动时能容忍的同步初始连接最大超时次数10乘以tickTime就是20秒。syncLimit5是Follower和Leader之间心跳的超时次数也就是10秒。这两个值在3台机器同机房、网络抖动小的情况下保持默认就够。clientPort2181是客户端连接端口。server.1zk1:2888:3888这种配置后面第一个端口2888是Follower和Leader之间同步数据的通道第二个端口3888是Leader选举时的通信端口这两个端口不能和clientPort混用。dataDir和dataLogDir指向刚才建好的目录。另外建议加上maxClientCnxns60限制单台客户端连接数autopurge.snapRetainCount3和autopurge.purgeInterval24让ZK自动清理历史快照避免数据目录无限膨胀。生产环境里这两个autopurge参数尤其重要我见过ZK目录被快照撑满的情况加了这个配置之后就再也没出现。3.3 用myid标记节点身份集群里每台ZK都要一个唯一ID这个ID写在dataDir下的myid文件里。三台机器分别执行echo 1 /data/zookeeper/data/myid、echo 2和echo 3。关键是myid里的数字必须和zoo.cfg里server.x的数字一一对应比如zk1这台机器上写1那么zoo.cfg里server.1zk1:2888:3888指向的必须是这台机器的IP或主机名。这里有个很多人忽略的细节server.x后面的主机名在三台机器的/etc/hosts里必须都能解析到否则节点启动后互相找不到。我用的是内网IP直接在zoo.cfg里写IP省去DNS配置。小集群里这是最不容易出错的方式但如果你以后要接Kubernetes这类动态环境还是得用稳定的服务名因为Pod重启后IP会变。myid文件不要放在共享存储上每台机器必须有自己独立的myid。3.4 启动、验证与托管启动之前先在命令行前台跑一次zkServer.sh start-foreground前台模式能把启动日志直接打出来有配置错误一眼就能看到。确认没有异常再使用zkServer.sh start正常启动最后用zkServer.sh status查看角色。3台机器都起来之后应该有一台显示leader另外两台显示follower这说明集群选主完成状态正常。为了管理方便我给ZK配了systemd托管用systemctl start/status/restart控制。这里注意systemd的ExecStart要写成zkServer.sh start-foreground因为如果写start脚本会再fork一个子进程systemd会追踪不到主进程。下面是一个可用的Service片段[Unit] DescriptionApache ZooKeeper Afternetwork.target[Service] Userzookeeper Typeforking ExecStart/opt/zookeeper/bin/zkServer.sh start ExecStop/opt/zookeeper/bin/zkServer.sh stop Restarton-failure PIDFile/data/zookeeper/data/zookeeper_server.pid[Install] WantedBymulti-user.target第一台ZK起来时日志里可能会出现“Cannot open channel to X at election address”这样的连接告警这正常因为另外两台还没起来。等全部节点启动后告警会消失不用紧张。但如果三台都起来后status仍然显示Unknown或报错就要回头查端口占用和防火墙了。4. 手把手搭Kafka集群核心配置是成败关键4.1 下载解压与第一个配置文件Kafka的安装包同样从Apache官网下载下载kafka_2.13-3.3.2.tgz解压到/opt/kafka。需要说明的是包名里的2.13是Scala编译版本3.3.2才是Kafka版本。如果以后配Flink或Spark对接Kafka注意Scala版本要匹配但那是客户端的事服务端不涉及。解压之后先不要急着启动把config/server.properties完整看一遍。Kafka的配置项非常多但集群搭建阶段真正需要改的也就十几个其他保持默认就好。我先给Kafka创建了一个专用系统用户用户名就叫kafka然后把/opt/kafka和后续的数据目录属主都改成它。这一步和ZK那边一样都是为了权限隔离避免程序以root权限运行。4.2 server.properties逐项拆解我第一次搭的时候以为默认配置就能跑结果启动后所有Broker都注册到了同一个ZK根路径下行为不可控。正确做法是尽早给集群配置一个独立的ZK chroot比如/kafka-cluster。在zookeeper.connect这一项写成zk1:2181,zk2:2181,zk3:2181/kafka-cluster因为ZK本身就是个目录树加chroot能让Kafka的元数据都挂在/kafka-cluster下面方便和其他使用ZK的系统隔离。其他关键参数按实际场景逐项说明broker.id每台Broker唯一建议1、2、3listeners监听地址格式PLAINTEXT://内网IP:9092advertised.listeners客户端连接的广播地址必须填客户端能访问到的IP和端口这个配置最容易踩坑很多集群外连接失败都是这个值写成了localhostlog.dirs数据目录支持多个路径逗号分隔num.partitions3和default.replication.factor3新建Topic的默认分区数和副本数三节点集群就配3副本min.insync.replicas2至少两个副本同步才算写入成功配合acksall可以避免数据丢失offsets.topic.replication.factor3和transaction.state.log.replication.factor3Kafka内部topic的副本因子也要改成3否则默认只有1副本等于把元数据放在单点上。log.retention.hours168是数据保留七天具体保留多久按业务场景来定不能一概而论。auto.create.topics.enablefalse建议直接关掉防止误操作导致Topic副本数不对生产环境尤其重要。如果你不确定某个参数的含义宁可保持默认也不要乱改Kafka的参数之间经常有联动关系。4.3 启动Broker并注册到ZooKeeper配置检查完之后先在每台机器上用kafka-server-start.sh -daemon /opt/kafka/config/server.properties启动。启动后第一件事是看日志我在/opt/kafka/logs/server.log里扫一眼有没有ERROR或FATAL同时用jps确认进程存在能看到Kafka进程就没大问题。接着用ZooKeeper客户端验证Broker注册情况。在任意一台ZK上执行zkCli.sh -server zk1:2181然后执行ls /kafka-cluster/brokers/ids如果看到[1,2,3]说明三台Broker已经成功注册这一步标志着集群的底层通道打通了。如果只看到部分ID就要去对应Broker的server.log里查它为什么没有注册上来常见原因还是网络问题或者broker.id冲突。4.4 用Topic验证集群是否真正可用Broker注册成功不代表消息就能正常流动必须创建一个Topic并走一遍生产消费验证。创建命令里指定分区数3、副本数3然后用kafka-topics.sh --describe查看它落在哪些Broker上正常情况下三个分区Leader会分别落在三台机器上这是副本均匀分布的表现。如果所有Leader都挤在同一台机器上说明默认分区分配策略对你当前节点布局不友好但小集群里一般不会出现。验证生产和消费我开两个终端一个跑kafka-console-producer.sh --bootstrap-server 192.168.1.11:9092 --topic test另一个跑kafka-console-consumer.sh --bootstrap-server 192.168.1.11:9092 --topic test --from-beginning。生产者这边输入字符串消费者那边立刻能看到说明从生产到Broker到消费的整条链路通了。在这个验证过程中我强烈建议加上--from-beginning否则新消费组默认只拉取启动之后的新消息你输入的消息看不到容易误判成故障。5. 集群上线后的必备验证与运维动作5.1 故障转移演练干掉一个Broker看看会发生什么集群不是搭起来能跑就算完你必须亲手验证它到底扛不扛得住故障。最简单有效的演练是记录当前某个Topic的分区Leader分布然后找到Leader所在的那台Brokerkill掉Kafka进程观察集群如何反应。正常情况下几秒钟后ZooKeeper会感知到Broker会话超时Controller会重新为受影响的分区选出新的LeaderTopic仍然可读可写。用kafka-topics.sh --describe再看一遍Leader已经切换了ISR列表里也没有刚才那台机器的ID。接下来把杀掉的那个Broker重新启动它会作为Follower加入集群慢慢追赶副本数据ISR恢复成3个。这个过程让我对“Kafka高可用”有了直观的理解高可用不是靠单机不挂而是靠副本机制在故障发生时自动顶上。5.2 可视化工具接入让集群状态一眼可见命令行工具能干活但日常巡检还是得有个可视化界面。Kafka生态里常用有Kafka UI、CMAK和Kafka Eagle。我用的是Kafka UI它对消费组管理和Topic查看支持得比较好Docker部署只需要环境变量配置好KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS指向任意一个Broker地址即可。可视化工具要提醒一点它本质上也是一个客户端会把集群的元数据拉一遍在生产环境接入时要控制刷新频率不要开好几套工具轮询同一个集群否则会白白增加Broker的负担。管理类操作比如变更分区、删除Topic我建议仍然在命令行里做界面只用于观察。可视化工具对新手最大的价值是能直观展示分区副本分布和消费组Lag这两项数据比看一堆日志更容易建立对集群的体感。5.3 消息延迟高与消费堆积的排查套路上线之后最常被找上门的问题就是“消息延迟高”。我的排查顺序固定是先看消费组Lag再看生产端最后看Broker性能不要一上来就重启Broker。查消费Lag用kafka-consumer-groups.sh --bootstrap-server 192.168.1.11:9092 --describe --group 你的消费组如果某个分区LAG一直增长那问题大概率在消费端先看消费逻辑和数据库连接池有没有瓶颈。如果Lag为0但端到端延迟还是高那重点转向生产端确认生产者的acks配置acksall会比acks1慢不少但数据安全性更高同时确认Broker的磁盘IO和CPU是不是已经到了瓶颈用iostat看磁盘util如果长时间超过85%说明磁盘拖后腿了扩容或换SSD是硬道理。延迟问题很少是单点原因把链路拆开一段一段量才能定位准确。5.4 扩容与升级的注意事项集群上线半年后业务变大扩容是迟早的事。Kafka加Broker相对简单新机器装好Kafka改一个不同的broker.idzookeeper.connect指向同一个chroot启动后它就会加入集群。但注意新Broker加入后已经存在的分区不会自动往它上面迁移需要手动把部分分区重分配过去否则新节点就是空转。ZooKeeper集群扩容更敏感不要一下子加两台容易引发重新选举。我建议扩容时一次加一台确认follower角色正常再继续下一台。升级方面Kafka和ZK的版本升级都遵循同一个原则先升级ZooKeeper再滚动升级Kafka Broker。每一台Broker的升级都是杀掉再启动消费者端要配合重试机制否则滚动窗口期的报错会把你手机打爆。升级前务必把server.properties完整备份升级后逐一核对配置差异这是最简单也最容易被跳过的步骤。6. 常见问题排查实录这些坑我替你踩过了6.1 Broker启动后老是连接不上ZooKeeper这个问题我遇到过不止一次原因五花八门。最基础的是网络不通先telnet zk1 2181看端口是否能通不通就查防火墙和安全组。然后确认zookeeper.connect地址写的是否和ZK实际监听地址一致这里最好直接写内网IP而不是主机名省去DNS解析的变数。最后还要看chroot路径如果用/kafka-cluster先确认ZK里已经存在这个znode否则Kafka启动时会尝试创建但可能因为权限问题失败。日志里如果滚动刷“Unable to connect to zookeeper”把这几个方向按顺序查一遍基本都能解决。6.2 Topic创建成功但生产消费都报错Topic能创建说明Broker和ZK正常但生产消费报错时优先级最高的是检查advertised.listeners。排查时我见过太多案例本地测试改成localhost能跑到了集群外客户端就超时报错提示metadata没有返回。这是因为Broker广播给客户端的连接地址就是advertised.listeners里的值客户端拿到这个地址去连如果连的是localhost或者内网不可达的地址自然失败。解决方法是把advertised.listeners显式写成客户端可达的IP:端口比如PLAINTEXT://192.168.1.11:9092然后重启Broker。这个配置不重启不会生效改完一定要挨个重启。如果你发现改了之后客户端还是连不上还有一种可能是Broker的listeners同时监听了多个网卡这时候要用listeners和advertised.listeners成对配置保证广播出去的地址确实是被监听的地址。6.3 节点重启后一直处于UnderReplicated状态Broker故障恢复后有时候kafka-topics.sh --describe看到ISR一直不是满的比如3副本分区只有2个副本在ISR里。这多半是新加入的副本追数据太慢数据量很大时同步需要时间正常现象等一会儿再看即可。但如果是长时间卡住就要看这台Broker的磁盘空间和复制线程配置磁盘满了或者replica.fetch.max.bytes太小都会让追齐变得困难。还有一种隐蔽情况Broker已经重新注册到ZK但机器时间不统一导致offset校验错乱。这时候回到第2节说的时间同步问题把ntp/chrony跑好再触发一次分区重分配基本就能恢复。排查UnderReplicated时不要只看一个Topic要整体看所有Topic的ISR状态如果所有分区都缺同一台Broker那是节点问题如果只有个别分区缺那更可能是那个分区
返回列表