集群这件事,我前后搭过不下二十遍,从最开始照着截图一步步抄,到后来闭着眼也能把几个 XML 里的关键项写对,中间踩的坑基本都在“系统层”和“配置联动”上,反而 Hadoop 本身没那么难伺候。用 Hadoop 搭建完全分布式集群,说白了就是把 NameNode、ResourceManager、DataNode、NodeManager 这些角色拆到不同机器上,让它们通过 RPC 互相通信,形成一个能真正扛住单点压力、能横向扩容的存储与计算底座。它解决的是伪分布式模式下所有进程挤在一台机器、数据只有一份、算力和磁盘都无法扩展的问题。这篇文章适合已经跑通过本地模式或伪分布式、准备把集群铺到两三台机器上的朋友,也适合那些启动脚本跑完、jps 也看到了进程,但 Web UI 打不开、DataNode 死活不注册、跑个 WordCount 就报找不到主类的同学——后面这几类问题我会拆得比较细。
1. 从伪分布式跨到完全分布式,先想清楚几件事
1.1 三种部署模式的本质差别
本地模式其实就是拿 Hadoop 当一套 jar 包用,读写的是本地文件系统,连 HDFS 都不启动;伪分布式是把所有守护进程塞进一台机器,但走的是完整的 RPC 链路,跟真实集群在代码路径上已经没有区别了;完全分布式才是把角色拆开,机器之间通过网络通信。很多人觉得伪分布式已经“学完了”,其实差别不在功能,而在状态与容错。伪分布式里 DataNode 只有一个,副本数只能设 1,NameNode 一停整个集群就没了;完全分布式里至少三个 DataNode,副本能真正落成三份,某一个节点挂掉对上层任务几乎是透明的。
我把这三者的对比整理成了一张表,方便你判断自己现在该不该升级部署模式:
| 维度 | 本地模式 | 伪分布式 | 完全分布式 |
|---|---|---|---|
| 守护进程数量 | 无 | 全部在一台 | 分布在多台 |
| 文件系统 | 本地 ext4/xfs | HDFS | HDFS |
| 默认副本能力 | 无副本 | 只能 1 副本 | 可 3 副本及以上 |
| 数据可靠性 | 依赖单盘 | 依赖单机 | 依赖多机多盘 |
| 能否验证机架感知 | 不能 | 不能 | 能 |
| 典型用途 | 调试 API | 学习配置项 | 验证架构与调优 |
看清楚这张表,你就会明白为什么很多教程劝你先跑伪分布式再上集群——因为配置文件是同一套,你在伪分布式里写错一个属性名,到集群里照样错,只是排查成本高了十倍。我的建议是:伪分布式只用来熟悉core-site.xml、hdfs-site.xml这些文件的骨架,真正开始配集群时,把所有配置项重新写一遍,不要复制粘贴旧文件,这是当年我吃过亏的地方。
1.2 三个节点的角色分配与选型理由
我这里用最经典的三节点拓扑来演示,主机名统一用 node01、node02、node03,IP 分别是 192.168.10.101 到 103,走同一网段的二层互通。三节点不是随便定的数:一个 NameNode、一个 DataNode 加上一个 SecondaryNameNode,刚好满足“NameNode 与 SecondaryNameNode 不同机”的基本要求,同时又凑够三副本的落盘条件。如果你用两台机器,副本数设 3 就永远处于副本不足状态,hdfs dfsadmin -report里会一直显示 Under-replicated blocks。
具体角色分配我按下面这张表来,这也是我这些年反复验证下来最省心的布局:
| 节点 | 守护进程 | 内存建议 | 说明 |
|---|---|---|---|
| node01 | NameNode、ResourceManager、JobHistoryServer | 8G 起 | 主控节点,磁盘要求不高但要稳 |
| node02 | DataNode、NodeManager、SecondaryNameNode | 8G 起 | 兼顾元数据检查点与数据存储 |
| node03 | DataNode、NodeManager | 8G 起 | 纯工作节点,可批量复制扩容 |
把 ResourceManager 和 NameNode 放一起是权衡的结果。严格意义上把它们拆开更利于故障隔离,但三节点的小集群里拆开意味着每台机器都要多跑一组 JVM,内存反而更紧张。等集群扩到八台以上,我会毫不犹豫地把 ResourceManager 挪到独立节点上去。
注意:SecondaryNameNode 不是 NameNode 的热备,它只做元数据检查点合并,NameNode 挂掉它顶不上。真正的高可用需要 NameNode HA 加 JournalNode 加 ZKFC,那是另一套工程量,小集群阶段别被名字误导。
1.3 版本与硬件基线
版本上我一般锁3.3.x里的某个稳定小版本,三台机器必须完全一致,连 minor 号都不能差。原因很实际:RPC 协议在跨大版本时是有兼容风险的,混版跑出来的报错往往指向某个诡异的序列化异常,排查方向会完全跑偏。JDK 我统一用 8 或者 11,Hadoop 3.3 对这两个都支持,但同一集群里绝对不能混。
硬件基线这块,最重要的不是 CPU 而是磁盘和网络。所有节点建议至少一块独立数据盘挂到单独目录,不要把dfs.datanode.data.dir放在系统盘根分区;网络至少千兆,百兆环境下你跑 WordCount 会觉得是配置写错了,其实就是带宽被副本传输吃满了。另外三台机器的时钟差要控制在几十秒以内,否则日志时间线对不上,排查问题时你会怀疑人生。
2. 系统层准备:那些看起来无关却最容易翻车的地方
2.1 主机名、hosts 解析与网络基线
很多人的集群起不来,根子就在这里。Hadoop 内部大量使用主机名做通信和标识,hdfs://node01:8020里的 node01 在客户端和各个守护进程里都要能解析。如果你只在 node01 的 hosts 里写了映射,node02 上去连 node01 时就会抛java.net.UnknownHostException,而报错位置往往出现在某个不起眼的 DataNode 注册日志里。
我的做法是三台机器全部执行hostnamectl set-hostname,然后统一编辑/etc/hosts,把三行映射写全,最后逐台ping node01、ping node02、ping node03验证一遍回环正常。
# 三台机器都执行 hostnamectl set-hostname node01 # node02 / node03 对应修改 cat >> /etc/hosts <<'EOF' 192.168.10.101 node01 192.168.10.102 node02 192.168.10.103 node03 EOF # 逐台验证 for h in node01 node02 node03; do ping -c1 $h >/dev/null && echo "$h ok"; done防火墙和 SELinux 这部分,在内网自建的实验环境里我通常直接关掉,避免为了一两个端口反复开规则;如果是能连外网的环境,就改成按端口放行,把 8020、9870、8088、19888、9868 这几个开出来。这一步不做的话,最典型的现象是进程都在、jps也正常,但浏览器打开 9870 一直转圈。
注意:
/etc/hosts改完之后不要忘了重启网络服务或者至少重连 SSH 会话,某些系统上 hostname 缓存会让新映射延迟生效。
2.2 SSH 免密登录到底在配什么
start-dfs.sh这类脚本的本质,是在当前节点上通过 SSH 去其他节点拉起进程,所以免密必须是双向可达的。最省事的做法是三台机器各自生成密钥对,然后把公钥互相追加到对方的authorized_keys里,形成全互联。只配 node01 到 node02、node03 的单向免密,在你停集群或者 SecondaryNameNode 需要反向通信时会卡住。
# 三台机器各自执行 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa # 把三台的公钥汇总,再分发回三台 cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys # 在 node01 上把 node02、node03 的公钥收集过来,再 scp 回去配完一定要逐台ssh node0X验证,第一次连接会问 yes/no,必须提前回答掉,否则脚本跑到一半卡在交互提示上,你以为它挂了其实在等你敲键盘。顺手把~/.ssh/config里加上StrictHostKeyChecking no会更省事,但生产环境慎用。还有个细节是 SSH 端口,如果改过默认 22 端口,start-dfs.sh不会自动识别,需要在hadoop-env.sh里通过HADOOP_SSH_OPTS指定。
2.3 JDK、环境变量与时间同步
JDK 装完必须让 Hadoop 明确知道它在哪。很多人只在/etc/profile里配了 JAVA_HOME,hdfs命令能跑,但 DataNode 起不来,因为守护进程是由脚本以非登录 shell 拉起的,读不到你的 profile。稳妥的做法是双保险:/etc/profile.d/hadoop.sh里配全局,同时把JAVA_HOME硬写进$HADOOP_HOME/etc/hadoop/hadoop-env.sh。
export JAVA_HOME=/opt/module/jdk1.8.0_202 export HADOOP_HOME=/opt/module/hadoop-3.3.6 export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbinHADOOP_CONF_DIR这个变量经常被忽略,它的作用是让客户端在任何目录下执行hadoop jar时都能找到集群配置。不配的话,你在家目录下跑作业会默认连本地文件系统,输出目录跑到你当前路径下,看起来像是“作业跑完了但 HDFS 里没东西”。
时间同步我是用最简单的定时任务对齐,三台机器都装上时间同步客户端,指向同一个内网源,或者干脆让 node02、node03 跟 node01 对齐。时间差带来的问题很隐蔽,比如 JobHistory 页面上任务顺序看起来乱、日志里时间戳前后矛盾,排查时极容易误判成代码 bug。
3. Hadoop 四大配置文件逐项拆解
3.1 core-site.xml:集群的入口地址
这个文件只需要改几项,但每一项都影响全局。fs.defaultFS是整个集群的“门牌号”,所有客户端靠它找到 NameNode;hadoop.tmp.dir是所有临时数据的根目录,NameNode 元数据、DataNode 块、本地中间结果默认都往这下面塞。我习惯把它指到独立数据盘上,因为默认的/tmp在部分系统上会被定期清理,集群跑着跑着元数据没了,那种崩溃是毁灭性的。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node01:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/tmp</value> </property> <property> <name>hadoop.http.staticuser.user</name> <value>hadoop</value> </property> <property> <name>hadoop.proxyuser.hadoop.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.hadoop.groups</name> <value>*</value> </property> </configuration>后面三项看着像是可选项,实际上决定了 Web UI 能不能浏览文件、后续接 Hive 之类的组件时会不会被代理权限拦住。我第一次搭集群时没写,结果 9870 页面点进 Browse the file system 直接报权限错误,折腾了半小时才反应过来是静态用户没配。
3.2 hdfs-site.xml:副本、目录与检查点
这个文件是 NameNode 和 DataNode 的行为定义。副本数设 3 是基于“能容忍两台机器同时故障”的假设,同时保证读请求能被就近调度;dfs.namenode.name.dir和dfs.datanode.data.dir必须落到独立目录,而且可以写多个用逗号隔开,实现多盘并行写。
<configuration> <property> <name>dfs.namenode.http-address</name> <value>node01:9870</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>node02:9868</value> </property> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file://${hadoop.tmp.dir}/dfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file://${hadoop.tmp.dir}/dfs/data</value> </property> <property> <name>dfs.blocksize</name> <value>134217728</value> </property> </configuration>dfs.namenode.secondary.http-address这一项要特别注意,只有当它指向 node02 时,SecondaryNameNode 才会在 node02 上启动。很多教程只写了 NameNode 的地址,结果三台机器上jps都看不到 SecondaryNameNode,还以为是自己少装了组件。另外${hadoop.tmp.dir}这种变量引用在 hdfs-site.xml 里是能被解析的,前提是 core-site.xml 里已经定义过。
3.3 yarn-site.xml 与 mapred-site.xml:算力调度这一侧
YARN 的配置我踩过的坑最多,尤其是内存和容器这一块。yarn.nodemanager.aux-services必须开mapreduce_shuffle,否则 MapReduce 任务的中间结果没法在节点间传递,表现是任务卡在 map 100% reduce 0% 很久然后失败。yarn.resourcemanager.hostname指向 node01。
<configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>node01</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>6144</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>4</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>6144</value> </property> <property> <name>yarn.log-aggregation-enable</name> <value>true</value> </property> <property> <name>yarn.log.server.url</name> <value>http://node01:19888/jobhistory/logs</value> </property> </configuration>yarn.nodemanager.resource.memory-mb设成 6144 而不是 8192,是因为 8G 内存的机器还要留给操作系统、DataNode 的 JVM 和页缓存,全给 YARN 会导致 NodeManager 自己先被系统杀掉。这个值的经验法则是物理内存的 70% 到 75%,留足余量比压榨更重要。
mapred-site.xml 里除了指定yarn框架,那三个env属性是我强烈建议写上的:
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.jobhistory.address</name> <value>node01:10020</value> </property> <property> <name>mapreduce.jobhistory.webapp.address</name> <value>node01:19888</value> </property> <property> <name>yarn.app.mapreduce.am.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> <property> <name>mapreduce.map.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> <property> <name>mapreduce.reduce.env</name> <value>HADOOP_MAPRED_HOME=/opt/module/hadoop-3.3.6</value> </property> </configuration>不写这三个的话,任务提交后大概率在 AM 启动阶段报Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster。这个报错的迷惑性在于,你的本地环境变量明明配好了,hadoop classpath也能打出一长串路径,但容器里跑的那个 JVM 根本没继承到,只能通过显式声明来传递。
3.4 workers 文件、hadoop-env.sh 与环境变量的联动
workers(2.x 时代叫slaves)这个文件决定了start-dfs.sh会去哪些机器拉起 DataNode 和 NodeManager。里面一个主机名一行,不要写多余空格,也不要把 node01 写进去——除非你打算让 node01 也当 DataNode。我在小集群里通常不把 node01 做 DataNode,目的是让主控节点专一,同时三副本能均匀落在 node02、node03 和外挂的一块盘上。如果你只有两台工作节点又想三副本,那就只能把 node01 加进去。
# $HADOOP_HOME/etc/hadoop/workers node02 node03hadoop-env.sh里至少要改两处:JAVA_HOME和 NameNode 的启动内存。默认HADOOP_HEAPSIZE偏小,元数据一多就容易 OOM。
export JAVA_HOME=/opt/module/jdk1.8.0_202 export HDFS_NAMENODE_OPTS="-Xmx2048m -Xms2048m" export HDFS_DATANODE_OPTS="-Xmx1024m -Xms1024m" export YARN_RESOURCEMANAGER_OPTS="-Xmx1024m -Xms1024m"配好之后,把这些文件从 node01 分发给 node02、node03。分发是我最强调的一步,etc/hadoop整个目录必须完全一致,任何一台漏了一个属性,行为就会不一致,而排查时你往往只盯着 node01 看。
for h in node02 node03; do rsync -av $HADOOP_HOME/etc/hadoop/ $h:$HADOOP_HOME/etc/hadoop/ done4. 格式化、启动与全链路验证
4.1 NameNode 格式化:一次就好,多了出事
格式化是整个流程里唯一“不可重来”的动作。它的作用是初始化 NameNode 的元数据目录,生成VERSION文件并分配一个 clusterID。DataNode 首次启动时会去找 NameNode 要这个 clusterID,如果对不上就拒绝注册。
hdfs namenode -format执行完看到Storage directory /data/hadoop/tmp/dfs/name has been successfully formatted才算成功。这条命令在同一个集群上绝对只能成功执行一次。第二次格式化会生成新的 clusterID,而之前已经启动过的 DataNode 还记着旧的,于是你会看到 DataNode 反复重启、NameNode 上hdfs dfsadmin -report一个节点都没有。真出这种情况,处理办法是把所有节点的dfs/data目录清空,重新格式化一次,让所有节点都从零开始。
注意:清目录之前确认里面没有你需要的数据。生产环境下正确的做法是备份
VERSION文件再修复 clusterID,而不是直接格盘。
4.2 启动顺序与进程核对
启动顺序我遵循“先存储后计算”:先start-dfs.sh,确认 HDFS 三个节点都正常,再start-yarn.sh,最后单独拉起历史服务。
start-dfs.sh start-yarn.sh mapred --daemon start historyserverstart-dfs.sh在 node01 上执行,它会按workers文件 SSH 到 node02、node03 拉起 DataNode,同时在 node02 上拉起 SecondaryNameNode。启动完逐台jps核对,正常应该是:
| 节点 | 期望进程 |
|---|---|
| node01 | NameNode、ResourceManager、JobHistoryServer |
| node02 | DataNode、NodeManager、SecondaryNameNode |
| node03 | DataNode、NodeManager |
进程数和预期对不上,先别急着改配置,去看对应节点的日志。$HADOOP_HOME/logs/下按守护进程名分文件,找最新的那个.log和.out,.out里通常是启动阶段的 stderr,ClassNotFoundException、UnknownHostException这类都是从这里冒出来的。
4.3 Web UI 与 HDFS 命令验证
三个端口记牢:9870 是 HDFS 的,8088 是 YARN 的,19888 是历史任务的。打开http://node01:9870,看 Overview 里的 Live Nodes 是不是 3,如果只有 2,说明有一台 DataNode 没注册上,直接去那台机器看日志。Storage 页面能看到每个 DataNode 的容量和已用,这是判断数据分布是否均衡的第一入口。
命令行侧我一般跑这套组合拳:
hdfs dfs -mkdir -p /data/input hdfs dfs -ls / hdfs dfs -put /etc/passwd /data/input/ hdfs dfs -cat /data/input/passwd | head -3 hdfs fsck /data/input/passwd -files -blocks -locationsfsck那条最有价值,它会告诉你这个文件的每个块落在哪个 DataNode 上。副本数设 3 但只看到 2 个 location,说明有一个节点没写成功,这时候去看那台机器的磁盘空间和权限。
4.4 用 WordCount 打通全链路
HDFS 通了不代表 YARN 通了,必须跑一个真正的分布式作业。用自带示例最省事:
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar \ wordcount /data/input /data/output hdfs dfs -cat /data/output/part-r-00000 | head -10提交后立刻去 8088 看 RUNNING 状态,正常情况下会看到 map 和 reduce 各若干个容器。这里有个细节值得注意:输出目录/data/output必须不存在,否则作业会直接报FileAlreadyExistsException秒退。跑第二次之前先hdfs dfs -rm -r /data/output。
如果任务卡在 ACCEPTED 很久不动,八成是 YARN 没有可用资源,去 8088 的 Scheduler 页面看队列内存是不是被占满;如果 map 跑完 reduce 卡在 0%,去看 Shuffle 端口 13562 是否被防火墙拦了。
5. 常见问题排查实录
5.1 五类高频故障的定位路径
DataNode 起不来或启动即退出,九成是 clusterID 不匹配或者数据目录权限不对。日志里搜Incompatible clusterIDs能一秒定位。权限问题则表现为Permission denied后跟dfs/data路径,处理办法是让运行 Hadoop 的账号对数据目录有读写权限。
Web UI 打不开但进程在,先在本机curl -I http://node01:9870看返回。本机能通、别的机器不通,就是防火墙;本机也不通,去看hdfs-site.xml里的dfs.namenode.http-address是不是绑到了 0.0.0.0 之外的地址。
作业报找不到 MRAppMaster,回到第 3.3 节那三个 env 属性。这个问题的排查成本很高,因为错误信息指向的是 Java 类加载,很少有人会联想到环境变量传递。
NodeManager 启动后很快消失,去nodemanager.log里搜killed by或者OutOfMemory。常见原因是yarn.nodemanager.resource.memory-mb设得超过了实际可用内存,或者虚拟内存检查过于严格把容器杀了。
副本一直不足,hdfs dfsadmin -report里看 Under-replicated blocks。如果 Live Nodes 只有 2 个而副本数是 3,那就永远补不齐;如果 Live Nodes 是 3 但还是不足,说明某个 DataNode 的磁盘满了,写不进去。
5.2 一张能直接对着查的表
| 现象 | 高概率原因 | 快速验证方式 |
|---|---|---|
| DataNode 拒绝注册 | clusterID 不一致 | 日志搜 Incompatible clusterIDs |
| 9870 打不开 | 防火墙或绑定地址 | 本机 curl 与远程 curl 对比 |
| UnknownHostException | hosts 缺映射 | 各节点分别 ping 主机名 |
| MRAppMaster 找不到主类 | HADOOP_MAPRED_HOME 未传 | 检查 mapred-site.xml 三个 env |
| 任务卡 ACCEPTED | 队列资源不足 | 8088 Scheduler 页面 |
| 副本数长期不足 | 节点数少于副本数或磁盘满 | dfsadmin -report |
| start-dfs.sh 反复要密码 | 免密只配了单向 | 逐台 ssh 验证反向 |
| reduce 卡 0% | Shuffle 端口被拦 | telnet 目标节点 13562 |
5.3 我踩过的三个坑,写下来省你几个小时
第一个坑是用 root 跑集群。一开始图省事,所有进程都是 root 起的,后来发现 Web UI 里浏览 HDFS 文件报权限错误,改文件 owner 也没用,因为静态用户配的是 hadoop。正确的姿势是建一个专用账号,所有节点统一 UID,从安装到启动一以贯之。
第二个坑是数据目录和临时目录混在一起。我把hadoop.tmp.dir和dfs/data都指到了同一个挂载点,结果磁盘写满之后连 NameNode 的 edits 都写不进去,整个集群直接只读。后来我把这两者分到不同物理盘,一个写满不影响另一个。
第三个坑是配置改完只重启了 node01。HDFS 的部分参数是 NameNode 侧生效的,部分要在 DataNode 侧生效,如果只重启主节点,新参数在数据节点上根本没加载。现在我的习惯是改完配置统一stop-dfs.sh && start-dfs.sh,宁可多花两分钟。
提示:每次改配置前先备份
etc/hadoop目录,加个时间戳后缀。调参调到集群起不来的时候,把旧目录盖回去是最快的回滚手段。
6. 参数调优与日常运维
6.1 块大小和副本数为什么是这两个数
块大小 128MB 不是拍脑袋定的,它来自一个权衡:寻址时间占总传输时间的比例要足够小,否则磁盘大部分时间花在找磁道上。假设单盘顺序写速率约 100MB/s,期望寻址开销占比控制在 1% 左右,寻址时间约 10ms,那么合适的块大小大约是100MB/s × 10ms × 100 ≈ 100MB,工程上取 128MB 这个 2 的幂更好对齐。如果换成 SSD,顺序写能到 500MB/s 以上,这个值就可以适当调大。
副本数 3 的逻辑是可靠性与成本的平衡。一份副本等于没有冗余,两份只能容忍一块盘坏,三份能容忍两台机器同时故障,同时读的时候三个副本可以就近调度。副本再多,存储成本和写放大都会上去,小集群里没必要。
6.2 YARN 内存与 vcore 的规划落地
一台 8G 内存的机器,NodeManager 可用内存我给 6144,vcore 给物理核数(不按超线程算)。这两个数决定了单个容器能吃多少资源:yarn.scheduler.minimum-allocation-mb默认 1024,意味着哪怕你的 map 只需要 200MB,也会占掉一个 1024 的槽位。如果小任务特别多,把这个最小值降到 512,能在同样资源下多跑几个容器。
vcore 方面,yarn.nodemanager.resource.cpu-vcores设成 4 的时候,单个容器默认 1 个 vcore,理论上能并发 4 个容器。但实际 CPU 争抢会让容器跑得比预期慢,所以我通常再用yarn.scheduler.capacity.resource-calculator配成基于内存优先的调度,避免 CPU 被过度切片。
6.3 扩容与数据均衡
加节点其实很轻量:新机器装好同样的 JDK 和 Hadoop,配置从现有节点 rsync 过去,主机名和 hosts 补齐,把新主机名加进workers,然后在 node01 上单独hdfs --daemon start datanode拉起来。新节点会自动向 NameNode 注册,但已有数据不会自动搬过去,集群会处于不均衡状态。
这时候用 balancer:
hdfs balancer -threshold 5 -bandwidthPerSec 50m-threshold 5表示各节点使用率差异控制在 5% 以内就停,-bandwidthPerSec限制搬迁带宽避免把网络吃满。这个命令可以跑很久,建议放在业务低峰期。另外dfs.datanode.balance.bandwidthPerSec也可以在配置里设,但命令行动态指定的优先级更高,更灵活。
真正开始用之后你会发现,Hadoop 集群的“搭起来”和“跑得稳”完全是两件事。搭起来只是把进程拉起来、端口通、作业能提交;跑得稳需要你盯着 log 目录、盯着磁盘水位、盯着 YARN 队列的排队情况。我现在的习惯是每周扫一次dfsadmin -report和 8088 的 Scheduler 页面,磁盘超过 75% 就开始准备扩容,而不是等它写满再救火。