WSL 与 Hyper-V 中安装 Hadoop

安装前准备

根据 Hadoop Java Versions,最新版 Hadoop 支持 Java 8 与 Java 11 的运行时环境,同时还支持 Java 8 的编译。此外,Apache Hadoop 社区编译测试都使用 OpenJDK,因此这里我们选择安装 OpenJDK 8。Ubuntu 仓库中的 headless 版 JDK 是针对无需 GUI 界面的 Java 程序的软件包,占用空间更小。

此外,Hadoop 官方文档要求安装并启用 ssh,并且推荐安装 pdsh 更好地管理 ssh 资源。

1
sudo apt install openjdk-8-jdk-headless ssh pdsh

使用命令 sudo service ssh start 启动 ssh(WSL 不能使用 systemctl,只能用 SysV init 的命令),若出现 sshd: no hostkeys available -- exiting. 的错误,使用 sudo ssh-keygen -A 生成一个 hostkey 后重试。

从 https://hadoop.apache.org/releases.html 下载合适的二进制版本压缩包(这里选择 hadoop 3.3.4)。

安装 Hadoop

使用 tar xvaf hadoop-3.3.4.tar.gz 解压,随后进入 hadoop-3.3.4 目录,编辑目录下的 etc/hadoop/hadoop-env.sh 文件,定义环境参数:

1
2
# 设置 Java 安装的根目录,若使用 apt 安装 openjdk-8-jdk-headless,路径应为下述路径
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

随后在目录中执行 bin/hadoop 可以看到一些简要说明。

Okay…… 其实已经安装结束了,可以使用单机模式的 Hadoop 了。

尝试一下 Hadoop 提供的 MapReduce 的示例程序:

1
2
3
4
mkdir input
cp etc/hadoop/*.xml input # 复制配置目录作为输入
bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.4.jar grep input output 'dfs[a-z.]+' # 查找并显示给定正则表达式的每个匹配项,输出到给定的 output 目录
cat output/*

如果没有问题的话会出现很多运行信息,最后的文件内容输出是:

1
2
neko@ubuntu:~/hadoop-3.3.4$ cat output/*
1 dfsadmin

伪分布式集群配置

伪分布式集群指在一台机器上开多个 Hadoop 的 Java 进程来模拟在多台机器上运行 Hadoop 的效果。

修改配置文件

在目录中编辑 etc/hadoop/core-site.xml 文件:

找到最末尾的

1
2
<configuration>
</configuration>

替换为

1
2
3
4
5
6
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>

编辑 etc/hadoop/hdfs-site.xml 文件:

找到最末尾的

1
2
3
<configuration>

</configuration>

替换为

1
2
3
4
5
6
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>

配置免密码登录 ssh

尝试使用 ssh localhost 连接本地 ssh。

若出现 ssh: connect to host localhost port 22: Connection refused 的问题,可能是 ssh 未启动,可以使用 sudo service ssh status 查看 ssh 的状态。

若连接需要密码,需要使用如下命令创建密钥以实现免密码登录:

1
2
3
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys

运行 MapReduce 任务

  1. 格式化文件系统:

    1
    bin/hdfs namenode -format
  2. 启动 NameNode 守护进程和 DataNode 守护进程:

    1
    sbin/start-dfs.sh
    • 若出现类似 pdsh@ubuntu: localhost: rcmd: socket: Permission denied 的错误,可能是 pdsh 的默认 rcmd 配置不是 ssh。

      使用 pdsh -q -w localhost 命令检查,若 Rcmd type 一栏内容不是 ssh,在 ~/.bashrc 或你的 shell 解释器配置文件中加入 export PDSH_RCMD_TYPE=ssh,并使用 source ~/.bashrc 或你的 shell 解释器对应命令更新当前配置。随后再次尝试。

  3. 浏览器中打开 NameNode 网页接口,应该能看到相关信息。URL 默认是 http://localhost:9870/

  4. 创建 HDFS 用户目录以运行 MapReduce 任务:

    1
    2
    bin/hdfs dfs -mkdir /user
    bin/hdfs dfs -mkdir /user/<username> # <username> 替换为你当前系统的用户名,以下命令若未使用绝对路径,则默认是从 /user/<username> 开始检索的
  5. 复制任务输入文件到分布式文件系统中:

    1
    2
    bin/hdfs dfs -mkdir input
    bin/hdfs dfs -put etc/hadoop/*.xml input
  6. 尝试执行示例程序:

    1
    bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.4.jar grep input output 'dfs[a-z.]+'

    和单机示例程序一样,如果没有问题的话会出现很多运行信息。

  7. 从分布式文件系统中拷出文件至本地并检查:

    1
    2
    3
    # 执行命令前记得先删掉之前单机示例程序创建的 output 目录,不然会拷出到那个目录下
    bin/hdfs dfs -get output output
    cat output/*

    或者可以直接查看分布式文件系统中的文件:

    1
    bin/hdfs dfs -cat output/*

    运行结果应该是:

    1
    2
    1       dfsadmin
    1 dfs.replication
  8. 结束守护进程:

    1
    sbin/stop-dfs.sh

Hadoop 守护进程的日志会写入 $HADOOP_LOG_DIR 文件夹,此文件夹默认在 $HADOOP_HOME/logs

多开虚拟机配置“真”分布式集群

通常,集群中需要有一台 NameNode,一台 ResourceManager,它们是主服务器用于控制。其他服务(例如 Web App Proxy Server 和 MapReduce Job History 服务器)通常在专用硬件或共享基础设施上运行,具体取决于负载。

集群中的其余机器同时充当 DataNode 和 NodeManager,作为从服务器用于计算。

在这套模拟配置中,我们开三台 Hyper-V 虚拟机,均部署 DataNodeNodeManager 服务,1 号额外部署 NameNodeResourceManager 服务。

这里之所以不用 WSL 是因为 WSL 开多个发行版实际上是在一台虚拟机中运行的,这会导致多个发行版(虚拟磁盘)系统用的 IP 是同一个,不便于批量管理。

配置 Hadoop

Hadoop 有两类重要的配置文件:

  • 只读的默认配置文件
    • core-default.xml, hdfs-default.xml, yarn-default.xmlmapred-default.xml.
  • 每个站点单独的配置文件
    • etc/hadoop/core-site.xml, etc/hadoop/hdfs-site.xml, etc/hadoop/yarn-site.xmletc/hadoop/mapred-site.xml.

此外还有环境配置的文件,比如 etc/hadoop/hadoop-env.sh

要配置集群,需要配置 Hadoop 守护服务器的 environmentconfiguration parameters 参数。

HDFS 的守护服务器包括 NameNode、SecondaryNameNode 和 DataNode。YARN 的守护服务器有ResourceManager、NodeManager 和 WebAppProxy。如果要使用 MapReduce,那么 MapReduce Job History 服务器也将运行。对于大型集群,它们通常运行在不同的主机上。

配置环境

和前面一样,先配置 etc/hadoop/hadoop-env.sh 中的 Java 路径。

各个守护进程还有各自的运行环境配置选项,如 Java 虚拟机内存等,可以在 etc/hadoop/hadoop-env.sh 看到各选项的说明和示例,这里暂时不做配置。

此外,通常还会配置 HADOOP_HOME 环境变量,可以在 /etc/profile.d 文件夹中添加一个 sh 文件,包含以下两行:

1
2
HADOOP_HOME=/home/neko/hadoop-3.3.4 # 这里填写 Hadoop 的路径
export HADOOP_HOME

使用 source /etc/profile 使其生效。

配置 hosts

为方便起见,可以配置 /etc/hosts 文件指定各台 Hadoop 服务器的 IP 到某个 hostname。

此外,要在 /etc/hostname 中将不同的虚拟机修改为不同的主机名,避免在管理面板中发现多个节点合并显示的情况。

下面的配置均使用 hadoop00x 代替第 x 台虚拟机的 IP 地址。

配置密钥

和上面一样,为三台服务器配置各自的密钥。

此外还需将 1 号虚拟机的公钥写到 2 号、3 号虚拟机的 ~/ .ssh/authorized_key 文件中:

1
2
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop002
ssh-copy-id -i ~/.ssh/id_rsa.pub hadoop003

配置守护进程

编辑 etc/hadoop/core-site.xml,将末尾几行修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop001:9000</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>131072</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/neko/hadoop_tmp</value>
</property>
</configuration>
  • fs.defaultFS:NameNode 的 URI,值的格式为 hdfs://host:port/
  • io.file.buffer.size:顺序文件的读写缓存大小。
  • hadoop.tmp.dir:Hadoop 集群存储临时文件的目录

编辑 etc/hadoop/hdfs-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<configuration>
<property>
<name>dfs.namenode.name.dir</name>
<value>/home/neko/hadoop-3.3.4/data</value>
</property>
<property>
<name>dfs.blocksize</name>
<value>268435456</value>
</property>
<property>
<name>dfs.namenode.handler.count</name>
<value>100</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/home/neko/hadoop-3.3.4/blocks</value>
</property>
</configuration>
  • dfs.namenode.name.dir:NameNode 持久存储命名空间和事务日志的本地路径,可用逗号分隔多个路径以存储多份。
  • dfs.hosts / dfs.hosts.exclude:允许/排除的 DataNode,如有必要可以设置。
  • dfs.blocksize:HDFS 大文件系统的块大小。
  • dfs.namenode.handler.count:NameNode 服务线程数。
  • dfs.datanode.data.dir:在 DataNode 中存储块的本地路径列表,以逗号分隔。如果这是一个逗号分隔的目录列表,那么数据将存储在所有目录中,通常会设置不同的设备/磁盘。

编辑 etc/hadoop/yarn-site.xml

1
2
3
4
5
6
7
8
9
10
<configuration>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hadoop001</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>
  • yarn.resourcemanager.hostname:可以设置它来代替设置所有 yarn.resourcemanager*address 地址资源。端口为 ResourceManager 组件的默认端口。
  • yarn.nodemanager.aux-services:若需要使用 MapReduce 需要设置成 mapreduce_shuffle
  • 其余配置省略

编辑 etc/hadoop/mapred-site.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.map.memory.mb</name>
<value>1536</value>
</property>
<property>
<name>mapreduce.map.java.opts</name>
<value>-Xmx1024M</value>
</property>
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>3072</value>
</property>
<property>
<name>mapreduce.reduce.java.opts</name>
<value>-Xmx2560M</value>
</property>
<property>
<name>mapreduce.task.io.sort.mb</name>
<value>512</value>
</property>
<property>
<name>mapreduce.task.io.sort.factor</name>
<value>100</value>
</property>
<property>
<name>mapreduce.reduce.shuffle.parallelcopies</name>
<value>50</value>
</property>
<property>
<name>yarn.app.mapreduce.am.env</name>
<value>HADOOP_MAPRED_HOME=/home/yu/hadoop-3.3.4</value>
</property>
<property>
<name>mapreduce.map.env</name>
<value>HADOOP_MAPRED_HOME=/home/yu/hadoop-3.3.4</value>
</property>
<property>
<name>mapreduce.reduce.env</name>
<value>HADOOP_MAPRED_HOME=/home/yu/hadoop-3.3.4</value>
</property>
</configuration>
  • mapreduce.framework.name:执行框架(local / yarn)
  • mapreduce.map.memory.mb:map 资源限制(单位:MB)
  • mapreduce.map.java.opts:用于 map 的子 jvm 的堆大小
  • mapreduce.reduce.memory.mb:reduce 资源限制(单位:MB)
  • mapreduce.reduce.java.opts:用于 reduce 的子 jvm 的堆大小
  • mapreduce.task.io.sort.mb:用于排序以提高效率的内存限制
  • mapreduce.task.io.sort.factor:在对文件排序时一次合并的流
  • mapreduce.reduce.shuffle.parallelcopies:reduce 从大量的 map 中获取输出所运行的并行副本数
  • mapreduce.jobhistory.address:MapReduce Job History 服务的 host:port,默认端口为 10020
  • mapreduce.jobhistory.webapp.address:MapReduce Job History 的 Web UI 的 host:port,默认端口为 19888
  • mapreduce.jobhistory.intermediate-done-dir:MapReduce 作业写入历史文件的目录
  • mapreduce.jobhistory.done-dir:由 MapReduce Job History 服务管理历史文件的目录。

编辑 etc/hadoop/workers

一行一个所有的 woker 的 hostname:

1
2
3
hadoop001
hadoop002
hadoop003

Hadoop 管理

1
2
3
4
5
6
7
8
# 在 NameNode 节点(hadoop001)上格式化 HDFS
$HADOOP_HOME/bin/hdfs namenode -format
# 如果设置了 etc/hadoop/workers 和 SSH 免密钥登录,可以在 NameNode 节点(hadoop001)上使用此命令同步启动所有节点的 HDFS 和 YARN:
$HADOOP_HOME/sbin/start-dfs.sh
$HADOOP_HOME/sbin/start-yarn.sh
# 停止则使用如下命令:
$HADOOP_HOME/sbin/stop-dfs.sh
$HADOOP_HOME/sbin/stop-yarn.sh

默认管理端口号:

HDFS:9870

YARN:8088

可以通过 hadoop001:9870hadoop001:8088 或者使用对应 IP 查看相应的管理面板。

注意请谨慎使用格式化命令,多次格式化后会导致 NameNode 的 clusterID 和 DataNode 的 clusterID 不匹配,无法启动 DataNode。如果在 HDFS 的管理界面中发现 Live Nodes 数为 0,在 DataNode 节点中使用 jps 查看发现没有 datanode 进程,可能就是这个原因导致的。这个时候可以选择从头重来一遍,或者修改配置文件(直接在 hadoop 目录下搜索文件名 VERSION)以匹配两者的 clusterID,或者删除 DataNode 节点 hadoop 目录下的 blocks 文件夹,删除 NameNode 节点 hadoop 目录下的 data 文件夹,以及所有节点 hadoop 目录下的 logs 文件夹,重新格式化后启动。

阅读材料

  1. 深入浅出大数据:到底什么是Hadoop?