最近在学习Hadoop,正好服务器还没有过期,于是打算用其搭建学习hadoop的环境。使用Docker搭建Hadoop环境,更灵活、可扩展,管理也方便。Docker简化了部署过程。
但是最后运行测试程序失败了,可能是因为服务器内存太小的原因。
环境搭建
制作镜像
docker pull ubuntu
# 提前创建一个文件夹用于存放下载好的jdk和hadoop:/home/hadoop/build
# 进入/home/hadoop/build,下载hadoop
# 直接下载java有点麻烦,先下载到windows,再上传到linux服务器:jdk-8u212-linux-x64.tar.gz
# 下载网站:https://www.oracle.com/cn/java/technologies/downloads/archive/ 下载需要登录doge
wget https://archive.apache.org/dist/hadoop/common/hadoop-3.1.3/hadoop-3.1.3.tar.gz
docker run -it -v /home/hadoop/build:/root/build --name ubuntu ubuntu
# 更新相关配置
apt-get update
apt-get install vim
apt-get install ssh
/etc/init.d/ssh start # 开启ssh
# 生成公私钥对 :如果不进行这一步,后续镜像启动需要在集群之间发送公私钥对,才能实现无密登录
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
# 测试一下能否无密码连接
ssh localhost
#进入我们挂载的目录解压jdk和hadoop到对应目录:/root/build
tar -zxvf jdk-8u212-linux-x64.tar.gz -C /usr/lib/jvm
tar -zxvf hadoop-3.1.3.tar.gz -C /usr/local
vim ~/.bashrc
#分别写入环境变量
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_212/
export PATH=$PATH:$JAVA_HOME/bin
export HADOOP_HOME=/usr/local/hadoop-3.1.3
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
# 刷新配置
source ~/.bashrc
# 测试java环境和Hadoop环境搭建是否成功
java -version
hadoop version
# 安装rsync这个工具,方便后续使用
apt install rsync
#退出容器,将刚刚配置好的环境做成镜像
docker commit 861f228e220a ubuntu-hadoop:1.0
在容器内配置了hadoop的环境变量,制作成特定的镜像,方便后面搭建学习用的集群。
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
# 查看容器的ip:将container_name_or_id 替换成容器名称和id即可
# 后面启动服务需要对比hosts中写入的主机名称和ip是否和实际容器ip一致。
多个hadoop容器
docker run --name master -p 7001:8020 -p 7002:9870 -p 7003:9868 -p 7004:8088 -p 7005:19888 -itd --hostname master -v /home/hadoop/ubuntu:/usr/ubuntu ubuntu-hadoop:2.0
# 用上面命令分别起3个服务:master,slave1,slave2
#分别进入容器内修改hosts文件为实际的ip
# 如进入master中
docker exec -it master /bin/bash
vim /etc/hosts # 分别添加slave1,slave2的ip
# 向/etc/hosts写入对应节点主机名称和ip的映射关系,需要查看具体ip,根据实际情况修改
# 测试一下是否可以免密登录,ssh连接如果失败,需要查看ssh服务是否开启,默认没有开启,需要3台都要开启ssh服务
service ssh status # 查看是否开启
service ssh start # 开启ssh服务
分词Demo运行
master中:在hadoop目录创建一个wcinput目录,存放用于统计词频率的文件
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar
wordcount wcinput wcoutput
# wcoutput 目录不能存在
集群分发脚本
scp命令和rsync命令
scp 是 Secure Copy 的缩写,是一种通过 SSH(Secure Shell)协议在本地主机和远程主机之间安全地复制文件的命令行工具。它提供了一种简单的方式来传输文件,并且可以在不同的主机之间进行拷贝。
scp 的基本语法如下:
scp [选项] 源文件 目标文件
其中,源文件 表示要复制的源文件的路径,可以是本地文件或远程文件。目标文件 表示复制文件的目标路径,可以是本地路径或远程路径。
scp -r username@source:/path/to/source username@destination:/path/to/destination
常用的选项包括:
-r:递归复制目录及其内容。-p:保留源文件的时间戳和权限设置。-v:显示详细的复制进度和信息。-P:显示进度条。
rsync 是一个强大的文件同步和备份工具,它可以在本地主机或远程主机之间进行文件和目录的高效传输和同步。rsync 使用差异算法来比较源和目标文件,只传输变化的部分,从而减少传输的数据量,提高传输速度。
使用需要安装有该命令
apt install rsync
rsync 的基本语法如下:
rsync [选项] 源文件/目录 目标文件/目录
其中,源文件/目录 表示要同步的源文件或目录的路径,可以是本地路径或远程路径。目标文件/目录 表示同步的目标路径,可以是本地路径或远程路径。
rsync -avz username@source:/path/to/source username@destination:/path/to/destination
常用的选项包括:
-a:归档模式,保持文件的属性、权限和时间等信息,递归同步目录。-v:显示详细的同步进度和信息。-z:传输时压缩数据,减少传输的数据量。-P:显示进度条。--delete:删除目标目录中不存在于源目录中的文件。
分发脚本
vim myrsync # 创建脚本
脚本
#!/bin/bash
if [ $# -lt 1 ]
then
echo Not Enough Arguement!
exit;
fi
#向slave1和slave2完成同步
for host in slave1 slave2
do
echo ==================== $host ====================
for file in $@
do
if [ -e $file ]
then
pdir=$(cd -P $(dirname $file); pwd)
fname=$(basename $file)
ssh $host "mkdir -p $pdir"
rsync -av $pdir/$fname $host:$pdir
else
echo $file does not exists!
fi
done
done
chmod +x myrsync # 添加执行权限
mv myrsync /bin/myrsync # 移动到bin目录,或者其他加入环境变量的目录,方便使用
myrsync ~/build # 向slave1和slave2完成/build目录的同步,不会删除slave中不相同的文件
集群搭建
集群搭建计划
| master | slave1 | slave2 | |
|---|---|---|---|
| HDFS | NameNode,DataNode | DataNode | SecondaryNameNode,DataNode |
| YARN | NodeManager | resourceManager,NodeManager | NodeManager |
在各个节点上使用jps命令需要出现下面进程:
配置文件
core-site.xml
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://master:8020</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/local/hadoop-3.1.3/data</value>
</property>
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
</configuration>
hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!-- nn web 端访问地址-->
<property>
<name>dfs.namenode.http-address</name>
<value>master:9870</value>
</property>
<!-- 2nn web 端访问地址-->
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>slave2:9868</value>
</property>
</configuration>
yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>slave1</value>
</property>
<property>
<name>yarn.nodemanager.env-whitelist</name>
<value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.log.server.url</name>
<value>http://master:19888/jobhistory/logs</value>
</property>
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
</configuration>
map
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>master:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>master:19888</value>
</property>
</configuration>
works
master
slave1
slave2
集群启动脚本
可以单独切换到各个节点按照需求一个一个启动
#!/bin/bash
if [ $# -lt 1 ]
then
echo "No Args Input..."
exit ;
fi
case $1 in
"start")
echo " =================== start hadoop cluster ==================="
echo " --------------- start hdfs ---------------"
ssh master "/usr/local/hadoop-3.1.3/sbin/start-dfs.sh"
echo " --------------- start yarn ---------------"
ssh slave1 "/usr/local/hadoop-3.1.3/sbin/start-yarn.sh"
echo " --------------- start historyserver ---------------"
ssh master "/usr/local/hadoop-3.1.3/bin/mapred --daemon start historyserver"
;;
"stop")
echo " =================== stop hadoop cluster ==================="
echo " --------------- stop historyserver ---------------"
ssh master "/usr/local/hadoop-3.1.3/bin/mapred --daemon stop historyserver"
echo " --------------- stop yarn ---------------"
ssh slave1 "/usr/local/hadoop-3.1.3/sbin/stop-yarn.sh"
echo " --------------- stop hdfs ---------------"
ssh master "/usr/local/hadoop-3.1.3/sbin/stop-dfs.sh"
;;
*)
echo "Input Args Error..."
;;
esac
集群jps脚本
jps是java提供查看java进程的命令行工具
#!/bin/bash
for host in master slave1 slave2
do
echo =============== $host ===============
# 这里的/usr/lib/jvm/jdk1.8.0_212/bin/jps可以替换成jps,如果启动不了,切换成完整目录
ssh $host /usr/lib/jvm/jdk1.8.0_212/bin/jps
done
通过脚本启动集群,首次启动集群需要进行格式化,在master上完成。
hdfs namenode -format
通过jps查看到所有容器内的进程都正常启动和预期一致,不一致需要在3个容器内都清空hadoop下的data和logs目录,进行重新格式化。
测试
web界面查看HDFS的NameNode
Web 端查看 YARN 的 ResourceManager
查看 JobHistory
http://master:19888/jobhistory
上面都要根据docker具体映射的端口去访问!
编写一个需要统计词频的文件,先上传一个一个文件到hadoop中
hadoop fs -put word.txt /input
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount /input /output
可以在http://slave1:8088 查看到具体运行的进度。我运行词语测试程序,resourceManager会莫名其妙的挂掉了,最终解决了没有挂掉的问题,但是运行进程卡在了百分之5,最终没有解决问题,导致这个问题原因报错都是一致的。
查看了slave1的resourceManager的日志
tail -500 /usr/local/hadoop-3.1.3/logs/hadoop-root-resourcemanager-slave1.log
报错如下:
查阅资料后说是由于yarn默认设置的内存比较大,需要改小一点。
在yarn-site.xml中加入:
<property>
<name>yarn.app.mapreduce.am.resource.mb</name>
<value>256</value>
</property>
但是很遗憾,修改大小后依旧没有解决问题,可能是服务器内存太小了(阿里云2核4G)。起了3个hadoop节点,我自己部署其他容器会莫名其妙的会关闭,跑了3个月的博客,今天早上在搭建hadoop时自动停了。看来后续学习,得换本地虚拟机。
遇到的且能解决的问题
启动报JavaHome找不到的错误
在/opt/module/hadoop-3.1.3/etc/hadoop/hadoop-env.sh文件中的JAVA_HOME添加变量
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_212/
启动报缺少root用户
在所有节点的hadoop的sbin目录的start-dfs.sh,stop-dfs.sh 文件中分别加入:
#!/usr/bin/env bash
HDFS_DATANODE_USER=root
HADOOP_SECURE_DN_USER=hdfs
HDFS_NAMENODE_USER=root
HDFS_SECONDARYNAMENODE_USER=root
在sbin目录的start-yarn.sh,和stop-yarn.sh加入:
#!/usr/bin/env bash
YARN_RESOURCEMANAGER_USER=root
HADOOP_SECURE_DN_USER=yarn
YARN_NODEMANAGER_USER=root
jps脚本问题
最开始jps脚本 并没有使用完整的路径,而是直接使用jps命令。
网上都说是java环境变量要配置到~/.bashrc中,但是我没成功。不知道什么原因,如果还失败,可以直接指定jps所在的完整目录,百分百解决问题。