这篇文章是大数据项目实战系列的第二篇,按计划承接上一篇的基础环境准备。上一篇我们把腾讯轻量云服务器买好、系统初始化完毕,这一篇直接进入正题:用三台轻量云服务器,从零部署一套带高可用能力的Hadoop集群,并装上Hive和MySQL元数据库,最后跑通一个真正的分布式WordCount任务。如果你正在准备大数据面试,或者想给自己搭一套能用来做数仓项目、练手Flink的学习环境,这篇流程会非常实用。我会把所有步骤、配置项、为什么这么配置,以及实际踩过的坑全部写出来。
整个实践过程中有一个核心指导思想:平台可以不复杂,但流程必须完整。很多人学大数据卡在“只会在本地跑单机伪分布式”,一到面试聊起分布式架构就没话说。这套自建的轻量云集群虽然规模小,但完整复刻了生产环境的核心组件和部署逻辑,包括NameNode高可用、YARN资源调度、Hive元数据管理。你把它吃透了,后面看任何企业级大数据平台架构都会轻松很多。
1. 环境与选型:为什么把大数据集群建在轻量云上
1.1 为什么我坚持自己搭集群,而不是直接用EMR
先说选型。市面上有现成的EMR、托管Hadoop服务,点几下控制台就能给你一个集群,看起来很香,但我还是建议你自己从零搭一遍。原因很简单:面试不会问你“怎么在控制台点击创建EMR集群”,而是会问“NameNode挂了怎么办”“DataNode为什么不注册”“YARN任务为什么一直ACCEPTED”。这些问题,只有自己亲自部署过、排查过,脑子里才会有真实的模型。
腾讯轻量云服务器在这里的优势很明显:价格便宜,按量计费或者包年包月都能接受;镜像里面自带CentOS、Ubuntu可选;同账号同地域的几台机器默认内网互通,这对Hadoop集群内部通信非常友好。我这边用的配置是三台2核4G的轻量云,系统盘60G,装CentOS 7或者Rocky Linux都行。这个量级跑学习型集群完全够用,跑简单的MapReduce任务没问题,唯一的瓶颈是内存,所以后面我会专门讲YARN内存怎么调。
1.2 三节点怎么排角色,成本怎么算
集群规模我定的是三台,而不是一台伪分布式,也不是七八台大集群。一台机器只能验证“能跑”,但验证不了“分布式”,比如HDFS数据块怎么分布在不同节点、数据副本机制怎么工作、YARN怎么跨节点调度任务。这些才是大数据的核心概念,也是面试题的高频考点。
三台机器的角色规划如下:
| 主机名 | 内网地址 | 机器规格 | 部署角色 |
|---|---|---|---|
| nw1 | 10.0.0.11 | 2核4G | NameNode(Active)、ResourceManager(Active)、ZooKeeper、Hive |
| nw2 | 10.0.0.12 | 2核4G | NameNode(Standby)、ResourceManager(Standby)、ZooKeeper |
| nw3 | 10.0.0.13 | 2核4G | DataNode、NodeManager、JournalNode、ZooKeeper、MySQL |
为什么要三台?因为高可用需要奇数个ZooKeeper节点做选举,两个NameNode加一个或两个JournalNode才能支撑HDFS的共享编辑日志。三台是最小的高可用集群规模,同时也是成本最低的学习方案。我算过一笔账,三台2核4G轻量云包年的话,一天平均下来基本就是一杯咖啡的价钱,换来的是一个可以反复折腾、随时重置的实验环境,我觉得很值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 集群基础准备:节点规划与系统初始化
2.1 服务器初始化清单:hosts、SSH免密、JDK一个都不能少
这一节很多教程都一笔带过,但恰恰是后面各种莫名其妙问题的根源。我的建议是按清单一步步来,每个节点都做一遍,不要跳步。
第一步,修改主机名:
bash复制# 三台机器分别执行
hostnamectl set-hostname nw1
# nw2、nw3 同理
第二步,修改 /etc/hosts,把三台机器的内网IP映射写进去。这里有个关键点:集群内部通信一定要用内网IP,不要用公网IP。公网IP走公网链路,带宽有限且延迟高,HDFS传数据块会非常慢,而且部分云厂商的公网带宽是计费的。
bash复制cat >> /etc/hosts <<EOF
10.0.0.11 nw1
10.0.0.12 nw2
10.0.0.13 nw3
EOF
第三步,统一创建运行用户。我习惯用 hadoop 用户,而不是root跑集群。原因有两层:一是Hadoop官方文档和绝大多数脚本默认排除root,用root启动会报环境变量相关的错误;二是实际生产环境也不会用root跑,提前养成好习惯。创建用户并配置sudo权限:
bash复制useradd hadoop
echo "hadoop123" | passwd --stdin hadoop
echo "hadoop ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
第四步,配置SSH免密登录。这个不做的话,Hadoop的脚本在跨节点启停进程时会不断提示输入密码,自动化根本没法玩。从nw1生成密钥,然后把公钥分发到三台机器:
bash复制su - hadoop
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
ssh-copy-id hadoop@nw1
ssh-copy-id hadoop@nw2
ssh-copy-id hadoop@nw3
第五步,安装JDK。Hadoop 3.x要求Java 8或Java 11,我这边用OpenJDK 1.8,稳定且兼容性最好:
bash复制yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
确认版本:
bash复制java -version
把 JAVA_HOME 写入 /etc/profile,特别是后续配置Hadoop的 JAVA_HOME 时需要这个路径。用 readlink -f $(which java) 找到实际路径,一般是 /usr/lib/jvm/java-1.8.0-openjdk。
2.2 目录规划与权限设置:数据到底放哪里
很多新手在集群部署失败后选择重新格式化,但忽略了目录规划,格式化完依然报错。这里我给出当时使用的目录结构:
bash复制mkdir -p /data/hadoop/hdfs/namenode
mkdir -p /data/hadoop/hdfs/datanode
mkdir -p /data/hadoop/journaldata
mkdir -p /data/hadoop/logs
chown -R hadoop:hadoop /data
为什么不用默认的 /tmp/hadoop-* 目录?因为系统会定期清理 /tmp,一旦NameNode元数据被清理,集群等于报废,还得重新格式化。另外,轻量云一般默认系统盘是唯一的数据盘,空间有限,所以 /data 目录直接建在系统盘上;如果你的套餐能够挂载独立的数据盘,把 /data 挂到数据盘上更好,日志、HDFS数据块尽量别跟系统盘抢空间。
还有个容易踩的坑是权限。HDFS的NameNode、DataNode进程都是以 hadoop 用户启动的,如果目录是root创建的,启动时会报 Permission denied。所以每次创建完目录,记得 chown -R hadoop:hadoop。
顺手把防火墙放行或者直接关掉,学习环境我倾向于直接关掉系统防火墙:
bash复制systemctl stop firewalld
systemctl disable firewalld
但是!腾讯云轻量控制台还有一个“防火墙”页面,这里是云平台层面的规则,跟系统防火墙是两回事。你需要登录轻量控制台,在防火墙规则里放行Hadoop相关端口,不然外部访问Web UI和RPC端口都会被拦。我下面放一个常用的端口清单,建议收藏:
| 组件 | 端口 | 说明 |
|---|---|---|
| SSH | 22 | 远程登录 |
| ZooKeeper | 2181 | 客户端连接 |
| NameNode RPC | 8020 | HDFS客户端连接 |
| NameNode Web | 9870 | HDFS管理界面 |
| DataNode Web | 9864 | DataNode信息 |
| YARN ResourceManager Web | 8088 | YARN管理界面 |
| NodeManager Web | 8042 | 节点信息 |
| JournalNode RPC | 8485 | QJM通信 |
| Hive Metastore | 9083 | Hive元数据服务 |
| HiveServer2 | 10000 | Beeline连接 |
| MySQL | 3306 | 元数据库 |
以上端口如果你用的是云平台自带的防火墙,都要去控制台放行。我一开始只关了系统防火墙,结果Web UI始终访问不了,排查了半天才发现是云平台防火墙把9870端口拦住了。
3. ZooKeeper与Hadoop高可用部署:核心配置逐项拆解
3.1 ZooKeeper集群搭建:先解决“选主”问题
ZooKeeper在高可用Hadoop里承担两个核心任务:一是给YARN的ResourceManager做选举,二是配合DFSZKFailoverController实现NameNode的自动故障切换。所以我先把ZooKeeper装好。
版本方面,我用的Zookeeper 3.7.1,解压到 /usr/local/zookeeper。每台机器上创建配置文件 /usr/local/zookeeper/conf/zoo.cfg:
ini复制tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper
clientPort=2181
server.1=nw1:2888:3888
server.2=nw2:2888:3888
server.3=nw3:2888:3888
2888是ZooKeeper集群内部通信端口,3888是选举端口。每个节点还要在 /data/zookeeper 目录下创建 myid 文件,内容分别是1、2、3,对应 server.1/2/3。这一步漏了的话,ZooKeeper会一直报找不到 myid。
启动前记得把 /data/zookeeper 属主改成 hadoop,然后依次在三台机器启动:
bash复制su - hadoop
/usr/local/zookeeper/bin/zkServer.sh start
用 jps 查看进程,应该能看到 QuorumPeerMain。再用一条命令检查集群状态,确保有一台是leader、两台是follower:
bash复制/usr/local/zookeeper/bin/zkServer.sh status
如果你看到三台都是follower或者报连接超时,多半是 myid 没配对,或者 server.x 的主机名解析失败,去 /etc/hosts 再检查一遍。
3.2 Hadoop高可用配置:core-site与hdfs-site逐项拆解
Hadoop版本我选的是Hadoop 3.3.6,下载解压到 /usr/local/hadoop。配置目录是 /usr/local/hadoop/etc/hadoop,核心就是几个xml文件。我建议你别直接复制别人的整包配置,而是逐项理解每个参数,这样出问题才知道从哪里找。
先看 core-site.xml:
xml复制<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://ns</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>nw1:2181,nw2:2181,nw3:2181</value>
</property>
</configuration>
fs.defaultFS 是HDFS的访问入口,这里不在,为什么还要设置?因为在非HA环境下你会直接写 hdfs://nw1:8020,但HA模式下,客户端需要一个逻辑名称服务,让它可以自动在多个NameNode之间切换。这个 ns 就是我们给名称服务起的名字,后面在 hdfs-site.xml 里会用到。
再看 hdfs-site.xml:
xml复制<configuration>
<property>
<name>dfs.nameservices</name>
<value>ns</value>
</property>
<property>
<name>dfs.ha.namenodes.ns</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.ns.nn1</name>
<value>nw1:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.ns.nn2</name>
<value>nw2:8020</value>
</property>
<property>
<name>dfs.namenode.http-address.ns.nn1</name>
<value>nw1:9870</value>
</property>
<property>
<name>dfs.namenode.http-address.ns.nn2</name>
<value>nw2:9870</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://nw1:8485;nw2:8485;nw3:8485/ns</value>
</property>
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/data/hadoop/journaldata</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/hadoop/.ssh/id_rsa</value>
</property>
<property>
<name>dfs.replication</name>
<value>2</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/data/hadoop/hdfs/namenode</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/data/hadoop/hdfs/datanode</value>
</property>
</configuration>
逐项拆解。dfs.nameservices 和 dfs.ha.namenodes.ns 定义了逻辑名称服务下有两个NameNode,分别是nn1和nn2。dfs.namenode.rpc-address.ns.nn1 和 dfs.namenode.rpc-address.ns.nn2 分别指向nw1和nw2的8020端口,这是HDFS客户端和NameNode通信的入口。
dfs.namenode.shared.edits.dir 是重点。HA模式下,两个NameNode必须共享一份编辑日志,否则主备切换后元数据会不一致。这里我用的是QJM(Quorum Journal Manager)方案,三台节点都充当JournalNode的角色,日志写到 /data/hadoop/journaldata。QJM能容忍一半节点的故障,所以3个JournalNode是合理的。
dfs.ha.automatic-failover.enabled 开启后,NameNode主动或被动故障时,ZooKeeper会自动完成主备切换。dfs.ha.fencing.methods 配置的是 sshfence,意思是主节点故障时,用SSH远程执行命令把旧主节点强制踢掉,防止两个NameNode同时进入Active状态(俗称脑裂)。
dfs.replication 我故意设成2,因为三节点集群里如果副本数默认3,每个块都要写3份,磁盘消耗大且没必要。学习环境副本数是2可以节省空间,同时你仍然能看到数据块在不同DataNode上的分布效果。
yarn-site.xml 也需要配置ResourceManager的高可用:
xml复制<configuration>
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>mycluster</value>
</property>
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>nw1</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>nw2</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>nw1:8088</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>nw2:8088</value>
</property>
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>nw1:2181,nw2:2181,nw3:2181</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>2048</value>
</property>
<property>
<name>yarn.nodemanager.pmem-check-enabled</name>
<value>false</value>
</property>
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
</configuration>
yarn.nodemanager.resource.memory-mb 我设置为2048,因为单台机器只有4G内存,如果默认配置是8G,YARN会认为每个NodeManager有8G可用,结果申请容器时节点根本没有那么多内存,任务就会一直卡住或者被系统杀死。pmem-check-enabled 和 vmem-check-enabled 建议直接设为false,小集群经常因为虚拟内存限制导致任务失败,关闭这些检查能减少很多莫名其妙的报错。
最后配置 mapred-site.xml:
xml复制<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>
这就是明确的“我让MapReduce任务跑在YARN上”的意思。workers 文件(Hadoop 3.x 是 workers,Hadoop 2.x 是 slaves)里写上DataNode和NodeManager所在的节点:
code复制nw1
nw2
nw3
这里我让三台机器都作为DataNode和NodeManager。需要提醒的是,workers 文件保存的是“数据节点列表”,它会影响 start-dfs.sh 和 start-yarn.sh 把DataNode和NodeManager进程分发到哪些机器。
3.3 启动顺序与常见启动报错
配置完成后,启动顺序很重要,顺序错了会报各种奇怪的错。顺序是:先启动ZooKeeper,再启动JournalNode,然后格式化NameNode和ZooKeeper,再启动HDFS和YARN。
第一步,在三台机器启动ZooKeeper,确认状态正常。
第二步,在 nw1、nw2、nw3 三台机器上启动JournalNode:
bash复制/usr/local/hadoop/bin/hdfs --daemon start journalnode
第三步,在 nw1 上执行NameNode格式化:
bash复制/usr/local/hadoop/bin/hdfs namenode -format
格式化会生成集群ID和元数据。这里有两个点要特别注意:
一是格式化成功后再执行 start-dfs.sh,不要手贱反反复复格式化。反复格式化会导致NameNode生成的集群ID跟DataNode上已有的集群ID不一致,DataNode起不来。
二是HA模式下还需要初始化ZooKeeper上的状态:
bash复制/usr/local/hadoop/bin/hdfs zkfc -formatZK
这个命令会在ZooKeeper里创建HA相关的节点,让两个NameNode可以互相感知状态。
第四步,在 nw1 上把Active NameNode的元数据同步到 nw2。你可以在 nw2 上执行:
bash复制/usr/local/hadoop/bin/hdfs namenode -bootstrapStandby
这个命令会把nw1上的元数据拉取到nw2。如果你省略这一步,nw2 启动时会发现自己的元数据目录是空的,直接起不来。
第五步,在 nw1 上启动整个HDFS集群:
bash复制/usr/local/hadoop/sbin/start-dfs.sh
这个脚本会读取 workers 文件,自动把DataNode分发到各节点,并在 nw1、nw2 上启动NameNode,然后启动ZKFC(DFSZKFailoverController)。
第六步,启动YARN:
bash复制/usr/local/hadoop/sbin/start-yarn.sh
启动完用 jps 查看进程。正常情况下:
- nw1:NameNode、DFSZKFailoverController、ResourceManager、NodeManager、JournalNode、QuorumPeerMain
- nw2:NameNode、DFSZKFailoverController、ResourceManager、NodeManager、JournalNode、QuorumPeerMain
- nw3:DataNode、NodeManager、JournalNode、QuorumPeerMain
如果某个节点缺了进程,先看日志,日志在 /usr/local/hadoop/logs 目录下。这里我遇到过最常见的问题是DataNode起不来,报 Incompatible clusterIDs,这就是反复格式化导致的,解决办法是删掉DataNode目录下的数据文件,重新格式化NameNode,或者手动把DataNode的clusterID改成和NameNode一致。
4. Hive与MySQL部署:让集群能用SQL查数
4.1 元数据库准备:MySQL安装与驱动
Hadoop集群跑通后,下一步是装Hive。Hive本质上是把SQL翻译成MapReduce或Tez任务,它自己并不存储数据,而是把表结构、分区、字段这些元数据存在关系型数据库里,默认是Derby,但生产环境基本都用MySQL。我这里在nw3上装MySQL 5.7作为元数据库。
bash复制# 配置yum源后执行
yum install -y mysql-community-server
systemctl start mysqld
安装完成后,root用户会有一个临时密码,通过 grep 'temporary password' /var/log/mysqld.log 查看。登录MySQL后,创建Hive专用的库和用户,并授权:
sql复制CREATE DATABASE IF NOT EXISTS hive DEFAULT CHARACTER SET utf8mb4;
CREATE USER 'hive'@'%' IDENTIFIED BY 'Hive@123';
GRANT ALL PRIVILEGES ON hive.* TO 'hive'@'%';
FLUSH PRIVILEGES;
这里有两个坑。第一个是字符集,Hive元数据里大量表字段是字符串,如果字符集选latin1,后面建表时字段类型映射会报错,所以用 utf8mb4 最稳。第二个是认证方式,如果装的是MySQL 8.0,默认认证插件是 caching_sha2_password,而Hive 3.1.3内置的JDBC驱动不一定能直接兼容,所以我建议直接用MySQL 5.7避免折腾。如果你实在想用8.0,可以创建一个使用 mysql_native_password 插件认证的用户,但没必要在这个环节浪费时间。
MySQL准备完后,把JDBC驱动拷贝到Hive的lib目录:
bash复制cp mysql-connector-java-5.1.49.jar /usr/local/hive/lib/
注意驱动版本要跟MySQL版本对应,版本不匹配时连接会报 Connection refused 或者 Unknown database 之类的错误。
4.2 Hive初始化与beeline实操
Hive版本用3.1.3,解压到 /usr/local/hive。修改 conf/hive-site.xml,填入关键配置:
xml复制<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://nw3:3306/hive?useSSL=false&useUnicode=true&characterEncoding=UTF-8</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hive</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>Hive@123</value>
</property>
<property>
<name>hive.metastore.uris</name>
<value>thrift://nw1:9083</value>
</property>
<property>
<name>hive.server2.thrift.bind.host</name>
<value>nw1</value>
</property>
</configuration>
hive.metastore.uris 让Hive客户端通过Thrift接口连接Metastore服务,这样可以保证多个客户端共用一个元数据服务。hive.server2.thrift.bind.host 设置的是HiveServer2监听地址,beeline连接就是往这个服务发SQL。
在nw1上初始化Metastore schema:
bash复制/usr/local/hive/bin/schematool -initSchema -dbType mysql
看到 “schemaTool completed” 就表示初始化成功。然后启动元数据服务和HiveServer2:
bash复制nohup /usr/local/hive/bin/hive --service metastore > /data/hadoop/logs/metastore.log 2>&1 &
nohup /usr/local/hive/bin/hive --service hiveserver2 > /data/hadoop/logs/hiveserver2.log 2>&1 &
启动后先别急着连beeline,先看一眼日志有没有报错,尤其是元数据连接这块,端口不通或者认证失败都会在这里暴露。日志没问题后,用beeline连接:
bash复制/usr/local/hive/bin/beeline -u jdbc:hive2://nw1:10000 -n hadoop
进去之后执行一条最简单的建表和查询,验证全链路:
sql复制CREATE TABLE test(id INT, name STRING);
INSERT INTO test VALUES (1, 'hello');
SELECT * FROM test;
第一次执行INSERT会被翻译成MapReduce任务,慢是正常的,说明Hive底层确实在调用集群算力,而不是本地跑。
5. 首个分布式任务与性能调优
5.1 跑通一个真正的分布式WordCount
Hive验证完之后,我们回到最原始的方式,用Hadoop自带的MapReduce示例跑一遍WordCount,这能直观看到数据如何被拆分、并发处理、汇总。
先在HDFS上建一个测试目录,并上传数据文件:
bash复制hdfs dfs -mkdir -p /input
echo "hadoop spark flink hadoop" > /tmp/test.txt
hdfs dfs -put /tmp/test.txt /input/
然后执行示例Jar包:
bash复制hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /input /output
执行期间,打开YARN的Web UI(http://nw1:8088),你会看到一个Application从 ACCEPTED 变成 RUNNING,最后变成 SUCCEEDED。这个过程解释了很多面试题,比如“MapReduce是怎么切分数据的”“为什么Map端输出要落本地磁盘”。
结束后查看结果:
bash复制hdfs dfs -cat /output/part-r-00000
看到每个单词出现的次数,说明整个集群链路已经是通的:HDFS负责存储,YARN负责调度,MapReduce负责计算,三块拼起来就是一个最小可用的大数据平台。
5.2 2核4G小集群的调优方向
小集群跑通简单任务不难,但要让它稳定运行、不频繁被OOM干掉,还是得调几个关键参数。
第一个是YARN内存参数。我在 yarn-site.xml 里已经设置了 yarn.nodemanager.resource.memory-mb=2048,每台节点给YARN的容器使用2G内存,剩下2G留给系统、DataNode、NodeManager和ZooKeeper。千万别把4G全给YARN,否则系统本身会卡死。
第二个是NameNode堆内存。默认情况下NameNode的堆内存大小在 hadoop-env.sh 里可以通过 HADOOP_NAMENODE_OPTS 控制。小集群数据量不大,给1G到1.5G足够,不用盲目调大。盲目调大反而会让抢占系统资源。
第三个是 dfs.replication。副本数我之前设成了2,对于三节点集群,这样可以保证任意一台DataNode挂掉之后数据依然完整。如果你的磁盘实在紧张,也可以设成1,但那样就失去了分布式副本机制的意义。
第四个是数据目录和日志的持续清理。轻量云系统盘只有60G,跑一段时间后HDFS日志、YARN日志、ZooKeeper数据、Hive日志会慢慢涨起来。我建议写一个简单的定时任务,定期清理日志和MySQL binlog。这是我在实战中体会到的最实际的一条经验,因为系统盘满了之后,所有写数据的操作都会失败,包括HDFS上传、Hive建表、YARN容器日志写入。
6. 常见问题排查与避坑记录
6.1 网络与Web UI访问问题
我在整个部署过程中排查最多的问题就是端口访问不了。现象是节点上HDFS命令能执行,但是浏览器访问 http://公网IP:9870 打不开页面。
排查顺序是:
- 确认HDFS进程是否正常,
jps看有没有NameNode进程。 - 确认系统防火墙是否放行:
firewall-cmd --list-all查看,或者直接systemctl stop firewalld验证。 - 确认云平台防火墙是否放行9870端口,这个在腾讯云轻量控制台的“防火墙”页面操作。
- 如果端口都放了,用
curl http://nw1:9870在本机试一下,确认服务本身是否监听。
我遇到的情况就是第3步:系统防火墙关了,但云平台防火墙没有放行,导致外部访问被拦。后来把9870、8088、9864、8042这批端口都加进云平台防火墙规则,问题才解决。
6.2 进程状态与任务失败排查
任务跑着跑着失败,也是家常便饭。我遇到最多的是下面几类,整理成一个速查表:
| 现象 | 排查方向 | 解决办法 |
|---|---|---|
| DataNode起不来,报Incompatible clusterIDs | 反复格式化导致集群ID不一致 | 清空DataNode数据目录,重新格式化 |
| YARN任务一直ACCEPTED不执行 | NodeManager内存过小或配置超限 | 调大 yarn.nodemanager.resource.memory-mb,关闭内存检查 |
| Hive连接无响应 | Metastore没起来或端口被防火墙拦 | 查看metastore日志,确认9083端口放行 |
| ZK选主失败 | myid配置不对或server地址解析错误 | 检查 myid 和 /etc/hosts |
| JournalNode启动失败 | edits目录权限不足 | chown -R hadoop:hadoop /data/hadoop/journaldata |
| HDFS写文件报空间不足 | 系统盘满了 | 清理HDFS垃圾回收站、日志和本地临时文件 |
有一个比较容易忽略的问题是时钟同步。HA模式下,NameNode和JournalNode对时间同步比较敏感,如果三台机器时间差太大,ZooKeeper选举和QJM写入都会出现诡异问题。轻量云一般都默认开了NTP同步,但你还是可以用 date -s 手动校准一下,或者安装chrony做时间同步,这是生产环境的标准操作。
6.3 我这次踩过的坑
这里整理几个特别值得说的小教训。
第一个坑是格式化顺序。我第一次搭的时候,在格式化NameNode之前就启动了 start-dfs.sh,结果JournalNode连不上,NameNode启动失败。后来才明白,QJM依赖JournalNode先就绪,而格式化NameNode之前就必须启动好JournalNode。顺序这个事,真的要严格遵守。
第二个坑是Hive的schema初始化必现utf8mb4索引超长问题。我在MySQL里建库时用了 utf8mb4,然后执行 schematool -initSchema 时报错“Specified key was too long”。这是因为Hive元数据里很多表字段是varchar(255)加唯一索引,utf8mb4 每个字符占4字节,超过了索引键长度限制。当时我改的方案是把数据库字符集改成 utf8,然后重新初始化schema,问题就解决了。如果你非要保留 utf8mb4,可以调小相关字段长度,但太麻烦,学习环境没必要。
第三个坑是忘了给Hive的元数据服务设置时区。Metastore连接MySQL时,如果连接串里不加 serverTimezone=Asia/Shanghai,MySQL 8.0可能会报时区相关的错误。为了避免麻烦,当时我换成MySQL 5.7,并且连接串加了 useSSL=false 和 characterEncoding=UTF-8,之后就没有再被这类问题卡过。
第四个坑是日志太多导致系统盘爆满。YARN的应用程序日志默认存很多,再加上HDFS的DataNode日志,60G很快就满了。我当时清理的办法是:
- 清理YARN日志:
rm -rf /usr/local/hadoop/logs/* - 清理临时文件:
hdfs dfs -rm -r /tmp或者配置更短的垃圾回收时间 - 关闭或限制Hive、Metastore的INFO级日志
我还会写一个crontab每周清一次。这个操作看起来不起眼,但在长期使用集群的过程中非常关键。
这些坑每一条我都付过“学费”,写出来就是希望你能直接绕过去。整个集群搭建到这里,你手里已经有一套完整可用的学习环境了:HDFS分布式存储、YARN资源调度、ZooKeeper协调、Hive SQL分析都齐了。接下来最值得做的事情,是往这套集群里灌一些真实数据,比如爬点商品数据、日志数据,用它跑一个迷你数仓项目,把分层建模、ETL、指标统计完整走一遍。只有反复在这样的真实环境里折腾,面试时聊起大数据项目才不会心虚。
