Kafka InconsistentClusterIdException 导致容器无限重启,磁盘打满排查与修复
环境
| 组件 | 版本 |
|---|---|
| OS | Ubuntu 22.04 |
| Docker | 24.x |
| Kafka | bitnami/kafka:3.1 |
| ZooKeeper | bitnami/zookeeper:latest |
| 部署方式 | docker-compose,3 broker(kafka0/1/2)+ 1 zookeeper |
告警现象
收到磁盘使用率告警,持续超过 85%,触发值 88.6%。
登机排查:
bash
df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda3 492G 416G 57G 89% /
492G 的数据盘用掉了 416G。
定位大文件
bash
du -x --max-depth=3 / 2>/dev/null | sort -rn | head -10
435209908 /
389902464 /data
383231436 /data/pika
297562320 /data/pika/log
39105660 /var/lib
34201472 /var/lib/docker
33078652 /var/lib/docker
/data/pika/log 占 297G,但那是业务历史日志,另一个异常点是 /var/lib/docker 占了 33G------对一个没有大数据量业务的容器目录来说不正常。继续查大文件:
bash
find / -xdev -size +500M -printf '%s %p\n' 2>/dev/null | sort -rn | head -10
13608618309 /var/lib/docker/containers/4c7e0a9cc36c.../4c7e0a9c...-json.log
13608511637 /var/lib/docker/containers/0d31fb8d4322.../0d31fb8d...-json.log
1887436880 /data/pika/log/pika.INFO.20250103-075343
...
两个 Docker 容器日志各 13.6 GB,合计 27 GB,立刻引起注意。
找到是哪个容器
bash
docker ps --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}'
CONTAINER ID NAMES IMAGE STATUS
1c1d656988 kafka0 bitnami/kafka:3.1 Up 17 months
4c7e0a9cc36c kafka1 bitnami/kafka:3.1 Restarting (1) 41 seconds ago
0d31fb8d4322 kafka2 bitnami/kafka:3.1 Restarting (1) 41 seconds ago
23a393a15e4a zookeeper bitnami/zookeeper:... Up 17 months
kafka1 和 kafka2 正在反复重启,而 kafka0 正常运行了 17 个月。
查重启次数:
bash
docker inspect --format 'Name={{.Name}} RestartCount={{.RestartCount}} ExitCode={{.State.ExitCode}}' kafka1 kafka2
Name=/kafka1 RestartCount=712689 ExitCode=1
Name=/kafka2 RestartCount=712689 ExitCode=1
712,689 次。每次启动刷几十行日志,两年积累下来正好是 13.6 GB。
根因:InconsistentClusterIdException
bash
docker logs --tail 20 kafka1 2>&1
[2026-06-01 02:41:40,083] INFO Cluster ID = kqjY-URYRa2IGe0oJZcHyw
[2026-06-01 02:41:40,094] ERROR Fatal error during KafkaServer startup. Prepare to shutdown
kafka.common.InconsistentClusterIdException: The Cluster ID kqjY-URYRa2IGe0oJZcHyw doesn't match
stored clusterId Some(Lj1BvdqOSp-EXuAo5LICDQ) in meta.properties.
The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong.
at kafka.server.KafkaServer.startup(KafkaServer.scala:228)
kafka2 报完全相同的错。
原因 :Kafka broker 启动时从 ZooKeeper 读取集群 ID,再与本地 meta.properties 里存的 ID 比对。两者不匹配则拒绝启动。
时间线还原
查容器创建时间:
bash
docker inspect --format 'Name={{.Name}} Created={{.Created}}' kafka0 kafka1 kafka2 zookeeper
Name=/kafka0 Created=2024-02-27T11:50:10Z
Name=/kafka1 Created=2024-02-23T08:37:29Z
Name=/kafka2 Created=2024-02-23T08:37:29Z
Name=/zookeeper Created=2024-02-27T11:48:28Z
事件序列已经清晰:
- 2024-02-23 :kafka1、kafka2 初次部署,三节点集群正常运行,集群 ID 写入各 broker 本地
meta.properties(旧 ID:Lj1BvdqOSp-EXuAo5LICDQ)。 - 2024-02-27 :某次操作重建了 ZooKeeper 容器。ZooKeeper 无 volume 挂载 ,重建后数据全部丢失,ZK 重新初始化,生成新集群 ID(
kqjY-URYRa2IGe0oJZcHyw)。同时 kafka0 也被重建,以新 ID 注册成功。 - kafka1/kafka2 未重建 ,本地
meta.properties还是旧 ID------从此每次启动都报InconsistentClusterIdException,restart: always让它们无限重试。 - 2026-06-01:两年积累的日志打满磁盘,触发告警。
影响评估
- kafka0(port 9092)一直正常,业务流量由它单独承担,功能上未中断。
- 集群无副本,kafka0 一旦故障,消息队列完全不可用。
- docker-compose.yml 里 kafka-manager 已把 kafka1/kafka2 注释掉(
#- kafka1),说明当时有人发现问题但未彻底修复。
修复步骤
第一步:截断日志(立即止血)
容器运行中可直接截断 json.log,不影响容器本身:
bash
> /var/lib/docker/containers/4c7e0a9cc36c7f18.../4c7e0a9c...-json.log
> /var/lib/docker/containers/0d31fb8d4322ad87.../0d31fb8d...-json.log
df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda3 492G 126G 347G 27% /
磁盘从 89% 降至 27%,告警消除。
第二步:重建 kafka1/kafka2
两个容器均无 volume 挂载,容器内没有需要保留的数据,直接删除重建:
bash
docker stop kafka1 kafka2
docker rm kafka1 kafka2
cd /data/docker/kafka
docker-compose up -d kafka1 kafka2
观察启动日志,不应再出现 InconsistentClusterIdException:
bash
docker logs -f kafka1
kafka 02:41:37.97 INFO ==> ** Starting Kafka **
[INFO] starting (kafka.server.KafkaServer)
[INFO] [KafkaServer id=1] started
第三步:验证集群恢复
bash
# 确认 3 个 broker 都已注册
docker exec kafka0 /opt/bitnami/kafka/bin/kafka-broker-api-versions.sh \
--bootstrap-server 192.168.1.1:9092 2>/dev/null | grep "id:"
id: 0 ...
id: 1 ...
id: 2 ...
第四步:配置 Docker 日志限制(防复发)
bash
cat > /etc/docker/daemon.json << 'EOF'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "500m",
"max-file": "3"
}
}
EOF
# reload 不重启容器,新限制对此后新建容器生效
systemctl reload docker
为什么重建可以修复
Kafka broker 第一次启动时,若本地无 meta.properties,会向 ZooKeeper 查询当前集群 ID 并写入本地文件。重建容器后 meta.properties 不存在,broker 用现有 ZooKeeper 的 ID 重新注册,集群 ID 一致,启动成功。
快速参考
定位大日志
bash
# 找超过 500M 的文件
find / -xdev -size +500M -printf '%s %p\n' 2>/dev/null | sort -rn | head -20
# 查 Docker 容器磁盘占用
docker system df
截断容器日志(不删文件,不重启容器)
bash
> /var/lib/docker/containers/<容器ID>/<容器ID>-json.log
查 Kafka broker 重启次数和退出码
bash
docker inspect --format 'Name={{.Name}} RestartCount={{.RestartCount}} ExitCode={{.State.ExitCode}}' <容器名>
重建无 volume 的 Kafka 容器
bash
docker stop kafka1 kafka2 && docker rm kafka1 kafka2
cd /path/to/docker-compose && docker-compose up -d kafka1 kafka2
易错点
- ZooKeeper 容器无 volume:重建 ZK 会丢失集群 ID,必须同步重建所有 broker。
meta.properties在容器可写层:有 volume 挂载时需手动删除该文件再重启,无 volume 时直接重建容器更干净。restart: always+ 无限崩溃 = 日志无上限增长,必须配合max-size限制。