前言

  • 建议服务器/虚拟机至少4G内存,本文制作的 Hadoop 镜像: Java 1.8、Hadoop 2.9.2 。

  • 一、制作Hadoop 镜像 过程只做展示,笔者已经制作好 Hadoop 镜像,可跳过制作过程直接下载镜像即可。


一、制作 Hadoop 镜像

获取 CentOS 镜像

要求该 CentOS 镜像安装 SSH,已经制作好了,直接下载即可

docker pull ryaning/centos-ssh

构建 Hadoop 镜像

# 编辑 Dockerfile
vi Dockerfile

新增以下内容

# 基础镜像
FROM ryaning/centos-ssh
# 作者
MAINTAINER  Ryan <me@ryana.cn>
# 构建镜像
ADD jdk-8u162-linux-x64.tar.gz /usr/local/
RUN mv /usr/local/jdk1.8.0_162 /usr/local/jdk1.8
ENV JAVA_HOME /usr/local/jdk1.8
ENV PATH $JAVA_HOME/bin:$PATH

ADD hadoop-2.9.2.tar.gz /usr/local
RUN mv /usr/local/hadoop-2.9.2 /usr/local/hadoop
ENV HADOOP_HOME /usr/local/hadoop
ENV PATH $HADOOP_HOME/bin:$PATH

RUN yum install -y which sudo

基于 centos-ssh 镜像,把 Java 和 Hadoop 的环境都配置好,构建成 Docker Hadoop 镜像。

构建镜像

docker build -t ryaning/hadoop .

或者直接下载已经构建好的镜像

docker pull ryaning/hadoop

二、搭建 Hadoop 分布式集群

规划

准备搭建一个具有三个节点的集群,一主两从

  • 主节点:hadoop0 ip:192.168.10.10
  • 从节点1:hadoop1 ip:192.168.10.11
  • 从节点2:hadoop2 ip:192.168.10.12

配置 IP

docker 容器在启动时默认使用的是 bridge 模式,docker 容器启动后,会连接到一个名为 docker0 的虚拟网桥,故每次启动 docker 容器的 IP 都不是固定的,不方便管理,有时候需要进行固定 IP 映射,比如 docker 集群管理时。docker 在 1.9 版本版后,提供了创建自定义网络功能命令。

# 创建自定义网络 

# ip段为:192.168.10.1/24,名字为:hadoop
docker network create --subnet=192.168.10.1/24 hadoop
# 显示自定义网络列表
docker network ls

运行

运行3个 hadoop 容器,分别命名为 hadoop0,hadoop1,hadoop2,其中 hadoop0 作为 master, 并且映射了端口号,50070 和 8088,用来在浏览器中访问 hadoop WEB 界面的。

命令说明:

  • -e TZ="Asia/Shanghai" 增加环境变量,指定时区
  • -v /etc/localtime:/etc/localtime:ro:挂载系统时间到容器内
  • --net hadoop --ip 192.168.10.10:配置 Hadoop 集群节点的固定 IP
  • --add-host hadoop1:192.168.10.11:除了需要配置好 Hadoop 集群节点的固定 IP 外,还需要修改 Hadoop 容器内部的 hosts 文件,设置主机名与 ip 的映射。在 docker 中直接修改 /etc/hosts 文件,在重启容器后会被重置、覆盖。因此需要通过容器启动脚本 docker run 的 --add-host 参数将主机和 ip 地址的对应关系传入,容器在启动后会写入 hosts 文件中。
# hadoop0
docker run --name hadoop0 \
--hostname hadoop0 \
--net hadoop --ip 192.168.10.10 \
--add-host hadoop1:192.168.10.11 \
--add-host hadoop2:192.168.10.12 \
-p 50070:50070 \
-p 8088:8088 \
-d -P ryaning/hadoop

# hadoop1
docker run --name hadoop1 \
--hostname hadoop1 \
--net hadoop --ip 192.168.10.11 \
--add-host hadoop0:192.168.10.10 \
--add-host hadoop2:192.168.10.12 \
-d -P ryaning/hadoop

# hadoop2
docker run --name hadoop2 \
--hostname hadoop2 \
--net hadoop --ip 192.168.10.12 \
--add-host hadoop0:192.168.10.10 \
--add-host hadoop1:192.168.10.11 \
-d -P ryaning/hadoop

设置 SSH 免密码登录

前面已经为容器配置 IP 了,在进行 ssh 时需要输入要登陆的容器的 root 密码,Hadoop 集群要求集群间机器 SSH 连接时无密码登陆,下面讲述容器间如何配置 SSH 无密码登陆。

以 hadoop0 容器为例,hadoop1、hadoop2 容器同样需要修改。

# 进入 hadoop0 容器内
docker exec -it hadoop0 bash

# 执行后会有多个输入提示,不用输入任何内容,全部直接回车即可
ssh-keygen

# 执行命令后需要输入登录密码,默认为 123456
ssh-copy-id -i /root/.ssh/id_rsa -p 22 root@hadoop0
ssh-copy-id -i /root/.ssh/id_rsa -p 22 root@hadoop1
ssh-copy-id -i /root/.ssh/id_rsa -p 22 root@hadoop2

修改 Hadoop 配置文件

要想真正的运行 hadoop 应用还需要修改 hadoop 运行参数;以 hadoop0 为例,进入到容器内 /usr/local/hadoop/etc/hadoop 目录下,需要修改的可执行文件与配置文件包括:hadoop-env.shyarn-env.shcore-site.xmlhdfs-site.xmlyarn-site.xmlmapred-site.xml

  • hadoop-env.sh

注释掉原有的配置 export JAVA_HOME=${JAVA_HOME},修改成当前的 export JAVA_HOME=/usr/local/jdk1.8

export JAVA_HOME=/usr/local/jdk1.8
  • yarn-env.sh

同样是重新指定 export JAVA_HOME=/usr/local/jdk1.8

export JAVA_HOME=/usr/local/jdk1.8
  • core-site.xml
<configuration>
    <!-- 指定 HDFS 中 NameNode 的地址 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://hadoop0:9000</value>
    </property>
    <!-- 指定 hadoop 运行时产生文件的存储目录 -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/usr/local/hadoop/tmp</value>
    </property>
     <property>
         <name>fs.trash.interval</name>
         <value>1440</value>
    </property>
</configuration>
  • hdfs-site.xml
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
    <property>
        <name>dfs.permissions</name>
        <value>false</value>
    </property>
</configuration>
  • yarn-site.xml
<configuration>
    <property>
       <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.auxservices.mapreduce.shuffle.class</name>
        <value>org.apache.hadoop.mapred.ShuffleHandler</value>
    </property>
    <property>
        <name>yarn.resourcemanager.address</name>
        <value>hadoop0:8032</value>
    </property>
    <property>
        <name>yarn.resourcemanager.scheduler.address</name>
        <value>hadoop0:8030</value>
    </property>
    <property>
        <name>yarn.resourcemanager.resource-tracker.address</name>
        <value>hadoop0:8031</value>
    </property>
    <property>
        <name>yarn.resourcemanager.admin.address</name>
        <value>hadoop0:8033</value>
    </property>
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>hadoop0:8088</value>
    </property>
</configuration>
  • mapred-site.xml

这个文件默认不存在,需要从 mapred-site.xml.template 复制过来。

mv mapred-site.xml.template mapred-site.xml
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>
  • slaves 配置

修改 hadoop0 中的从机(slaves)配置

vi /usr/local/hadoop/etc/hadoop/slaves

删除原来的所有内容,修改为如下

hadoop1
hadoop2

hadoop 集群配置分发

在 hadoop0 中执行命令,将 hadoop0 中的配置复制到其他两个节点中。

scp -r /usr/local/hadoop hadoop1:/usr/local
scp -r /usr/local/hadoop hadoop2:/usr/local

三、启动

第一次启动集群时,需要初始化

  • 初始化
# 初始化
hdfs namenode -format

出现类似下面命令说明格式化成功。

19/01/20 14:51:17 INFO namenode.FSImage: Allocated new BlockPoolId: BP-417246956-172.17.0.9-1547995877501
19/01/20 14:51:17 INFO common.Storage: Storage directory /usr/local/hadoop/tmp/dfs/name has been successfully formatted.
19/01/20 14:51:17 INFO namenode.FSImageFormatProtobuf: Saving image file /usr/local/hadoop/tmp/dfs/name/current/fsimage.ckpt_0000000000000000000 using no compression
19/01/20 14:51:17 INFO namenode.FSImageFormatProtobuf: Image file /usr/local/hadoop/tmp/dfs/name/current/fsimage.ckpt_0000000000000000000 of size 323 bytes saved in 0 seconds .
19/01/20 14:51:17 INFO namenode.NNStorageRetentionManager: Going to retain 1 images with txid >= 0
19/01/20 14:51:17 INFO namenode.NameNode: SHUTDOWN_MSG: 
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at hadoop0/172.17.0.9
************************************************************/

注:格式化操作不能重复执行。如果一定要重复格式化,带参数 -force 即可。

启动 hadoop 集群

# /usr/local/hadoop 目录下执行
sbin/start-all.sh

第一次启动的过程中需要输入 yes 确认一下。

使用 jps,检查进程是否正常启动?能看到下面几个进程表示启动成功

[root@hadoop0 hadoop]# jps
1040 SecondaryNameNode
884 DataNode
759 NameNode
1389 Jps
1247 NodeManager
383 ResourceManager
[root@hadoop0 hadoop]#

停止 hadoop 集群

# /usr/local/hadoop 目录下执行
sbin/stop-all.sh

注:在主节点 hadoop0 启动 hadoop,从节点 hadoop1、hadoop2 会自动启动。

浏览器中访问

http://localhost:8088

hadoop-nodes

四、验证集群是否正常

可以正常访问的话,说明集群启动成功了,但不一定能正常运行,还需要下面的实际验证。

测试验证

创建本地测试文件,在 /opt 目录下创建测试文件目录。

mkdir wcinput
cd wcinput
vi wc.input

wc.input文件内容如下:

hadoop mapreduce
hadoop yarn
hadoop hdfs
mapreduce spark
hadoop hello

创建 HDFS 目录

hdfs dfs -mkdir -p /user/hadoop/input

上传文件,把测试文件上传到刚刚创建的目录中

hdfs dfs -put /opt/wcinput/wc.input /user/hadoop/input

查看文件上传是否正确

hdfs dfs -ls /user/hadoop/input
[root@hadoop0 wcinput]# hdfs dfs -ls /user/hadoop/input
Found 1 items
-rw-r--r--   1 root supergroup         70 2019-01-21 10:07 /user/hadoop/input/wc.input
[root@hadoop0 wcinput]#

运行 mapreduce 程序

hadoop 安装包中提供了一个示例程序,我们可以使用它对刚刚上传的文件进行测试

hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.9.2.jar wordcount /user/hadoop/input /user/hadoop/output

注:在执行过程中,如果长时间处于 Running 状态不动,虽然没有报错,但实际上是出错了,后台在不断重试,需要到 logs 目录下(/usr/local/hadoop/logs)查看日志文件中的错误信息。

查看输出结果

hdfs dfs -ls /user/hadoop/output
[root@hadoop0 wcinput]# hdfs dfs -ls /user/hadoop/output
Found 2 items
-rw-r--r--   1 root supergroup          0 2019-01-22 05:35 /user/hadoop/output/_SUCCESS
-rw-r--r--   1 root supergroup         51 2019-01-22 05:35 /user/hadoop/output/part-r-00000

_SUCCESS 表示 HDFS 文件状态,生成的结果在 part-r-00000 中查看。

hdfs dfs -cat /user/hadoop/output/part-r-00000
[root@hadoop0 wcinput]# hdfs dfs -cat /user/hadoop/output/part-r-00000
hadoop    4
hdfs    1
hello    1
mapreduce    2
spark    1
yarn    1
[root@hadoop0 wcinput]#

以上就是使用 Docker 环境搭建 Hadoop 镜像容器,配置 Hadoop 集群,并启动和测试的实例,测试用的是 hadoop 官方给的一个 wordcount 统计,利用 hadoop 安装包里的 mapreduce 示例 jar 计算指定 HDFS 文件里的单词数,并将结果输出到指定 HDFS 目录。后面会介绍 HDFS 常用文件操作命令。


可能遇到的问题

2019-01-21 11:59:03,762 ERROR org.apache.hadoop.hdfs.StateChange: BLOCK* NameSystem.getDatanode: Data node DatanodeRegistration(192.168.10.11:50010, datanodeUuid=d0414725-2312-4b4c-b076-d88ced1c8a72, infoPort=50075, infoSecurePort=0, ipcPort=50020, storageInfo=lv=-57;cid=CID-8ce81437-477e-47f8-b9b5-a77620c12eae;nsid=1136296144;c=1548061612190) is attempting to report storage ID d0414725-2312-4b4c-b076-d88ced1c8a72. Node 192.168.10

原因: 拷贝hadoop安装包时,包含 data 与 tmp 文件夹,未成功格式化 datanode, 清空该 hadoop 节点目录下东西即可。

解决:

rm -rf /usr/local/hadoop/tmp/dfs/data/*
hadoop datanode -format
org.apache.hadoop.hdfs.server.namenode.SafeModeException: Cannot delete /tmp/hadoop-yarn/staging/root/.staging/job_1548075342638_0001. Name node is in safe mode.
The reported blocks 0 needs additional 21 blocks to reach the threshold 0.9990 of total blocks 22.
The number of live datanodes 2 has reached the minimum number 0. Safe mode will be turned off automatically once the thresholds have been reached. NamenodeHostName:hadoop0

这是说明 NameNode 由于系统断电,内存不足等原因导致 dataNode 丢失超过设置的丢失百分比,系统自动进入安全模式,那么如何退出安全模式呢?

# 执行命令退出安全模式
hadoop dfsadmin -safemode leave
# 执行健康检查,删除损坏掉的block
hdfs fsck  /  -delete
ERROR org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode: Exception in doCheckpoint

可能是删除此 hadoop 文件中的 tmp 目录后没有重新格式化的结果。

解决:

# 进入到 `/usr/local/hadoop` 目录下
bin/hdfs namenode -format -force

results matching ""

    No results matching ""