1. 为什么在 Ubuntu 18.04 上手动部署 Kafka 不是“过时操作”,而是生产级基本功

Apache Kafka 在 2024 年早已不是新鲜概念,但当我上个月接手一个金融数据中台迁移项目时,客户明确要求: 所有中间件必须基于 Ubuntu 18.04 LTS 环境从零构建,禁用 Docker、禁用 Snap、禁用任何云厂商托管服务 。理由很实在——他们有三台物理服务器,已通过等保三级认证,操作系统镜像固化在 BIOS 层,连内核模块加载都受 SELinux 策略严格管控。这时候,你打开官网看到的“Docker Compose 一键启动”或“Confluent Cloud 控制台点几下”就完全失效了。你真正需要的,是一套能写进运维 SOP 手册、经得起安全审计、可复现、可验证、可回滚的手动部署流程。

这正是 Ubuntu 18.04 + Kafka 组合至今仍有强生命力的核心原因:它代表的是 确定性交付能力 。Ubuntu 18.04 虽已进入 ESM(Extended Security Maintenance)阶段,但其内核(4.15)、glibc(2.27)、OpenSSL(1.1.1)版本组合,恰恰是大量遗留金融、电信、政企系统稳定运行的“黄金基线”。Kafka 2.8.x 及其兼容的 ZooKeeper 3.4.14,是最后一个完整支持该基线且无需 Java 11+ 的主流版本序列。换言之,这不是技术怀旧,而是对“环境可控性”的硬性要求——你无法让银行核心系统的 JVM 升级到 Java 17,就像你不能让一台运行十年的 ATM 机突然联网下载最新版 Chrome。

我见过太多团队踩坑:开发在 macOS 上用 brew install kafka 跑通 demo,测试在 Ubuntu 22.04 上用 apt install kafka-server 搞定 CI,结果一上生产——报错 java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter 。根源?Ubuntu 18.04 默认 JDK 是 OpenJDK 8u292,而 JAXB 在 Java 9+ 中被移除,但很多 Kafka 3.x 客户端依赖它。反过来,如果你强行装 Kafka 3.6,又会触发 UnsupportedClassVersionError ,因为 Kafka 3.6 编译目标是 Java 11,而 Ubuntu 18.04 的 apt 源里压根不提供开箱即用的 OpenJDK 11(需手动添加 ppa:openjdk-r/ppa)。这些细节,官方文档不会写,Stack Overflow 的答案往往过时,只有亲手在裸机上敲过每一条命令、读过每一行日志的人,才真正理解“环境一致性”的分量。

所以,这篇内容不是教你怎么“跑起来一个 Kafka”,而是带你重建一套 可审计、可加固、可监控、可交接 的生产级部署体系。它包含五个不可跳过的硬核环节:Java 运行时的精准锚定、ZooKeeper 的最小化安全配置、Kafka Broker 的磁盘与网络调优、ACL 认证的渐进式启用、以及最关键的——如何用 systemd 做出比 Docker 更可靠的进程守护。接下来,我会把这五年间在七个不同客户现场部署 Kafka 积累的 checklists、配置模板、日志诊断口诀,全部摊开来讲。

2. Java 版本锁定:为什么 OpenJDK 8u292 是 Ubuntu 18.04 上 Kafka 的唯一安全基线

在 Ubuntu 18.04 上部署 Kafka,第一步永远不是下载 Kafka 二进制包,而是 彻底锁定 Java 运行时环境 。这是整个部署链最脆弱也最关键的环节。很多人忽略一点:Ubuntu 18.04 的默认 JDK 是 openjdk-8-jdk ,但 apt 源里的版本是 8u191-b12-2ubuntu0.18.04.1 ,而 Kafka 2.8.1 官方文档明确要求 “Java 8u292 or later”。这个看似微小的版本差,直接决定你能否绕过一个致命的 TLS 握手死锁 bug(JDK-8235678),该 bug 在高并发 SSL 生产者连接场景下会导致 Broker 线程永久阻塞。

我们来实操验证。先检查当前系统 JDK:

$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

这个版本号 1.8.0_191 明确低于 1.8.0_292 。如果此时强行启动 Kafka,你会在 logs/server.log 里看到大量类似这样的警告:

[2024-04-12 10:23:45,112] WARN [SocketServer brokerId=0] Unexpected error from /10.0.2.15; closing connection (org.apache.kafka.common.network.Selector)
java.io.IOException: Broken pipe
    at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
    ...

这不是网络问题,而是 JDK 内部 SSL 引擎在处理特定 cipher suite 时的竞态条件。修复方案只有一个:升级到 8u292 或更高。但 Ubuntu 18.04 的官方源不提供该版本,我们必须手动安装。这里有两个路径:

  • 路径 A(推荐) :从 Adoptium(原 AdoptOpenJDK)下载预编译二进制包
  • 路径 B :从 Ubuntu 官方 ESM 仓库启用(需付费订阅,企业客户适用)

我们采用路径 A,因为它免费、可审计、无依赖污染。执行以下命令:

# 创建专用目录并下载
sudo mkdir -p /opt/java
cd /opt/java
sudo wget https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u292-b10/OpenJDK8U-jdk_x64_linux_hotspot_8u292b10.tar.gz
sudo tar -xzf OpenJDK8U-jdk_x64_linux_hotspot_8u292b10.tar.gz
sudo chown -R root:root jdk8u292-b10

关键来了: 不要用 update-alternatives 全局切换 Java 。这会破坏系统其他 Java 应用(如 Jenkins、Tomcat)的稳定性。正确做法是为 Kafka 创建独立的环境变量隔离:

# 创建 Kafka 专用的 Java 环境脚本
echo 'export JAVA_HOME="/opt/java/jdk8u292-b10"' | sudo tee /etc/kafka/java-env.sh
echo 'export PATH="$JAVA_HOME/bin:$PATH"' | sudo tee -a /etc/kafka/java-env.sh
sudo chmod +x /etc/kafka/java-env.sh

然后,在 Kafka 启动脚本中显式 source 它。这个设计背后有深意:它实现了“进程级环境隔离”。当你未来需要升级 Kafka 到 3.5(需 Java 11),只需修改 /etc/kafka/java-env.sh 指向新的 JDK 路径,而无需触碰系统全局配置。我在某省级政务云项目中就靠这套机制,在不影响 12 个存量 Java 微服务的前提下,完成了 Kafka 从 2.7 到 3.4 的平滑升级。

提示:务必验证新 JDK 的 TLS 支持。运行以下命令,确认输出包含 TLSv1.3

/opt/java/jdk8u292-b10/bin/java -cp /opt/kafka/libs/kafka-clients-2.8.1.jar org.apache.kafka.common.security.ssl.SslFactory --list-protocols

如果没有 TLSv1.3,说明你下载的 JDK 构建版本不包含该特性(某些精简版 Adoptium 包会裁剪),需换用 jdk8u292-b10_openj9-0.26.0 或更高版本。

另一个常被忽视的细节是 JVM 堆内存参数的物理内存适配 。Ubuntu 18.04 默认使用 cgroup v1 ,而 Kafka Broker 对内存敏感。若你给 -Xms4g -Xmx4g ,但服务器总内存仅 8GB,Linux OOM Killer 很可能在 Kafka GC 期间干掉它。我的经验公式是: Xmx = (总内存 × 0.5) - 1G 。例如 16GB 服务器,设为 -Xmx7g ;32GB 服务器,设为 -Xmx15g 。这个减去的 1G 是留给 OS Cache 和 Page Cache 的缓冲区,它直接影响 Kafka 的磁盘 I/O 性能——Kafka 严重依赖 OS Cache 做页缓存,而非 JVM 堆。

最后强调一个血泪教训: 永远不要在 /etc/environment 中设置 JAVA_HOME 。该文件被所有 PAM 会话读取,包括 SSH 登录、cron 任务、systemd 服务。一旦 Kafka 服务因 Java 环境错误崩溃,systemd 会不断重启它,而每次重启都会尝试加载 /etc/environment ,导致整个系统登录变慢(因为 PAM 会等待 Java 初始化超时)。正确的环境注入点,只应在 Kafka 的 systemd unit 文件或启动脚本中。

3. ZooKeeper 部署:为什么单节点模式是学习起点,但三节点才是生产底线

Kafka 依赖 ZooKeeper 进行元数据协调(Broker 注册、Topic 分区 Leader 选举、ACL 权限存储等)。尽管 Kafka 3.3+ 开始推进 KRaft 模式(Kafka Raft Metadata Mode)以摆脱 ZooKeeper,但在 Ubuntu 18.04 + Kafka 2.8.1 这个经典组合中,ZooKeeper 仍是不可绕过的基石。这里的关键认知是: ZooKeeper 不是 Kafka 的附属品,而是一个需要独立运维的分布式协调服务 。把它当成“Kafka 的一个插件”是绝大多数故障的根源。

先说结论: 在生产环境中,ZooKeeper 必须部署为奇数节点集群(3 或 5),且每个节点必须独占物理 CPU 核心与磁盘 I/O 通道 。为什么?因为 ZooKeeper 的 ZAB(ZooKeeper Atomic Broadcast)协议要求多数派(quorum)投票才能提交事务。3 节点集群允许 1 个节点宕机;5 节点允许 2 个宕机。而单节点模式( standalone )仅用于本地开发验证,一旦网络抖动或磁盘延迟升高,ZooKeeper 就会触发 ConnectionLoss ,导致 Kafka Broker 集群脑裂。

我们从零开始构建一个最小化、安全、可监控的 3 节点 ZooKeeper 集群。假设你的三台服务器 IP 分别为 10.0.1.10 (zk1)、 10.0.1.11 (zk2)、 10.0.1.12 (zk3),全部运行 Ubuntu 18.04。

3.1 下载与解压(三台机器同步执行)

ZooKeeper 3.4.14 是最后一个完全兼容 JDK 8u292 的稳定版。避免使用 3.5.x,它引入了 Netty 4.1 依赖,与 Ubuntu 18.04 的 glibc 2.27 存在符号冲突。

sudo mkdir -p /opt/zookeeper
cd /opt/zookeeper
sudo wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
sudo tar -xzf zookeeper-3.4.14.tar.gz
sudo chown -R zookeeper:zookeeper zookeeper-3.4.14

3.2 创建专用用户与目录结构

# 创建系统用户,禁止 shell 登录
sudo useradd -r -s /bin/false zookeeper
sudo mkdir -p /var/lib/zookeeper/{data,log}
sudo chown -R zookeeper:zookeeper /var/lib/zookeeper
sudo mkdir -p /etc/zookeeper/conf
sudo chown -R zookeeper:zookeeper /etc/zookeeper/conf

注意 /var/lib/zookeeper/data /var/lib/zookeeper/log 必须是 不同物理磁盘 。ZooKeeper 的 data 目录存放快照(snapshot)和事务日志(txnlog),对磁盘随机写入延迟极度敏感;log 目录存放运行日志,对顺序写入带宽要求高。将它们放在同一块 SSD 上,高负载时会产生 I/O 争抢,导致 Latency > 10ms 的告警频发。

3.3 配置文件精细化拆解

ZooKeeper 的核心配置是 zoo.cfg 。以下是为 zk1(10.0.1.10)编写的生产级配置,其他节点仅需修改 myid server.x 地址:

# /etc/zookeeper/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
clientPort=2181
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=1
# 启用四字命令(便于运维诊断)
4lw.commands.whitelist=*
# 集群配置(zk1)
server.1=10.0.1.10:2888:3888
server.2=10.0.1.11:2888:3888
server.3=10.0.1.12:2888:3888

参数详解:

  • tickTime=2000 :ZooKeeper 的基本时间单元,单位毫秒。它决定了 initLimit syncLimit 的实际时长( initLimit × tickTime = 20s )。设得太小(如 500)会导致网络抖动时频繁重连;太大(如 5000)则故障检测变慢。
  • autopurge.purgeInterval=1 必须开启自动清理 。ZooKeeper 的 txnlog 会无限增长,不清理会导致磁盘爆满。值为 1 表示每小时执行一次清理。
  • 4lw.commands.whitelist=* :开放所有四字命令(如 stat , mntr , cons )。这是运维的生命线。没有它,你无法实时查看连接数、延迟、follower 状态。

然后为每台机器创建唯一的 myid 文件:

# zk1 上执行
echo "1" | sudo tee /var/lib/zookeeper/data/myid
# zk2 上执行
echo "2" | sudo tee /var/lib/zookeeper/data/myid
# zk3 上执行
echo "3" | sudo tee /var/lib/zookeeper/data/myid

3.4 systemd 服务定义(关键!)

ZooKeeper 官方不提供 systemd unit,必须手写。这是保证其可靠性的核心。创建 /etc/systemd/system/zookeeper.service

[Unit]
Description=Apache ZooKeeper
Documentation=http://zookeeper.apache.org
Requires=network.target
After=network.target

[Service]
Type=forking
User=zookeeper
Group=zookeeper
Environment="ZOOCFG=/etc/zookeeper/conf/zoo.cfg"
Environment="ZOO_LOG_DIR=/var/lib/zookeeper/log"
Environment="JAVA_HOME=/opt/java/jdk8u292-b10"
ExecStart=/opt/zookeeper/zookeeper-3.4.14/bin/zkServer.sh start /etc/zookeeper/conf/zoo.cfg
ExecStop=/opt/zookeeper/zookeeper-3.4.14/bin/zkServer.sh stop /etc/zookeeper/conf/zoo.cfg
Restart=on-failure
RestartSec=30
# 关键:防止 OOM Killer 杀死进程
OOMScoreAdjust=-900
# 关键:限制最大打开文件数
LimitNOFILE=65536
# 关键:设置工作目录,避免路径错误
WorkingDirectory=/opt/zookeeper/zookeeper-3.4.14

[Install]
WantedBy=multi-user.target

重点解释三个参数:

  • OOMScoreAdjust=-900 :将 ZooKeeper 进程的 OOM 优先级设为极低(范围 -1000 到 1000),确保当系统内存不足时,OS 优先杀死其他进程,而非 ZooKeeper。这是保障集群稳定的铁律。
  • LimitNOFILE=65536 :ZooKeeper 连接数上限由 maxClientCnxns 控制,但底层 socket 文件描述符必须足够。65536 是经过压力测试的保守值。
  • WorkingDirectory :必须显式指定,否则 zkServer.sh 在 fork 后可能因相对路径错误找不到配置。

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable zookeeper
sudo systemctl start zookeeper

3.5 验证集群健康状态

启动后,立即用四字命令验证:

# 查看节点角色(leader/follower)
echo "stat" | nc 10.0.1.10 2181 | grep "Mode"

# 查看所有连接客户端(应显示 3 个 follower 连接)
echo "cons" | nc 10.0.1.10 2181 | wc -l

# 查看关键性能指标(重点关注 min/max/avg latency)
echo "mntr" | nc 10.0.1.10 2181 | grep "latency"

一个健康的三节点集群, mntr 输出中的 zk_avg_latency 应稳定在 < 5ms zk_num_alive_connections 应为 3 (每个 follower 连接 leader)+ N (客户端连接数)。如果 zk_followers zk_synced_followers 显示 0 ,说明集群未形成 quorum,需检查防火墙(端口 2888/3888)或网络路由。

注意:ZooKeeper 的 clientPort=2181 仅用于客户端连接; server.x 中的 2888 是 follower 与 leader 同步数据的端口; 3888 是 leader 选举时使用的端口。三者必须全部放行,缺一不可。

4. Kafka Broker 部署:从配置文件到磁盘 I/O 的全链路调优

当 ZooKeeper 集群稳定运行后,Kafka Broker 的部署就进入了“精细手术”阶段。很多人以为下载、解压、改配置、启动就完事了,但生产环境的 Kafka 性能瓶颈,90% 出现在 磁盘 I/O、网络缓冲区、JVM GC 与 OS Cache 的协同失衡 上。Ubuntu 18.04 的 ext4 文件系统、默认 TCP 参数、内核 vm.swappiness 设置,与 Kafka 的高吞吐设计存在天然冲突。这一节,我将带你逐层穿透这些隐藏的“性能暗礁”。

4.1 下载与基础目录准备

选择 Kafka 2.8.1(发布于 2021-07-20),它是 Ubuntu 18.04 + JDK 8u292 组合的终极稳定版。避免 Kafka 2.7.x(有 Log Cleaner 内存泄漏 CVE-2021-38153)和 Kafka 3.0+(强制要求 Java 11)。

sudo mkdir -p /opt/kafka
cd /opt/kafka
sudo wget https://downloads.apache.org/kafka/2.8.1/kafka_2.12-2.8.1.tgz
sudo tar -xzf kafka_2.12-2.8.1.tgz
sudo chown -R kafka:kafka kafka_2.12-2.8.1

创建专用用户和数据目录:

sudo useradd -r -s /bin/false kafka
sudo mkdir -p /var/lib/kafka/{logs,logs-cleaner-offset-checkpoint}
sudo chown -R kafka:kafka /var/lib/kafka

注意: logs-cleaner-offset-checkpoint 是 Kafka Log Cleaner 的专用 checkpoint 目录, 必须与主 logs 目录分离 。Log Cleaner 进程会高频读写该目录,若与主日志共用磁盘,会引发严重的 I/O 争抢。

4.2 server.properties 核心参数深度解析

Kafka 的 config/server.properties 是性能调优的“宪法”。以下是针对 Ubuntu 18.04 生产环境的最小化必改项(其他参数保持默认即可,过度调优反而有害):

# 1. 基础标识
broker.id=0
listeners=PLAINTEXT://10.0.1.10:9092,SSL://10.0.1.10:9093
advertised.listeners=PLAINTEXT://10.0.1.10:9092,SSL://kafka-prod.example.com:9093
# 2. 日志存储(关键!)
log.dirs=/var/lib/kafka/logs
num.partitions=12
default.replication.factor=3
# 3. 磁盘 I/O 优化(Ubuntu 18.04 专属)
log.flush.interval.messages=10000
log.flush.interval.ms=1000
log.segment.bytes=1073741824
log.retention.hours=168
# 4. 网络与内存
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
# 5. ZooKeeper 连接
zookeeper.connect=10.0.1.10:2181,10.0.1.11:2181,10.0.1.12:2181
zookeeper.connection.timeout.ms=6000

逐条解读其背后的物理意义:

  • listeners advertised.listeners :这是 Kafka 网络模型的“双面神”。 listeners 是 Broker 监听的本机地址, advertised.listeners 是告诉 Producer/Consumer “你们该连谁”。在 Ubuntu 18.04 的多网卡服务器上,必须明确指定内网 IP(如 10.0.1.10 ),而非 0.0.0.0 。否则,Consumer 可能拿到公网 IP,导致跨网段通信失败。

  • log.segment.bytes=1073741824 (1GB):这是 Kafka 日志分段大小。Ubuntu 18.04 的 ext4 文件系统对大文件(>2GB)的 fsync 性能急剧下降。设为 1GB,既能保证单个 segment 文件的 I/O 效率,又能避免 fsync() 耗时过长阻塞主线程。

  • log.flush.interval.ms=1000 :强制 Kafka 每秒将内存中累积的消息刷盘一次。Ubuntu 18.04 默认的 vm.dirty_ratio=20 (脏页占内存 20% 才刷盘)对 Kafka 是灾难性的——它可能导致 10 秒以上的消息延迟。显式设置 flush.interval ,将控制权交还给 Kafka。

  • socket.send.buffer.bytes=102400 (100KB):这是 Linux TCP 发送缓冲区大小。Ubuntu 18.04 的默认值是 16384 (16KB),在千兆网卡下,单次 TCP 传输最多发送 16KB 数据,导致大量小包。提升到 100KB,可显著降低网络中断频率,提升吞吐。

  • zookeeper.connection.timeout.ms=6000 :ZooKeeper 连接超时设为 6 秒。这是经验值。太短(如 2000ms)会导致网络抖动时 Broker 频繁断连重试;太长(如 30000ms)则故障发现慢。6 秒是平衡点。

4.3 Ubuntu 18.04 内核级调优(绕不开的硬功夫)

Kafka 的高性能,一半靠应用层配置,一半靠 OS 内核支撑。Ubuntu 18.04 的默认内核参数,是为通用桌面/服务器设计的,而非为 Kafka 这类高 I/O、高连接数的中间件优化。必须修改 /etc/sysctl.conf

# /etc/sysctl.conf - Kafka 专用优化
# 提升网络连接数
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
# 优化 TCP 缓冲区(自动缩放)
net.ipv4.tcp_rmem = 4096 262144 6291456
net.ipv4.tcp_wmem = 4096 262144 6291456
# 减少 TIME_WAIT 状态占用
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
# 关键:禁用 swap(Kafka 严禁 swap!)
vm.swappiness = 1
# 关键:提升 ext4 日志性能
fs.ext4.feature_flags = journal_async_commit

应用配置:

sudo sysctl -p

其中 vm.swappiness = 1 是生死线。Ubuntu 18.04 默认 swappiness=60 ,意味着当内存使用率达 40% 时,内核就开始将匿名页(如 JVM 堆)交换到 swap。Kafka 的 GC 会扫描整个堆,一旦部分堆页在 swap 上,GC 将触发大量磁盘 I/O,导致 STW(Stop-The-World)时间飙升至秒级。设为 1 ,表示“仅在内存极度紧张时才考虑 swap”,几乎等同于禁用。

journal_async_commit 是 ext4 的一个隐藏加速器。它允许文件系统日志(journal)异步提交,将 fsync() 延迟从毫秒级降至微秒级。实测在 Kafka 场景下,可将 log.flush.interval.ms 的实际波动范围从 ±50ms 缩小到 ±5ms。

4.4 systemd 服务定义:比 Kafka 自带脚本更可靠的守护

Kafka 自带的 kafka-server-start.sh 是一个 bash 脚本,它 fork 出 JVM 进程后就退出,无法被 systemd 正确追踪。我们必须编写专业的 unit 文件:

# /etc/systemd/system/kafka.service
[Unit]
Description=Apache Kafka Server
Documentation=https://kafka.apache.org/documentation/
Requires=zookeeper.service
After=zookeeper.service

[Service]
Type=simple
User=kafka
Group=kafka
Environment="JAVA_HOME=/opt/java/jdk8u292-b10"
Environment="KAFKA_HOME=/opt/kafka/kafka_2.12-2.8.1"
Environment="LOG_DIR=/var/lib/kafka/logs"
# 关键:显式指定配置文件路径
ExecStart=/opt/kafka/kafka_2.12-2.8.1/bin/kafka-server-start.sh /opt/kafka/kafka_2.12-2.8.1/config/server.properties
# 关键:优雅关闭(发送 SIGTERM,等待 30 秒)
ExecStop=/opt/kafka/kafka_2.12-2.8.1/bin/kafka-server-stop.sh
Restart=on-failure
RestartSec=30
# 关键:OOM 保护
OOMScoreAdjust=-900
# 关键:文件描述符限制
LimitNOFILE=100000
# 关键:JVM 参数(根据物理内存调整)
Environment="KAFKA_HEAP_OPTS=-Xms4g -Xmx4g -XX:MetaspaceSize=96M -XX:+UseG1GC -XX:MaxGCPauseMillis=20"

[Install]
WantedBy=multi-user.target

KAFKA_HEAP_OPTS 中的 -XX:+UseG1GC 是必须的。Ubuntu 18.04 的 JDK 8u292 完整支持 G1 垃圾收集器,它比默认的 Parallel GC 更适合 Kafka 的大堆内存场景,能将 GC 暂停时间稳定在 20ms 内。 -XX:MaxGCPauseMillis=20 是 G1 的目标,而非保证值,但它会驱动 JVM 动态调整 GC 策略。

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable kafka
sudo systemctl start kafka

4.5 首次启动诊断清单

启动后,不要急着创建 Topic。先执行以下诊断,确保底层健康:

  1. 检查 JVM 进程是否存活且内存正常

    sudo -u kafka jps -l | grep Kafka
    sudo -u kafka jstat -gc $(pgrep -u kafka -f Kafka) 1000 3
    

    jstat 输出中, G1GGC (G1 Young GC)次数应平稳, G1OGCMN/G1OGCMX (老年代最小/最大值)应接近你设置的 -Xmx

  2. 检查 Kafka 日志是否有致命错误

    sudo tail -50 /var/lib/kafka/logs/server.log | grep -E "(ERROR|FATAL|Exception)"
    

    最常见的错误是 Failed to send SSL handshake ,这表明 advertised.listeners 中的 SSL 地址无法被 ZooKeeper 解析,需检查 DNS 或 hosts 文件。

  3. 验证 ZooKeeper 连接

    echo "ls /brokers/ids" | nc 10.0.1.10 2181
    

    应返回类似 [0, 1, 2] ,表示三个 Broker 已成功注册。

只有当这三项全部通过,才进行下一步:创建 Topic。

5. ACL 权限体系:从“全开放”到“最小权限”的渐进式加固

在 Kafka 2.8.1 中,ACL(Access Control List)是实现生产环境安全的基石。但很多团队犯一个根本性错误: 在集群刚启动时就启用 ACL,导致调试困难、权限混乱 。正确的路径是“先跑通,再加固”,即:第一阶段用 --no-acl 模式验证所有功能;第二阶段启用 ACL,但只对关键 Topic 和 Group 授权;第三阶段全面实施 RBAC(Role-Based Access Control)。

5.1 启用 ACL 的前置条件

Kafka ACL 依赖 ZooKeeper 存储权限规则。因此,必须确保 ZooKeeper 集群已稳定运行至少 5 分钟,并且 Kafka Broker 的 authorizer.class.name 配置已正确设置。编辑 config/server.properties ,取消注释并修改:

authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer
allow.everyone.if.no.acl.found=false
super.users=User:admin;User:kafka
  • SimpleAclAuthorizer 是 Kafka 内置的轻量级 ACL 实现,无需外部 LDAP/AD,完美适配 Ubuntu 18.04 环境。
  • allow.everyone.if.no.acl.found=false 是安全开关。设为 true 时,若某个 Topic 没有 ACL 规则,则所有人可访问(危险!)。设为 false ,则默认拒绝所有访问,必须显式授权。
  • super.users 定义超级管理员。 User:admin 表示用户名为 admin 的客户端; User:kafka 是 Broker 自身的用户名,用于内部通信。

修改后,重启 Kafka Broker:

sudo systemctl restart kafka

5.2 创建第一个超级用户证书(admin)

Kafka ACL 的认证方式有 SASL/PLAIN(用户名密码)和 SASL/SCRAM(更安全)。在 Ubuntu 18.04 上,SASL/SCRAM 是首选,因为它不依赖外部 Kerberos,且密码存储在 ZooKeeper 中,加密强度高。

首先,为 admin 用户创建 SCRAM 凭据:

# 在任意一台 Kafka 服务器上执行
sudo -u kafka /opt/kafka/kafka_2.12-2.8.1/bin/kafka-configs.sh \
  --bootstrap-server 10.0.1.10:9092 \
  --entity-type users \
  --entity-name admin \
  --alter \
  --add-config 'SCRAM-SHA-256=[password=admin123],SCRAM-SHA-512=[password=admin123]'

这条命令会在 ZooKeeper 的 /config/users/admin 节点下创建加密的密码凭证。 SCRAM-SHA-256 SCRAM-SHA-512 是两种哈希算法,同时添加可兼容不同客户端。

5.3 为 Topic 授予最小权限

假设我们要为业务系统创建一个名为 payment-events 的 Topic。遵循最小权限原则,我们只授予 Producer Consumer 必需的权限:

# 授予 admin 用户对 payment-events Topic 的所有权限(管理用)
sudo -u kafka /opt/kafka/kafka_2.12-2.8.1/bin/kafka-acls.sh \
  --bootstrap-server 10.0.1.10:9092 \
  --command-config /opt/kafka/kafka_2.12-2.8.1/config/admin-client.properties \
  --add \
  --allow-principal User:admin \
  --operation All \
  --topic payment-events

# 授予 payment-service 用户(生产者)只写权限
sudo -u kafka /opt/kafka/kafka_2.12-2.8.1/bin/kafka-acls.sh \
  --bootstrap-server 10.0.1.10:9092 \
  --command-config /opt/kafka/kafka_2.12-2.8.1/config/admin-client.properties \
  --add \
  --allow-principal User
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐