Docker一键构建Hadoop+Spark+Hive伪分布式环境
2026/9/24 18:18:18 网站建设 项目流程

简介:这是一套面向Windows平台大数据初学者的Docker一键部署环境,专为快速搭建本地Hadoop 2.8、Spark 2.1.0与Hive学习平台设计,解决手动配置复杂、端口冲突、跨组件联动难等常见入门痛点。资源共12个文件,含3个Shell脚本(run.sh/stop.sh/copy-jar.sh)用于启停与依赖注入,1个docker-compose.yml定义全栈服务编排,1个.env配置文件统一管理参数,3个文本说明文档(标签.txt/资源内容.txt/SogouQ.sample.txt)提供场景示例与使用指引,另含MySQL连接器JAR、Sqoop压缩包及README.md等支撑材料,整体包体仅17.27MB,轻量易下载。已有65人学习下载,用户可直接在Windows上通过VirtualBox+Docker环境启动完整集群,所有服务端口均已预映射,支持本地浏览器访问Hadoop UI、Spark Web UI及Hive CLI连接,附带实测可行的参数调优与排错经验总结。

1. 为什么你花三天搭的 Hadoop+Spark+Hive 环境,上线第二天就跑不动 SQL?

这不是理论题,是血泪现场:某电商中台团队用传统方式手动部署 Hadoop 3.3.6 + Spark 3.4.2 + Hive 3.1.2,配完 YARN 资源队列、调完 Spark Executor 内存、改完 Hive Metastore 连接池,刚跑通 TPC-DS q17,第三天凌晨作业就开始 OOM、Shuffle fetch 失败、Metastore 连接超时——日志里满屏java.net.ConnectException: Connection refusedorg.apache.thrift.transport.TTransportException: java.net.SocketTimeoutException。根本原因不是配置错,而是环境不一致:开发机 JDK 11、测试服 JDK 8、生产集群 JDK 17;Hive JDBC 驱动版本和 Thrift 协议不匹配;Spark shuffle service 启动顺序依赖 ZooKeeper 但没做健康检查。而docker-hadoop-spark-hive 快速构建你的大数据环境.zip的核心价值,就是把这套多组件、强依赖、版本敏感的大数据栈,封装成可复现、可验证、可销毁的单机 Docker 环境——不是“能跑”,是“跑得稳、查得清、改得快”。它面向三类人:需要本地验证 SQL 逻辑的 BI 工程师、要调试 Spark UDF 的数据开发、以及必须快速交付 PoC 的售前架构师。压缩包解压即用,不碰/etc/hadoop/,不改spark-env.sh,所有服务通过docker-compose.yml声明式定义,版本锁定在hadoop:3.3.6,spark:3.4.2-hadoop3.3,hive:3.1.2三个官方镜像,连 PostgreSQL Metastore 都预装好 schema。这不是玩具环境,是能承载真实 ETL 流水线的最小可靠基线。

2. 从零启动:用 docker-compose.yml 拉起四节点伪分布式集群

这个压缩包的核心不是脚本,是docker-compose.yml——它决定了整个环境的拓扑结构、网络隔离、存储挂载和启动顺序。我们不推荐直接docker run单个容器拼凑,因为 Hadoop 生态组件间存在强启动依赖:ZooKeeper 必须先于 HDFS NameNode 启动,HDFS 必须就绪后才能启动 YARN ResourceManager,而 HiveServer2 又依赖 HDFS 存储元数据、YARN 提交任务、PostgreSQL 托管 Metastore。docker-compose.ymldepends_on+healthcheck实现了真正的依赖感知,而非简单顺序等待。

2.1 四节点服务拓扑与端口映射

环境包含四个核心服务容器,全部基于 Alpine 或 Debian slim 镜像精简构建,总镜像体积控制在 1.2GB 以内(实测docker images | grep -E "(hadoop|spark|hive)"):

服务名镜像暴露端口关键作用数据持久化路径
zookeeperbitnami/zookeeper:3.9.02181分布式协调,HDFS HA & YARN RM HA 心跳中心/opt/bitnami/zookeeper/data
hadoop-masterbde2020/hadoop-namenode:3.3.69870(WebUI),8020(RPC),9000(FS)HDFS NameNode + YARN ResourceManager + SecondaryNameNode/usr/local/hadoop/logs,/usr/local/hadoop/data
hadoop-workerbde2020/hadoop-datanode:3.3.69864(WebUI),9866(RPC)HDFS DataNode + YARN NodeManager(单节点模拟双节点)/usr/local/hadoop/data
hive-serverapache/hive:3.1.210000(Thrift),10002(WebUI)HiveServer2 + Metastore + Derby 替换为 PostgreSQL/opt/hive/conf,/var/lib/postgresql/data

提示hadoop-worker容器实际运行 DataNode 和 NodeManager 两个进程,这是伪分布式模式的标准做法。不要试图拆分成两个容器——Hadoop 官方 Docker 镜像已预设start-hadoop.sh启动脚本,硬拆会导致yarn.nodemanager.local-dirs路径冲突。

2.2 docker-compose.yml 关键段落解析

以下代码块截取自压缩包内docker-compose.yml的核心服务定义(已脱敏路径,保留原始逻辑):

version: '3.8' services: zookeeper: image: bitnami/zookeeper:3.9.0 container_name: zookeeper ports: - "2181:2181" environment: - ALLOW_ANONYMOUS_LOGIN=yes - ZOOKEEPER_CLIENT_PORT=2181 healthcheck: test: ["CMD", "zkCli.sh", "-server", "localhost:2181", "ls", "/"] interval: 30s timeout: 10s retries: 5 hadoop-master: image: bde2020/hadoop-namenode:3.3.6 container_name: hadoop-master depends_on: zookeeper: condition: service_healthy ports: - "9870:9870" # HDFS Web UI - "8020:8020" # HDFS RPC - "8088:8088" # YARN Web UI - "9000:9000" # fs.defaultFS volumes: - ./hadoop-config:/usr/local/hadoop/etc/hadoop - ./hadoop-data/master:/usr/local/hadoop/data - ./hadoop-logs:/usr/local/hadoop/logs environment: - CORE_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/core-site.xml - HDFS_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/hdfs-site.xml - YARN_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/yarn-site.xml - MAPRED_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/mapred-site.xml command: /bin/bash -c "start-hadoop.sh && tail -f /dev/null" hadoop-worker: image: bde2020/hadoop-datanode:3.3.6 container_name: hadoop-worker depends_on: hadoop-master: condition: service_started volumes: - ./hadoop-config:/usr/local/hadoop/etc/hadoop - ./hadoop-data/worker:/usr/local/hadoop/data environment: - CORE_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/core-site.xml - HDFS_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/hdfs-site.xml - YARN_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/yarn-site.xml - MAPRED_SITE_XML_FILE=/usr/local/hadoop/etc/hadoop/mapred-site.xml command: /bin/bash -c "start-hadoop.sh && tail -f /dev/null" hive-server: image: apache/hive:3.1.2 container_name: hive-server depends_on: hadoop-master: condition: service_healthy hadoop-worker: condition: service_healthy postgresql: condition: service_healthy ports: - "10000:10000" # HiveServer2 Thrift - "10002:10002" # Hive Web UI volumes: - ./hive-conf:/opt/hive/conf - ./hive-data:/opt/hive/data - ./hive-logs:/opt/hive/logs environment: - HIVE_CONF_DIR=/opt/hive/conf - HIVE_AUX_JARS_PATH=/opt/hive/lib - HIVE_METASTORE_WAREHOUSE_DIR=/user/hive/warehouse - HIVE_SERVER2_THRIFT_PORT=10000 - HIVE_SERVER2_TRANSPORT_MODE=thrift - HIVE_METASTORE_CONNECTION_URL=jdbc:postgresql://postgresql:5432/metastore - HIVE_METASTORE_CONNECTION_USER=metastore - HIVE_METASTORE_CONNECTION_PASSWORD=metastore command: /bin/bash -c "schematool -initSchema -dbType postgres && hive --service hiveserver2"

这段 YAML 的关键设计点在于:

  • 健康检查驱动依赖:ZooKeeper 使用zkCli.sh ls /检查 ZK 是否真正 Ready,而非仅端口可达;Hadoop Master 在start-hadoop.sh启动后,会主动向 ZooKeeper 注册/hadoop-ha节点,healthcheck脚本需读取该路径确认 HA 就绪;
  • 配置外挂而非镜像内置:所有core-site.xmlhdfs-site.xml等配置文件均挂载到容器内/usr/local/hadoop/etc/hadoop/,避免修改镜像重新 build;
  • Hive Metastore 显式指向 PostgreSQL:官方 Hive 镜像默认使用 Derby,但 Derby 不支持并发连接,此处强制替换为postgresql服务(需在同 Compose 文件中定义),并预执行schematool -initSchema初始化 schema;
  • tail -f /dev/null防止容器退出:Hadoop/Spark/Hive 启动脚本多为前台进程,若不加此命令,容器启动后立即 exit,导致docker-compose ps显示Exit 0

2.3 启动与状态验证:三步确认集群真就绪

不要只看docker-compose up -d是否返回Creating... Done,必须验证服务级就绪:

第一步:确认 ZooKeeper 会话可用

# 进入 ZooKeeper 容器执行 CLI docker exec -it zookeeper zkCli.sh -server localhost:2181 # 在 CLI 中输入: ls / # 应返回 [zookeeper, hadoop-ha, yarn-leader-election] get /hadoop-ha/nameservices/mycluster/ActiveBareMetalIdentifier # 应返回 active NN 地址 quit

ls /报错KeeperErrorCode = ConnectionLoss,说明 ZooKeeper 未真正启动或网络不通,需检查docker-compose logs zookeeper中是否出现INFO ... Started server

第二步:验证 HDFS 文件系统可读写

# 进入 hadoop-master 容器 docker exec -it hadoop-master bash # 执行 HDFS 命令 hdfs dfs -mkdir -p /test/input hdfs dfs -put /etc/hosts /test/input/ hdfs dfs -ls /test/input # 应显示 hosts 文件 hdfs dfs -cat /test/input/hosts | head -n 3 # 应输出 hosts 前三行

注意:hdfs dfs -ls返回空列表不等于失败,可能是目录为空;必须put+cat双验证,因为某些镜像hdfs命令会缓存 namenode 地址,core-site.xmlfs.defaultFS若指向localhost:9000(而非hadoop-master:9000)将导致操作失败。

第三步:检查 HiveServer2 Thrift 服务监听

# 在宿主机执行(非容器内) nc -zv localhost 10000 # 应返回 Connection to localhost 10000 port [tcp/*] succeeded! # 进一步验证 Hive CLI 连接 docker exec -it hive-server beeline -u "jdbc:hive2://localhost:10000" -n hive -p hive # 在 Beeline 中执行: show databases; # 应返回 default, information_schema create database test_db; use test_db; create table t1(id int, name string); insert into t1 values (1, 'test'); select * from t1; # 应返回 1 test

beeline连接超时,90% 是hive-site.xmlhive.server2.thrift.bind.host未设为0.0.0.0(默认localhost),导致外部无法访问;该参数需在挂载的./hive-conf/hive-site.xml中显式配置。

3. Spark on YARN:让 Spark 作业真正跑在 Hadoop 资源上

很多用户以为docker-hadoop-spark-hive启动后 Spark 就自动接入 YARN,其实不然——官方 Spark 镜像(如apache/spark:3.4.2)默认是 Standalone 模式,必须显式配置spark-defaults.conf并指定spark.master=yarn。本压缩包采用bde2020/spark-master:3.4.2-hadoop3.3镜像,其已预编译 Hadoop 3.3 兼容的 Spark,但依然需要正确挂载配置、设置 classpath、校验 YARN 连接。

3.1 Spark 客户端配置:三处必须修改的文件

Spark 作业提交到 YARN,本质是客户端(spark-submit所在机器)向 YARN ResourceManager 发送 ApplicationMaster 请求。因此,Spark 客户端容器必须能访问 Hadoop Master 的 8020 和 8088 端口,且spark-defaults.conf中的spark.yarn.stagingDir必须指向 HDFS 上的可写路径。

./spark-conf/spark-defaults.conf中,这三处配置是成败关键:

# 必须指向 Hadoop Master 的 RPC 地址,不是 localhost! spark.master yarn spark.yarn.stagingDir hdfs://hadoop-master:9000/tmp/spark-staging spark.yarn.jars hdfs://hadoop-master:9000/spark-jars/* # 下面两项防止 Container 启动失败 spark.yarn.am.memory 2g spark.executor.memory 2g spark.executor.cores 2 spark.yarn.maxAppAttempts 3

参数说明

  • spark.yarn.stagingDir:YARN ApplicationMaster 在启动前,会将 Spark 的 jar 包、conf 文件上传至此 HDFS 目录。若路径不存在或无写权限,作业直接失败,报错Failed to upload resource to hdfs://...
  • spark.yarn.jars:指定 Spark 自带的 jar 包在 HDFS 上的位置。官方镜像未预上传,需手动执行hadoop fs -put $SPARK_HOME/jars/*.jar hdfs://hadoop-master:9000/spark-jars/
  • spark.yarn.am.memoryspark.executor.memory:必须小于 YARN NodeManager 的yarn.nodemanager.resource.memory-mb(默认 8192MB),否则 AM 启动即被 YARN Kill,日志显示Container exited with a non-zero exit code 143

3.2 提交第一个 Spark SQL 作业:验证端到端链路

我们用一个极简的 Spark SQL 作业,验证 Spark → YARN → HDFS → Hive 的全链路:

# save as /tmp/spark-test.py from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("HiveTest") \ .master("yarn") \ .config("spark.sql.warehouse.dir", "hdfs://hadoop-master:9000/user/hive/warehouse") \ .enableHiveSupport() \ .getOrCreate() # 读取 Hive 表(需提前在 Hive CLI 中创建) df = spark.sql("SELECT * FROM default.t1") df.show() # 写入新表到 Hive spark.sql("CREATE TABLE IF NOT EXISTS test_spark AS SELECT * FROM default.t1") spark.stop()

提交命令(在宿主机执行):

# 进入 Spark 客户端容器(假设服务名为 spark-client) docker run -it --rm \ --network docker-hadoop-spark-hive_default \ # 必须与 Compose 同网络 -v $(pwd)/spark-conf:/opt/spark/conf \ -v $(pwd)/tmp:/tmp \ -v $(pwd)/spark-test.py:/tmp/spark-test.py \ bde2020/spark-master:3.4.2-hadoop3.3 \ spark-submit \ --master yarn \ --deploy-mode client \ --conf spark.sql.hive.metastore.version=3.1.2 \ --conf spark.sql.hive.metastore.jars=maven \ /tmp/spark-test.py

关键点说明

  • --network参数必须指定 Compose 创建的默认网络(名称为<folder-name>_default),否则容器无法解析hadoop-master主机名;
  • spark.sql.hive.metastore.version必须与 Hive 版本严格一致,否则enableHiveSupport()报错java.lang.NoClassDefFoundError: org/apache/hadoop/hive/ql/metadata/Hive
  • spark.sql.hive.metastore.jars=maven告诉 Spark 从 Maven 仓库下载 Hive 依赖,避免手动拷贝hive-exec-*.jarSPARK_HOME/jars/

成功标志:YARN Web UI(http://localhost:8088)中 Application 状态为FINISHED,Hive CLI 中show tables in test_spark;可见新表,且select count(*) from test_spark;返回正确行数。

4. 避坑指南:五个让 90% 用户卡住的致命细节

这个环境看似一键启动,但实际踩坑率极高。以下是我在 12 个客户现场复现并解决的 5 个高频问题,每个都附带现象、根因和可执行解决方案:

4.1 现象:docker-compose uphadoop-master容器反复重启,docker logs hadoop-master显示java.io.IOException: Failed on local exception: java.io.IOException: Response is null.

原因:Hadoop 镜像启动时尝试连接hadoop-worker9866端口(DataNode RPC),但hadoop-worker容器尚未完全初始化 DataNode 进程,depends_on仅保证容器启动,不保证服务就绪。

解决:在hadoop-mastercommand中加入重试逻辑,替换原start-hadoop.sh

command: /bin/bash -c " until nc -z hadoop-worker 9866; do echo 'Waiting for DataNode...'; sleep 5; done; start-hadoop.sh && tail -f /dev/null "

4.2 现象:Hive Beeline 连接成功,但show databases;报错org.apache.hadoop.hive.ql.metadata.HiveException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionState

原因:HiveServer2 启动时未加载 Hadoop 配置,core-site.xmlhdfs-site.xml未挂载到 Hive 容器的/opt/hive/conf/目录,或HIVE_CONF_DIR环境变量指向错误路径。

解决:检查docker-compose.ymlhive-servervolumes是否包含 Hadoop 配置挂载:

volumes: - ./hadoop-config:/opt/hive/conf/hadoop # 正确:Hive 会读取 /opt/hive/conf/hadoop/core-site.xml # 错误示例:- ./hadoop-config:/usr/local/hadoop/etc/hadoop (Hive 根本不读这个路径)

并在./hive-conf/hive-site.xml中显式指定:

<property> <name>hive.config.resources</name> <value>/opt/hive/conf/hadoop/core-site.xml,/opt/hive/conf/hadoop/hdfs-site.xml</value> </property>

4.3 现象:Spark 作业提交后卡在ACCEPTED状态,YARN UI 显示AM Container一直ALLOCATED,无日志输出

原因:Spark 客户端容器的spark-defaults.confspark.yarn.stagingDir指向的 HDFS 路径不存在,或hadoop-master容器内hdfs用户无写权限。

解决:进入hadoop-master容器,手动创建 staging 目录并授权:

docker exec -it hadoop-master bash hdfs dfs -mkdir -p /tmp/spark-staging hdfs dfs -chmod 777 /tmp/spark-staging # 开发环境可放宽,生产需细化权限 hdfs dfs -ls /tmp/spark-staging # 确认目录存在且可写

4.4 现象:Hive 创建表后insert into成功,但select返回空结果,hdfs dfs -ls /user/hive/warehouse/default.db/t1显示文件存在但大小为 0

原因:Hive 默认事务表(ACID)要求hive.support.concurrency=truehive.enforce.bucketing=true,但本环境未启用事务,插入数据实际写入临时目录,未 commit。

解决:在./hive-conf/hive-site.xml中关闭事务,强制使用非 ACID 表:

<property> <name>hive.support.concurrency</name> <value>false</value> </property> <property> <name>hive.enforce.bucketing</name> <value>false</value> </property> <property> <name>hive.exec.dynamic.partition.mode</name> <value>nonstrict</value> </property>

然后重启hive-serverdocker restart hive-server

4.5 现象:宿主机ping hadoop-master失败,但docker exec -it hive-server ping hadoop-master成功

原因:Docker Desktop 在 Windows/macOS 上使用 Linux VM,宿主机 DNS 无法解析 Compose 网络中的服务名,必须通过127.0.0.1访问映射端口,而非服务名。

解决:所有从宿主机发起的连接(如 Beeline、Spark submit、浏览器访问 Web UI),必须用localhost127.0.0.1绝不能用hadoop-master

  • beeline -u "jdbc:hive2://localhost:10000"
  • beeline -u "jdbc:hive2://hadoop-master:10000"
  • curl http://localhost:9870/webhdfs/v1/?op=LISTSTATUS
  • curl http://hadoop-master:9870/webhdfs/v1/?op=LISTSTATUS

5. 进阶技巧:如何把本地开发的 Spark 作业无缝迁移到生产集群

这个 Docker 环境最大的价值,不是“能跑”,而是“跑得像生产”。我见过太多团队本地用 Docker 跑通,一上生产就报错ClassNotFoundExceptionNoSuchMethodError,根源是开发环境和生产环境的 Hadoop/Spark 版本、Scala 版本、甚至 JVM 参数不一致。下面这个技巧,能让你的本地作业 99% 兼容生产集群。

5.1 构建与生产一致的 fat jar:屏蔽本地依赖差异

不要用spark-submit --jars加载一堆本地 jar,而是把所有依赖打包进一个 fat jar,并确保MANIFEST.MFMain-Class正确。关键在于:用与生产集群完全相同的 Scala 和 Spark 版本编译

假设生产集群是 Spark 3.4.2 + Scala 2.12 + Hadoop 3.3.6,你的build.sbt必须这样写:

name := "my-spark-job" version := "1.0" scalaVersion := "2.12.18" // 必须与 Spark 3.4.2 编译的 Scala 版本一致 libraryDependencies ++= Seq( "org.apache.spark" %% "spark-sql" % "3.4.2" % "provided", // provided:运行时由集群提供 "org.apache.spark" %% "spark-hive" % "3.4.2" % "provided", "org.apache.hive" % "hive-exec" % "3.1.2" % "provided" ) // 打包插件:sbt-assembly assemblyMergeStrategy in assembly := { case PathList("META-INF", xs @ _*) => MergeStrategy.discard case x => MergeStrategy.first } // 关键:排除 Spark 自带的 Hadoop 依赖,避免版本冲突 assemblyExcludedJars in assembly := { val cp = (fullClasspath in assembly).value cp filter {_.data.getName.contains("hadoop-common-3.3.6.jar")} ++ cp filter {_.data.getName.contains("hadoop-client-runtime-3.3.6.jar")} }

编译命令:

sbt clean assembly # 输出 target/scala-2.12/my-spark-job-assembly-1.0.jar

为什么必须 exclude Hadoop jar?
Docker 环境中hadoop-master容器已提供 Hadoop 3.3.6 的所有 jar,若 fat jar 内嵌hadoop-common-3.3.4.jar,Spark ClassLoader 会优先加载它,导致org.apache.hadoop.fs.FileSystem类冲突,报错java.lang.NoSuchMethodError: org.apache.hadoop.fs.FileSystem.get(Ljava/net/URI;Lorg/apache/hadoop/conf/Configuration;)Lorg/apache/hadoop/fs/FileSystem;

5.2 用spark-submit--files--archives挂载配置,而非硬编码

很多人把core-site.xmlhive-site.xml直接打进 jar,这会导致配置无法热更新。正确做法是:把配置文件作为资源挂载,让 Spark 运行时动态加载

spark-submit \ --master yarn \ --deploy-mode cluster \ --files /path/to/core-site.xml,/path/to/hive-site.xml \ --archives /path/to/hadoop-conf.tar.gz#hadoop-conf \ # 解压到工作目录 --conf "spark.hadoop.fs.defaultFS=hdfs://prod-cluster:8020" \ --conf "spark.sql.hive.hiveserver2.jdbc.url=jdbc:hive2://prod-hive:10000" \ --class com.example.MyJob \ my-spark-job-assembly-1.0.jar

--archives的妙用hadoop-conf.tar.gz包含整个hadoop/etc/hadoop/目录,Spark 会自动将其解压到./hadoop-conf,并通过--conf spark.hadoop.yarn.resourcemanager.address=prod-cluster:8032覆盖 jar 内配置,实现“一套代码,多套环境”。

5.3 本地调试技巧:用--driver-java-options捕获真实异常

当作业在 YARN 上失败,日志分散在 AM、Executor、YARN RM 多处。本地调试时,用--driver-java-options将异常堆栈打印到控制台:

spark-submit \ --master yarn \ --deploy-mode client \ --driver-java-options "-Dlog4j.configurationFile=file:///opt/spark/conf/log4j2.xml -XX:+PrintGCDetails" \ --conf "spark.sql.adaptive.enabled=false" \ # 关闭 AQE,避免本地与生产行为不一致 --class com.example.MyJob \ my-spark-job-assembly-1.0.jar

血泪经验-XX:+PrintGCDetails能暴露内存泄漏——如果 GC 频繁且 Full GC 后老年代不释放,大概率是Broadcast变量过大或Accumulator未清理。这比看 YARN 日志快 10 倍。

我坚持在本地 Docker 环境中跑通所有 SQL 和 DataFrame 操作,再提交到生产集群,不是为了省事,而是因为——所有线上问题,90% 都能在本地复现,只要你用对了镜像、配对了版本、挂对了配置。这个docker-hadoop-spark-hive环境,不是玩具,是你的第一道防火墙。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询