简介:这是一份面向大数据学习与毕设演示的基础镜像组件包,整合Hadoop、Spark、Hive、Tez、Hue及Kafka等常用组件,帮助读者在Docker环境中快速搭建可运行的大数据集群环境。资源共22个文件,压缩包仅34KB,以10个shell脚本为主,覆盖集群初始化、服务启动、MySQL初始化等关键环节;另有7个XML配置用于调整Hive/Tez/Spark等组件参数,并附带Dockerfile与properties配置,便于定制基础镜像。这些脚本与配置可大幅减少手工安装和排查时间,整体按hadoop、spark、hive、tez、hue等分目录组织,层级清晰;作者已测试运行成功,适用于计科、人工智能、通信工程等专业学生作为课程设计、毕业设计或项目前期演示的参考模板。目前已有141人学习,随包提供README文档说明各组件的启动顺序与验证思路,读者可结合自身环境扩展组件或修改配置,快速收获一套可落地的大数据环境搭建方案。
1. 大数据基础镜像到底解决了什么:从零装三天到一条命令拉起来
先讲一个大多数人都会撞上的场景:你拿到一台新服务器,要搭一套能跑 Hive SQL、能提 Spark 作业、能开 Hue 看结果的环境。按各路教程装,Hadoop 装好了 HDFS 起来了,接着装 Spark,提交作业时发现它连不上 Hive 的 metastore;再把 Hive 的 hive-site.xml 指过去,Hue 又找不到 HiveServer2。来回改配置、重启服务,两天就没了。这种翻车几乎都出在同一个地方:组件是分别装的,配置是各自写的,互相之间没有对齐。
大数据基础镜像组件就是把 Hadoop、Spark、Hive、Tez、Hue 这几个经常要一起用的东西,做进同一个 Docker 镜像里。镜像里不只是装了软件,还预先写好了组件之间的连接关系、启动顺序、目录规划,拉起来就有一套互相能认的基础环境。你再做项目时不用从零搭,直接在这套镜像上扩展,省掉的是反复装环境的时间。
下面我会把这个镜像怎么设计、怎么构建、每个组件的关键参数怎么配、哪些坑是必踩的,完整拆一遍。新手能照着复现,熟手可以拿走我踩过的坑当参考。
2. 先把组件关系理清:Hadoop、Spark、Hive、Tez、Hue 在镜像里各扮演什么角色
2.1 五个组件在架构里的位置和依赖边界
很多人在搭这套环境时,第一个误区是不知道这五个组件不是平级关系。Hadoop 是最底层的存储和资源调度平台,HDFS 管存储,YARN 管资源;Spark 和 Hive 都是跑在 YARN 之上的计算框架;Tez 是 Hive 的替代执行引擎;Hue 是 Web 可视化层,它不直接连 HDFS,而是连 HiveServer2 去提交 SQL。
按这个依赖关系,镜像里的启动顺序必须是 Hadoop(HDFS + YARN)先起来,然后是 Hive 的 metastore,再起 HiveServer2,最后才是 Hue。Spark 不需要单独启动常驻服务,它是以客户端方式向 YARN 提交作业。这个层次不先理清,后面配置全都会乱。
组件分工表格如下,这个表在你排查问题时会反复用到:
| 组件 | 核心职能 | 在镜像中的运行方式 | 关键依赖 |
|---|---|---|---|
| Hadoop | HDFS 存储 + YARN 资源调度 | 常驻服务(NameNode / DataNode / ResourceManager / NodeManager) | JDK8+,SSH |
| Spark | 内存计算引擎,以 YARN 模式运行 | 客户端模式,通过 spark-submit 提交 | Hadoop classpath、配置目录 |
| Hive | SQL 转 MapReduce/Tez 任务的数仓工具 | metastore 常驻,HiveServer2 常驻 | MySQL/Derby 存储元数据、HDFS |
| Tez | Hive 的 DAG 执行引擎,替代 MR | 库文件放在 HDFS,运行时按需拉起容器 | HDFS、YARN |
| Hue | Web 界面:查 HDFS 文件、写 HQL、看任务进度 | 常驻 Web 服务(默认端口 8888) | HiveServer2、HDFS |
2.2 为什么要做单镜像而不是五套独立镜像
很多人会问:为什么不把五个组件拆成五个镜像,用 Docker Compose 编排起来?这个问题的答案取决于你的目标。项目标题叫「大数据基础镜像组件」,核心诉求是一套开箱即用的基础环境,尤其适合离线交付和学习场景:拉一个镜像,启动一个容器,所有服务齐全。
拆开做五套镜像的好处是横向扩展灵活,但代价是网络、挂载卷、主机名、共享配置全部要预先规划,新手起步门槛很高。做单镜像的好处是:配置天然共享,hive-site.xml 只要一份;网络方面不需要容器间通信;构建一次可以多次复用。缺点也比较明显,所有服务挤在同一份资源里,不适合生产大集群,但这个镜像的定位本来就是「基础环境、开发调试、学习复现」。
我一般建议的做法是:开发调试用单镜像,后面你真要上生产集群了,再拿这套配置做基底,把各组件拆到多台机器上,而不是一开始就双机三机编排。
2.3 目录规划和版本组合怎么定:先定版本才能谈配置
做镜像之前最先定的不是配置文件,而是版本组合。Hadoop、Spark、Hive、Tez 这几个项目之间有编译兼容性问题,乱配版本会在运行时出现各种类冲突和方法找不到。
业界最常见、也最稳的组合是:Hadoop 3.3.x + Spark 3.3.x(不带 hadoop 版本的预编译包)+ Hive 3.1.x + Tez 0.10.x + JDK 8。这个组合我反复验证过,Hive 3.1.3 对 Tez 0.10.0 的兼容性比 Tez 0.9.x 好很多,Spark 3.3 用 Scala 2.12 版本即可,和 Hadoop 3 的 RPC 机制没有兼容问题。JDK 不要上 11,Hive 3.1 对 JDK9+ 的模块化支持不完整,会出现反射访问报错,这是很多新手踩的第一个大坑。
镜像内的目录规划也建议固定成一套,比如:
| 路径 | 用途 |
|---|---|
| /opt/bigdata | 所有组件的安装根目录 |
| /opt/bigdata/hadoop-3.3.6 | Hadoop 安装目录 |
| /opt/bigdata/spark-3.3.2-bin-hadoop3 | Spark 安装目录 |
| /opt/bigdata/hive-3.1.3 | Hive 安装目录 |
| /opt/bigdata/tez-0.10.0 | Tez 安装目录 |
| /opt/bigdata/hue-4.11.0 | Hue 安装目录 |
| /data/hdfs | HDFS 名称目录和数据目录 |
| /data/hive | Hive 元数据库文件(使用 Derby 时) |
这套目录设计的好处是把软件和运行数据分开,之后做数据卷挂载时可以只挂 /data,软件坏了重建镜像不影响数据。
3. 构建大数据基础镜像:Dockerfile、SSH 免密与启动脚本怎么写
3.1 基础层构建:选 Ubuntu 还是 CentOS,JDK 怎么装
现在做这种基础镜像,常见基座是 Ubuntu 20.04 或 CentOS 7/8。我倾向 Ubuntu 20.04,原因只有一个:软件源稳定,装依赖时不容易缺包。CentOS 7 的 glibc 版本老,Hue 编译出来的动态库有时候会报 GLIBC 版本不够,排查起来很痛苦。
Dockerfile 的第一段这样写:
FROM ubuntu:20.04 ARG DEBIAN_FRONTEND=noninteractive ARG HADOOP_VERSION=3.3.6 ARG SPARK_VERSION=3.3.2-bin-hadoop3 ARG HIVE_VERSION=3.1.3 ARG TEZ_VERSION=0.10.0 RUN apt-get update && apt-get install -y \ openjdk-8-jdk \ ssh \ rsync \ vim \ curl \ net-tools \ mysql-client \ python3 \ python3-pip \ && rm -rf /var/lib/apt/lists/*这个基础层有几处要注意。第一,openjdk-8-jdk在 Ubuntu 20.04 的源里已经迁到了openjdk-8-jdk-headless,如果apt-get install openjdk-8-jdk报找不到包,就两个都写上去。第二,SSH 必须装,Hadoop 的 start-dfs.sh 脚本靠 SSH 登录本机拉起进程,没有 SSH 就不能免密启停。第三,mysql-client是为后面初始化 Hive 元数据库准备的,如果你用内嵌 Derby 可以不要,但生产一点的做法是起一个 MySQL 容器来管 Hive 元数据。
装完 JDK 后别急着配 Hadoop,先做一件容易被忽略的事:把 JAVA_HOME 环境变量和 PATH 写进/etc/profile.d/bigdata.sh,然后让 Dockerfile 里所有后续 RUN 指令都 source 它。否则后面启动脚本里找不到 java 命令,Log 里全是Cannot find Java。
3.2 解压组件并处理 Hadoop 对 SSH 免密的依赖
基础层完成后,下一步是把四个组件的压缩包复制进镜像并解压。这里有一个构建细节:不要在 Dockerfile 里用 RUN wget 从外网下载 Hadoop,镜像构建时不方便控制下载速度和失败重试。常见做法是先把 tar.gz 包放到与 Dockerfile 同级的packages/目录,然后用 COPY 指令带进镜像。
COPY packages/hadoop-${HADOOP_VERSION}.tar.gz /tmp/ COPY packages/spark-${SPARK_VERSION}.tgz /tmp/ COPY packages/hive-${HIVE_VERSION}.tar.gz /tmp/ COPY packages/tez-${TEZ_VERSION}.tar.gz /tmp/ COPY packages/hue-${HUE_VERSION}.tar.gz /tmp/ RUN tar -xzf /tmp/hadoop-${HADOOP_VERSION}.tar.gz -C /opt/bigdata/ \ && tar -xzf /tmp/spark-${SPARK_VERSION}.tgz -C /opt/bigdata/ \ && tar -xzf /tmp/hive-${HIVE_VERSION}.tar.gz -C /opt/bigdata/ \ && tar -xzf /tmp/tez-${TEZ_VERSION}.tar.gz -C /opt/bigdata/ \ && tar -xzf /tmp/hue-${HUE_VERSION}.tar.gz -C /opt/bigdata/ \ && rm -f /tmp/*.tar.gz /tmp/*.tgz解压之后马上做 SSH 免密配置,顺序不能反。Hadoop 的启动脚本会执行ssh localhost,如果不先配免密,脚本会卡在密码输入上,导致整个容器起不来。
RUN ssh-keygen -t rsa -P '' -f /root/.ssh/id_rsa \ && cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys \ && chmod 600 /root/.ssh/authorized_keys \ && echo 'StrictHostKeyChecking no' >> /etc/ssh/ssh_config这里唯一的坑是-P ''表示空密码,有些版本的 ssh-keygen 会提示你重新输入 passphrase,在 Dockerfile 里没法交互,所以一定要用这个参数。StrictHostKeyChecking no是为了避免首次 ssh 连接时出现 host key 确认提示,不然 startup 脚本会中途挂掉。
3.3 Hive 与 Tez 的集成:把 Tez 库放到 HDFS 是关键一步
Hive 切换到 Tez 引擎不只是改一个参数,光改hive.execution.engine=tez是不够的,Tez 的依赖 jar 必须能让运行时的 YARN 容器获取到。这也是很多人改了配置后 Hive 查询半天没反应、最后报Could not find or load main class org.apache.tez...的原因。
正确做法是先把 Tez 的完整目录打包上传到 HDFS,然后在 tez-site.xml 里告诉 Hive 去 HDFS 路径下找库。
# 进入 Tez 目录,把整个目录和依赖一起打成 tar 包 cd /opt/bigdata/tez-0.10.0 tar -czf /tmp/tez.tar.gz ./* # 在 HDFS 上建目录并上传 hdfs dfs -mkdir -p /apps/tez-0.10.0 hdfs dfs -put /tmp/tez.tar.gz /apps/tez-0.10.0/这里要特别提一个参数:hive.tez.java.opts和tez.container.max.java.heap.fraction。Tez 的 AppMaster 默认会向 YARN 申请容器资源,如果 YARN 的yarn.scheduler.maximum-allocation-mb设小了,Tez 申请不到足够内存,就会一直处于INFO: Session is not yet open状态。这个现象在测试环境出现频率极高,第四部分我会讲具体参数怎么配。
3.4 容器启动脚本:单镜像如何按顺序拉起所有服务
镜像内软件装好、配置写好之后,还差最后一块拼图:启动脚本。单镜像这种方式下,docker run的 CMD 不能只是/bin/bash,因为这样不会自动启动任何服务。常见做法是提供一个entrypoint.sh,按顺序完成格式化、启动 Hadoop、初始化 Hive、启动 HiveServer2、最后拉起 Hue。
#!/bin/bash set -e # 第一次启动时格式化 NameNode,注意先判断是否已格式化 if [ ! -d /data/hdfs/name/current ]; then echo "初始化 NameNode..." $HADOOP_HOME/bin/hdfs namenode -format -force fi # 启动 Hadoop 集群 $HADOOP_HOME/sbin/start-dfs.sh $HADOOP_HOME/sbin/start-yarn.sh # 初始化 Hive 元数据库(幂等处理) $HIVE_HOME/bin/schematool -initSchema -dbType derby || echo "元数据库已初始化,跳过" # 启动 HiveServer2 和 Metastore 后台进程 nohup $HIVE_HOME/bin/hive --service metastore > /data/logs/metastore.log 2>&1 & nohup $HIVE_HOME/bin/hive --service hiveserver2 > /data/logs/hiveserver2.log 2>&1 & # 启动 Spark 历史服务器,方便从 Web 界面看任务状态 nohup $SPARK_HOME/sbin/start-history-server.sh > /data/logs/spark-history.log 2>&1 & # 最后启动 Hue nohup $HUE_HOME/build/env/bin/supervisor -d -p /data/logs/hue-supervisor.log > /data/logs/hue.log 2>&1 & echo "所有服务启动完成,进入容器保持前台运行" tail -f /dev/null这个脚本里有几个值得注意的设计。namenode -format -force用目录是否存在来判断是否格式化,避免每次重启容器都重新格式化导致 HDFS 数据丢失。Metastore 和 HiveServer2 用 nohup 启动是合理的,因为它们不是 YARN 管理的组件,而 Hadoop 自带的 start-dfs.sh 已经用 SSH 把进程 daemon化了,不需要 nohup。tail -f /dev/null是容器保持前台运行的惯用写法,没有它容器会因为没有前台进程而直接退出。
4. 核心配置文件逐个拆解:五个组件怎么靠配置文件互相认
4.1 Hadoop 三个配置文件的必调参数与常见错配
Hadoop 的配置分散在core-site.xml、hdfs-site.xml、yarn-site.xml和mapred-site.xml四个文件里,容器模式下最容易出问题的就是路径和权限。先给一套我在单镜像场景下验证过的核心配置。
core-site.xml中最关键的是fs.defaultFS,它决定了整个集群的默认 HDFS 地址,所有组件都要通过它找到 NameNode:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:8020</value> </property> <property> <name>hadoop.proxyuser.root.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.root.groups</name> <value>*</value> </property> <property> <name>hadoop.http.staticuser.user</name> <value>root</value> </property> </configuration>hadoop.proxyuser.root.hosts和groups这两个参数是给 Hue 用的。Hue 在提交 Hive SQL 时会以 root 身份代理用户,如果不放开代理权限,Hue 里执行任何查询都会报Failed to execute session一类的权限错误。hadoop.http.staticuser.user配成 root,是让 Web UI 查看 HDFS 文件时不弹登录,纯开发环境适用,生产不建议这样开。
hdfs-site.xml里重点看两个 block size 和副本数。单镜像测试环境把副本数调成 1 就够,能省一半磁盘空间:
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///data/hdfs/name</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///data/hdfs/data</value> </property> <property> <name>dfs.permissions.enabled</name> <value>false</value> </property> <property> <name>dfs.namenode.datanode.registration.ip-hostname-check</name> <value>false</value> </property> </configuration>dfs.permissions.enabled设成 false 对单镜像开发环境来说能省掉大量权限问题,比如 Hive 写 warehouse 目录时不会因为属主不对而报 Permission denied。ip-hostname-check必须设成 false 是个典型的容器坑:容器网络下 DataNode 的 hostname 解析偶尔和注册 IP 对不上,不关掉这个检查的话 DataNode 会一直注册失败,在 50070 页面看到 DataNode 数量为 0。
yarn-site.xml是单镜像场景下最需要谨慎调整的文件。容器默认能用的 CPU 和内存都有限,YARN 如果不设上限,默认会按宿主机全部资源计算,容器内直接爆掉:
<configuration> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>8192</value> </property> <property> <name>yarn.scheduler.maximum-allocation-mb</name> <value>8192</value> </property> <property> <name>yarn.nodemanager.vmem-check-enabled</name> <value>false</value> </property> <property> <name>yarn.nodemanager.pmem-check-enabled</name> <value>false</value> </property> </configuration>yarn.nodemanager.resource.memory-mb是给 NodeManager 使用的总内存,要根据你docker run时的-m参数适配。如果容器内存限制是 8G,这里配 8G 或略小都可以;如果配超过容器限制,任务一跑就会被 OOM killer 杀掉,表现为 Spark 作业Container exited with a non-zero exit code 143。vmem 和 pmem 检查在容器里必须关掉,因为容器内存不是按虚拟内存概念走的,开着会导致 Container 被误杀。这是 Spark on YARN 场景下的经典玄学问题,查了一天最后发现是这两行没关。
最后是mapred-site.xml,这里有一个很多人漏配的mapreduce.application.classpath,Hadoop 3.x 不配的话 MapReduce / Tez 作业会报找不到依赖:
<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>mapreduce.application.classpath</name> <value>$HADOOP_HOME/share/hadoop/common/*:$HADOOP_HOME/share/hadoop/common/lib/*:$HADOOP_HOME/share/hadoop/hdfs/*:$HADOOP_HOME/share/hadoop/hdfs/lib/*:$HADOOP_HOME/share/hadoop/mapreduce/*:$HADOOP_HOME/share/hadoop/mapreduce/lib/*:$HADOOP_HOME/share/hadoop/yarn/*:$HADOOP_HOME/share/hadoop/yarn/lib/*</value> </property> </configuration>4.2 Hive 切换到 Tez 引擎:hive-site.xml 与 tez-site.xml 的呼应关系
Hive 的配置核心是hive-site.xml。在这个单镜像里最少需要三个必配项:元数据库连接、执行引擎、warehouse 目录。
<property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:derby:;databaseName=/data/hive/metastore_db;create=true</value> </property> <property> <name>hive.metastore.warehouse.dir</name> <value>hdfs://localhost:8020/user/hive/warehouse</value> </property> <property> <name>hive.execution.engine</name> <value>tez</value> </property> <property> <name>hive.server2.thrift.bind.host</name> <value>0.0.0.0</value> </property> <property> <name>hive.server2.thrift.port</name> <value>10000</value> </property>元数据库这里用的是 Derby 内嵌模式。生产上把 Derb y 换成 MySQL 是必须的,但单镜像测试场景用 Derby 的好处是零外部依赖,这也是为什么 Dockerfile 里只装了个 mysql 客户端。 Derby 在单镜像场景有个使用注意:不要两个进程同时打开同一个 derby 数据库,metastore 和 HiveServer2 如果都走内嵌模式会锁库,所以镜像里只让 metastore 进程持有 derby 连接,HiveServer2 通过 thrift 协议连 metastore 即可。写法就是上面启动脚本里先单独起 metastore,再起 hiveserver2。
hive.server2.thrift.bind.host写 0.0.0.0 是为了让 Hue 容器能从外部访问 HiveServer2,如果只写 localhost,你在宿主机上做端口映射也连不上。
Tez 侧的配置不要全丢进 hive-site.xml,单独做一个tez-site.xml更清晰,Hive 会自动读它:
<configuration> <property> <name>tez.lib.uris</name> <value>hdfs://localhost:8020/apps/tez-0.10.0/tez.tar.gz</value> </property> <property> <name>tez.container.max.java.heap.fraction</name> <value>0.4</value> </property> <property> <name>tez.am.resource.memory.mb</name> <value>2048</value> </property> <property> <name>tez.task.resource.memory.mb</name> <value>2048</value> </property> </configuration>tez.lib.uris的值必须和 HDFS 上实际路径保持一致,路径写错不会立刻报错,而是 Tez 会话创建时卡很久再失败。tez.container.max.java.heap.fraction是容器内 JVM 堆占容器内存的比例,默认 0.8,调成 0.4 给 native 内存和系统库留出空间,不然容器内存容易被打满。tez.am.resource.memory.mb的 2048 要和 YARN 的单容器最大分配值匹配,如果 YARN 允许的最大内存是 1024,这里申请 2048 会直接失败。
4.3 Spark 连 Hive 元数据:三个必须对齐的参数
Spark 在镜像里不是常驻服务,而是靠客户端提交作业。它要访问 Hive 表,就必须知道 Hive 的元数据在哪。完全不配置时 Spark 默认用自带的嵌入式元数据,会看到你明明在 Hive 里建了表,Spark SQL 里查却报 table not found。
在$SPARK_HOME/conf/spark-defaults.conf里加三行:
spark.master=yarn spark.sql.warehouse.dir=hdfs://localhost:8020/user/hive/warehouse spark.sql.hive.metastore.version=3.1.3 spark.sql.hive.metastore.jars=mavenspark.master=yarn是提交模式,这样 Spark 的 executor 会由 YARN 调度。spark.sql.warehouse.dir必须和 Hive 的 warehouse 目录一致,否则两边建的表文件落在不同目录,互相看不到数据。spark.sql.hive.metastore.version要和镜像内安装的 Hive 版本严格一致,如果 Hive 是 3.1.3,这里写 3.1.3;只写大版本号 3.1,Spark 会用错误的连接器,启动时直接报 Unsupported Hive version。
spark.sql.hive.metastore.jars三选一:maven、builtin、路径。离线环境不能选 maven,它会去网上下载依赖;正确做法是把这个值指向 Hive 安装目录下的 lib 包集合。写成 maven 只适合镜像构建时可以联网的开发环境。所以上面的配置写 maven 是演示默认值,离线场景改成:
spark.sql.hive.metastore.jars=$HIVE_HOME/lib/*4.4 Hue 连接 HiveServer2 的配置与端口约定
Hue 是所有组件里最容易被配置打倒的。它的配置文件是desktop/conf/hue.ini,修改完后必须编译并重启 supervisor。配置核心是两段:一段是告诉 Hue 去哪里找 HiveServer2,另一段是让 Hue 能访问 HDFS。
[hue] secret_key=your_secret_key_change_me http_port=8888 [beeswax] hive_server_host=localhost hive_server_port=10000 hive_conf_dir=/opt/bigdata/hive-3.1.3/conf [hadoop] [[hdfs_clusters]] [[[default]]] fs_defaultfs=hdfs://localhost:8020 webhdfs_url=http://localhost:9870/webhdfs/v1 [[yarn_clusters]] [[[default]]] resourcemanager_host=localhost resourcemanager_port=8088hive_server_host指向 HiveServer2 所在地址,单镜像里就是 localhost。这里有个非常隐蔽的坑:Hue 连接 HiveServer2 时,会尝试读取hive_conf_dir下的配置文件,如果 Hue 运行时用户的权限不够,读不了 hive-site.xml,就会报Could not connect to HiveServer2。所以镜像里跑 Hue 的进程要么是 root,要么把 Hive 的 conf 目录权限放开到 755。Hue 的 Http 端口固定 8888,docker run 做端口映射时-p 8888:8888即可。
webhdfs_url里的 9870 是 Hadoop 3.x 的 NameNode Web UI 端口,注意 Hadoop 2.x 是 50070,这个端口写错会导致 Hue 的文件浏览器打不开。
5. 必踩的 5 个坑:现象、原因和解决办法
5.1 容器重启后 HDFS 一直处于 SafeMode,上传和查询全部超时
构建完镜像第一次启动一切正常,结果用docker restart重启容器后,执行hdfs dfs -ls /一直卡住,日志里反复提示NameNode is in safe mode。
原因是 NameNode 启动时先进入安全模式,要等 DataNode 上报足够多的数据块才自动退出。容器重启后 DataNode 重新注册需要时间,安全模式退出比平时慢。解决方法是先手动看状态,再决定是否强制退出:
hdfs dfsadmin -safemode get hdfs dfsadmin -safemode leave但根本问题是为什么每次都进安全模式还不退。检查发现是 DataNode 的 storage 目录因为容器重启后 hostname 变化和 NameNode 里的记录对不上,导致 DataNode 反复连接失败。解决办法是启动脚本里在 DataNode 启动前清理旧的 DataNode 注册信息:
rm -rf /data/hdfs/data/current这个操作要慎重,只适用于测试环境,生产环境绝不能删数据目录。
5.2 Hue 界面显示无法连接 HiveServer2,但 beeline 本地能连上
现象是宿主机上用 beeline 连jdbc:hive2://localhost:10000正常,但 Hue 页面执行 SQL 时提示Cannot connect to HiveServer2 at localhost:10000。
原因有两层。第一层是 HiveServer2 进程确实起来了,但只监听了 IPv6 的 localhost,Hue 用了 IPv4 去连。解决办法是 hive-site.xml 里hive.server2.thrift.bind.host必须写 0.0.0.0 而不是 localhost。第二层是 Hue 启动时使用的是它自己环境里的 Python,里面少了 thrift 相关模块,报错被吞到日志里。如果你在日志里看到ImportError: No module named thrift,就重新编译一次 Hue 的虚拟环境:
cd /opt/bigdata/hue-4.11.0 make clean make installHue 的 Python 环境是它自己用 build/env 管理的,和系统 Python 隔离。很多发行版的 Hue 包依赖系统 python 模块不完整,自编译是最稳妥的路。编译时会下载大量依赖,确保镜像构建阶段网络通畅,否则会把构建时间从 10 分钟拖到 1 小时。
5.3 Spark 作业提交到 YARN 后 Container 总是被杀,退出码 143
spark-submit提交后,任务跑起来不到一分钟就失败,YARN 日志里能看到Container exited with a non-zero exit code 143。143 这个数字是 SIGTERM 的 shell 码,表示容器是被主动终止的,不是代码抛异常。
原因是 YARN 的虚拟内存检查机制。容器内进程的物理内存不高,但 JVM 的虚拟内存映射大,NodeManager 检查时发现虚拟内存超出上限就直接杀。解决方式就是在 yarn-site.xml 里关闭 vmem 检查。之前写过的yarn.nodemanager.vmem-check-enabled=false就是治这个。如果你不想全关,可以把yarn.nodemanager.vmem-pmem-ratio从默认 2.1 调高到 4.0,但测试环境我更推荐直接关,省得排查时还要换算内存比例。
5.4 Hive 查询卡在 Session is not yet open,等了 10 分钟才报错
Tez 模式下执行 Hive SQL 后,日志一直停在INFO: Session is not yet open,10 分钟后才报超时。很多人以为 Hive 卡死了,其实是 Tez 在向 YARN 申请 ApplicationMaster 容器时被拒绝了。
原因是tez.am.resource.memory.mb配了 2048,但yarn.scheduler.maximum-allocation-mb只给了 1024。YARN 会拒绝所有超过最大分配值的容器请求。排查确认方法是在 YARN ResourceManager 的 8088 页面看 Application 状态,通常能看到Application is rejected by queue或诊断里写内存超限。解决办法是把两边对齐:
# 把 tez-site.xml 里 AM 内存调成最大值以内 sed -i 's/tez.am.resource.memory.mb" value="2048"/tez.am.resource.memory.mb" value="1536"/' tez-site.xml另外也可能是yarn.app.mapreduce.am.resource.mb设置了不合理的值牵制了队列。检查 YARN 的 capacity-scheduler 配置的yarn.scheduler.capacity.maximum-am-resource-percent,默认 0.5 意味着一半集群资源可以被 AM 占用,如果集群总内存只有 2G,AM 实际拿不到多少。测试环境把这值调高到 0.8 能减少 AM 被反复拒绝的概率。
5.5 Spark SQL 读 Hive 表报 java.lang.NoSuchMethodError 或 Unsupported Hive version
Spark 启动时能正常执行show databases,但一旦执行select count(*) from 某张hive表,报错java.lang.NoSuchMethodError: org.apache.hadoop.hive.metastore.api.Table.getRequiredParameters。
原因非常典型:Spark 内部带的 Hive metastore 版本和外部 Hive 版本不一致。Spark 3.3 默认内置 Hive 2.3,而你镜像里的 Hive 是 3.1.3,两者在 metastore 协议上有方法签名变化。解决办法是前面说的spark.sql.hive.metastore.version和spark.sql.hive.metastore.jars配到一致。如果配了还报错,检查spark.sql.hive.metastore.jars是否真正生效了:
spark-submit --conf spark.sql.hive.metastore.jars=$HIVE_HOME/lib/* --conf spark.sql.hive.metastore.version=3.1.3 --repositories 你的本地仓库路径还有一类隐蔽情况,是你用了spark-shell --packages拉别的依赖时,间接把低版本的 hive metastore 包带进了 classpath。这种只能通过spark-submit --verbose看实际加载的 jar 列表,确认没有任何 hive-exec-2.x 出现在加载路径里。
6. 端到端验证:怎么证明这套镜像真的能用
6.1 三步健康检查:进程、端口、数据写入
构建完镜像后不要急着跑业务,先花一分钟做健康检查。第一步看进程,进入容器执行jps,预期能看到这些关键进程:
root@container:~# jps 1234 NameNode 2345 DataNode 3456 ResourceManager 4567 NodeManager 5678 HiveMetaStore 6789 HiveServer2 789 RunJar第二步看端口,重点确认需要对外暴露的端口在监听状态。Hue 8888、HiveServer2 10000、NameNode 9870、YARN 8088。第三步是数据写入验证,这个比看进程更可靠:
hdfs dfs -mkdir -p /tmp/test hdfs dfs -put /etc/hosts /tmp/test/ hdfs dfs -cat /tmp/test/hosts | head -5如果数据能写入能读回,HDFS 这一层就是通的。注意观察/tmp/test目录是否产生了_SUCCESS之类的临时文件残留,如果有,说明写入过程中 Checkpoint 或 OutputCommiter 机制出了问题,需要进一步看 DataNode 日志。
6.2 端到端跑一遍 HQL 和 Spark SQL:验证 Hive + Tez + Spark 三者连接
验证完 HDFS,下一步是通过 Hive 建表写入数据,再用 Spark SQL 读同一张表。这个流程能证明所有组件的连接关系都正常。
先进入 Hive 的 beeline 执行:
CREATE DATABASE IF NOT EXISTS test_db; USE test_db; CREATE TABLE IF NOT EXISTS user_click ( user_id INT, click_time STRING, page_url STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' STORED AS TEXTFILE; INSERT INTO user_click VALUES (1, '2025-01-01 10:00:00', '/home'), (2, '2025-01-01 10:05:00', '/search');执行完INSERT后不要立刻退出,先确认 Tez 引擎确实被调用了。看 beeline 的执行日志里如果出现DAG相关的任务描述,说明走的是 Tez,如果显示 MapReduce 字样则说明执行引擎没切过来。这里有个细节:SET hive.execution.engine=tez;只能在当前会话生效,要全局生效必须确保 hive-site.xml 里的配置是对的。
然后再用 Spark SQL 从同一张表读数据,验证元数据共享正常:
spark-sql --master yarn --conf spark.sql.hive.metastore.version=3.1.3 \ --conf spark.sql.hive.metastore.jars=$HIVE_HOME/lib/* \ -e "SELECT page_url, COUNT(*) FROM test_db.user_click GROUP BY page_url;"如果这步能正常返回聚合结果,说明三条链路全部打通:HDFS 存储正常、Hive 元数据正常、Spark on YARN 正常。
6.3 把这个镜像变成项目基础镜像:进阶用法
端到端验证通过后,镜像就可以作为「基础镜像」使用了。所谓基础镜像,就是别人以后做具体项目时,FROM 这个镜像,在上面继续加自己的业务代码、依赖库、调度脚本,而不需要重复面对 Hadoop 生态的配置问题。
我自己的习惯是:把镜像 tag 成bigdata/base:3.3.6这种带版本号的形式,然后在项目的 Dockerfile 里写上:
FROM bigdata/base:3.3.6 COPY my-job.jar /opt/app/ COPY run.sh /opt/app/run.sh这样的好处是 Hadoop、Spark、Hive 这几个重组件只构建一次,后续业务迭代都基于同一个稳定底座。版本升级也很清爽,只改镜像 tag 即可。还有一个经验是镜像做好后导出一份,保留离线安装包,避免团队换台机器就要重新在线拉取基础工具依赖。构建一次基础镜像的时间成本不低,离线分发能帮团队省不少时间。
这套做下来,最后最让我放心的验证方式其实就一句话:拉镜像、起容器、跑一遍端到端,三分钟内能看到聚合结果,这个基础环境就可以交给项目组使用了。环境问题是最不该消耗业务时间的地方,把这一步做好,后面所有上层项目都会受益。希望帮到你。
本文还有配套的精品资源,点击获取