news 2026/10/26 11:26:49

HBase完全分布式 ZooKeeper集群搭建与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HBase完全分布式 ZooKeeper集群搭建与避坑指南

搞 HBase 完全分布式,十个人里有八个卡在同一处:ZooKeeper 集群起不来。有人把 myid 写错,有人把 2888 和 3888 弄反,还有人三台机器都显示 standalone,自我感觉配好了,结果 HBase Master 一启动就不断刷 ConnectionLoss。这篇就专门讲 ZooKeeper 这一段,从节点规划、目录规划、zoo.cfg 逐行拆解,到启动验证、和 HBase 对接、以及我自己在各个环境里反复踩过的坑。假设你手上只有三台能互相 ping 通的机器,Java 装没装都不确定,对 ZooKeeper 的理解停留在“听说 HBase 要用它”,那就从头跟着敲,每一步我都写清楚为什么要这么干,不只是给命令。HBase 完全分布式配置里 ZooKeeper 这一环,说难不难,但它是整条链路的地基,地基歪了后面调 HBase 参数全白搭。

1. ZooKeeper 在 HBase 完全分布式里的角色定位

1.1 先搞清楚 HBase 到底把哪些东西交给了 ZooKeeper

很多人配 HBase 的时候把 ZooKeeper 当成一个“必须存在的依赖”,配完就跑,根本不知道里面存了什么。我建议你先花十分钟理解这件事,后面排查问题是事半功倍。HBase 把集群的“元信息中枢”放在了 ZooKeeper,主要几类:一是 Master 的选举信息,哪台是 Active Master、哪台是 Backup,写在 /hbase/master 这个 znode 里;二是 RegionServer 的注册与心跳,每台 RS 启动后会自己在 /hbase/rs 下面建一个临时节点,进程挂了会话超时节点自动消失,Master 靠这个感知宕机;三是 meta 表所在的 RegionServer 地址,客户端第一次连集群要先问 /hbase/meta-region-server,才能找到 hbase:meta 表在哪;四是集群状态和拆分合并等协调任务用的锁节点。

这就解释了一个现象:为什么你 HBase 起不来的时候,日志里全是 ZooKeeper 相关的异常。因为 HBase 启动的第一步不是读 HDFS,而是先去连 ZooKeeper,连不上或者 znode 内容脏了,后面全停。你可以把 ZooKeeper 理解成整个集群的通讯录加会议室预订系统:谁在哪、谁还活着、谁负责哪块,全记在这里。通讯录本身不存业务数据,但没有它,所有人互相找不到。

理解了这个定位,你就明白配置时哪些参数是“必须对”的:连接地址(quorum 和端口)、znode 根路径、会话超时。这三个错了,HBase 一定起不来。而 dataDir 放哪、日志怎么分盘,属于性能与运维层面的事,配错了不一定会立刻报错,但会在跑一段时间后给你颜色看。

1.2 三节点还是五节点:集群规模与可用性怎么算

ZooKeeper 的部署有一条不能绕开的原则:节点数必须是奇数,靠过半机制(quorum)做决策。三台容忍挂一台,五台容忍挂两台,两台不但没有任何容错收益,反而比单机更容易脑裂式地卡死,因为凑不出多数派。我见过有人在两台机器上配 server.1 和 server.2,然后抱怨集群一直选不出 leader,这就是原理没搞明白。

具体怎么选,看你的实际用途。学习和验证阶段,三台完全够用,虚拟机三开,每台 2C4G 就能跑起来,ZooKeeper 本身非常轻,真正吃资源的是 HBase 的 RegionServer。生产环境或者要模拟真实故障演练,建议五台,因为 ZooKeeper 的多数派机制意味着挂掉一台后,剩下四台还能正常工作,但此时如果你要做滚动重启(一台一台维护),三节点集群在挂掉一台后就变成两节点,任何一台再停就失去多数派,整个集群立即只读甚至不可用。五节点给了你滚动维护的空间。

还有一个容易被忽略的点:ZooKeeper 集群的节点数不等于 HBase 的节点数。你完全可以在三台 ZooKeeper 上跑十台 HBase RegionServer,ZooKeeper 只负责协调,不承载数据流量。所以别为了“凑 ZooKeeper 奇数”去硬加机器,反过来也别因为 RegionServer 多就盲目扩 ZooKeeper,五台基本能支撑相当大规模的 HBase 集群。

1.3 独立部署还是复用:为什么我不建议图省事

HBase 自带了一个“托管模式”,也就是设置 HBASE_MANAGES_ZK=true,HBase 启动时自动拉起一个 ZooKeeper 实例。这个设计是为单机模式和快速体验准备的,完全分布式场景下强烈建议关掉,独立部署一套 ZooKeeper。原因有三个。

第一,生命周期耦合。托管模式下 HBase 停了 ZooKeeper 也停,你调 HBase 参数需要反复重启,每次重启都让整个协调层抖动一遍,出问题时你根本分不清是 HBase 的锅还是 ZK 的锅。独立部署后,ZK 可以长期不动,专心调 HBase。

第二,无法组集群。托管模式本质上是单实例,没有 leader 选举,没有容错。你的 HBase 集群号称完全分布式,结果协调层是单点,这不叫分布式,这叫把单点藏起来了。

第三,运维可见性差。独立部署时你可以单独看 ZK 的日志、单独用四字命令查状态、单独调整堆大小和事务日志目录。托管模式下这些全被 HBase 的启动脚本吞掉,出问题时排查成本成倍上升。

所以我的做法一直很固定:ZooKeeper 独立集群,HBase 侧把 HBASE_MANAGES_ZK 显式设为 false,两边通过 hbase-site.xml 里的 quorum 地址对接。下面所有内容都基于这个前提展开。

2. 动手前的环境准备与自检清单

2.1 主机名、hosts 映射与免密登录

这一步看起来最没技术含量,但它是后面所有配置能跑通的前提,我把它称为“翻车高发区第一名”。三台机器先定好主机名,我这里用 node1、node2、node3 代指,你换成自己的即可。每台机器执行 hostnamectl set-hostname 对应名字,然后三台的 /etc/hosts 里都要有完整的映射:

10.0.0.11 node1 10.0.0.12 node2 10.0.0.13 node3

为什么强调三台都要写全,而不只是本机?因为 zoo.cfg 里的 server.N 用的是主机名,ZooKeeper 启动时会去做域名解析,如果某台机器解析不到另外两台,它会启动失败或者反复重连。用 IP 地址写 server.N 也能跑,但一旦后续换网段就要全改,主机名加 hosts 是更省心的做法。

免密登录这一步,很多人以为只有 Hadoop 需要,其实 ZooKeeper 启动脚本里如果要批量分发、批量启动,同样用得上。做法是每台机器生成密钥对,然后把公钥汇总到三台的 authorized_keys。验证方法很简单,在 node1 上依次 ssh node2、ssh node3,都不需要输密码就说明通了。我第一次配的时候偷懒只做了单向,结果后面写批量启动脚本时一半机器起不来,又回头补,纯属浪费时间。

注意:hosts 文件里不要保留重复的主机名映射,尤其是一些系统镜像自带的 127.0.1.1 条目,容易让解析结果和你想的不一致。改完后用 getent hosts node1 验证一下,比 ping 更准确。

2.2 JDK 版本、时间同步与文件句柄

ZooKeeper 3.5 以后的版本要求 Java 8 及以上,我一般统一用 JDK 8 或者 JDK 11,三台机器的版本必须一致,不要一台 8 一台 17,序列化相关的兼容问题排查起来极其痛苦。安装完确认两件事:JAVA_HOME 环境变量在三台机器上都指向同一个路径,并且 java -version 输出的版本号一致。ZooKeeper 的启动脚本会读 JAVA_HOME,如果没设,它会去 PATH 里找 java,找到的可能是系统自带的精简版 JRE,然后给你一个莫名其妙的报错。

时间同步经常被跳过,但它是 ZooKeeper 集群能不能正常工作很关键的一环。ZooKeeper 的会话、租约、选举都基于本地时间做判断,三台机器时间偏差过大时,会出现 leader 反复选举、follower 不断掉线重连的现象,日志里表现为大量“LOOKING”状态切换,或者 HBase 侧不断报会话过期。用 chrony 或者系统自带的时间同步服务把三台机器对齐,误差控制在秒级以内,动手前用 date 命令比对一下,三台的输出应该基本一致。

文件句柄数也要提前放开。ZooKeeper 每个客户端连接都会占用文件描述符,HBase 的 RegionServer 连接数动辄成百上千。临时修改用 ulimit -n 65536,永久生效要写进 /etc/security/limits.conf,注意同时给 root 和你的操作用户都配上,因为 ZooKeeper 启动脚本切换用户时可能用的是另一个身份。我遇到过跑了两天突然所有客户端连不上的情况,最后查出来是句柄数到了默认的 1024,改完再没复发。

2.3 端口占用、防火墙与目录权限

ZooKeeper 集群要用三个端口:2181 客户端连接、2888 集群内部数据同步、3888 选举通信。另外如果开了管理端口还有 8080 之类,建议先确认这三个核心端口在三台机器上都没有被占用。检查用 ss -lntp | grep 2181 这类命令,逐台过一遍,别嫌烦,我见过有人在 2181 上跑了别的服务,然后 ZoeKeeper 日志一直报绑定失败,排查了半天。

防火墙这一块,如果是内网实验环境,直接关掉最省事;如果环境要求开启,就要放行这三台机器之间的 2181、2888、3888,注意是双向的、三台机器互相之间都要通,而不是只开本机的入站。验证连通性不要只用 telnet 测 2181,2888 和 3888 也要测,因为这两个端口只在集群内部用,很多时候 2181 通了但 2888 被拦,表现出来就是集群永远选不出 leader,或者其中一台一直卡在 LOOKING。

目录权限也是一个隐藏的坑。ZooKeeper 的 dataDir 和 dataLogDir 必须是启动用户有读写权限的目录,不能用 root 创建目录后切普通用户去启动。我的习惯是统一在 /data 下建目录,比如 /data/zookeeper/data 和 /data/zookeeper/datalog,创建时用 chown -R 把属主改成操作用户,后面所有操作都用同一个用户,不来回切 root 和普通账号,能少踩很多权限相关的坑。

3. ZooKeeper 安装与 zoo.cfg 逐行拆解

3.1 版本选择与解压规划

版本怎么选,取决于你 HBase 的版本。HBase 2.x 的客户端依赖较新的 ZooKeeper,用 3.4.x 会碰到一些兼容问题,我一般统一用 3.5.7 或 3.6.x 这两个系列,社区实践最多,文档也最全。别追最新的大版本,ZooKeeper 3.7、3.8 本身没问题,但如果你 HBase 用的是老版本,两边对四字命令、认证机制的处理有差异,容易在细节上翻车。原则就一条:版本以 HBase 官方文档给出的兼容列表为准,能选列表里的中间版本就不要选边界版本。

目录规划我习惯这样:软件装在 /opt/module/zookeeper,数据放 /data/zookeeper/data,事务日志放 /data/zookeeper/datalog,日志文件放 /data/zookeeper/logs。注意事务日志和快照数据一定要分开放,最好还分在不同物理盘上。原因在于快照是定期全量落盘,事务日志是每条写请求顺序追加,两者混在一个目录会互相争抢磁盘 IO,写性能下降得非常明显。实验环境只有一块盘时至少也分两个目录,收益虽然有限,但结构上是对的,将来迁到多盘机器上直接改路径就行。

解压之后先在 node1 上把配置改好,再整个目录分发到另外两台,这样能保证三台的配置文件完全一致,后面只需要单独改 myid。分发用 scp -r 或者 rsync 都行,rsync 更好一点,重复分发时只传差异,节约时间。分发完记得检查三台的目录结构和权限是否一致。

3.2 zoo.cfg 每个参数到底在管什么

配置文件位置是 conf/zoo.cfg,这是一个纯文本的键值配置,注意它的名字不是 zoo_sample.cfg,你可以从样例文件复制一份改名,也可以直接新建。下面是三节点集群的一份完整配置,我按顺序讲每一项:

tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/datalog clientPort=2181 maxClientCnxns=300 autopurge.snapRetainCount=5 autopurge.purgeInterval=24 4lw.commands.whitelist=* admin.enableServer=false server.1=node1:2888:3888 server.2=node2:2888:3888 server.3=node3:2888:3888

tickTime 是整个 ZooKeeper 的时间基准单位,单位毫秒,默认 2000。它本身不直接决定任何超时,而是作为其他超时参数的乘数。理解这一点很重要,因为后面 HBase 侧配置会话超时时,你会发现它和 tickTime 有整数倍的关系。

initLimit 是 follower 启动时与 leader 完成数据同步的时限,计算方式是 tickTime 乘以 initLimit,也就是 2000×10=20000 毫秒,20 秒。集群数据量大的时候,新加入的 follower 同步快照可能超过这个时间,会被踢掉重来,形成死循环。如果你的集群里 znode 特别多,把这个值调大到 15 甚至 20。反过来,数据量很小但机器性能差、启动慢的场景,也要适当放宽。

syncLimit 是 leader 和 follower 之间心跳超时,同样是 tickTime 的倍数,5×2000=10 秒。超过这个时间 leader 认为 follower 挂了,把它踢出集群。这个值配太小,网络稍有抖动就掉节点,表现出来就是集群成员不断变化;配太大,真正的故障节点会长时间占着位子影响决策。内网环境 5 是合适的,跨机房或者网络质量一般的话,调到 8 到 10。

dataDir 是快照目录,也是 myid 文件所在的位置,这一点必须记住,后面 myid 放错目录是最高频的错误。dataLogDir 是事务日志目录,如果不单独配置,ZooKeeper 会把事务日志也写进 dataDir,这就是前面说的 IO 争抢问题。

clientPort 是客户端连接端口,默认 2181。如果你一台机器上要跑多个 ZooKeeper 实例(做伪集群实验时常见),就要给每个实例配不同端口,同时 server.N 里的端口也要跟着改。生产环境一台机器一个实例,保持 2181 就好。

maxClientCnxns 是单个客户端 IP 允许的最大连接数,默认只有 60。这个默认值在 HBase 场景下很容易成为瓶颈,因为一台 RegionServer 会用连接池同时连多个 ZK 节点,连接数很快就能超过 60,然后你会看到大量“Too many connections”的报错。我一般直接调到 300,规模大的集群甚至调到 600。

autopurge 两项是自动清理,snapRetainCount 是保留最近多少个快照,purgeInterval 是清理间隔(小时)。默认 purgeInterval 是 0,也就是不清理,快照会一直堆积把磁盘撑满。这是我见过最隐蔽的故障之一:集群跑了几个月突然全体起不来,一查磁盘 100%,全是 ZK 的快照。生产环境必须配,保留 5 个快照、每小时清一次是常用的组合。

4lw.commands.whitelist=* 是 3.5 以后的四字命令白名单,不配的话 stat、mntr 这些命令会被拒绝,导致你以为集群挂了,其实只是命令被禁。实验环境可以开 *,生产环境建议只放行 srvr、mntr 这类只读命令。

admin.enableServer=false 用来关掉内置的 AdminServer,它默认会占用 8080。除非你确实需要那个 HTTP 界面,否则关掉,避免和别的服务抢端口。

3.3 myid 与 server.N 的对应关系:最容易翻车的一步

server.N 里的 N 就是这台机器的编号,myid 文件里写的数字必须和它一一对应,且每台机器只能写自己那个编号。node1 写 1,node2 写 2,node3 写 3,不能重复,也不能有机器漏写。myid 文件放在 dataDir 目录下,文件名叫 myid,内容是纯数字,不要有多余的空格和空行。

# 在 node1 上 echo 1 > /data/zookeeper/data/myid # 在 node2 上 echo 2 > /data/zookeeper/data/myid # 在 node3 上 echo 3 > /data/zookeeper/data/myid

看起来很简单,但这是出错最多的地方,我把见过的翻车方式列一下:第一,myid 放在了 ZooKeeper 解压目录而不是 dataDir,配置读不到,一律当成编号缺失;第二,echo 的时候手滑写成 echo "1 " > myid,末尾带空格,ZooKeeper 解析不出来,报 Myid file is missing or not valid;第三,三台机器用了同一份 dataDir 拷贝,导致 myid 全是 1,表现是集群只有一台能起来,另外两台一直报绑定冲突或加入失败;第四,用了 Windows 换行符,数字后面跟了 \r,同样解析失败。

注意:检查 myid 内容用 cat -A /data/zookeeper/data/myid,末尾如果出现 ^M 或者多余空格,立刻用 echo 重写一遍。这个命令是我排查 ZK 启动问题时第一个会敲的。

另外要注意 server.N 的写法,三个字段分别是主机或 IP、集群通信端口、选举端口。2888 是 follower 连 leader 用来同步数据的,3888 是投票选举用的,两个不能相同,也不能跟 clientPort 冲突。三台机器上这三个端口的配置必须完全一致,只在 myid 上做区分。

3.4 日志分离与 JVM 堆参数

ZooKeeper 的日志有两套:一套是 log4j 输出的运行日志,一套是上面说的事务日志。运行日志默认输出到启动目录下的 zookeeper.out,这个设计有点坑,因为你在哪个目录下执行 zkServer.sh,zookeeper.out 就生成在哪,多启动几次后你会发现不同目录下散落着一堆日志文件,还每个都不小。我的做法是在 conf/log4j.properties 里把日志路径改成固定目录,比如 /data/zookeeper/logs,既能统一管理,也方便做日志轮转。

JVM 堆参数在 conf/zkEnv.sh 或者通过环境变量 SERVER_JVMFLAGS 设置。ZooKeeper 的内存需求和 znode 数量强相关,一般三节点小集群给 1G 到 2G 就够,别给太大,堆大了 Full GC 停顿反而更长,而 ZooKeeper 对停顿非常敏感,一次长 GC 就可能触发 leader 重选。我通常设成 -Xms1g -Xmx1g,固定大小避免动态扩容带来的抖动,再加上 GC 日志参数方便事后分析。

还有一个常被忽略的点:ZooKeeper 的快照文件会越来越大,尤其是集群里 znode 数量多、变更频繁的场景。快照写入过程中会阻塞一部分请求,如果磁盘慢,表现出来就是客户端周期性卡顿。所以 dataDir 一定要放在本地盘,不要放网络盘或者容器里做了限速的卷上。这一点在容器化部署时特别容易出错。

4. 启动集群、验证角色与客户端连通性

4.1 三台机器依次启动的实操记录

启动命令很简单,bin/zkServer.sh start,但顺序和观察方法有讲究。我的习惯是先在 node1 启动,这时它会在日志里输出找不到 leader,进入 LOOKING 状态,然后不断尝试连接其他节点。别慌,这是正常的,因为另外两台还没起来,凑不够多数派。接着启动 node2,此时两台起来已经超过半数(三台里的两台),理论上可以选出 leader,但通常还是会因为第三台缺失而等待。最后启动 node3,三台齐了,选举立刻完成。

# 第一次启动建议前台观察一台,能直接看到异常 cd /opt/module/zookeeper bin/zkServer.sh start # 查看启动日志尾部 tail -f /data/zookeeper/logs/zookeeper.out

第一次配完,我建议至少把其中一台用前台方式启动一次,也就是执行 bin/zkServer.sh start-foreground,日志直接打在屏幕上,任何配置错误、端口冲突、myid 问题都会立刻显形。用后台启动的话,进程启动失败就退出了,你只会在 zookeeper.out 里看到几行栈信息,很容易漏掉。

启动之后第一件事不是看 status,而是先用 jps 或者 ps 确认进程真的活着。正常情况应该能看到 QuorumPeerMain 这个进程。如果 jps 里什么都没有,说明进程启动后立刻退出了,这时候去看 zookeeper.out,日志里会明确告诉你是配置错误、端口被占还是 myid 有问题。

批量启动的脚本我一般这么写,把三台机器的启动串起来,省得来回切终端:

#!/bin/bash ZK_HOME=/opt/module/zookeeper for host in node1 node2 node3; do echo "===== starting zookeeper on $host =====" ssh $host "$ZK_HOME/bin/zkServer.sh start" done

第一次跑这个脚本时不要闭着眼睛看输出,逐台确认返回结果。zkServer.sh 的返回值有时候不太可靠,比如端口被占用时它可能还是会打印 STARTED,实际上进程已经退出了。所以要配合 jps 和 status 双重验证。

4.2 用 status 和四字命令确认真实状态

三台都起来后,逐台执行 bin/zkServer.sh status,正确的结果应该是其中两台显示 Mode: follower,一台显示 Mode: leader。只要出现 standalone,就说明这台机器根本没加入集群,它在自己玩自己的。这时候不用怀疑别的,一定是 zoo.cfg 里的 server.N 配置有问题,或者 myid 和 server.N 对不上,或者 2888、3888 端口不通。

# node1 bin/zkServer.sh status # 输出示例:Mode: follower

status 之外,我更信任四字命令。用 echo 配合 nc 打过去,能看到更细的运行时数据:

echo stat | nc node1 2181 echo mntr | nc node1 2181

stat 返回客户端连接数、znode 数量、模式等信息,可以快速确认这台是不是 leader。mntr 返回的指标更适合排查性能问题,重点关注 zk_avg_latency、zk_outstanding_requests、zk_followers 这几项。如果 zk_avg_latency 一直在涨,说明有慢请求堆积,通常和磁盘或者 GC 有关。zk_followers 在 leader 上应该显示 2,在 follower 上不显示。

注意:四字命令在 3.5 以后默认只放行 srvr,如果返回 “stat is not executed because it is not in the whitelist”,说明 4lw.commands.whitelist 没配或者配错了。别以为是集群出问题,先回头检查这一行配置。

如果某一台一直是 LOOKING 状态,说明它没参与到选举里,常见原因是它连不上其他两台。可以用 ss -tnp 看 3888 端口有没有建立连接,或者直接 telnet node2 3888 测一下。这一步做完,ZooKeeper 自身的活基本就干完了。

4.3 用 zkCli 做一次读写验证

status 正常只代表集群选出了 leader,不代表数据路径是通的。我每次配完都会用客户端做一次最基础的读写,确认 znode 能创建、能读回、能删除。

bin/zkCli.sh -server node1:2181,node2:2181,node3:2181

进去之后依次执行:

create /test-znode hello get /test-znode delete /test-znode quit

能正常返回 hello 就说明读写链路通了。这里有一个细节值得注意:连接串里把三台都写上,客户端会自动做故障转移,某一台挂了会连下一台。这也正是 HBase 侧配置 quorum 时应该采用的写法,而不是只写一台。只写一台的话,那台一旦重启,所有客户端立刻失去连接,虽然会自动重连,但中间那几秒的会话中断可能已经导致 RegionServer 被判定下线了。

如果 create 卡住不返回,一般是集群没有形成多数派,或者客户端的会话超时被设得极短。如果报 NodeExists,说明之前残留了同名的 znode,这也提醒你:ZooKeeper 的 znode 是持久化的,节点重启不会自动清掉,后面 HBase 那一节我们会专门讲怎么清理残留,这是个大坑。

5. 让 HBase 接上这套 ZooKeeper

5.1 hbase-site.xml 里那几个关键属性

ZooKeeper 集群跑起来只是第一步,真正让 HBase 用上它,靠的是 hbase-site.xml 里这几个属性。我按重要程度排一下:

<property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>node1,node2,node3</value> </property> <property> <name>hbase.zookeeper.property.clientPort</name> <value>2181</value> </property> <property> <name>zookeeper.znode.parent</name> <value>/hbase</value> </property> <property> <name>hbase.rootdir</name> <value>hdfs://mycluster/hbase</value> </property>

hbase.zookeeper.quorum 必须把三台全写上,用逗号分隔,中间不要有空格。这个属性名有点误导,它不是“ZooKeeper 集群的 quorum 数量”,而是“地址列表”。很多人只写一台,短期内看不出问题,一旦那台重启就暴露了。

clientPort 要和 zoo.cfg 里保持一致,默认 2181。如果你改过端口,HBase 这边不改,表现出来就是连接超时,日志里一堆 ConnectionLoss。这种错误排查起来特别费时间,因为配置文件分散在两个地方,建议改端口时两边一起改,并且在文档里记一笔。

zookeeper.znode.parent 是 HBase 在 ZooKeeper 里使用的根路径,默认是 /hbase。我建议显式写出来,不要依赖默认值。原因有两个:一是多集群共用一套 ZooKeeper 时,必须靠不同的根路径隔离,比如 /hbase-prod 和 /hbase-test;二是排查时能一眼确认自己连的是哪个集群的数据。

hbase.rootdir 指向 HDFS,和 ZooKeeper 没有直接关系,但它决定了 HBase 数据落在哪,两个路径必须是同一个集群的,否则会出现“元数据在 ZK 里指向 A 集群,数据实际在 B 集群”的诡异状态。

另外还有一处容易漏的:conf/hbase-env.sh 里必须显式关闭托管模式。

export HBASE_MANAGES_ZK=false

不写这一行,HBase 启动时会尝试自己拉起一个 ZooKeeper 实例,可能和我们独立部署的那套抢 2181 端口,也可能连到一个空的新实例上,然后你发现 zkCli 里明明有 /hbase 节点,HBase 却一直说找不到集群。这个坑我踩过,症状是 Master 反复报 “Failed to become active master”,查了半天才发现连接的根本不是同一套 ZK。

5.2 启动顺序与 znode 目录观察

启动顺序固定为:ZooKeeper 全部起来并确认选出 leader,然后 HDFS 起来并退出安全模式,最后启动 HBase Master 和 RegionServer。不要反过来,也不要在 HBase 已经启动的情况下重启 ZooKeeper,否则会触发大量会话超时,RegionServer 可能被重新分配,短则几十秒,长则几分钟集群不可用。

HBase 启动后,最有价值的验证动作是回到 zkCli 里看 znode 结构:

ls /hbase # 正常应该看到: # [backup-masters, hbaseid, master, meta-region-server, namespace, rs, running, table, tmp]

这几个节点的含义前面提过:master 存活跃 Master 地址,rs 下是各个 RegionServer 的临时节点,meta-region-server 是元数据表位置。如果 ls /hbase 下只有一两个节点,或者根本没有 /hbase,说明 HBase 没有成功写入注册信息,基本可以断定是连接配置有问题。

还有一个技巧:get /hbase/master 可以直接读到当前活跃 Master 的主机名和启动码,比去翻日志快多了。当你不确定哪台是 Active Master 时,用这个命令一秒钟就能确认。RegionServer 那边同理,ls /hbase/rs 能列出所有在线的节点,如果你发现某个 RegionServer 进程明明活着但不在列表里,那它大概率是会话超时被踢了,需要看它自己的日志确认原因。

5.3 停机和重配时的清理纪律

这里要把一个非常关键的规矩说清楚:只要 HBase 集群停下来了,你就不能随便动 ZooKeeper 里的 /hbase 节点。反过来说,如果你要重置整个 HBase 集群(比如格式化 HDFS、重新配置 rootdir),必须先停 HBase,再用客户端把 /hbase 整个删掉,否则残留的元数据会指向一个已经不存在的 HDFS 路径,HBase 会卡在启动阶段,日志里反复报找不到文件。

删除命令在 3.5 以后用 deleteall:

# 必须在 HBase 完全停止后执行 bin/zkCli.sh -server node1:2181 deleteall /hbase quit

老版本是 rmr,但 rmr 在 3.5 以后被移除了,网上很多教程还在用,你照着敲会报错,别怀疑自己。另外,删除之前务必确认 HBase 的 Master 和所有 RegionServer 进程都已经退出,判断方法是 jps 里没有 HMaster 和 HRegionServer。如果进程还在,删 znode 会引起它们疯狂重连并重建节点,可能造成数据不一致。

注意:deleteall 是不可逆操作,执行前确认 zookeeper.znode.parent 的路径,多集群共用 ZK 时删错路径会直接影响另一个集群。我一般在执行前先 ls 一遍,看清楚再动手。

重配的完整顺序我总结成一条:停 HBase 全部进程,删 /hbase,必要时清 HDFS 上的 hbase 目录,改配置,启动 HDFS,启动 HBase。这个顺序不要乱,尤其是删 znode 和删 HDFS 目录这两步,顺序反了也没关系,但必须都在启动 HBase 之前完成。

6. 踩坑实录与排查速查表

6.1 集群起不来的三类典型症状

第一类:进程起不来。jps 里看不到 QuorumPeerMain,日志结尾是异常栈。这时候优先看三个方向,端口被占、myid 非法、配置文件路径找不到。端口用 ss 查,myid 用 cat -A 查,配置路径的错误通常在日志里写得很清楚。这类问题其实最好排查,因为异常信息很直接,怕的是不看日志凭感觉猜。

第二类:进程活着但集群没形成。三台都在,但 status 全是 standalone,或者两台 LOOKING 一台 LEADING 反复切换。这说明节点之间通信有问题,重点查 2888 和 3888 端口是否互通、server.N 里的主机名能否解析、三台机器的配置是否完全一致。我遇到过一次是其中一台机器的 hosts 文件少写了一行,导致它解析不到另外两台,那台就一直 LOOKING,怎么看配置都找不出问题,最后 diff 三台 hosts 才发现。

第三类:集群正常但 HBase 连不上。status 一切正常,zkCli 也能连,偏偏 HBase 报 ConnectionLoss。这一类就要往 HBase 侧的配置找了,重点看 quorum 是否写全、clientPort 是否一致、HBASE_MANAGES_ZK 是否关掉、以及 znode.parent 是否和之前用过的路径冲突。还有一个隐蔽原因:HBase 的 RegionServer 日志里如果出现 Session expired,可能是 ZK 侧 maxClientCnxns 太小导致连接被拒,这种情况 ZK 日志里会有对应记录,两边对照着看很快就能定位。

6.2 常见问题速查表

现象大概率原因处理动作
启动即退出,日志报 Myid file is missingmyid 不在 dataDir 下,或内容带空格确认路径,用 cat -A 检查内容后重写
status 显示 standalone没读到 server.N 配置,或本机 myid 与其他节点冲突检查 zoo.cfg 路径与三台 myid 唯一性
长期停留在 LOOKING2888/3888 不通,或主机名无法解析telnet 端口,比对三台 hosts
反复选举,leader 不断切换三台时间不同步,或 GC 停顿过长校时,检查堆大小与 GC 日志
stat 命令被拒绝执行四字命令白名单未配置在 zoo.cfg 加 4lw.commands.whitelist
HBase 报 ConnectionLossquorum 未写全,端口不一致核对 hbase-site.xml 与 zoo.cfg
HBase 连上了但找不到集群连到了另一套 ZK,或 znode 路径冲突确认 HBASE_MANAGES_ZK 与 znode.parent
磁盘突然写满快照未自动清理配 autopurge 两项并清理历史快照
客户端大量 Too many connectionsmaxClientCnxns 默认值偏小调到 300 以上
集群跑几小时后集体掉线文件句柄数达到上限调整 limits.conf 并重启进程

这张表我基本是照着实际处理过的工单整理出来的,遇到问题先对号入座,能省掉大量盲猜的时间。

6.3 几条压箱底的实操心得

第一条心得:配置改完先别急着三台一起启,挑一台用 start-foreground 跑一遍,看它能不能正常解析配置、绑定端口。这一步花不了两分钟,但能挡掉一大半低级错误。等三台都确认能单独启动,再组集群,问题范围会小很多。

第二条心得:养成 diff 三台配置的习惯。三台机器的 zoo.cfg 必须完全一致,hbase-site.xml 也必须一致。我一般用 md5sum 对一下,五个文件三台机器输出了六个不同的哈希值,那种感觉很难形容。批量分发配置时用 rsync 加 --checksum 参数,比手工 scp 可靠。

第三条心得:日志要看针对性。ZooKeeper 的问题看 zookeeper.out 和 log4j 输出的运行日志,HBase 连不上的问题要看 RegionServer 的日志,两边时间戳对齐着看,很快就能判断是谁先出的问题。很多人只盯着一头看,越看越迷茫。

第四条心得:不要在集群运行期间改 zoo.cfg 里的 server.N 配置。真要改,必须三台全停,改完再一起启动。ZooKeeper 在运行中动态改成员配置有专门的 reconfig 机制,但不是简单改文件,用错了会导致集群彻底分裂,恢复起来相当麻烦。

第五条心得:给集群留一点余量。数据目录别把磁盘占满,zk 快照和事务日志的空间按未来一年的数据量估,堆内存别顶到物理内存上限,句柄数别设成刚好够用。ZooKeeper 这类协调组件出问题往往不是因为负载高,而是因为某一个资源先到了边界,然后引发连锁反应。我现在的习惯是配完之后主动制造一次单节点故障,停掉一台看看集群能不能正常选主、HBase 是否无感。这个演练做过一次,你对这套配置的信任度会完全不一样,也能提前发现那些“看起来能跑但其实没有容错”的配置问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 11:26:47

OCPP 1.6协议解析:充电桩通信的核心功能与限制

1. OCPP 1.6协议的核心定位解析 OCPP&#xff08;Open Charge Point Protocol&#xff09;1.6作为充电桩通信的事实标准协议&#xff0c;其设计初衷是解决充电基础设施与中央管理系统之间的基础通信需求。这个2015年发布的版本主要聚焦三大核心功能&#xff1a;充电桩状态监控&…

作者头像 李华
网站建设 2026/10/26 11:26:32

DeepGEMM:FP8精度下Tensor Core矩阵乘法的极致优化

我是在折腾某个大模型推理引擎的算子性能时第一次接触到DeepGEMM这个项目的。简单说&#xff0c;它是一套用CUDA和C写的、面向FP8精度的GEMM&#xff08;通用矩阵乘法&#xff09;kernel集合&#xff0c;专门围绕Tensor Core做了非常激进的优化&#xff0c;目标就是在当前主流的…

作者头像 李华
网站建设 2026/10/26 11:26:20

Codex 修复循环别只看“能跑”:用失败样例和回归命令保住下一次判断

Codex 修复循环别只看“能跑”:用失败样例和回归命令保住下一次判断 “修复后命令能跑”常常掩盖了真正的问题:原来的失败输入是否还会失败?把修复循环建立在失败样例上,下一次改动才有一个稳定的判断对象。重点不是让测试越多越好,而是留下能复现标题所说问题的最小输入…

作者头像 李华
网站建设 2026/10/26 11:24:58

OpenPi物联网平台Docker化部署与优化实践

1. OpenPi项目背景与Docker化价值 OpenPi作为一款开源的物联网数据可视化平台&#xff0c;其核心价值在于为开发者提供轻量级的设备管理和数据展示解决方案。我在实际部署中发现&#xff0c;传统安装方式需要手动配置Python环境、数据库依赖和前端资源&#xff0c;整个过程至少…

作者头像 李华
网站建设 2026/10/26 11:24:55

PyCharm手动配置uv虚拟环境实战指南

1. 为什么需要手动配置uv虚拟环境 PyCharm作为Python开发者最常用的IDE之一&#xff0c;其原生支持venv和conda等虚拟环境管理工具。但在实际开发中&#xff0c;我们经常会遇到需要手动配置特殊虚拟环境的情况&#xff0c;特别是像uv这样新兴的轻量级虚拟环境工具。 uv相比传统…

作者头像 李华
网站建设 2026/10/26 11:24:31

3月GitHub热门开源项目精选与解析

1. 项目概述作为一名长期活跃在GitHub社区的开发者&#xff0c;每个月我都会花大量时间挖掘平台上的优质项目。3月份GitHub上涌现了不少让人眼前一亮的开源项目&#xff0c;这些项目要么解决了特定领域的技术痛点&#xff0c;要么提供了创新的开发思路。今天我就来分享14个我认…

作者头像 李华