☰
CentOS7单机Kafka部署实战:JDK+ZooKeeper+Kafka配置详解
2026/10/15 5:17:34 网站建设 项目流程

CentOS7上搭一个单机Kafka,说穿了就是“JDK + ZooKeeper + Kafka”三件套,装完、配好、起服务,二十分钟内跑通生产消费不是问题。但很多新手栽在版本匹配和配置文件上,比如JDK版本太新、listeners配置不对、防火墙没放行,导致本地起得来、同事连不上。这篇文章就用一台全新CentOS7机器,从零开始走一遍单机Kafka部署,包含每个配置文件里关键参数的作用、启动验证方式、以及我实测中踩过的几个坑。适合刚接触Kafka、想在家搭环境练手的开发同学,也适合需要给团队快速准备一套可用Kafka实例的运维朋友。

1. 整体思路与部署方案选型

1.1 单机Kafka到底能拿来干什么

单机Kafka不是生产环境的终极形态,但它能让你用最小成本把消息的“生产、存储、消费”这条链路完整跑通。我自己搭这套环境的初衷很简单:本地验证一段业务逻辑,模拟一个producer往某个topic写数据,另一个consumer消费并做处理,整个过程不涉及跨节点协调,也不需要处理副本同步。对学习Kafka内部机制的人来说,单机环境足够你把分区、副本、offset提交这些概念摸清楚。

另一个典型场景是给中小型测试环境提供一个稳定的消息通道。比如公司内部的环境里已经有了一套业务系统,想在两套服务之间解耦异步调用,但又不想为一两个topic上三台机器做集群,这时候单机Kafka完全够用。它的吞吐量在测试环境没什么瓶颈,消息堆积量只要不超出磁盘大小,也没有问题。

当然要明确它的边界。单机意味着没有副本,broker一挂消息就丢,日志目录一坏就完蛋。所以在生产或预发环境,我不会只用一个节点,至少也要三节点集群。但在快速验证、开发联调、跑通demo这个层面,单机是最优解。省机器、省运维、出问题排查路径短。

1.2 部署模式选型:ZooKeeper模式还是KRaft模式

Kafka从2.8版本开始引入了KRaft模式,目的是逐步去掉对ZooKeeper的依赖。到3.x版本,KRaft已经可以在生产环境使用了,但ZooKeeper模式仍然是大量存量系统和教程里的主流选择。我这次部署选的是传统的ZooKeeper模式,原因有三个:

第一,资料多、排错成本低。你搜“Kafka部署”能看到的大部分文章都是ZooKeeper模式,遇到问题能参考的案例数量不是一个量级。

第二,CentOS7上JDK8配合ZooKeeper的方案被验证过无数次,稳定性最可靠。

第三,很多团队现有的监控、管理工具(比如各类Kafka Manager)对ZooKeeper模式的适配依然更完善。

如果你要在单机环境体验新架构,KRaft模式也可以,只需要一个Kafka进程就能跑,不需要单独启动ZooKeeper,确实更轻量。但它对Kafka版本有要求(推荐3.3+),而且当前阶段如果你不是要深入学习新架构原理,没必要在快速部署的时候给自己增加额外的变量。一句话:追求稳妥选ZooKeeper模式,追求尝鲜选KRaft,我下面的步骤以ZooKeeper模式为主。

对比项ZooKeeper模式KRaft模式
依赖组件Kafka + ZooKeeper仅Kafka
进程数2个1个
推荐版本全版本通用3.3及以上版本
资料丰富度非常丰富相对较少
适合场景常规部署、学习、存量系统新架构体验、新项目尝鲜

2. 环境准备与软件版本搭配

2.1 安装JDK并配置环境变量:这是最容易翻车的一步

Kafka本身是用Java写的,运行时必须有JDK。这里有两个容易踩的坑:一是只装了JRE没装JDK,启动脚本找不到编译相关工具;二是JDK版本过高,比如直接装了个Java17,Kafka部分版本的脚本兼容性会有问题。

我在这台CentOS7上选择的是OpenJDK 1.8。为什么是1.8?因为CentOS7自带的软件源里就有现成包,安装快、兼容性好,Kafka 3.x系列官方文档里也明确支持JDK8和JDK11。用yum安装非常省事:

yum install -y java-1.8.0-openjdk-devel

这里一定要装-devel版本,而不是默认的java-1.8.0-openjdk,因为Kafka的启动脚本会用到tools.jar,非devel版本里没有。安装完验证一下:

java -version

正常会输出类似openjdk version "1.8.0_xxx"的信息。然后我习惯再手动确认一下JAVA_HOME指向,虽然CentOS的yum安装通常会自动设置,但如果你的机器是别人装过的,保不准会残留其他版本的JDK。执行:

echo $JAVA_HOME

如果没有输出,需要在/etc/profile里加上:

export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export PATH=$PATH:$JAVA_HOME/bin

注意:加完记得source /etc/profile再重新登录终端,否则当前会话还是读不到新环境变量。

2.2 下载Kafka与ZooKeeper,版本匹配建议

Kafka版本迭代很快,ZooKeeper版本也跟着更新,两个组件之间并没有一个硬性的匹配表,但选择一个经过了广泛验证的组合能省掉很多麻烦。我这台机器用的是:

  • Kafka:3.4.0(对应二进制包是kafka_2.13-3.4.0.tgz)
  • ZooKeeper:3.8.1

这个组合在CentOS7 + JDK8环境下跑了很久没出问题。下载时注意从官网下载页面复制binary版tgz包地址,不要下源码包,源码包需要自己编译,没必要。

建议目录规划如下:

/opt/kafka_2.13-3.4.0 # Kafka部署目录 /opt/zookeeper-3.8.1 # ZooKeeper部署目录 /data/zookeeper/data # ZooKeeper数据目录 /data/kafka-logs # Kafka消息日志目录

我特意把数据目录放在/data而不是解压目录里,原因有两个:一是系统盘通常是SSD但空间有限,消息日志增长很快,放在独立数据盘上更容易扩容;二是后续如果升级Kafka版本,直接把程序目录替换掉,数据不受影响。

下载解压后的目录权限,我习惯统一给运行用户授权,这里直接用的root做演示,生产环境建议创建专用用户,后面会单独讲。

3. 核心配置逐项拆解

3.1 ZooKeeper配置与启动

ZooKeeper的配置文件在/opt/zookeeper-3.8.1/conf/zoo.cfg,下载完成后默认没有这个文件,只有zoo_sample.cfg,需要复制一份再改:

cp /opt/zookeeper-3.8.1/conf/zoo_sample.cfg /opt/zookeeper-3.8.1/conf/zoo.cfg

单机部署ZooKeeper的配置非常简洁,我实际用到的核心项就四行:

tickTime=2000 dataDir=/data/zookeeper/data clientPort=2181 maxClientCnxns=60

tickTime是ZooKeeper的基本时间单元,单位毫秒,默认2000即可,一般不用调。dataDir是ZooKeeper保存快照和事务日志的目录,这个必须在启动前手动创建好,否则启动直接报错。clientPort是客户端连接端口,Kafka会通过这个端口和ZooKeeper通信,默认2181。maxClientCnxns是最大客户端连接数,单机环境60够了,不需要像集群那样调大。

启动ZooKeeper之前,先创建数据目录:

mkdir -p /data/zookeeper/data

然后启动:

cd /opt/zookeeper-3.8.1 bin/zkServer.sh start

看到STARTED字样后,还要确认端口真的在监听:

ss -lntp | grep 2181

如果端口没起来,去看/opt/zookeeper-3.8.1/logs/zookeeper.log,大部分情况都是dataDir目录不存在或权限不对。

3.2 server.properties关键参数解读

Kafka的主配置文件是/opt/kafka_2.13-3.4.0/config/server.properties,这个文件决定了Kafka的行为方式。很多人部署失败就是因为照着网上的配置乱抄,然后listeners和advertised.listeners没有理解透彻。

我用的最小可用配置如下:

broker.id=0 listeners=PLAINTEXT://0.0.0.0:9092 advertised.listeners=PLAINTEXT://192.168.1.100:9092 num.network.threads=3 num.io.threads=8 socket.send.buffer.bytes=102400 socket.receive.buffer.bytes=102400 socket.request.max.bytes=104857600 log.dirs=/data/kafka-logs num.partitions=3 num.recovery.threads.per.data.dir=1 log.retention.hours=168 log.segment.bytes=1073741824 log.retention.check.interval.ms=300000 zookeeper.connect=localhost:2181 zookeeper.connection.timeout.ms=18000 group.initial.rebalance.delay.ms=0 offsets.topic.replication.factor=1 transaction.state.log.replication.factor=1 transaction.state.log.min.isr=1

逐项说下关键点。

broker.id=0是broker的唯一编号,单机只有一个broker,写0没问题。如果以后要加节点组成集群,这个id必须全局唯一,不能重复。

listeners=PLAINTEXT://0.0.0.0:9092是Kafka监听所有网卡的9092端口,注意这里不要写localhost,否则外部机器连不上。协议方式是PLAINTEXT,也就是明文传输,如果业务有加密需求,需要用SSL协议,但那属于进阶话题,单机快速部署不涉及。

advertised.listeners是这个文件里最容易出问题的参数。它的作用是告诉客户端“你应该连这个地址”,如果配置成localhost,即使listeners监听的是0.0.0.0,客户端拿着localhost也连不上远程broker。所以我这台机器的内网IP是192.168.1.100,就填这个。如果你只是本机测试,不打算让其他机器连接,那么可以填PLAINTEXT://localhost:9092。

log.dirs是消息日志目录,注意它不是“日志文件”的意思,而是Kafka真正存储topic数据的地方。这个目录必须手动创建,并且保证磁盘空间充足。如果目录不存在,Kafka启动会报错。

num.partitions=3是新建topic时的默认分区数。单机环境下分区数多不代表性能高,因为所有分区都在同一块磁盘上。我设成3是为了后续测试并发消费方便,实际生产可以根据流量评估,一般建议至少跟broker核数持平。

log.retention.hours=168表示消息保留7天,超过这个时间旧数据会被清理。如果你的业务希望消息积压久一点,可以调大;如果磁盘紧张,可以调小到24。注意Kafka清理的是日志段文件,不是单条消息,所以它没法做到“消费完立刻删掉某条消息”。

offsets.topic.replication.factor=1和transaction.state.log.replication.factor=1这两个参数在网络教程里经常被忽略。它们的含义是Kafka内部主题(offsets、事务状态)的副本因子。单机只有一个broker,如果这里不改成1,Kafka默认会尝试创建副本数为3的内部主题,然后因为找不到足够副本数而启动失败。这是单机部署最常见的启动报错之一。

group.initial.rebalance.delay.ms=0让消费者组在第一次订阅时立即触发rebalance,而不是等待3秒。这个参数在新版本里用于减少测试等待时间,生产环境一般保持默认3秒更好,可以避免频繁变动消费者组成员时出现的重复消费问题。

3.3 启动Kafka与systemd托管

配置文件改好之后,先创建日志目录:

mkdir -p /data/kafka-logs

然后启动Kafka。官方脚本位于bin/kafka-server-start.sh,后台启动时加-daemon参数:

cd /opt/kafka_2.13-3.4.0 bin/kafka-server-start.sh -daemon config/server.properties

启动后检查两个东西。一是进程是否存活:

jps

正常会看到QuorumPeerMain(ZooKeeper)和Kafka两个进程。二是看Kafka日志末尾:

tail -f logs/server.log

出现started (kafka.server.KafkaServer)字样就是启动成功。如果日志提示无法连接ZooKeeper,先检查ZooKeeper进程和2181端口。

手动-daemon方式启动的进程,在终端退出后依然会运行,但服务器重启后不会自动拉起。所以我习惯再写一个systemd service,让Kafka和ZooKeeper都变成受管服务。以Kafka为例,在/etc/systemd/system/kafka.service里写入:

[Unit] Description=Kafka Broker After=network.target zookeeper.service [Service] Type=simple User=root ExecStart=/opt/kafka_2.13-3.4.0/bin/kafka-server-start.sh /opt/kafka_2.13-3.4.0/config/server.properties ExecStop=/opt/kafka_2.13-3.4.0/bin/kafka-server-stop.sh Restart=on-failure [Install] WantedBy=multi-user.target

ZooKeeper的service同样思路,只是ExecStart指向/opt/zookeeper-3.8.1/bin/zkServer.sh start-foreground。配置好后执行systemctl daemon-reload,再用systemctl enable kafka开启开机自启。这一套下来,运维省心很多。

4. 生产消费验证与网络连通性检查

4.1 用命令行快速验证整条链路

Kafka部署完成后,不急着接业务代码,先用官方自带命令行工具跑一遍生产消费,验证链路是否通畅。

创建topic:

cd /opt/kafka_2.13-3.4.0 bin/kafka-topics.sh --create --topic demo-topic --partitions 3 --replication-factor 1 --bootstrap-server localhost:9092

如果看到Created topic demo-topic,说明topic创建成功。接着查看topic详情:

bin/kafka-topics.sh --describe --topic demo-topic --bootstrap-server localhost:9092

能看到分区数3、副本数1、leader分布在broker 0上,都符合预期。

然后开一个消费者终端,把topic内容打印出来:

bin/kafka-console-consumer.sh --topic demo-topic --from-beginning --bootstrap-server localhost:9092

再开另一个终端,启动生产者,输入几条消息:

bin/kafka-console-producer.sh --topic demo-topic --bootstrap-server localhost:9092 >hello kafka >first message

切换到消费者终端,应该能看到这两条消息。注意:如果消费者先启动、生产者后写入,看不到历史消息是正常现象;如果需要从头消费,必须加--from-beginning参数。

4.2 防火墙和安全组:外网连接排查的关键

命令行验证用的是localhost,不涉及网络问题。但如果你想在同一局域网的另一台机器上消费这个Kafka,就必须处理防火墙和安全组。

CentOS7默认启用了firewalld服务,即使Kafka监听了0.0.0.0,外部流量也会被挡在9092端口外面。最简单的排查方式是先在Kafka服务器上确认端口监听:

ss -lntp | grep 9092

如果能看到0.0.0.0:9092在LISTEN,说明Kafka本身没问题,问题在系统防火墙或外部防火墙。

关闭firewalld或放行端口:

systemctl stop firewalld systemctl disable firewalld

如果不能在测试环境关防火墙,那就只放行9092和2181:

firewall-cmd --permanent --add-port=9092/tcp firewall-cmd --permanent --add-port=2181/tcp firewall-cmd --reload

接着要确认服务器安全组是否放行了这两个端口。很多云主机有两层防火墙,系统firewalld关了,安全组没放行也连不上。这里不点名具体厂商,但对需要远程访问的服务,务必同时检查这两层。

最后还有一个容易忽略的点:如果客户端用的不是Kafka服务器的内网IP,而是通过公网IP访问,那么advertised.listeners里的IP必须配置成客户端实际能访问到的IP,否则会出现“能连上9092端口但生产者发送消息超时”的诡异情况。这个问题我在第6章的排查表里还会再提。

5. 单机环境的资源分配与监控调优

5.1 JVM堆内存与垃圾回收参数

Kafka默认的JVM堆内存是1GB,单机测试环境如果服务器内存只有2GB,再跑一个ZooKeeper和系统本身,内存压力就会很大。我建议在一开始就把堆内存调到合理范围。

修改bin/kafka-server-start.sh,找到KAFKA_HEAP_OPTS那行,默认是:

export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"

我把两台低配测试机都改成:

export KAFKA_HEAP_OPTS="-Xmx512M -Xms512M"

这里不建议把堆内存调得太低,比如128M,因为Kafka正常处理消息时会用大量page cache,堆内存过小会导致GC频繁,反而影响吞吐。512M对单机测试场景足够。另外GC算法方面,新版Kafka脚本里默认配置了G1,如果你的JDK是8u45以前的旧版本,G1性能并不好,但CentOS7源里的JDK8版本较新,基本不用管。

5.2 系统层优化:文件描述符和Swap

Kafka对文件描述符的需求比较激进,尤其是topic多、分区多的时候。单机测试环境虽然压力不大,但建议还是把ulimit -n改大,避免后续写入量上来后报Too many open files。

修改/etc/security/limits.conf,添加:

root soft nofile 65535 root hard nofile 65535

重新登录终端后执行ulimit -n验证,如果显示65535就生效了。这里强调一下:不要只改这个文件而忽略systemd服务,如果Kafka是用systemd启动的,还要在service文件里加LimitNOFILE=65535,否则系统的limits配置对service进程不生效。

Swap这一项上,Kafka官方建议有足够内存时减少swap使用,因为Kafka大量使用page cache加速读写,如果频繁swap到磁盘,性能会明显下降。CentOS7默认的vm.swappiness是30,我测试时一般把它降到10:

sysctl -w vm.swappiness=10 echo 'vm.swappiness=10' >> /etc/sysctl.conf

这个值的意思是系统“倾向于使用swap的程度”,调到10并不代表不用swap,而是优先保留内存给page cache。对于内存充足的机器,也可以直接设为0,但要谨慎,别让内存耗尽触发OOM。

5.3 轻量监控:不用上全家桶也能看到Kafka状态

单机Kafka不需要一上来就接Prometheus和Grafana,那样反而背离了“快速部署”的初衷。我常用的监控手段有两种。

第一种是开启JMX端口,然后用jconsole连接查看。在bin/kafka-server-start.sh里设置环境变量:

export JMX_PORT=9999

启动Kafka后,本机执行jconsole,在远程连接里输入ip:9999,就能看到堆内存、线程、GC等基础指标。这个方式零依赖,适合开发环境应急。要注意:JMX端口不要暴露在公网,否则有安全风险,测试环境可以设置防火墙只允许内网访问。

第二种是直接看Kafka的日志目录和topic指标。用脚本定时检查磁盘占用:

du -sh /data/kafka-logs

同时用kafka-topics.sh --describe查看所有topic的分区leader是否正常。虽然这种机械式检查不够优雅,但在单机场景下,只要磁盘不写满、进程不退出、端口在监听,基本不会有什么大事。

6. 常见报错与排查实录

6.1 单机部署常见错误速查表

整理了一张我在实际部署中遇到的报错对照表,按出现频率从高到低排列:

错误现象可能原因解决办法
启动Kafka时报InvalidReplicationFactorException内部topic副本数仍为3,单机broker不足配置offsets.topic.replication.factor=1和transaction.state.log.replication.factor=1
生产者发送消息超时,报TimeoutExceptionadvertised.listeners配置了localhost或错误IP改成客户端实际访问的IP,同时检查防火墙
远程连接9092端口不通系统firewalld未放行或云安全组未放行放行9092端口,或直接关闭firewalld测试
启动时报Unable to connect to zookeeperZooKeeper进程未启动或2181端口未监听确认zkServer.sh status为正常,检查dataDir目录
Kafka启动很快退出但日志无报错堆内存过小或日志目录不可写检查/data/kafka-logs是否存在且可写,调整KAFKA_HEAP_OPTS
JPS看到QuorumPeerMain但看不到Kafka启动脚本里JAVA_HOME未找到确认java -version正常,echo $JAVA_HOME有输出
磁盘被消息日志写满保留策略太长或log.retention.bytes未限制调小log.retention.hours,或设置log.retention.bytes
消费端报LeaderNotAvailableException分区Leader正在选举中,或者broker启动未完成等30秒左右再消费,用kafka-topics.sh --describe确认leader
Address already in use9092端口被其他进程占用`ss -lntp
客户端连接时提示Invalid url连接格式错误确认使用ip:9092格式,topic命令用--bootstrap-server ip:9092

6.2 我实际踩过的三个坑

第一个坑和advertised.listeners有关。我以为只要listeners=PLAINTEXT://0.0.0.0:9092就可以让局域网内其他机器连接,结果生产端一直在“发送消息超时”。后来用kafka-console-producer在另一台机器上测试,发现TCP连接能建立,但元数据请求返回的broker地址是localhost。这就是advertised.listeners没配置成真实IP导致的,客户端拿到了一个它访问不了的地址,于是反复重试直到超时。这个问题在局域网场景里极其隐蔽,因为“看起来端口是通的”。

第二个坑是ZooKeeper的dataDir目录没有提前创建,导致zkServer.sh start命令报“Unable to load database”。我当时还以为是网络问题,后来看日志才发现纯属目录不存在。这可能是我所有Kafka部署里最蠢的一次报错,但也是新手最容易忽略的——ZooKeeper不会帮你自动创建数据目录,Kafka的log.dirs也一样。

第三个坑和systemd有关。我最初只用nohup后台启动Kafka,后来手动重启了一次机器,结果Kafka没有自动起来,整个测试环境的消费者全部断开,还被同事问了一顿。从那之后我养成了所有中间件都接systemd的习惯,确保意外重启也能恢复。如果你的机器经常被人重启,这一点尤其重要。

单机Kafka部署本身没有太多玄学,核心就是把JAVA_HOME配对、ZooKeeper数据目录建好、server.properties里副本因子设成1、advertised.listeners填对。把这些搞定,剩下的都是熟练度的问题。我个人在实际操作中还喜欢开局就用systemd托管两个服务,并把JMX端口打开,这样后续调优和排查都能少走很多弯路。如果在部署过程中遇到上面没覆盖到的报错,建议第一件事去看logs/server.log,绝大多数Kafka问题都会在日志里留下明确的指向性信息,比瞎猜高效得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询