1. 为什么说KRaft模式是Kafka 4.0生产部署的必选项?
如果你之前部署过Kafka,大概率被ZooKeeper“折磨”过。没错,我说的就是那个需要单独部署、维护,并且一旦出问题整个Kafka集群就可能“罢工”的ZooKeeper。在Kafka 4.0时代,这一切都成为了历史。KRaft(Kafka Raft)模式,简单来说,就是Kafka自己“当家作主”,把元数据管理从ZooKeeper手里拿了回来,用内置的Raft共识算法来管理集群元数据。
我经历过从依赖ZooKeeper的集群迁移到KRaft模式的过程,最大的感受就是:部署变简单了,运维变轻松了,集群更稳了。以前部署一个三节点集群,你得先搞定一个三节点的ZooKeeper集群,确保它们先跑起来,然后再去配置Kafka。现在呢?直接一套配置搞定所有节点,启动顺序也没那么严格了。对于生产环境来说,这意味着部署复杂度直线下降,故障点也少了一个。更重要的是,KRaft模式在元数据读写性能、集群扩展性(比如支持更多分区)和故障恢复速度上,都比ZooKeeper时代有了显著提升。所以,如果你现在要搭建新的Kafka生产集群,我强烈建议你直接上KRaft模式,别再回头去看ZooKeeper了。
2. 部署前的核心规划:兵马未动,粮草先行
在真正动手敲命令之前,花点时间做好规划,能让你在后续的部署和运维中省下大把的力气。这里我结合一个典型的三节点(10.62.1.181/182/183)生产环境,跟你聊聊怎么规划。
2.1 角色与节点规划
在KRaft模式下,一个节点可以扮演三种角色:纯粹的Broker(处理消息存储和读写)、纯粹的Controller(管理集群元数据),或者两者兼有(Broker+Controller)。对于大多数中小规模的生产集群(比如3到5个节点),我推荐使用 “合二为一” 的模式,也就是让每个节点同时承担Broker和Controller的角色。这样做的好处是资源利用率高,架构简单,并且只要超过半数的节点存活,集群就能正常工作。对于我们的三节点集群,规划如下表所示:
| 服务器IP | 节点ID (node.id) | 角色 (process.roles) | Broker监听端口 | Controller监听端口 | 数据目录 |
|---|---|---|---|---|---|
| 10.62.1.181 | 1 | broker,controller | 9092 | 9093 | /datas/kafka |
| 10.62.1.182 | 2 | broker,controller | 9092 | 9093 | /datas/kafka |
| 10.62.1.183 | 3 | broker,controller | 9092 | 9093 | /datas/kafka |
这里有几个关键点需要注意。第一,node.id 在整个集群中必须是全局唯一的整数,它替代了旧版本中的 broker.id。第二,process.roles 明确指定了本节点的职责。第三,我们为Broker客户端通信和Controller内部通信分配了不同的端口(9092和9093),避免冲突。
2.2 资源与目录规划
硬件资源方面,假设我们每台机器是8核16GB内存。对于Kafka来说,内存主要被用于页面缓存(Page Cache)来加速磁盘读写,以及Java堆内存用于运行程序。一个比较实用的分配建议是:将系统50%-60%的内存留给Kafka的堆内存。对于16GB的机器,我们可以分配8GB。剩下的内存留给操作系统做页面缓存,这对Kafka的性能至关重要,因为Kafka重度依赖磁盘顺序读写,页面缓存能极大提升吞吐量。
磁盘规划是另一个重中之重。绝对不要把Kafka的数据日志放在系统盘上!一定要使用单独的高性能数据盘(如SSD或高速SAS盘),并挂载到独立的目录,比如 /datas/kafka。如果单块磁盘的IOPS或容量可能成为瓶颈,你可以在 log.dirs 配置项中配置多个用逗号分隔的目录,Kafka会自动将分区数据均匀分布到这些目录下,这能有效提升并行IO能力。另外,确保磁盘有足够的空间,根据你的消息保留策略(比如保留7天或1个月)和预估的每日数据量来计算。
3. 一步步搭建KRaft三节点集群
理论规划好了,我们开始动手实操。我会把每个步骤都拆开讲清楚,你跟着做就行。
3.1 基础环境准备
首先,确保三台服务器的基础环境一致。我们以Ubuntu 22.04为例。Kafka 4.0需要JDK 17或更高版本,别再用JDK 8了。
第一步,安装JDK 17。你可以从Oracle官网或OpenJDK获取安装包。这里我们下载并解压到 /opt/module 目录。
# 在三台服务器上分别执行
sudo mkdir -p /opt/module
cd /opt/package # 假设你的安装包放在这里
sudo tar -xzf jdk-17_linux-x64_bin.tar.gz -C /opt/module/
第二步,下载并解压Kafka 4.0。去Apache Kafka官网下载 kafka_2.13-4.0.0.tgz(2.13是Scala版本,选这个兼容性好的就行)。
wget https://archive.apache.org/dist/kafka/4.0.0/kafka_2.13-4.0.0.tgz
sudo tar -xzf kafka_2.13-4.0.0.tgz -C /opt/module/
第三步,创建专用的系统用户和目录。为了安全,不建议用root用户直接运行Kafka。
sudo useradd -m -s /bin/bash kafka
sudo mkdir -p /datas/kafka
sudo ln -sf /opt/module/kafka_2.13-4.0.0 /opt/module/kafka # 创建一个软链接方便管理
sudo chown -R kafka:kafka /opt/module/kafka_2.13-4.0.0 /datas/kafka
第四步,配置环境变量。创建一个全局的配置文件,让kafka和java命令随处可用。
sudo tee /etc/profile.d/myenv.sh << 'EOF'
export KAFKA_HOME=/opt/module/kafka
export JAVA_HOME=/opt/module/jdk-17.0.8
export PATH=$PATH:$KAFKA_HOME/bin:$JAVA_HOME/bin
EOF
source /etc/profile.d/myenv.sh
java -version # 验证JDK安装成功
3.2 核心配置文件详解与定制
这是最关键的一步,server.properties 文件决定了集群的行为。我会逐段解释重要参数,并给出针对我们三节点16GB内存环境的优化值。请注意,三个节点的配置文件大部分相同,只有少数几个标识本机的参数需要修改。
首先,处理节点1(10.62.1.181)的配置。用你喜欢的编辑器(如vim)创建 /opt/module/kafka/config/server.properties 文件。
############################# Server Basics #############################
# 在KRaft模式下,不再需要 broker.id 和 zookeeper.connect 了!
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@10.62.1.181:9093,2@10.62.1.182:9093,3@10.62.1.183:9093
process.roles=broker,controller:声明本节点身兼两职。node.id=1:此节点的唯一ID。controller.quorum.voters:这是KRaft模式的核心配置之一。它列出了所有Controller角色的节点。格式是node.id@host:port。注意这里的端口是Controller内部通信端口(我们定为9093),三个节点必须全部列出。
############################# Socket Server Settings #############################
listeners=PLAINTEXT://10.62.1.181:9092,CONTROLLER://10.62.1.181:9093
advertised.listeners=PLAINTEXT://10.62.1.181:9092
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
listeners:定义本机监听的协议、IP和端口。PLAINTEXT://:9092给Broker(客户端用),CONTROLLER://:9093给Controller内部通信。advertised.listeners:非常重要! 这是告诉客户端和其他Broker,应该通过哪个地址来连接我。在生产环境中,如果客户端不在同一网络,这里可能需要配置主机名或公网IP。inter.broker.listener.name:指定Broker之间通信使用哪个监听器。controller.listener.names:指定Controller通信使用的监听器名称。
num.network.threads=8
num.io.threads=20
socket.send.buffer.bytes=204800
socket.receive.buffer.bytes=204800
socket.request.max.bytes=209755200
这部分是网络和线程调优。对于8核CPU的机器:
num.network.threads=8:处理网络请求的线程数,通常设置为CPU核数。num.io.threads=20:处理磁盘I/O的线程数,可以设置为CPU核数的2-3倍。socket.request.max.bytes=209755200:约200MB,允许接收更大的消息批次,提升吞吐量。如果你的消息通常很大,可以再调高。
############################# Log Basics #############################
log.dirs=/datas/kafka
num.partitions=16
num.recovery.threads.per.data.dir=1
log.dirs:数据日志目录,指向我们之前创建的专属数据盘目录。num.partitions=16:默认分区数。这是一个全局默认值。分区数决定了Topic的最大并行消费能力。这里设为16是一个适中的起步值,你可以根据业务Topic的实际情况在创建时覆盖它。num.recovery.threads.per.data.dir=1:每个数据目录的日志恢复线程数。如果用了多块盘,可以适当增加。
############################# Internal Topic Settings #############################
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=2
内部Topic(如存储消费者偏移量的 __consumer_offsets)的配置。对于三节点集群,复制因子设为3,确保高可用。min.isr=2 表示至少需要2个副本同步成功,才认为写入成功,在可用性和一致性间取得平衡。
############################# Log Retention Policy #############################
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
日志保留策略:
log.retention.hours=168:消息保留7天。根据你的存储容量和合规要求调整。log.segment.bytes=1073741824:每个日志段文件大小为1GB。这个值影响磁盘IO效率和清理频率。log.retention.check.interval.ms=300000:每5分钟检查一次是否有日志需要删除。
############################# Other Settings #############################
auto.create.topics.enable=false
default.replication.factor=3
unclean.leader.election.enable=false
message.max.bytes=10485760
fetch.max.bytes=10485760
几个重要的杂项:
auto.create.topics.enable=false:生产环境强烈建议设为false!避免应用程序误写Topic名而自动创建出不符合规范的主题。default.replication.factor=3:新Topic的默认副本数,设为3保证数据冗余。unclean.leader.election.enable=false:禁止非同步副本(非ISR)参与领导者选举,防止数据丢失。message.max.bytes和fetch.max.bytes:都设为10MB,允许处理较大的单条消息。
对于节点2(10.62.1.182)和节点3(10.62.1.183),你只需要修改上面配置文件中的三个地方:
node.id:分别改为2和3。listeners和advertised.listeners中的IP地址:分别改为本机IP10.62.1.182和10.62.1.183。
其他所有配置,包括 controller.quorum.voters 列表,必须保持完全一致。你可以用scp命令把节点1的配置文件拷贝到其他节点,然后再修改这几个差异项。
3.3 集群初始化与启动
配置完成后,不能直接启动,需要先为集群初始化元数据。
第一步,生成集群ID。 这个操作只需要在任意一个节点上执行一次。
cd /opt/module/kafka
bin/kafka-storage.sh random-uuid
你会得到一个类似 bNtvX3PhRO6o2vU-g8b1VQ 的唯一字符串,这就是你的集群ID,记下来。
第二步,格式化存储目录。 这个操作需要在所有三个节点上分别执行。目的是在数据目录(log.dirs)下创建必要的元数据文件。
# 在每个节点上执行,将 <cluster-id> 替换为上一步生成的ID
bin/kafka-storage.sh format -t bNtvX3PhRO6o2vU-g8b1VQ -c config/server.properties
执行成功后,检查 /datas/kafka 目录,应该会看到一个 meta.properties 文件,里面记录了集群ID和本节点ID。
第三步,调整启动内存。 编辑 bin/kafka-server-start.sh 脚本,找到设置JVM堆内存的地方。对于16GB机器,建议设置为8GB。
# 在脚本中找到类似下面的部分,进行修改
if [ "x$KAFKA_HEAP_OPTS" = "x" ]; then
export KAFKA_HEAP_OPTS="-Xmx8G -Xms8G"
fi
将 -Xmx1g -Xms1g 修改为 -Xmx8G -Xms8G。-Xms 和 -Xmx 设为相同值可以避免堆内存动态调整带来的性能波动。
第四步,启动集群。 现在可以启动所有节点了。KRaft模式对启动顺序没有严格要求,可以逐个启动,也可以同时启动。
# 在每个节点上,以后台方式启动
sudo -u kafka nohup bin/kafka-server-start.sh config/server.properties > /dev/null 2>&1 &
启动后,用 jps 命令查看是否有 Kafka 进程,或者查看日志文件 logs/server.log 是否有错误。
第五步(推荐),配置Systemd服务。 使用Systemd管理服务,可以实现开机自启、优雅停止和状态监控。
sudo tee /usr/lib/systemd/system/kafka.service << 'EOF'
[Unit]
Description=Apache Kafka server (KRaft mode)
After=network.target
[Service]
Type=simple
User=kafka
Group=kafka
Environment="JAVA_HOME=/opt/module/jdk-17.0.8"
ExecStart=/opt/module/kafka/bin/kafka-server-start.sh /opt/module/kafka/config/server.properties
ExecStop=/opt/module/kafka/bin/kafka-server-stop.sh
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable kafka
sudo systemctl start kafka
sudo systemctl status kafka # 检查状态
4. 功能验证与生产就绪检查
集群跑起来了,但我们得确认它真的工作正常,并且配置是生产就绪的。
4.1 基础功能测试
首先,创建一个测试Topic,验证集群的元数据管理和数据复制功能。
# 使用任意一个节点的地址即可
bin/kafka-topics.sh --create \
--bootstrap-server 10.62.1.181:9092 \
--topic test-topic \
--partitions 3 \
--replication-factor 3
创建成功后,查看Topic详情,确认分区和副本分布是否均匀分布在三个节点上。
bin/kafka-topics.sh --describe --topic test-topic --bootstrap-server 10.62.1.181:9092
输出应该显示3个分区,每个分区有3个副本(Leader和Isr),并且Leader均匀分布在不同节点上。
接下来,进行简单的生产消费测试,验证数据通路。
# 终端1:启动一个控制台生产者
bin/kafka-console-producer.sh --topic test-topic --bootstrap-server 10.62.1.181:9092
# 终端2:启动一个控制台消费者,从头开始消费
bin/kafka-console-consumer.sh --topic test-topic --from-beginning --bootstrap-server 10.62.1.181:9092
在生产者终端输入几条消息,比如 Hello KRaft,在消费者终端应该能立刻看到这些消息。这就证明集群的基本读写功能正常。
4.2 高可用与容错模拟测试
生产环境最怕单点故障。我们需要模拟节点宕机,看集群能否自动恢复。注意:请在测试环境进行此操作!
- 观察当前状态:先用
describe命令记下test-topic每个分区的Leader在哪个节点上。 - 模拟节点故障:选择其中一个非所有分区Leader的节点(比如182),通过
systemctl stop kafka或kill命令停止其Kafka进程。 - 观察集群反应:
- 立即再次执行
kafka-topics.sh --describe。你会发现,原先在182节点上的分区Leader,可能会转移到181或183节点上。Isr(同步副本)列表中也移除了182节点。 - 尝试在生产者终端继续发送消息。消息发送应该不受影响(可能会有瞬间的重连或超时,但很快恢复)。消费者也能继续消费。
- 立即再次执行
- 恢复节点:启动182节点的Kafka服务 (
systemctl start kafka)。 - 观察恢复过程:等待几十秒后再次查看Topic描述。你会发现182节点重新加入Isr列表,并可能重新成为某些分区的Follower。这个过程证明了KRaft模式下的控制器能够协调副本的领导者选举和ISR管理,实现了高可用。
4.3 关键监控指标与配置复查
集群运行起来后,不能当“甩手掌柜”。这里有几个必须关注的点和配置复查项。
第一,日志监控。 定期查看 logs/server.log 和 logs/controller.log(KRaft模式下控制器日志独立了),关注是否有 ERROR 或 WARN 级别的报错,特别是关于副本同步、控制器选举、网络连接方面的。
第二,使用Kafka内置工具查看状态。
# 查看所有Broker节点状态
bin/kafka-broker-api-versions.sh --bootstrap-server 10.62.1.181:9092
# 查看消费组状态(如果有的话)
bin/kafka-consumer-groups.sh --bootstrap-server 10.62.1.181:9092 --list
第三,核心配置复查清单:
advertised.listeners:确认客户端网络能访问这个地址。这是生产环境连接失败的常见“坑”。log.dirs磁盘空间:设置监控告警,确保磁盘使用率不超过80%。num.partitions:评估当前默认分区数是否满足未来业务增长需求。分区数一旦创建,虽然可以增加,但减少很麻烦。min.insync.replicas=2:这个配置和replication.factor=3搭配,意味着一次写入需要至少2个副本确认才算成功。这保证了即使挂掉一个节点,数据也不会丢失,且写入可用性依然存在。这是生产环境数据可靠性的黄金配置。
5. 性能调优与生产环境进阶配置
基础集群搭好了,但要应对真实的生产流量,还需要一些“打磨”。这部分我分享一些针对8核16GB环境的调优经验。
5.1 内存与GC优化
我们给了JVM 8GB堆内存,但Kafka的性能关键其实是操作系统页面缓存。Kafka利用Linux的 Page Cache 来缓存磁盘数据,读写都在内存中进行,再由操作系统异步刷盘。所以,要确保系统有足够空闲内存给Page Cache。我们的16GB机器,8GB给JVM,剩下的8GB留给系统,基本够用。如果机器内存更大,比如32GB,可以给Kafka堆内存分配12-16GB,剩下的全留给系统缓存。
JVM垃圾回收器选择也很重要。对于Kafka这种低延迟、高吞吐的应用,推荐使用G1垃圾回收器。你可以在 kafka-server-start.sh 脚本的 KAFKA_HEAP_OPTS 后面添加GC参数:
export KAFKA_HEAP_OPTS="-Xmx8G -Xms8G -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M"
-XX:+UseG1GC:启用G1收集器。-XX:MaxGCPauseMillis=20:目标最大GC停顿时间,设为20毫秒左右比较合理。-XX:InitiatingHeapOccupancyPercent=35:当堆内存使用率达到35%时启动并发GC周期。-XX:G1HeapRegionSize=16M:设置G1区域大小。
5.2 磁盘与IO优化
磁盘是Kafka最主要的性能瓶颈。除了使用SSD,在配置上也有优化空间。
- 多目录挂载:如果单盘性能不足,可以加多块盘。在
log.dirs里配置多个目录,如/data/kafka1,/data/kafka2,/data/kafka3。Kafka会将不同分区的日志段分布到不同磁盘,实现IO负载均衡。 - 文件系统选择:推荐使用XFS或ext4。挂载时可以考虑使用
noatime选项来减少元数据更新开销。 - 调整刷盘策略:Kafka默认依赖操作系统后台刷盘,可靠性已经很高。除非有极强的持久化要求(如金融交易),否则不建议调整
log.flush.interval.messages和log.flush.interval.ms让其更频繁地同步刷盘,这会极大降低吞吐量。
5.3 网络与线程池调优
我们之前设置了 num.network.threads=8 和 num.io.threads=20。这是一个基于8核CPU的起点。如何判断是否需要调整?
- 监控Broker的CPU使用率。如果
network线程池经常打满,可以适当增加num.network.threads。 - 更关键的是
num.io.threads,它处理磁盘请求。如果磁盘是SSD且IOPS很高,而CPU尚有富余,可以适当增加这个值(比如到32或48),以充分发挥磁盘性能。监控磁盘IO等待时间,如果等待队列长,增加IO线程可能会有帮助。
socket.request.max.bytes 我们设为了200MB。如果你的业务场景是海量小消息(如日志采集),这个值可以适当调小(比如50MB),以减少单次请求的内存占用和处理延迟。如果是大数据批处理,消息体很大,则可以保持或调大。
5.4 主题级别的高级配置
集群级别的配置是默认值,创建具体Topic时,可以根据其重要性、流量模式进行个性化设置。例如,创建一个要求极高可靠性和可用性的订单Topic:
bin/kafka-topics.sh --create \
--bootstrap-server 10.62.1.181:9092 \
--topic orders \
--partitions 12 \ # 更高的并行度
--replication-factor 3 \
--config min.insync.replicas=2 \
--config cleanup.policy=compact \ # 对于关键数据,可能需要compact清理策略保留最新值
--config retention.ms=604800000 # 保留7天
而对于一些临时性的、吞吐量巨大的日志Topic,则可以牺牲一些可靠性换取性能:
bin/kafka-topics.sh --create \
--bootstrap-server 10.62.1.181:9092 \
--topic app-logs \
--partitions 24 \ # 更多分区应对高吞吐
--replication-factor 2 \ # 副本数可降为2,节省存储
--config retention.bytes=107374182400 # 按总大小保留,最多保留100GB
6. 运维实战:日常维护与问题排查指南
集群上线后,日常运维和问题排查是保证其长期稳定运行的关键。这里分享几个我踩过坑后总结的经验。
第一,关于节点重启与扩容。 在KRaft模式下,重启一个节点相对安全。直接使用 systemctl restart kafka 即可。节点重启后会从Leader副本同步数据,重新加入ISR。如果要扩容新节点,流程也很清晰:1) 在新机器上安装相同版本的Kafka;2) 分配一个全新的、未使用的 node.id(比如4);3) 在 controller.quorum.voters 配置中暂时不需要加入新节点(因为新节点只作为Broker);4) 配置好其他参数并启动;5) 使用 kafka-reassign-partitions.sh 工具将部分分区数据迁移到新节点上,实现负载均衡。
第二,监控告警配置。 除了看日志,必须搭建监控系统。关键指标包括:
- Broker级别:CPU使用率、网络流入流出流量、磁盘使用率和IOPS、JVM GC时间和频率、各监听器的连接数。
- Topic/分区级别:分区Leader分布是否均衡、ISR数量是否正常、分区未同步副本数、生产和消费的延迟。
- Controller指标:特别是Raft层的指标,如控制器选举次数、元数据提交延迟等。可以使用JMX暴露指标,然后由Prometheus采集,Grafana展示。
第三,常见问题排查思路。
- 生产者发送失败:首先检查
advertised.listeners配置的地址和端口,从客户端网络是否可达。其次检查Topic的Leader是否存活(所有Leader都挂掉的可能性极小)。查看生产者日志和Broker的server.log。 - 消费者拉不到消息:确认消费者组是否正常,
__consumer_offsetsTopic是否正常。检查消费者是否订阅了正确的Topic,或者其消费的偏移量(offset)是否已经远大于最新消息的位置。 - 副本不同步(Under Replicated Partitions):这是生产环境常见警告。可能原因有:某个Broker节点网络断开、磁盘IO过慢导致同步跟不上、或者GC停顿时间过长。需要结合Broker的日志、网络监控和磁盘监控来判断。
- Controller频繁选举:在KRaft模式下,如果Controller节点不稳定,会影响元数据操作。检查Controller节点的负载(CPU、内存、网络)是否过高,
controller.log是否有异常。确保Controller节点之间的9093端口网络延迟低且稳定。
最后,记得定期对集群配置和客户端配置进行审计和备份。尤其是 server.properties 和重要的Topic配置。在每次做出变更前,最好在测试环境验证。KRaft模式让Kafka集群变得更简洁、更强大,但它的稳定运行依然离不开细致周到的规划和持续用心的运维。希望这份从规划到调优的实战指南,能帮你顺利搭建并驾驭好你的Kafka 4.0生产集群。
502



被折叠的 条评论
为什么被折叠?



