ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

用Docker快速搭建Hadoop分布式集群:从镜像构建到跑通WordCount

用Docker快速搭建Hadoop分布式集群:从镜像构建到跑通WordCount 搞大数据的同学基本都绕不开“搭集群”这道坎。刚接触Hadoop的时候我第一反应是搞三台虚拟机结果光是装系统、配JDK、调SSH就折腾了一整天还没摸到HDFS的影子。后来切换成Docker来装Hadoop分布式集群整个流程被压缩到了十几分钟而且环境是隔离的坏了直接删了重建不用怕把宿主机搞乱。这篇文章就把我从镜像构建、Compose编排、配置调优到跑通WordCount的完整过程以及过程中踩过的坑一次性讲清楚。适合刚入门分布式、想快速起一套实验环境或者准备面试想亲手敲一遍集群操作的读者。1. 为什么我劝你别再用虚拟机搭Hadoop一次环境灾难引发的反思1.1 传统搭建方式的痛点时间都耗在环境上很多人最初接触Hadoop集群教材里都是“准备三台Linux机器”。有条件的用三台物理机没条件的开三个虚拟机窗口。物理机不用想大多数人的电脑跑三个虚拟机基本就卡死了虚拟机方案呢每台要装系统、装JDK、装Hadoop、配SSH免密新手按文档操作光是一个“找不到JAVA_HOME”就能卡半小时。更难受的是装好之后出了问题排查起来也很费劲因为环境差异太大你同学的配置能跑通你的就是起不来。1.2 虚拟机快照不是银弹容器镜像才是可复现的有人会说虚拟机可以先做一个模板然后用克隆。这话没错但虚拟机克隆出来的镜像动辄几个GB分发、备份、在不同机器间迁移都很痛苦。而Docker镜像把系统、JDK、Hadoop、甚至SSH免密都打包成了几百MB的层一条命令就能在任意机器上拉起一整套集群。真正的“基础设施即代码”Dockerfile写清楚了任何人都能复现出一模一样的环境不再有“我这儿明明是好的怎么你那儿不行”的玄学。1.3 这套方案到底能帮你省多少事按照本文的步骤你最终会得到一个NameNode节点同时跑ResourceManager两个DataNode节点同时跑NodeManager容器间SSH免密可以一键执行start-dfs.sh和start-yarn.sh浏览器直接访问NameNode和ResourceManager的Web UI能在集群上真正跑通一个MapReduce作业整个过程如果网络条件好几分钟起步比虚拟机方案省掉至少一个下午。这套环境用来学习、做课程设计、验证配置文件改动、练习运维命令都很顺手。2. 开工之前镜像选型、集群拓扑和版本规划2.1 现成镜像还是自建镜像别图省事建议自己构建Docker Hub上有不少现成的Hadoop镜像比如bde2020/hadoop-namenode、bde2020/hadoop-datanode这种按角色拆分的镜像也有sequenceiq/hadoop-docker这种整包镜像。现成镜像的好处是拉下来就能用适合快速看一眼效果坏处是配置已经写死你想改副本数、改内存参数、调整端口映射的时候很可能要绕开它原本的启动脚本最后还是得自己改文件。所以我更推荐自建镜像一次构建到处运行每一步配了什么心里都有数。踩坑的时候也知道该去哪个文件里翻。2.2 集群拓扑设计三个容器各司其职我选的拓扑是最经典的一主两从结构容器名主机名角色hadoop-masterhadoop-masterNameNode、SecondaryNameNode、ResourceManagerhadoop-worker1hadoop-worker1DataNode、NodeManagerhadoop-worker2hadoop-worker2DataNode、NodeManager为什么选两个DataNode而不是三个副本数默认是3但两个DataNode的时候我把副本数调成2既能看到多副本的实际效果又不会因为副本数大于可用节点而产生警告。如果机器配置足够加一个worker节点也就是在Compose文件里复制粘贴一段而已。2.3 版本选择JDK 8 Hadoop 3.3.6的稳妥组合Hadoop 3.3.x是目前最常用的稳定版本线支持Java 8和Java 11。我选的是Hadoop 3.3.6配OpenJDK 8这个组合在社区里验证得最多踩坑资料也好搜。注意Hadoop 2.x的Web UI端口是50070Hadoop 3.x改成了9870很多老教程截图还是50070照着配如果是3.x版本会连不上这里提前说一下避免你对着老文章一头雾水。2.4 准备构建目录把需要的包先下载好我的习惯是在宿主机建一个独立目录把所有素材放在一起方便管理和清理。目录结构大概是这样的hadoop-cluster/ ├── Dockerfile ├── docker-compose.yml ├── config/ │ ├── core-site.xml │ ├── hdfs-site.xml │ ├── mapred-site.xml │ ├── yarn-site.xml │ └── workers ├── OpenJDK8U-jdk_x64_linux_hotspot_8u382b05.tar.gz └── hadoop-3.3.6.tar.gzJDK的tar包可以从Adoptium项目下载Hadoop的tar包从Apache官网或国内开源镜像站下载。把这两个包放到构建目录里Dockerfile通过ADD命令把它们打进镜像。这样做的好处是构建镜像时不依赖外网下载速度更快也不容易因为下载源失效导致构建失败。3. Dockerfile逐行拆解免密、环境变量、配置注入一次搞定3.1 基础镜像与系统依赖我选择ubuntu:22.04作为基础镜像体积适中软件源稳定。Hadoop集群运行还需要SSH服务因为start-dfs.sh和start-yarn.sh会通过SSH远程到各节点启动进程这是Hadoop自带脚本的运作方式绕不开。FROM ubuntu:22.04 ENV DEBIAN_FRONTENDnoninteractive RUN apt-get update apt-get install -y --no-install-recommends \ openssh-server \ openssh-client \ rsync \ vim \ net-tools \ rm -rf /var/lib/apt/lists/*rsync是Hadoop启动脚本在节点间同步时会用到的工具漏装的话偶尔会报一些奇怪的错误。net-tools提供ifconfig、netstat等命令排查网络问题时很有用。DEBIAN_FRONTENDnoninteractive是为了避免安装包时弹出交互式配置界面导致构建卡住这是一个细节但很关键。3.2 解压JDK和Hadoop用通配符避免版本路径问题JDK解压后的目录名通常带版本号比如jdk8u382-b05Hadoop解压后是hadoop-3.3.6。为了避免升级版本后还要改Dockerfile我用通配符统一重命名ADD OpenJDK8U-jdk_x64_linux_hotspot_8u382b05.tar.gz /opt/ ADD hadoop-3.3.6.tar.gz /opt/ RUN mv /opt/jdk* /opt/jdk mv /opt/hadoop-3.3.6 /opt/hadoop ENV JAVA_HOME/opt/jdk ENV HADOOP_HOME/opt/hadoop ENV PATH${JAVA_HOME}/bin:${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:${PATH}ENV在这里定义了全局环境变量容器启动后所有进程都能直接用java、hdfs、hadoop这些命令不用每次进容器都手动export。3.3 配置SSH免密让集群脚本跑得丝滑Hadoop的master节点要能免密登录所有worker我直接在构建镜像时生成密钥对并把公钥写进authorized_keys。因为所有节点用的是同一个镜像所以每个容器里都有同一对密钥master登录任何节点都是免密的。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 \ sed -ri s/#PermitRootLogin prohibit-password/PermitRootLogin yes/ /etc/ssh/sshd_config \ echo StrictHostKeyChecking no /etc/ssh/ssh_config这里有两个小细节。第一ssh-keygen -P 表示私钥不设置密码否则SSH连接时会要你输入密钥口令集群起停脚本就卡住了。第二StrictHostKeyChecking no很重要首次SSH连接时不会弹出“是否信任该主机”的确认提示脚本才能全自动跑下去。在真实生产环境里这样做有安全隐患但实验环境追求的是省事这个问题可以接受。3.4 Hadoop配置注入把配置项做成文件再COPY我习惯把core-site.xml、hdfs-site.xml这些配置文件放在宿主机config/目录里构建时统一复制进去。这样想改配置直接改宿主机文件再重新构建镜像比进入容器里用vim改要清晰得多。ADD config/ /tmp/hadoop-config/ RUN cp /tmp/hadoop-config/*.xml ${HADOOP_HOME}/etc/hadoop/ \ cp /tmp/hadoop-config/workers ${HADOOP_HOME}/etc/hadoop/workers \ mkdir -p /opt/hadoop/data/namenode /opt/hadoop/data/datanode /opt/hadoop/logs同时在hadoop-env.sh里补充Java环境变量和各守护进程的用户配置。Hadoop 3.x要求显式声明HDFS_NAMENODE_USER等变量不然以root用户执行启动脚本时会报类似“Attempting to operate on hdfs namenode as root”的错误。RUN echo export JAVA_HOME${JAVA_HOME} ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export HDFS_NAMENODE_USERroot ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export HDFS_DATANODE_USERroot ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export HDFS_SECONDARYNAMENODE_USERroot ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export YARN_RESOURCEMANAGER_USERroot ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export YARN_NODEMANAGER_USERroot ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh \ echo export HADOOP_HEAPSIZE512 ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh3.5 容器启动命令让SSH前台运行容器是一个前台进程模型如果所有进程都在后台启动容器会直接退出。我用sshd前台运行来占住主进程EXPOSE 22 9870 8088 9000 CMD [/usr/sbin/sshd, -D]这样容器起来后就是一个可以随时被SSH进入的节点。之后通过docker exec进入master容器手工启动HDFS和YARN既符合真实集群的操作习惯也方便调试。构建镜像的命令docker build -t hadoop-cluster:3.3.6 .构建完成后可以用docker run -it hadoop-cluster:3.3.6 bash进去看一眼环境变量确认java -version和hadoop version都正常再继续下一步。4. Compose编排与核心配置直接给出一套能跑的配置4.1 docker-compose.yml三节点如何连成一张网Docker Compose最方便的地方在于它会自动创建一个自定义网络同一Compose文件里的服务可以通过服务名互相解析。也就是说在hadoop-worker1容器里直接访问hadoop-master就能连到master容器的IP这个特性天然适配Hadoop的主机名解析需求不用像虚拟机方案那样手动改所有节点的/etc/hosts。version: 3.8 services: hadoop-master: image: hadoop-cluster:3.3.6 container_name: hadoop-master hostname: hadoop-master ports: - 9870:9870 - 8088:8088 - 19888:19888 networks: - hadoop-net hadoop-worker1: image: hadoop-cluster:3.3.6 container_name: hadoop-worker1 hostname: hadoop-worker1 ports: - 8042:8042 networks: - hadoop-net hadoop-worker2: image: hadoop-cluster:3.3.6 container_name: hadoop-worker2 hostname: hadoop-worker2 ports: - 8043:8042 networks: - hadoop-net networks: hadoop-net: driver: bridge端口映射的规划如下后面验证集群状态全靠这些端口端口对应服务说明9870NameNode Web UI查看HDFS状态、数据块分布8088ResourceManager Web UI查看YARN作业和节点状态19888JobHistory Server查看历史作业需另启服务8042 / 8043NodeManager Web UI查看单个Worker的资源使用情况如果你本机的9870或者8088已经被占用可以把左侧宿主机端口改成别的比如19870:9870访问时就用http://localhost:19870。4.2 core-site.xmlNameNode地址就是整个集群的“总机号码”客户端访问HDFS第一步要找到NameNodefs.defaultFS就是干这个的。所有节点和客户端都会用这个配置去连接NameNode所以必须指向master节点?xml version1.0 encodingUTF-8? configuration property namefs.defaultFS/name valuehdfs://hadoop-master:9000/value /property property namehadoop.tmp.dir/name value/opt/hadoop/tmp/value /property /configuration9000是NameNode RPC通信的默认端口HDFS客户端、DataNode注册、MapReduce读写文件都走这个端口。4.3 hdfs-site.xml副本数、数据目录、反向校验开关这份配置决定了数据怎么存、存几份、存在哪?xml version1.0 encodingUTF-8? configuration property namedfs.namenode.name.dir/name valuefile:///opt/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name valuefile:///opt/hadoop/data/datanode/value /property property namedfs.replication/name value2/value /property property namedfs.namenode.http-address/name value0.0.0.0:9870/value /property property namedfs.namenode.datanode.registration.ip-hostname-check/name valuefalse/value /property /configuration重点说两个容易被忽略的地方。第一dfs.replication设为2是因为只有两个DataNode设成默认的3反而会出现副本无法完全落盘的情况。第二dfs.namenode.datanode.registration.ip-hostname-check强烈建议设为false容器环境经常出现主机名反解析和容器IP对不上的情况NameNode会因此拒绝DataNode注册用了这一项能省掉很多烦恼。4.4 yarn-site.xml与mapred-site.xml计算框架怎么接进来YARN负责资源调度MapReduce作业要跑在YARN上这两份配置必须配对正确?xml version1.0 encodingUTF-8? configuration property nameyarn.resourcemanager.hostname/name valuehadoop-master/value /property property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property /configurationaux-services里配了mapreduce_shuffle这是MapReduce在YARN上运行时用于shuffle阶段数据传输的辅助服务漏配的话作业会卡在运行中然后失败。yarn.resourcemanager.hostname则告诉所有NodeManager和客户端ResourceManager在哪个机器上。?xml version1.0 encodingUTF-8? configuration property namemapreduce.framework.name/name valueyarn/value /property /configuration4.5 workers文件告诉master去哪些节点启动DataNodeHadoop 3.x把这个文件改成了workersHadoop 2.x还叫slaves。文件内容很简单hadoop-worker1 hadoop-worker2start-dfs.sh执行时会逐行读取这个文件通过SSH到对应主机名启动DataNode进程。如果你增加节点除了要在Compose里加服务还要记得在这个文件里加一行。5. 启动集群和验证从格式化到跑通第一个WordCount5.1 格式化NameNode一次性的动作必须想清楚再做docker compose up -d docker exec -it hadoop-master bash hdfs namenode -format -force格式化会清空NameNode的数据目录生成新的集群ID。这个命令只在首次部署时执行平时不要随便执行。因为你一旦重复格式化NameNode的clusterID就变了旧DataNode里的clusterID对不上全部DataNode会启动失败日志里报一堆Incompatible clusterIDs。遇到这种情况不要慌把每个节点数据目录下的current目录删掉再重启让DataNode重新向NameNode注册即可。5.2 启动HDFS和YARN一条命令进入守护进程在master容器内依次执行start-dfs.sh start-yarn.shstart-dfs.sh会先在当前节点启动NameNode和SecondaryNameNode然后SSH到worker1、worker2启动DataNode。接下来start-yarn.sh启动ResourceManager和NodeManager。如果SSH免密没配对这里会提示输入密码然后卡住。5.3 jps检查一眼看出进程是否齐全在master容器内执行jps正常情况下能看到NameNode SecondaryNameNode ResourceManager然后在两个worker容器里执行jps应该看到DataNode NodeManager如果哪个进程没起来去对应的日志文件里查原因。日志都在/opt/hadoop/logs/目录下比如DataNode的日志是hadoop-root-datanode-hadoop-worker1.logNameNode的日志是hadoop-root-namenode-hadoop-master.log。查看日志是最有效的排查手段比瞎猜强一百倍。5.4 Web UI验证从浏览器看集群打开浏览器访问http://localhost:9870NameNode页面能看到集群健康状态、DataNode列表、HDFS存储量http://localhost:8088ResourceManager页面能看到节点列表、正在运行或已完成的应用在NameNode页面看到两个活的DataNode在ResourceManager页面看到两个活跃的NodeManager集群就算基本成了。5.5 跑一个WordCount集群是否真的能干活光看Web UI还不够跑一个真实的MapReduce作业才算数。以经典的wordcount为例docker exec -it hadoop-master bash # 准备一份测试数据 echo hello hadoop hello docker hello hdfs hadoop cluster /tmp/input.txt # 上传到HDFS hdfs dfs -mkdir -p /input hdfs dfs -put /tmp/input.txt /input # 运行wordcount hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /input /output # 查看结果 hdfs dfs -cat /output/part-r-00000执行hadoop jar后如果看到进度条从map 0% reduce 0%一直走到map 100% reduce 100%最后返回Job complete说明整个集群链路是通的。输出结果应该类似cluster 1 docker 2 hadoop 3 hello 3 hdfs 1如果卡在Running job不动大概率是资源不足或者内存参数没调好下一节专门讲。6. 生产级避坑指南这些坑我替你踩过了你直接绕开6.1 容器启动后立刻退出如果docker compose up -d后容器一启动就停多半是SSH服务没起来因为我们的CMD写的是/usr/sbin/sshd -D。先看容器日志docker logs hadoop-master如果SSH配置文件有问题这里会直接显示。常见原因是/etc/ssh/sshd_config里禁用了root登录或者宿主机22端口被占。我在Dockerfile里用sed替换了PermitRootLogin但不同版本的Ubuntu默认配置写法有差异如果替换没生效手动检查一下容器里的sshd_config。6.2 DataNode反复启动失败八成是clusterID不一致这是一个极其常见的坑。症状是NameNode UI里DataNode列表为空DataNode日志里出现Incompatible clusterIDs。原因是格式化NameNode时重新生成了clusterID但DataNode的数据目录里还残留着旧的clusterID。解决办法也很直接# 在每个worker容器中执行 rm -rf /opt/hadoop/data/datanode/current # 重启worker容器 docker restart hadoop-worker1 hadoop-worker2重启后DataNode会重新生成数据目录并且从NameNode获取新的clusterID。如果NameNode本身也乱过最稳妥的办法是把所有节点的/opt/hadoop/data删掉重新格式化一次。注意顺序先删数据再格式化最后启动。6.3 SSH免密失效start-dfs.sh卡在输密码Hadoop启动脚本走的是SSH免密失效时你会看到脚本停在某台节点上要密码。排查两个地方第一容器里/root/.ssh目录权限必须是700authorized_keys文件必须是600权限太宽松SSH会拒绝信任第二确认StrictHostKeyChecking no确实写进了/etc/ssh/ssh_config。如果都对了还是不行可以手动测试ssh hadoop-worker1 hostname如果能直接返回主机名说明SSH链路没问题问题出在Hadoop脚本读取的配置上比如workers文件名不对或者主机名拼写错误。6.4 WordCount提交后永远卡在Running job作业提交到YARN后一直不结束常见原因有两个。一个是内存不足我给每个容器默认设置了HADOOP_HEAPSIZE512如果你的机器内存有限还要调整YARN的容器内存参数在yarn-site.xml里加property nameyarn.nodemanager.resource.memory-mb/name value1024/value /property property nameyarn.scheduler.maximum-allocation-mb/name value1024/value /property property nameyarn.scheduler.minimum-allocation-mb/name value256/value /property另一个是Docker Desktop本身分配的内存不够。默认Mac或Windows的Docker Desktop只有2GB内存跑三节点Hadoop很容易把内存耗尽。建议在Docker Desktop的Settings里把内存调到至少4GB有条件就给6GB。我实测4GB能跑两个worker节点做完WordCount再低就悬了。6.5 端口映射冲突导致Web UI打不开启动容器时如果发现端口被占用Compose会报错。解决办法是改宿主机侧端口比如把9870:9870改成19870:9870。常见冲突端口就是9870、8088、8042。改完端口后访问Web UI的地址也要跟着变别忘记。6.6 容器删了HDFS里的数据还在吗默认情况下数据存在容器可写层一旦容器删除NameNode元数据和DataNode块数据全部丢失。如果你只是学习无所谓如果你想把环境留一段时间或跨机器迁移建议在Compose里加上命名卷比如volumes: - namenode-data:/opt/hadoop/data/namenode - datanode-data-1:/opt/hadoop/data/datanode在同一个Compose文件的底部声明volumes: namenode-data: datanode-data-1:这样docker compose down后数据还会保留重新up -d时能找回之前的HDFS状态。不过要注意如果有命名卷重新格式化NameNode之前必须把卷里的数据清掉否则还会遇到clusterID问题。6.7 节点扩容如何从两个Worker变成三个想加一个worker非常简单先在Compose里加一个hadoop-worker3服务再在workers文件里加一行hadoop-worker3然后docker compose up -d docker exec hadoop-master start-dfs.sh新节点会自动加入集群。如果需要更多副本数把dfs.replication改成对应值并滚动重启DataNode即可。这个操作很适合用来试验HDFS的数据均衡效果。我个人在实际部署中的体会是Docker装Hadoop这件事最大的价值不是“快”而是“可控”。每一条配置、每一次启动失败都能在干净的环境里反复试错不用怕把宿主机搞坏。刚开始跑通第一个WordCount、看到Web UI上两个DataNode都亮着绿灯时那种成就感的确让人上头。如果你按这篇步骤搭好了集群建议再自己做两个小练习一个是往HDFS里多塞几个文件观察数据块在两个节点上的分布另一个是手动kill掉一个worker容器的DataNode进程看NameNode怎么标记这个节点等它恢复后又是怎么重新同步的。这些操作做完你对分布式集群的理解会比看十篇理论文章都深。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表