资讯动态

Hadoop高可用配置实战:ZooKeeper与ZKFC自动故障转移详解

发布时间:2026/10/21 16:30:12 来源:尧图企业网站定制
1. 为什么高可用这件事值得单独写一篇配置说明做大数据平台运维的人迟早会碰到一个绕不开的坎单点故障。NameNode 挂了整个集群就瘫了ResourceManager 挂了所有作业都提不上去。早期搭测试集群的时候大家习惯性地把什么服务都往一台机器上塞跑得好好的一旦上了生产环境半夜一个进程崩了电话就被打爆。Hadoop 的高可用方案就是在这种血泪教训里被逼出来的而 ZooKeeper 在其中扮演的角色说白了就是那个“裁判”——它不干活但它决定谁来干活。这篇内容主要面向已经搭好基础 Hadoop 集群、准备从单点模式切换到 HA 模式的中级运维人员也适合刚接触大数据平台、想搞清楚 HA 到底改了哪些配置的初学者。我会把 ZooKeeper 高可用配置文件的修改逻辑、每个参数背后的含义、启动顺序以及踩过的坑从头到尾捋一遍。不是照搬官方文档那种“改这几个值就行”的写法而是把为什么改、改了会怎样、不改会出什么问题都讲清楚。整个方案的核心思路其实不复杂用 ZooKeeper 做分布式协调让多个 NameNode 通过 ZK 选举决定谁是 Active、谁是 StandbyJournalNode 负责在 Active 和 Standby 之间同步元数据编辑日志ZKFC 进程监控 NameNode 健康状态出问题时触发自动切换。听起来简单但配置文件里每一个参数写错一个字母整个 HA 就起不来而且报错信息往往含糊得让人抓狂。2. 高可用架构的核心组件与配置逻辑拆解2.1 ZooKeeper 在 HA 中的真实角色很多人第一次接触 Hadoop HA 的时候会有一个误解觉得 ZooKeeper 是“存储数据”的。其实 ZooKeeper 在 Hadoop HA 里只做三件事维护 Active NameNode 的锁、存储选举信息、以及在故障时协调切换。它不存 HDFS 的文件数据也不存元数据镜像这些活是 JournalNode 和 NameNode 自己干的。打个比方ZooKeeper 就像一个小区的物业前台它手里有一把“唯一钥匙”谁拿到这把钥匙谁就是 Active。当 Active 那台机器出问题了钥匙会被释放Standby 那边通过 ZKFC 监控到这个变化就会去抢钥匙抢到了就变成新的 Active。整个过程不需要人工干预这就是自动故障转移的核心机制。理解了这个角色定位你就能明白为什么 ZooKeeper 集群本身必须是奇数节点——3 个、5 个、7 个。因为 ZK 内部用 ZAB 协议做一致性保证需要过半节点存活才能对外提供服务。3 个节点允许挂 1 个5 个允许挂 2 个奇数节点在容错能力相同的情况下成本最低。2.2 配置文件全景到底要改哪几个文件Hadoop HA 涉及的配置文件不算多但每个文件里的改动点比较分散容易漏。我整理了一个清单按文件维度列出来配置文件核心改动内容作用范围core-site.xmlfs.defaultFS 指向 HA 逻辑名称全局hdfs-site.xmlnameservice、NameNode ID、JournalNode、ZKFC 相关HDFSyarn-site.xmlResourceManager HA 配置YARNzoo.cfgZK 集群节点列表、数据目录、端口ZooKeeperhadoop-env.sh日志目录、JVM 参数全局这里有个经验改配置之前一定要备份原始文件。我见过太多次改错了想回滚结果发现没备份只能凭记忆一个个改回来。备份命令很简单cp core-site.xml core-site.xml.bak cp hdfs-site.xml hdfs-site.xml.bak cp yarn-site.xml yarn-site.xml.bak别嫌麻烦这个习惯能救命。2.3 命名服务nameservice的设计考量HA 模式下HDFS 不再用单台机器的地址来标识而是引入了一个逻辑名称叫 nameservice。你可以把它理解成一个“域名”客户端访问的时候用这个名字具体连到哪台 NameNode 由配置决定。为什么这么设计因为如果客户端直接写死某台 NameNode 的 IP那台挂了客户端就废了。用逻辑名称之后客户端通过 nameservice 去找当前 Active 的节点切换对客户端透明。这个设计思路和 DNS 很像你访问网站用的是域名具体解析到哪台服务器你不用管。nameservice 的命名没有强制规范但建议用有意义的名字比如mycluster或者production-ns。不要用ns1这种过两个月你自己都不记得是哪个集群了。3. 逐文件配置修改实操与参数详解3.1 core-site.xml入口地址的切换这是最基础也是最重要的一步。单点模式下fs.defaultFS写的是hdfs://主机名:8020HA 模式下要改成hdfs://nameservice的形式。configuration property namefs.defaultFS/name valuehdfs://mycluster/value /property property nameha.zookeeper.quorum/name valuezk-node1:2181,zk-node2:2181,zk-node3:2181/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configurationha.zookeeper.quorum这个参数是 ZKFC 用来连接 ZooKeeper 集群的地址列表。注意这里写的是 ZK 的客户端端口 2181不是 ZK 集群内部通信的端口。三个节点用逗号分隔不要有空格我见过因为多了个空格导致 ZKFC 启动失败的案例。hadoop.tmp.dir建议指向一个独立的、空间充足的磁盘目录。默认值在/tmp下面很多系统重启会清理/tmp导致集群数据丢失。这个坑我踩过一次重启服务器之后整个集群起不来排查了半天才发现是临时目录被清了。3.2 hdfs-site.xmlHA 配置的主战场这个文件是改动最多的地方我分几块来说。第一块nameservice 与 NameNode 定义property namedfs.nameservices/name valuemycluster/value /property property namedfs.ha.namenodes.mycluster/name valuenn1,nn2/value /property property namedfs.namenode.rpc-address.mycluster.nn1/name valuenn-node1:8020/value /property property namedfs.namenode.rpc-address.mycluster.nn2/name valuenn-node2:8020/value /property property namedfs.namenode.http-address.mycluster.nn1/name valuenn-node1:9870/value /property property namedfs.namenode.http-address.mycluster.nn2/name valuenn-node2:9870/value /propertydfs.ha.namenodes.mycluster定义了这个 nameservice 下有哪些 NameNode名字可以自定义但后面所有引用都要保持一致。RPC 地址是给客户端和 DataNode 用的HTTP 地址是给 Web UI 用的。端口 8020 和 9870 是 Hadoop 3.x 的默认值2.x 版本 RPC 端口通常是 8020HTTP 端口是 50070这个要注意区分。第二块JournalNode 配置property namedfs.namenode.shared.edits.dir/name valueqjournal://jn-node1:8485;jn-node2:8485;jn-node3:8485/mycluster/value /property property namedfs.journalnode.edits.dir/name value/data/hadoop/journal/value /propertyJournalNode 至少需要 3 个节点因为它的写入也遵循过半确认机制。qjournal://后面的格式是主机:端口用分号分隔最后跟 nameservice 名称。dfs.journalnode.edits.dir是 JournalNode 存放编辑日志的本地目录这个目录一定要用独立磁盘因为写入非常频繁。第三块故障转移代理配置property namedfs.client.failover.proxy.provider.mycluster/name valueorg.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider/value /property property namedfs.ha.fencing.methods/name valuesshfence/value /property property namedfs.ha.fencing.ssh.private-key-files/name value/home/hadoop/.ssh/id_rsa/value /propertyConfiguredFailoverProxyProvider是客户端侧的故障转移实现类基本不用改。dfs.ha.fencing.methods定义的是隔离机制当需要切换时如何确保旧的 Active 真的已经停止服务。sshfence是最常用的方式通过 SSH 登录到旧 Active 节点把它杀掉。ssh.private-key-files指定免密登录的私钥路径。这里有个关键点sshfence 要求 NameNode 节点之间能够互相免密 SSH 登录。如果没配好切换的时候会卡住日志里会报unable to fence之类的错误。配置免密登录的步骤ssh-keygen -t rsa -P -f ~/.ssh/id_rsa ssh-copy-id nn-node1 ssh-copy-id nn-node2第四块自动故障转移开关property namedfs.ha.automatic-failover.enabled/name valuetrue/value /property这个参数设为 true 之后ZKFC 才会真正起作用。如果设为 false即使 ZKFC 进程起来了也不会自动切换需要手动执行hdfs haadmin -failover命令。3.3 yarn-site.xmlResourceManager HA 配置YARN 的 HA 配置相对简单一些核心是 ResourceManager 的 ID 定义和 ZK 地址。property nameyarn.resourcemanager.ha.enabled/name valuetrue/value /property property nameyarn.resourcemanager.cluster-id/name valuemycluster-rm/value /property property nameyarn.resourcemanager.ha.rm-ids/name valuerm1,rm2/value /property property nameyarn.resourcemanager.hostname.rm1/name valuerm-node1/value /property property nameyarn.resourcemanager.hostname.rm2/name valuerm-node2/value /property property nameyarn.resourcemanager.zk-address/name valuezk-node1:2181,zk-node2:2181,zk-node3:2181/value /propertyyarn.resourcemanager.cluster-id是 YARN 集群的唯一标识在 ZK 里用来区分不同的集群。如果你有多个 YARN 集群共用一套 ZK这个 ID 绝对不能重复否则会出现一个集群的 RM 抢另一个集群的锁的诡异情况。3.4 zoo.cfgZK 集群自身的配置ZooKeeper 的配置文件通常放在$ZK_HOME/conf/zoo.cfg内容如下tickTime2000 initLimit10 syncLimit5 dataDir/data/zookeeper/data dataLogDir/data/zookeeper/logs clientPort2181 server.1zk-node1:2888:3888 server.2zk-node2:2888:3888 server.3zk-node3:2888:3888tickTime是 ZK 的基本时间单位2000 毫秒。initLimit是 follower 与 leader 初始连接时能容忍的最多 tick 数10 表示 20 秒。syncLimit是 follower 与 leader 之间心跳超时时间5 表示 10 秒。server.N中的 N 是 ZK 节点的唯一 ID需要在每个节点的dataDir下创建一个myid文件内容就是对应的数字。比如 zk-node1 上myid文件内容就是1。这个文件没有扩展名内容就是一个纯数字写错了 ZK 起不来。echo 1 /data/zookeeper/data/myid2888是 follower 连接 leader 的端口3888是选举端口。这两个端口不要和别的服务冲突我遇到过 3888 被其他进程占用导致 ZK 集群无法选举的情况排查起来很费劲。4. 启动顺序与初始化流程4.1 启动顺序为什么不能乱Hadoop HA 集群的启动有严格的依赖关系ZooKeeper 必须先起来因为 ZKFC 依赖它JournalNode 必须在 NameNode 之前起来因为 NameNode 启动时要连接 JournalNode 加载编辑日志NameNode 格式化必须在 JournalNode 启动之后。正确的启动顺序是启动 ZooKeeper 集群所有 ZK 节点启动 JournalNode所有 JN 节点格式化 NameNode首次搭建时执行一次启动 NameNode先启动一个再启动另一个启动 ZKFC在每个 NameNode 节点上启动 DataNode所有 DN 节点启动 ResourceManager 和 NodeManager这个顺序不是随便定的每一步都有依赖。比如你先启动 NameNode 再启动 JournalNodeNameNode 会因为连不上 JournalNode 而进入安全模式或者直接启动失败。4.2 ZooKeeper 集群启动与验证在每个 ZK 节点上执行zkServer.sh start启动后验证状态zkServer.sh status输出会显示当前节点是 leader 还是 follower。三个节点中应该有一个 leader、两个 follower。如果全是 follower 或者全是 leader说明选举出了问题通常是 myid 配置错误或者端口被占用。用zkCli.sh连接进去测试一下zkCli.sh -server zk-node1:2181进入交互界面后执行ls /能看到 znode 列表就说明 ZK 正常工作了。4.3 JournalNode 启动在每个 JN 节点上执行hdfs --daemon start journalnode验证方式很简单看进程在不在jps | grep JournalNode另外可以检查dfs.journalnode.edits.dir目录下是否生成了current子目录有的话说明 JN 正常初始化了。4.4 NameNode 格式化与启动首次搭建时需要在其中一个 NameNode 上执行格式化hdfs namenode -format -clusterId mycluster-clusterId参数指定集群 ID两个 NameNode 必须使用相同的 clusterId否则无法组成 HA 对。格式化完成后启动这个 NameNodehdfs --daemon start namenode然后在另一个 NameNode 上执行同步hdfs namenode -bootstrapStandby这个命令会从 Active NameNode 或者 JournalNode 同步元数据到本地。执行成功后会提示BootstrapStandby相关的成功信息。同步完成后启动第二个 NameNodehdfs --daemon start namenode4.5 ZKFC 启动与自动切换验证在两个 NameNode 节点上分别启动 ZKFChdfs --daemon start zkfc启动后用以下命令查看 NameNode 状态hdfs haadmin -getServiceState nn1 hdfs haadmin -getServiceState nn2正常情况下一个显示active一个显示standby。如果两个都显示standby说明 ZKFC 没有成功选举检查 ZK 连接和ha.zookeeper.quorum配置。手动触发一次切换来验证自动故障转移是否工作hdfs haadmin -failover nn1 nn2这个命令会强制把 nn1 切到 standbynn2 切到 active。如果切换成功说明 fencing 机制和 ZKFC 都配置正确了。5. 常见问题排查与避坑经验5.1 启动失败类问题速查现象可能原因排查方向NameNode 启动报 “Cannot assign requested address”RPC 地址配置的主机名解析不了检查 /etc/hosts 和 DNSZKFC 启动后立即退出ZK 连接失败或 quorum 配置错误检查 ha.zookeeper.quorum 和 ZK 状态JournalNode 启动后 NameNode 仍报无法写入edits.dir 权限问题检查目录权限和磁盘空间两个 NameNode 都是 standbyZKFC 未启动或 ZK 中无 active 锁检查 ZKFC 进程和 ZK 中 /hadoop-ha 节点切换时卡在 fencingSSH 免密未配置或私钥路径错误手动测试 ssh 免密登录5.2 那些文档里不会写的坑坑一主机名解析问题。Hadoop 配置里大量使用主机名而不是 IP如果/etc/hosts没配好或者 DNS 解析不稳定会出现各种莫名其妙的连接超时。我的建议是在所有节点的/etc/hosts里把集群所有机器的主机名和 IP 都写死不依赖 DNS。坑二端口冲突。JournalNode 的 8485 端口、ZK 的 2181/2888/3888 端口、NameNode 的 8020/9870 端口这些如果在同一台机器上跑多个服务很容易冲突。启动前用netstat -tlnp | grep 端口号检查一下。坑三格式化次数。NameNode 格式化只能执行一次重复格式化会导致 clusterId 不一致两个 NameNode 无法组成 HA 对。如果不小心格式化了多次需要把两个 NameNode 的数据目录都清空重新来。这个操作会丢失所有 HDFS 数据生产环境一定要慎重。坑四时间同步。ZooKeeper 和 Hadoop 对节点间时间差有要求如果机器时间差太大会出现各种心跳超时和选举异常。建议所有节点配置 NTP 时间同步时间差控制在 1 秒以内。坑五日志目录权限。ZKFC 和 NameNode 写日志的目录如果权限不对进程启动时会报权限拒绝然后退出。确保hadoop-env.sh里配置的HADOOP_LOG_DIR目录存在且对运行用户可写。5.3 日常运维中的检查清单集群跑起来之后日常巡检建议关注这几个点ZooKeeper 集群是否保持奇数节点存活zkServer.sh status确认 leader 存在JournalNode 的 edits 目录磁盘使用率超过 80% 要清理或扩容NameNode 的堆内存使用情况频繁 Full GC 会影响切换速度ZKFC 日志中是否有 fencing 失败记录两个 NameNode 的元数据是否同步可以通过对比fsimage时间戳来检查6. 一些实操后的个人体会这套 HA 配置我前后搭过不下十次每次都会遇到不一样的问题。最开始的时候觉得配置项太多太杂后来慢慢理解了每个参数背后的设计意图改起来就有底了。ZooKeeper 的 quorum 配置、JournalNode 的过半写入、ZKFC 的 fencing 机制这三个东西构成了 Hadoop HA 的基石任何一个环节出问题都会导致切换失败。我个人的建议是第一次搭建的时候不要急着上生产先在测试环境把整个流程走通包括手动触发切换、模拟 NameNode 宕机、模拟 ZK 节点故障这些场景都验证一遍。只有亲眼看到自动切换成功你才能对这套机制有信心。另外配置文件的版本管理很重要。每次修改之前 commit 一下出问题了可以快速 diff 出改了什么。我现在的习惯是用一个简单的 git 仓库管理所有节点的配置文件改完 push其他节点 pull 更新比手动 scp 靠谱得多。最后说一个细节dfs.ha.fencing.methods除了sshfence还有shell方式可以自定义脚本。如果环境里 SSH 免密不好配可以考虑用shell加一个简单的 kill 脚本作为兜底。但sshfence始终是最推荐的方式因为它能确保旧 Active 真的被隔离了避免脑裂问题。脑裂在 HA 集群里是最危险的情况两个 NameNode 同时认为自己是 Active会导致数据不一致甚至丢失所以 fencing 机制绝对不能省。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑