四节点云主机+对象存储桶:Hadoop与Spark大数据实验全链路
简介:围绕华为云大数据实验形成的docx报告,面向云计算与大数据方向的学习者及高校实验课备查人群,聚焦在云上完成集群部署、云服务配置与交互式查询分析的完整实验链路。文档共1个docx文件,压缩包约4.36MB,以图文实验记录和目录化章节组织,目录含环境准备、Hadoop与Spark集群搭建、OBS互联测试等模块,便于按实验环节快速检索。正文覆盖云主机搭建Hadoop集群、利用云服务实现大数据处理、BigData Pro一站式方案实践,以及基于Spark SQL或Flink SQL的交互式查询分析,涉及ECS、OBS、访问密钥、节点通信等关键知识点。已有2607人学习,适合需要对照实验流程、理解云上大数据平台搭建思路、复盘配置与排错要点的读者参考。
1. 四台云主机加一个对象存储桶:这份 big data 实验报告的主线是什么
很多人第一次做云上大数据实验,卡住的地方不是 Hadoop 本身,而是买机器、开桶、配密钥这些"外围"动作——真正把集群跑起来之后回头看,八成时间其实花在了环境上。这份大数据实验报告的价值就在这儿:它把一条完整链路原样记录了下来,买 ECS(弹性云服务器)、建对象存储桶、四节点做 SSH 互信、装 Hadoop 并把存储指向对象存储、再叠一层 Spark 跑交互式查询,中间用 WordCount 做最小验证。内容按四个实验组织:云主机实现大数据、云服务实现大数据、BigData Pro 综合实验、交互式查询,主线是同一套集群从零到跑通。它适合两类人:一是要交课程实验、需要照着一步步复现的同学;二是刚接触云上集群、想把"手工搭一遍"这课补上的数据开发或后端。下面按我拆解的顺序讲,能抄的地方给命令,容易翻车的地方单独拎出来说。
2. 开实验前的环境账:四节点拓扑、ECS 规格与对象存储桶怎么定
动手之前先把账算清楚,比事后改规格省事得多。这套实验的最小可行形态是"1 台管理节点 + 3 台数据节点",四台机器同 VPC、同子网、内网互通,全部装同版本系统镜像。我见过太多人四台机器分别买、分别选镜像,结果一台是 CentOS 一台是 Ubuntu,后面 JDK 路径、包管理器、防火墙命令全对不上,光对齐环境就耗掉半天。规格这块别抠:管理节点跑 NameNode、ResourceManager 和 Spark 的 Driver,内存给足;数据节点跑 DataNode、NodeManager、Executor,CPU 和磁盘 IO 更关键。课程实验的数据量一般就几十兆到几 GB,用不着上高配,但 1C2G 的入门款一定会让你在跑 Spark SQL 的时候等到怀疑人生。
2.1 四节点拓扑与规格选型
节点命名沿用实验里的 node1~node4 最省心,报告、文档、命令里的引用都能对上。node1 兼做管理节点,node2~node4 做数据节点,四台全部加入同一个安全组,内网全通。下表是我一般会给学生/同事开的配置,按课程实验的数据量留了余量:
| 节点 | 角色 | 建议规格 | 系统盘 | 说明 |
|---|---|---|---|---|
| node1 | NameNode / ResourceManager / Spark Driver | 4 vCPU / 8 GB | 40 GB 高 IO | 元数据与调度压力集中在这台 |
| node2 | DataNode / NodeManager | 2 vCPU / 4 GB | 40 GB 高 IO | 可挂数据盘,实验阶段非必需 |
| node3 | DataNode / NodeManager | 2 vCPU / 4 GB | 40 GB 高 IO | 同上 |
| node4 | DataNode / NodeManager | 2 vCPU / 4 GB | 40 GB 高 IO | 同上 |
四台机器必须选同一可用区、同一 VPC 和子网。跨可用区虽然内网也通,但走的是可用区之间的链路,延迟和带宽都不是一个量级,HDFS 写副本时那种"说不上哪里慢但就是慢"的玄学体验,多半来源于此。购买时把登录方式设成密钥对或者统一密码,别一台密钥一台密码,后面批量分发脚本会难受。
买完第一件事不是装软件,是改名和写 hosts。四台机器分别执行:
# 每台机器改成自己的主机名,node1 上执行就是 node1 hostnamectl set-hostname node1 # 四台机器写入完全一致的 hosts 内容 cat >> /etc/hosts <<'EOF' 10.0.0.11 node1 10.0.0.12 node2 10.0.0.13 node3 10.0.0.14 node4 EOF这里的 IP 换成你自己实例的内网地址,不要用公网 IP。Hadoop 各组件之间通信用的是内网,写公网 IP 会额外产生流量费用,而且公网 IP 重启后可能变化,hosts 一失效整个集群就散架。改完在每台机器上ping node2、ping node3各打一次,四台都能通再往下走。这一步看着啰嗦,但它是后面所有配置的地基,hosts 不一致导致 NameNode 起不来,是这套实验里排前三的翻车原因。
2.2 对象存储桶与访问密钥的准备
实验二和综合实验一的核心是"云服务实现大数据",落到操作上就是让 Hadoop 和 Spark 能直接读写对象存储,而不是只会在本地磁盘上玩。这里要准备三样东西:桶、Endpoint、访问密钥。
桶的创建注意两点。一是区域必须和 ECS 一致,桶在 A 区、机器在 B 区,跨区访问的延迟会让你误以为是集群配置有问题;二是桶名全局唯一,起名带个随机后缀,别用test、data这种,会提示已占用。标准桶之外,如果实验里要求"并行文件系统",那是另一种形态,语义更接近 POSIX 文件系统,适合 Spark 直接当输出目录用,创建入口和普通桶不在一个地方,别找错。创建完成后控制台会给两个 Endpoint:普通地址和双栈地址,IPv4 环境用前者就行。
访问密钥在"我的凭证"里生成,下载下来的 credentials.csv 里三列分别是用户名、Access Key Id、Secret Access Key。这个文件泄露等于云账号泄露,我一般的处理方式是:下载后立刻把内容导进环境变量,然后把 csv 从本机删掉,绝不写进任何会提交到仓库的配置文件。导入方式:
# 写入当前 shell 会话,不要写进 ~/.bashrc 的明文里 export OBS_AK='你的AccessKeyId' export OBS_SK='你的SecretAccessKey' export OBS_ENDPOINT='obs.cn-north-4.myhuaweicloud.com' # 建议同时写到权限 600 的独立文件,供服务读取 umask 077 cat > ~/.obs_env <<EOF OBS_AK=$OBS_AK OBS_SK=$OBS_SK OBS_ENDPOINT=$OBS_ENDPOINT EOF参数说明:OBS_AK/OBS_SK是身份凭证,Hadoop 的 OBS 连接器在core-site.xml里可以直接引用这两个环境变量,避免把密钥硬编码进 XML;OBS_ENDPOINT决定请求打到哪个区域,写错区域会返回 403 而不是 404,这个报错很容易误导人往权限方向查。如果你用的云厂商是 S3 兼容接口,对应的是fs.s3a.access.key、fs.s3a.secret.key、fs.s3a.endpoint三个键,逻辑完全一样,只是前缀不同。
顺便说一个血泪经验:密钥是有权限范围的,创建时如果只给了只读策略,后面 Spark 往桶里写结果会直接失败。实验场景下给的策略至少覆盖桶内对象的读写和列举,别图省事只挂一个ReadOnlyAccess。
2.3 安全组与网络连通性基线
云上的机器默认是被安全组挡住的,这一点和本地虚拟机完全不同,也是新手最容易忽略的地方。四台机器在同一个安全组里,规则这样配:
| 方向 | 协议端口 | 源/目的 | 用途 |
|---|---|---|---|
| 入方向 | TCP 22 | 你的公网出口 IP | SSH 登录,别开 0.0.0.0/0 |
| 入方向 | 全部 | 安全组自身 | 节点间 RPC、数据传输 |
| 出方向 | 全部 | 0.0.0.0/0 | 下载安装包、访问对象存储 |
第二条是关键:源写"安全组 ID"而不是 CIDR,这样组内所有实例互相全通,后面 9000、9870、8088、7077、8032 这些端口一个都不用单独开。有人图快直接开全通 0.0.0.0/0,短期能跑通,但等于把 NameNode 的 Web UI 和 RPC 端口挂到公网上,属于典型的安全债。
系统层面的防火墙也统一关掉。实验环境里没必要折腾 firewalld 规则,四台机器执行:
# CentOS / 欧拉系 systemctl stop firewalld && systemctl disable firewalld # Ubuntu / Debian 系 systemctl stop ufw && systemctl disable ufw # 确认状态 systemctl status firewalld 2>/dev/null || systemctl status ufw关完用telnet node1 22、nc -zv node2 22这类命令从其他节点探一次,通了再开始装软件。这一步的价值在于把"网络问题"和"配置问题"提前隔离开——后面 Hadoop 起不来的时候,你就可以放心地不去怀疑网络了。
3. Hadoop 集群搭建:从 hosts 到 SSH 互信的可复现步骤
环境铺好之后,Hadoop 这一段其实是整份报告里最标准化的部分,步骤固定、顺序固定,只要不跳步,成功率很高。它分四层:基础运行时(JDK)、节点间信任(SSH 免密)、配置分发、启动验证。很多人失败不是某一步做错了,而是四台机器之间状态不同步——node1 装了 JDK,node3 没装;node2 的 workers 文件里少写了一台。所以这一段的正确姿势是:每一步都四台一起做,或者只在一台做然后分发。
3.1 基础环境:JDK、防火墙与时间同步
Hadoop 3.x 和 Spark 3.x 这个组合,JDK 用 8 是最稳的,别上 11 和 17 给自己找麻烦,某些模块的反射调用在新版本上会直接报InaccessibleObjectException。四台机器统一装 OpenJDK 8,用系统包管理器最省事:
# CentOS / 欧拉系 yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel # Ubuntu 系 apt-get update && apt-get install -y openjdk-8-jdk # 验证并定位安装路径 java -version readlink -f $(which java)readlink的输出形如/usr/lib/jvm/java-1.8.0-openjdk-xxx/jre/bin/java,去掉末尾的/jre/bin/java就是JAVA_HOME。四台机器路径要一致,不一致的话后面分发hadoop-env.sh时会出现部分节点起不来的情况。写完环境变量记得验证:
cat >> /etc/profile.d/bigdata.sh <<'EOF' export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk export HADOOP_HOME=/opt/hadoop export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin EOF source /etc/profile.d/bigdata.sh echo $JAVA_HOME时间同步这条容易被跳过,但它是分布式系统的隐性依赖。四台机器时间差超过几十秒,Kerberos(如果开了)会直接拒绝认证,YARN 的任务时间线也会乱。云主机一般自带 NTP 服务,检查一下timedatectl输出的System clock synchronized是不是yes,是就不用管。
3.2 四节点 SSH 互信:一次配好而不是来回补
Hadoop 的启动脚本靠 SSH 去各节点拉进程,所以 node1 必须能免密登录 node2~node4,被拉起的节点之间有时也需要互访。正确的做法是四台机器互相两两免密,也就是 12 条信任关系,一次配完。实操上有个简化技巧:先把四台的公钥收集到 node1,合并成一份authorized_keys,再分发回四台,这样一次操作就完成了全互联。
# 四台机器各自生成密钥对,一路回车,不设密码 ssh-keygen -t rsa -b 4096 -N '' -f ~/.ssh/id_rsa # node1 上收集四台的公钥(首次需要输入密码) for h in node2 node3 node4; do ssh-copy-id -i ~/.ssh/id_rsa.pub root@$h done # 在 node1 上把四台的公钥合并成一份 cat ~/.ssh/id_rsa.pub > /tmp/all_keys for h in node2 node3 node4; do ssh root@$h 'cat ~/.ssh/id_rsa.pub' >> /tmp/all_keys done # 分发到四台,权限必须是 600 for h in node1 node2 node3 node4; do scp /tmp/all_keys root@$h:/root/.ssh/authorized_keys ssh root@$h 'chmod 600 ~/.ssh/authorized_keys && chmod 700 ~/.ssh' done参数说明:-N ''表示空密码,免密登录的前提就是私钥无密码;-b 4096是密钥长度,默认 2048 也能用,但既然是一次性动作,加大没成本;chmod 600这步不能省,SSH 对authorized_keys的权限极其敏感,权限过宽会直接拒绝使用并报Authentication refused: bad ownership or modes——这个报错看着像密钥错了,其实是权限错了,是典型的黑匣子式误导。
验证方式别只看一台,四台两两互跳一遍:
# 在每台机器上执行,确认能无密码跳到其余三台 for h in node1 node2 node3 node4; do ssh -o BatchMode=yes -o ConnectTimeout=3 root@$h 'echo ok' || echo "FAIL: $h" doneBatchMode=yes的作用是禁止交互式输入密码,一旦需要密码就直接失败而不是卡在那里等你敲。四台全输出ok才算过,有一个 FAIL 就先去修那一台,别带着问题往下走——Hadoop 启动时只会告诉你"某个节点没起来",不会告诉你是 SSH 的问题。
3.3 四个核心配置文件与对象存储对接
Hadoop 的配置集中在$HADOOP_HOME/etc/hadoop下,真正要改的就四个文件加一个 workers。先看core-site.xml,这里决定了默认文件系统和对象存储的接入参数:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node1:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop/tmp</value> </property> <property> <name>fs.obs.endpoint</name> <value>obs.cn-north-4.myhuaweicloud.com</value> </property> <property> <name>fs.obs.access.key</name> <value>${env.OBS_AK}</value> </property> <property> <name>fs.obs.secret.key</name> <value>${env.OBS_SK}</value> </property> <property> <name>fs.obs.impl</name> <value>org.apache.hadoop.fs.obs.OBSFileSystem</value> </property> </configuration>fs.defaultFS指向 node1 的 9000 端口,这是 HDFS 的 RPC 入口,hostname 必须和 hosts 里的一致,写成 IP 也能跑,但换了实例就废了。hadoop.tmp.dir一定要显式指定,默认落在/tmp下,云主机重启或者系统清理临时目录时,元数据直接没了,集群需要重新格式化——这是很多人"明明昨天还好好的"的真正原因。后面四个fs.obs.*是对象存储连接器的配置,${env.OBS_AK}这种写法让 XML 里不出现明文密钥,前提是启动 Hadoop 的那个 shell 里有这两个环境变量;如果你是用 systemd 或者别的守护方式启动,得确认环境变量传进去了,否则会报Access Key Id is null。
hdfs-site.xml相对简单,重点是副本数和 NameNode 的元数据目录:
<configuration> <property> <name>dfs.replication</name> <value>3</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/hadoop/data/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/hadoop/data/data</value> </property> </configuration>副本数 3 对四节点来说合理,但实验阶段如果数据节点只有 3 台,副本数等于 3 意味着每份数据全量复制,磁盘压力不小。课程实验里我一般会调到 2,够用且省空间,报告里写明理由就行。dfs.namenode.name.dir千万别留在默认位置,理由同上。
mapred-site.xml和yarn-site.xml把计算框架指向 YARN:
<!-- mapred-site.xml --> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>yarn.app.mapreduce.am.env</name> <value>HADOOP_MAPRED_HOME=/opt/hadoop</value> </property> <property> <name>mapreduce.map.env</name> <value>HADOOP_MAPRED_HOME=/opt/hadoop</value> </property> <property> <name>mapreduce.reduce.env</name> <value>HADOOP_MAPRED_HOME=/opt/hadoop</value> </property> </configuration>那三个HADOOP_MAPRED_HOME是新手最容易漏的。不写的话,YARN 容器里的任务找不到 Hadoop 的类路径,报错信息是Could not find or load main class org.apache.hadoop.mapreduce.v2.app.MRAppMaster,看起来像安装包损坏,实际只是环境变量没传进去。
yarn-site.xml配 ResourceManager 地址和 NodeManager 的辅助服务:
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>node1</value> </property> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>3072</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>3072</value> </property> </configuration>内存参数按数据节点的实际规格填。4 GB 的机器上,NodeManager 可用内存给 3 GB 左右是合理的,留 1 GB 给系统和 DataNode。这里有个隐藏约束:yarn.nodemanager.resource.memory-mb必须大于等于yarn.scheduler.maximum-allocation-mb,否则提交任务时会报Invalid resource request,而且报错信息完全没提这两个参数的关系,属于典型的排查陷阱。
最后是workers文件(老版本叫 slaves),只写数据节点,一行一个:
cat > $HADOOP_HOME/etc/hadoop/workers <<'EOF' node2 node3 node4 EOF注意 node1 不写进去。它虽然是管理节点,但启动脚本会按这个文件去拉 DataNode 进程,把 node1 放进去也能跑,但角色就混了,排错时容易搞不清哪个进程在哪台机器上。
配置改完后,把整个$HADOOP_HOME分发到其余三台:
for h in node2 node3 node4; do rsync -av --delete $HADOOP_HOME/ root@$h:$HADOOP_HOME/ done用rsync --delete而不是scp,是因为配置改动往往是"删一个属性加一个属性",scp只覆盖不删除,改错了旧配置会残留。分发完在四台上分别hadoop version验一下,能打出 Hadoop 版本号说明环境变量和安装包都没问题。
3.4 格式化、启动与 WordCount 验证
格式化只在第一次做,而且只能做一次:
# 仅 node1 执行,且只执行一次 hdfs namenode -format格式化会在dfs.namenode.name.dir下生成一个current/VERSION文件,里面有clusterID。所有 DataNode 的 clusterID 必须和 NameNode 一致,如果重复格式化,NameNode 会生成新的 clusterID,而 DataNode 还拿着旧的,启动后 DataNode 就注册不上,Web UI 上活节点数一直是 0。这是本实验最经典的翻车点,后面避坑章节单独说。
启动集群:
# node1 上执行,脚本会按 workers 文件去拉进程 start-dfs.sh start-yarn.sh # 四台都跑一下 jps,看看进程齐不齐 jpsnode1 上应该看到NameNode、ResourceManager、SecondaryNameNode,node2~node4 上应该看到DataNode、NodeManager。少哪个就去对应节点的日志目录$HADOOP_HOME/logs里翻*.log,别只看终端输出,终端只打印一句starting datanode, logging to ...就没下文了。
验证分两层,先看 HDFS,再跑 WordCount:
# 建目录、上传本地文件、列出来 hdfs dfs -mkdir -p /user/root/input hdfs dfs -put $HADOOP_HOME/etc/hadoop/*.xml /user/root/input hdfs dfs -ls /user/root/input # 跑示例程序 hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar \ wordcount /user/root/input /user/root/output # 看结果 hdfs dfs -cat /user/root/output/part-r-00000 | head -20参数说明:wordcount后面跟两个路径,前者是输入目录(会遍历目录下所有文件),后者是输出目录,输出目录必须不存在,存在会直接抛FileAlreadyExistsException。这个设计是防止覆盖,但很多人反复跑测试时忘了删,看到报错以为程序坏了。删掉重跑一次就通了:hdfs dfs -rm -r /user/root/output。
WordCount 能出结果,说明 HDFS 读写、YARN 调度、MapReduce 运行链路全通,实验一的核心部分就算完成。这一条命令的价值在于它是端到端的,任何一环有问题都会失败,比单独去 ping 组件可靠得多。
4. Spark 集群接入:让计算层跑在已有存储之上
实验四要的是"大数据交互式查询",落到技术上就是 Spark SQL。这一段的定位要清楚:Hadoop 那套是存储加批处理,Spark 是往上叠的计算层,它复用 HDFS 做存储、复用 YARN 做资源调度,所以不需要重新搭一套分布式存储。理解这一点,配置就简单了——Spark 要做的只有两件事:找到 Hadoop 的配置,找到 YARN 的 ResourceManager。
4.1 版本配对与安装包选择
Spark 的安装包有多个预编译版本,下载页面上会标注Pre-built for Apache Hadoop 3.3 and later这类字样。选的时候记住一条:Spark 的 Hadoop 预编译版本要和实际装的 Hadoop 大版本对得上。Hadoop 装的是 3.x,就选 pre-built for Hadoop 3.x;选错了不会立刻报错,而是在读写 HDFS 的时候抛NoSuchMethodError或者ClassNotFoundException,报错栈里全是org.apache.hadoop.*,看起来像 Hadoop 装坏了,实际是版本不匹配。
下载解压到/opt/spark,然后配环境变量:
tar -zxvf spark-3.x.x-bin-hadoop3.tgz -C /opt ln -s /opt/spark-3.x.x-bin-hadoop3 /opt/spark cat >> /etc/profile.d/bigdata.sh <<'EOF' export SPARK_HOME=/opt/spark export PATH=$PATH:$SPARK_HOME/bin export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop export YARN_CONF_DIR=$HADOOP_HOME/etc/hadoop EOF source /etc/profile.d/bigdata.shHADOOP_CONF_DIR和YARN_CONF_DIR这两个变量是接入的关键,它们让 Spark 直接读 Hadoop 的core-site.xml和yarn-site.xml,省去了在 Spark 里重复配一遍 HDFS 地址和 ResourceManager 地址。漏配的表现是 Spark 启动后默认用本地文件系统,sc.textFile("hdfs://...")会报连接不上,或者提交到 YARN 时报Cannot initialize Cluster。
Spark 有三种运行模式:local、standalone、on YARN。课程实验场景下我建议直接用on YARN,原因是集群资源已经由 YARN 统一管理了,再起一套 standalone 的 Master/Worker 是重复建设,而且两套调度器抢内存,容易出莫名其妙的 OOM。用 on YARN,四台机器不用额外起任何 Spark 守护进程,提交任务时由 YARN 分配容器,干净。
4.2 spark-defaults 关键参数与提交姿势
$SPARK_HOME/conf下把spark-defaults.conf.template复制成spark-defaults.conf,填几个关键项:
spark.master yarn spark.submit.deployMode client spark.yarn.jars hdfs://node1:9000/spark-jars/* spark.sql.warehouse.dir hdfs://node1:9000/user/hive/warehouse spark.serializer org.apache.spark.serializer.KryoSerializer spark.driver.memory 1g spark.executor.memory 1g spark.executor.cores 1逐个说。spark.master固定成yarn,省得每次提交都敲--master;deployMode用client,Driver 跑在提交任务的机器上,日志直接打在终端,调试阶段最方便,等跑批的时候再改cluster。spark.yarn.jars这条强烈建议配上:不配的话,每次提交任务 Spark 都要把几百兆的 jar 包从本地传到 HDFS 的临时目录,第一次跑能等十分钟,而且日志里一堆上传进度,看着像卡死。正确做法是先把 jar 传上去:
hdfs dfs -mkdir -p /spark-jars hdfs dfs -put $SPARK_HOME/jars/*.jar /spark-jars/传完之后再提交任务,启动时间能从几分钟降到十几秒,这个差别在反复调试时非常明显。
内存参数按数据节点规格调。4 GB 的机器给executor.memory 1g加driver.memory 1g,再留一部分给 NodeManager 自己,刚好。executor.cores 1是保守值,实际可以到 2,但要保证executor 数量 × cores ≤ 节点总 vCPU,超了 YARN 会排队,表现为任务一直ACCEPTED不进入RUNNING,很多人误以为是 Spark 卡住了。
提交一个最简单的任务验证链路:
spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode client \ $SPARK_HOME/examples/jars/spark-examples_*.jar \ 10输出里能看到Pi is roughly 3.14...就说明 Spark on YARN 通了。这里的10是切片数,数值越大算得越久,验证用 10 就够,别用示例里的 100,白等。
4.3 Spark SQL 交互式查询跑通
综合实验四的交互式查询,本质是起一个spark-sql或者spark-shell会话,建表、灌数据、写 SQL、看结果。用spark-sql最直观,因为它就是一个 SQL 命令行:
spark-sql --master yarn --deploy-mode client进去之后建表、插数据、查询:
-- 建一张外部表,数据落在 HDFS 上 CREATE EXTERNAL TABLE IF NOT EXISTS sales ( dt STRING, region STRING, amount DOUBLE ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE LOCATION 'hdfs://node1:9000/user/root/warehouse/sales'; -- 灌几条数据进去(也可以提前用 hdfs dfs -put 上传 csv) INSERT INTO sales VALUES ('2024-01-01', 'north', 1200.5), ('2024-01-01', 'south', 980.0), ('2024-01-02', 'north', 1500.0); -- 按区域聚合 SELECT region, SUM(amount) AS total FROM sales GROUP BY region ORDER BY total DESC;几个要点。LOCATION指向 HDFS 路径,这样表是外部表,删表不删数据,反复实验时不用重灌。ROW FORMAT DELIMITED指定分隔符,csv 用逗号,制表符用\t,写错了查出来全是 NULL,而且不报错。STORED AS TEXTFILE是纯文本格式,实验够用;如果数据量大,换成PARQUET能快好几倍,但这属于进阶,课程实验阶段没必要。
如果想直接查对象存储里的数据,把LOCATION换成obs://你的桶名/sales,前提是core-site.xml里的fs.obs.*配好了、OBS_AK/OBS_SK在当前 shell 里。这条路径跑通,实验二"云服务实现大数据"和综合实验一"BigData Pro"的存储部分就算串起来了——同一份数据,HDFS 上能查,对象存储上也能查,这就是云上大数据和纯本地集群最大的区别。
再补一个实用技巧:spark-sql会话里执行SET spark.sql.shuffle.partitions=8;把 shuffle 分区数从默认的 200 降下来。四节点的小集群跑 200 个分区,每个分区几 KB 数据,光是调度开销就比计算本身大,看着任务跑了五分钟其实四分钟在起任务。
5. 避坑清单:这套实验里五处必翻车的地方和排查路径
下面这五条,是我带人做这套实验时出现频率最高的,每一条都按"看到什么现象 → 为什么 → 怎么修"来写,遇到问题时直接对号入座。
5.1 SSH 配了免密却还要输密码
现象:ssh node2仍然提示输入密码,或者直接Permission denied (publickey)。
原因:三种可能。一是~/.ssh/authorized_keys权限不是 600,SSH 出于安全考虑直接忽略它;二是公钥粘贴时被换行截断,ssh-rsa AAAA...中间断了;三是 SELinux 开着,.ssh目录的上下文不对。
解决:先ls -ld ~/.ssh ~/.ssh/authorized_keys看权限,不对就chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys。还不行就用ssh -vvv node2看详细日志,日志里会明确写出Authentication refused: bad ownership or modes for file。SELinux 的话setenforce 0临时关掉验证,确认是它再改配置文件。
5.2 NameNode 起来了,DataNode 一个都不注册
现象:jps能看到DataNode进程,但 HDFS Web UI 上Live Nodes是 0,上传文件报could only be replicated to 0 nodes。
原因:几乎都是 clusterID 不一致。重复执行了hdfs namenode -format,NameNode 生成了新的 clusterID,DataNode 本地dfs.datanode.data.dir/current/VERSION里还是旧的,握手时被拒绝。
解决:停掉集群,把每台 DataNode 上的dfs.datanode.data.dir和 NameNode 上的dfs.namenode.name.dir全部清空,然后只格式化一次,再启动。清空之前确认里面没有要留的数据。这个坑的预防办法是:格式化之前先用hdfs dfs -ls /探一下,能列出来就说明已经格式化过了,别再来一次。
5.3 访问对象存储报 403
现象:hdfs dfs -ls obs://桶名/返回AccessDenied或者403 Forbidden。
原因:按可能性排序——AK/SK 配错或环境变量没传进进程;桶的区域和 endpoint 对不上;密钥对应的账号没有该桶的读权限;桶策略限制了来源 IP。
解决:先在 shell 里echo $OBS_AK确认变量存在,再用hdfs dfs -Dfs.obs.access.key=xxx -Dfs.obs.secret.key=yyy -ls obs://桶名/显式传一次,能通就说明是环境变量没传进去(常见于 systemd 启动的服务)。endpoint 用控制台上给的那个,别自己拼。权限问题去桶的权限管理里看策略,实验阶段给到桶内对象读写就行。
5.4 任务一直 ACCEPTED,不进入 RUNNING
现象:yarn application -list里状态一直是ACCEPTED,Spark 或 MapReduce 任务卡住不动,日志里没有任何报错。
原因:资源不够,YARN 分配不出容器。典型情况是前面失败的 application 没释放资源,或者executor.memory × 数量超过了yarn.nodemanager.resource.memory-mb。
解决:先yarn application -list -appStates ALL看有没有残留的 RUNNING 任务,有就yarn application -kill <appId>。然后核对yarn-site.xml里的内存参数和提交时传的--executor-memory,保证单个 executor 的内存不超过节点可用内存,总数不超过集群总内存。四节点 4 GB 的机器,同时跑两个 1 GB executor 是安全的。
5.5 WordCount 报输出目录已存在
现象:org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory hdfs://node1:9000/user/root/output already exists。
原因:不是错误,是 Hadoop 的保护机制,防止覆盖已有结果。反复测试时最容易撞上。
解决:hdfs dfs -rm -r /user/root/output之后再跑。如果想每次都自动清理,可以在脚本里把删除和提交写在一起,但别养成无脑-rm -r /的习惯,路径写错一次就够后悔的。
6. 把环境做成可复用资产:快照、自检脚本与一次踩坑换来的习惯
这套实验搭一遍大概要两三个小时,但如果每次重装都从头来,时间全浪费在重复劳动上。我的做法是搭完并且验证通过之后,立刻做两件事:给 node1 做系统盘快照、把四台的配置文件打成模板。快照的作用是后悔药——后面装 Spark、改 YARN 参数、试各种实验扩展,改崩了直接回滚到"已知可用"的状态,比逐条排查快得多。配置文件模板则是把core-site.xml、hdfs-site.xml、yarn-site.xml、mapred-site.xml、spark-defaults.conf这五个文件抽出来放进一个 git 仓库,密钥字段全部替换成${env.OBS_AK}这种占位符,下次开新环境直接rsync过去,十分钟能起一套。
比快照更值钱的是一份自检脚本。环境出问题时,人容易陷入"东看一点西看一点"的状态,有个脚本按固定顺序扫一遍,能把问题范围迅速缩小。我一般会写这样一份:
#!/bin/bash # cluster_check.sh - 集群健康自检,在 node1 上执行 set -u PASS=0; FAIL=0 check() { local name=$1; shift if "$@" >/dev/null 2>&1; then echo "[ OK ] $name"; PASS=$((PASS+1)) else echo "[FAIL] $name"; FAIL=$((FAIL+1)) fi } # 1. 四台机器 SSH 免密 for h in node2 node3 node4; do check "ssh to $h" ssh -o BatchMode=yes -o ConnectTimeout=3 root@$h true done # 2. hosts 解析一致性 for h in node1 node2 node3 node4; do check "resolve $h" getent hosts $h done # 3. 关键进程 check "NameNode" jps -l | grep -q NameNode check "DataNode" ssh -o BatchMode=yes node2 "jps -l | grep -q DataNode" # 4. HDFS 可写 check "HDFS write" hdfs dfs -touchz /tmp/_health_check hdfs dfs -rm -f /tmp/_health_check >/dev/null 2>&1 # 5. YARN 有活跃节点 check "YARN nodes" bash -c 'yarn node -list 2>/dev/null | grep -qE "RUNNING|Node-Id"' # 6. 对象存储可访问(需要 OBS_AK/OBS_SK 已导出) check "OBS access" hdfs dfs -ls "obs://${OBS_BUCKET:-your-bucket}/" echo "----" echo "PASS=$PASS FAIL=$FAIL" [ "$FAIL" -eq 0 ] || exit 1脚本里check函数把每条检查封装成"命令成功就算过",好处是新增检查项只要加一行,不用改输出逻辑。第 4 项写文件再删掉,是验证 HDFS 真的可写而不只是能连上——这两种状态在副本不足的时候会不一致,能连上但写不进去,很多人排查半天以为是权限问题,其实是 DataNode 副本数不够。第 6 项依赖环境变量,跑之前source ~/.obs_env一下。
从那以后我每次改完集群配置,都强制走一遍这个脚本再加一次 WordCount,两个都过了才算改完。有次图省事跳过了,改完yarn-site.xml直接去跑 Spark SQL,结果卡在 ACCEPTED 十几分钟,回头一看是内存参数改小了导致容器分配不出来——如果先跑一遍自检,第二条就会亮红灯。这套习惯说到底就一句话:别相信"应该没问题",让命令告诉你有没有问题。
搭完之后你会发现,真正难的从来不是某条命令,而是把四台机器的状态始终对齐。把环境做成快照、把配置做成模板、把检查做成脚本,这三件事做完,后面换区域、换规格、加节点,都是照着填空。希望帮到你。
本文还有配套的精品资源,点击获取