Hadoop Learning

Tao Zou

2025-05-03

Hadoop 组成

Hadoop 用来存储分析海量数据,其组件为:

  • HDFS: 负责数据存储。一个NameNode管理多个DataNode,Second NameNode负责辅助NameNode。

  • Yarn: 负责资源调度。一个ResourceManager(RM)管理多个NodeManager。RM管理整个集群的内存和cpu。每个NodeManager上可以有多个container,用于运行任务。如果一个NodeManager上有2个cpu核心,则这个NM上最多可以同时运行2个container(这种说法严格来讲不准确)。

  • MapReduce: 负责计算。Map阶段并行处理输入数据,Reduce阶段汇总结果。MapReduce好像用于离线计算,Spark可以用于内存计算。MapReduce的上层有Hive支持,Spark的上层有Spark MLlib、Spark SQL、Spark Streaming等支持。

虚拟机配置

硬件

我的联想电脑的CPU核心数为12。虚拟机运行数量 * 每个虚拟机CPU数 * 每个CPU核心数 不要超过12。

IP

设置VMWare的IP地址

VMWare中,点击编辑->虚拟网络编辑器,会看见VMnet1和VMnet8。选中VMnet8,点击更改设置。再次点击编辑->虚拟网络编辑器,会看见VMnet0,VMnet1和VMnet8。选中VMnet8,可以将子网IP第三个八位字节设置为10(如192.168.10.0)。点击NAT设置,将网关IP也同样设置(网关IP的第四个八位字节一般是2)。最后,点击虚拟网络编辑器中的确定。

本机Windows中设置VMnet8的地址

在本机中,点击以太网->更改适配器选项->VMWare Network Adapter VMnet8->属性->Internet协议版本 4。按照下图配置。

虚拟机内部设置IP地址

打开文件。如果识别不出vim命令,则需要下载apt install vim

su root
sudo vim /etc/netplan/01-network-manager-all.yaml

键入i进入编辑模式。

network:
  version: 2
  renderer: networkd
  ethernets:
    ens33:
      dhcp4: no
      addresses:
        - 192.168.10.100/24
      gateway4: 192.168.10.2
      nameservers:
        addresses:
          - 192.168.10.2

Esc,再键入:wq可以保存修改的内容。

应用更改:

sudo netplan apply

修改主机名称:

vim /etc/hostname

建立IP地址与主机名的映射关系:

sudo vim /etc/hosts

增加下述内容:

192.168.10.100 hadoop100
192.168.10.101 hadoop101
192.168.10.102 hadoop102
192.168.10.103 hadoop103
192.168.10.104 hadoop104
192.168.10.105 hadoop105
192.168.10.106 hadoop106
192.168.10.107 hadoop107
192.168.10.108 hadoop108
  1. 使用ifconfig检查ens33部分的内容,查看inet是否为192.168.10.100

  2. 然后再ping www.baidu.com看看是否能访问外网。

  3. hostname查看主机名称是否为hadoop100

在克隆出的新的虚拟机中,需要修改01-netcfg.yaml中的ens33 的 addresses 为192.168.10.102192.168.10.103等,以及修改主机名即可。

Hadoop准备

安装Hadoop、JDK

安装JDK

安装JDK需要注意下载操作系统对应的版本,例如64位的Ubantu系统需要对应安装64位的JDK。如果错误地安装JDK版本,操作系统运行java命令会报错找不到对应文件。通过uname -m,如果显示x86_64则说明当前Ubantu系统是64位的。

解压文件,在压缩包路径下运行下述命令,-C参数指定解压路劲。

tar -zxvf jdk-22_linux-x64_bin.tar.gz -C /opt/JDK17

配置Java环境变量,创建一个环境文件my_env.sh

cd /etc/profile.d
sudo vim my_env.sh

编写Java环境变量

#JAVA_HOME
export JAVA_HOME=/opt/JDK17/jdk-22.0.1
export PATH=$PATH:$JAVA_HOME/bin

刷新

source /etc/profile
java -version
# 查看Ubantu机环境变量
echo $PATH

安装Hadoop

参照安装JDK,解压->打开my_env.sh,编写环境:

#HADOOP_HOME
export HADOOP_HOME=/opt/Hadoop336/hadoop-3.3.6
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
source /etc/profile

Hadoop本地模式

新建一个.txt文档如下:

aa bb
cc aa

运行下述命令:

hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount myinput/word.txt myoutput

输出文件会被保存在新创建的文件夹myoutput中,myoutput文件夹须由hadoop创建,该文件夹不能预先存在。

节点文件同步和SSH免密登录

scp复制文件到其他节点

使用scp命令复制文件到其他节点上。

# 从hadoop102拷贝jdk-22.0.1到hadoop103相同目录下
scp -r /opt/JDK17/jdk-22.0.1 zt@hadoop103:/opt/JDK17
# 从hadoop103拉取hadoop102的hadoop-3.3.6
scp -r zt@hadoop102:/opt/Hadoop336/hadoop-3.3.6 ./
# 在hadoop103执行命令将hadoop102的jdk-22.0.1和hadoop-3.3.6拷贝到hadoop104上
scp -r zt@hadoop102:/opt/* zt@hadoop104:/opt

上述代码在hadoop103将“公钥-私钥”对创建好了发送公钥给hadoop104后,才能够被成功执行。

rsync同步文件夹

rsync -av /opt/Hadoop336/hadoop-3.3.6 zt@hadoop104:/opt/Hadoop336/hadoop-3.3.6

循环复制文件到所有节点的相同目录下

使用自定义命令。

#在/root/bin目录下(没有则创建mkdir bin)
vim xsync

在xsync中写入:

if [ $# -lt 1 ]; then 
    echo "Not Enough Argument!"
    exit;
fi

for host in "hadoop102" "hadoop103" "hadoop104"; 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 exist!"
        fi
    done
done

然后执行chmod 777 xsync

然后在/root/bin目录下运行:xsync /bin即可将/bin文件夹及其内容同步到hadoop103和hadoop104的/bin中了。

注意该命令为用户自建命令。如果在root用户下执行该命令时要给该命令加上绝对路径,让root用户能找到该命令。

SSH免密登录

在hadoop103上创建一个“公钥-私钥对”。将公钥发送给hadoop104。当hadoop103发送用私钥加密的数据到hadoop104时,hadoop104会通过hadoop103的公钥将数据解密。hadoop104要给hadoop103发送响应时,也会使用hadoop103的公钥加密数据,hadoop103接收数据后通过私钥解密。

# 在.ssh目录下执行下述命令,之后输入三次回车,会在.ssh目录下生成id_rsa私钥和id_rsa.pub公钥两个文件
ssh-keygen -t rsa
# 通过下述命令可以将本地的公钥和密钥发送到hadoop103上,之后即可通过ssh hadoop103对hadoop 103无密访问。
ssh-copy-id hadoop103

.ssh/authorized_keys中可以看到哪些节点可以无密访问本节点。

注意“公钥-私钥”对对于每一个用户都是不同的。比如在zt用户下配置了本节点的公钥-密钥且传送到了hadoop104,但是切换到root用户后,依然不能对hadoop104进行无密访问。

hadoop102也需要自己对自己设置无密访问。

配置Hadoop集群

目标配置情况

hadoop102 hadoop103 hadoop104
HDFS NameNode, DataNode DataNode SecondaryNameNode, DataNode
YARN NodeManager ResourceManager, NodeManager NodeManager

寻找默认配置文件:

  1. ~/Hadoop336/hadoop-3.3.6/share/hadoop/common/中执行unzip hadoop-common-3.3.6.jar,可以在相同文件夹下得到文件core-default.xml

  2. ~/Hadoop336/hadoop-3.3.6/share/hadoop/hdfs/中执行unzip hadoop-hdfs-3.3.6.jar,可以在相同文件夹下得到文件hdfs-default.xml

  3. ~/Hadoop336/hadoop-3.3.6/share/hadoop/mapreduce/中执行unzip hadoop-mapreduce-client-core-3.3.6.jar,可以在相同文件夹下得到文件mapred-default.xml

  4. ~/Hadoop336/hadoop-3.3.6/share/hadoop/yarn/中执行unzip hadoop-yarn-common-3.3.6.jar,可以在相同文件夹下得到文件yarn-default.xml

可用户自定义的配置文件存在于$HADOOP_HOME/etc/hadoop中,分别为:core-site.xml, hdfs-site.xml, mapred-site.xml, yarn-site.xml

-site.xml配置文件,

core配置

vim $HADOOP_HOME/etc/hadoop/core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <!-- 指定NameNode的地址 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://hadoop102:8020</value>
    </property>
    <!-- 指定hadoop数据的存储目录。dfs.namenode.name.dir和dfs.datanode.data.dir会被自动设置到${hadoop.tmp.dir}/dfs/name和${hadoop.tmp.dir}/dfs/data中。dfs.namenode.checkpoint.dir和dfs.namenode.checkpoint.edits.dir路径则会被设置在${hadoop.tmp.dir}/dfs/namesecondary中。而   yarn.nodemanager.local-dirs会被自动设置在${hadoop.tmp.dir}/nm-local-dir中 -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/Hadoop336/hadoop-3.3.6/data</value>
    </property>
    <!-- 配置HDFS网页登录使用的静态用户为hdfs_user -->
    <property>
        <name>hadoop.http.staticuser.user</name>
        <value>hdfs_user</value>
    </property>
</configuration>

HDFS配置

$HADOOP_HOME/etc/hadoop/hdfs-site.xml

HDFS中的NameNode与DataNode通信的默认监听端口为8020

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <!-- 暴露NameNode的Web访问地址 -->
    <property>
        <name>dfs.namenode.http-address</name>
        <value>hadoop102:9870</value>
    </property>
    <!-- 暴露Secondary NameNode的Web访问地址 -->
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>hadoop104:9868</value>
    </property>
</configuration>

YARN配置

$HADOOP_HOME/etc/hadoop/yarn-site.xml

YARN中的ResourceManager与NodeManager通信的默认监听端口为8032(这可以在YARN的web页面的conf路径下找到)。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <!-- 指定MR走Shuffle -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <!-- 指定ResourceManaer的地址 -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>hadoop103</value>
    </property>
    <!-- 环境变量的继承 -->
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
    </property>
    <!-- 指定存储NodeManager聚合后的日志的HDFS路径 -->
    <property>
        <name>yarn.nodemanager.remote-app-log-dir</name>
        <value>/nmLogs</value>
    </property>
    
</configuration>

MapReduce配置文件

$HADOOP_HOME/etc/hadoop/mapred-site.xml

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <!-- 指定MapReduce运行在YARN上 -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <!-- 下述两个配置是设置mapreduce程序的执行过程的HDFS上的临时文件目录-->
    <property>
        <name>mapreduce.jobhistory.intermediate-done-dir</name>
        <value>/tmp/hadoop-yarn/staging/history/done_intermediate</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.done-dir</name>
        <value>/tmp/hadoop-yarn/staging/history/done</value>
    </property>
</configuration>

配置hadoop系统的JAVA_HOME

$HADOOP_HOME/etc/hadoop/hadoop-env.sh中追加下述内容,指定hadoop系统使用JAVA 8。这样做的好处是:即使我的系统JAVA_HOME是Java其他版本,也不会影响hadoop进程对Java 8的使用。

export JAVA_HOME=/opt/JDK8/jdk1.8.0_202

分发上述这个文件。

配置历史服务器和日志聚集

$HADOOP_HOME/etc/hadoop/mapred-site.xml中加入下述property,然后分发到其他节点。通过在$HADOOP_HOME/bin下执行命令mapred -daemon start historyserver运行历史服务器。在http://hadoop102:19888/jobhistory查看历史服务器的Web。

<property>
    <name>mapreduce.jobhistory.address</name>
    <value>hadoop102:10020</value>
</property>
<property>
    <name>mapreduce.jobhistory.webapp.address</name>
    <value>hadoop102:19888</value>
</property>

通过历史服务器可以查看YARN任务的配置参数、任务细节、运行时间、程序运行的日志等。

$HADOOP_HOME/etc/hadoop/mapred-site.xml中加入下述property。然后分发到其他节点。通过这种方式配置日志聚集,使得可以在历史服务器中看到程序的运行日志。

<property>
  <name>yarn.log-aggregation-enable</name>
  <value>true</value>
</property>
<property>
  <name>yarn.log.server.url</name>
  <value>http://hadoop102:19888/jobhistory/logs</value>
</property>
<!--设置日志的保留时间为7天  -->
<property>
  <name>yarn.log-aggregation.retain-seconds</name>
  <value>604800</value>
</property>

启动和测试集群

启动集群

配置data和logs文件夹

在集群启动前配置Linux用户hdfs_user:hadoop。今后一切与hadoop相关的操作都需要切换到hadoop用户组。

groupadd hadoop
useradd -g hadoop -m hdfs_user
usermod -s /bin/bash hdfs_user

下述两个文件夹必须提前配置好。

# 在root用户下执行下述所有代码:
ssh hadoop102 mkdir $HADOOP_HOME/data
ssh hadoop102 chown hdfs_user:hadoop $HADOOP_HOME/data
ssh hadoop102 chmod 770 $HADOOP_HOME/data
ssh hadoop103 mkdir $HADOOP_HOME/data
ssh hadoop103 chown hdfs_user:hadoop $HADOOP_HOME/data
ssh hadoop103 chmod 770 $HADOOP_HOME/data
ssh hadoop104 mkdir $HADOOP_HOME/data
ssh hadoop104 chown hdfs_user:hadoop $HADOOP_HOME/data
ssh hadoop104 chmod 770 $HADOOP_HOME/data

ssh hadoop102 mkdir $HADOOP_HOME/logs
ssh hadoop102 chown hdfs_user:hadoop $HADOOP_HOME/logs
ssh hadoop102 chmod 770 $HADOOP_HOME/logs
ssh hadoop103 mkdir $HADOOP_HOME/logs
ssh hadoop103 chown hdfs_user:hadoop $HADOOP_HOME/logs
ssh hadoop103 chmod 770 $HADOOP_HOME/logs
ssh hadoop104 mkdir $HADOOP_HOME/logs
ssh hadoop104 chown hdfs_user:hadoop $HADOOP_HOME/logs
ssh hadoop104 chmod 770 $HADOOP_HOME/logs

另外再对hadoop这个用户组配置container-executor的权限(这一步在Kerberos安全模式的部分也有说明):

ssh hadoop102 chown root:hadoop $HADOOP_HOME/bin/container-executor
ssh hadoop102 chmod 6050 $HADOOP_HOME/bin/container-executor
ssh hadoop103 chown root:hadoop $HADOOP_HOME/bin/container-executor
ssh hadoop103 chmod 6050 $HADOOP_HOME/bin/container-executor
ssh hadoop104 chown root:hadoop $HADOOP_HOME/bin/container-executor
ssh hadoop104 chmod 6050 $HADOOP_HOME/bin/container-executor

集群初始化

$HADOOP_HOME/etc/hadoop/workers中将localhost修改为:

hadoop102
hadoop103
hadoop104

在hdfs_user用户下执行命令hdfs namenode -format初始化。初始化过程会生成datalogs两个文件夹。在前置步骤中已经配置过这两个文件夹,这是更好的做法。

启动HDFS

在hadoop102节点上执行下述命令启动HDFS系统:

$HADOOP_HOME/sbin/start-dfs.sh

停止HDFS系统:

$HADOOP_HOME/sbin/stop-dfs.sh

另外,HDFS不允许用root用户启动。如果要强制使用root用户进行操作,可以在$HADOOP_HOME/etc/hadoop/hadoop-env.sh中添加下述内容。(非常不建议这样做,因为系统会不安全

export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root

export YARN_NODEMANAGER_USER=root
export YARN_RESOURCEMANAGER_USER=root

启动YARN

节点hadoop103$HADOOP_HOME目录下执行命令sbin/start-yarn.sh可以启动YARN。Web端yarn位于http://hadoop103:8088

测试集群

测试HDFS

# 在hadoop根目录下创建新文件夹
hadoop fs -mkdir /myfolder
# 上传本节点文件到myfolder文件夹
hadoop fs -put ~/mytest.txt /myfolder

上传到hadoop的上述文件实际上是被分成了多个数据块被存储到了每一个DataNode中,每个DataNode都有一份该文件的副本。具体路径为:

Hadoop336/data/dfs/data/current/BP-1433059334-192.168.10.102-1725551956628/current/finalized/subdir0/subdir0

每一个DataNode中都有所上传的mytest.txt文件的完整版。如果是一个大文件,它可能被拆分并存储到几个”blk_…“文件中。

测试YARN

# 在$HADOOP_HOME路径下执行下述命令。
# 确保在/myfolder下有一个用于被统计单词数量的.txt文件
# /myfolderOutput为输出文件夹
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /myfolder /myfolderOutput

Hadoop集群启停命令

我将之命名为hadoopstartstop.sh。在/root/bin目录下创建好hadoopstartstop.sh后,写入下述代码。之后执行chmod 777 hadoopstartstop.sh。之后使用hadoopstartstop.sh starthadoopstartstop.sh stop即可启停。

标准版

#!/bin/bash

if [ $# -lt 1 ]
then 
    echo "No Args Input..."
    exit ;
fi

case $1 in
"start")
        echo "====== 启动hadoop集群 ======"
        echo "------ 启动hdfs ------"
        ssh hadoop102 "/opt/Hadoop336/hadoop-3.3.6/sbin/start-dfs.sh"
        echo "------ 启动yarn ------"
        ssh hadoop103 "/opt/Hadoop336/hadoop-3.3.6/sbin/start-yarn.sh"
        echo "------ 启动history server ------"
        ssh hadoop102 "/opt/Hadoop336/hadoop-3.3.6/bin/mapred --daemon start historyserver"
;;
"stop")
        echo "====== 关闭hadoop集群 ======"
        echo "------ 关闭history server ------"
        ssh hadoop102 "/opt/Hadoop336/hadoop-3.3.6/bin/mapred --daemon stop historyserver"
        echo "------ 关闭yarn ------"
        ssh hadoop103 "/opt/Hadoop336/hadoop-3.3.6/sbin/stop-yarn.sh"
        echo "------ 关闭hdfs ------"
        ssh hadoop102 "/opt/Hadoop336/hadoop-3.3.6/sbin/stop-dfs.sh"
;;
*)
    echo "Input Args Error..."
;;
esac

Kerberos安全版

#!/bin/bash

if [ $# -lt 1 ]
then
    echo "No Args Input..."
    exit ;
fi

case $1 in
"start")
        echo "====== 启动hadoop集群 ======"
        echo "------ 启动hdfs ------"
        ssh hadoop102 "su - hdfs_user -c '/opt/Hadoop336/hadoop-3.3.6/sbin/start-dfs.sh'"
        echo "------ 启动yarn ------"
        ssh hadoop103 "su - yarn_user -c '/opt/Hadoop336/hadoop-3.3.6/sbin/start-yarn.sh'"
        echo "------ 启动history server ------"
        ssh hadoop102 "su - mapred_user -c '/opt/Hadoop336/hadoop-3.3.6/bin/mapred --daemon start historyserver'"
;;
"stop")
        echo "====== 关闭hadoop集群 ======"
        echo "------ 关闭history server ------"
        ssh hadoop102 "su - mapred_user -c '/opt/Hadoop336/hadoop-3.3.6/bin/mapred --daemon stop historyserver'"
        echo "------ 关闭yarn ------"
        ssh hadoop103 "su - yarn_user -c '/opt/Hadoop336/hadoop-3.3.6/sbin/stop-yarn.sh'"
        echo "------ 关闭hdfs ------"
        ssh hadoop102 "su - hdfs_user -c '/opt/Hadoop336/hadoop-3.3.6/sbin/stop-dfs.sh'"
;;
*)
    echo "Input Args Error..."
;;
esac

自定义查看所有节点Java进程情况

/root/bin下创建jpsall,并写入下述信息。然后执行chmod 777 jpsall

#!/bin/bash

for host in hadoop102 hadoop103 hadoop104
do
        echo ============ $host ============
        ssh $host /opt/JDK8/jdk1.8.0_202/bin/jps
done

HDFS

HDFS分布式存储系统具有一次写入,多次读出的特点:一个文件经过创建、写入、关闭之后就不再改变,除了追加。

HDFS的文件在物理上是分块存储的(Block1, Block 2, …),一般100MB/S的磁盘传输速率设置块大小为128MB,200-300MB/s的磁盘传输速率设置块大小为256MB。

每一个Block只能属于一个文件,每个文件由一个或多个Block组成。Block的大小并不会被预先分配,例如我上传一个1KB的文件,那么这个文件由实际只占用空间1KB的Block存储。再次上传新的文件时,又会有新的Block来存储新上传的文件。

HDFS缺点:

  1. 不适合低延时的数据访问。

  2. 无法高效地对大量小文件进行存储。因为会占用大量NameNode内存(一般是128G)来存储文件目录。

  3. 不能被多个线程并发地写。

NameNode作用:

  1. 管理HDFS的名称空间。

  2. 配置副本策略。

  3. 管理数据块(Block)的映射信息。

  4. 处理客户端读写请求。

DataNode作用:

  1. 实际存储数据块。

  2. 执行数据块的读写操作。

HDFS的Shell操作

HDFS的基本命令hadoop fs <命令>hdfs dfs <命令>

HDFS上传、下载操作

# 查看某一hadoop命令的帮助文档
hadoop fs -help rm
# 在hadoop的根目录下创建一个名为threekingdoms的空文件夹
hadoop fs -mkdir /threekingdoms
# 将本节点的当前目录下的本地文件剪切上传到HDFS中,本地被上传的文件会被删除
hadoop fs -moveFromLocal ./shukingdom.txt /threekingdoms
# 将本节点的当前目录下的本地文件复制上传到HDFS中,与-put命令效果相同
hadoop fs -copyFromLocal ./shukingdom.txt /threekingdoms
# 追加一个文件到目的文件的末尾
hadoop fs -appendToFile ./jinkingdom.txt /threekingdoms/weikingdom.txt
# 从HDFS拷贝到本地,与-get命令效果相同
hadoop fs -copyToLocal /threekingdoms/shukingdom.txt ./

HDFS常用操作

# 将HDFS中一个文件拷贝到HDFS中的另一个路径中
hadoop fs -cp /myfolder/mytest.txt /threekingdoms
# 将HDFS中一个文件移动到HDFS中的另一个路径中
hadoop fs -mv /myfolder/mytest.txt /threekingdoms
# 递归删除一个目录中所有的内容
hadoop fs -rm -r /threekingdoms
# 将HDFS文件的所有者改为zt:ztgroup,冒号前时owner,冒号后是Group
hadoop fs -chown zt:ztgroup /threekingdoms
# 显示一个文件末尾1KB的数据
hadoop fs -tail /threekingdoms/weikingdom.txt
# 显示HDFS一个文件夹的大小,比如:44 132,44表示在一个datanode上的文件大小,132表示在所有datanode上的文件大小。
hadoop fs -du -s -h /threekingdoms
# 显示HDFS一个文件夹中所有文件和子文件夹的大小
hadoop fs -du -h /threekingdoms
# 设置副本数量为10,10会记录在NameNode元数据中,因为实际只有三个DataNode,因此最多只能有三个副本。不会对物理内存造成影响。
hadoop fs -setrep 10 /threekingdoms

查询某个文件具体的存储情况

hdfs fsck <HDFS上的文件路径> -files -blocks -locations

输出的部分信息如下:

Connecting to namenode via https://hadoop102:9871/fsck?ugi=hdfs_user&files=1&blocks=1&locations=1&path=%2Ftmp%2Foutput4%2Fpart-00023
FSCK started by nn/hadoop102@EXAMPLE.COM (auth:KERBEROS_SSL) from /192.168.0.45 for path /tmp/output4/part-00023 at Sat May 03 15:26:18 CST 2025

/tmp/output4/part-00023 3 bytes, replicated: replication=3, 1 block(s):  OK
0. BP-1893721203-192.168.0.45-1736402046661:blk_1073742691_1867 len=3 Live_repl=3  [DatanodeInfoWithStorage[192.168.11.210:9866,DS-5a16df09-7e9c-4ce1-abb8-31248812d7dd,DISK], DatanodeInfoWithStorage[192.168.0.45:9866,DS-5e8c62ba-a995-4d3a-8989-77b168e37af9,DISK], DatanodeInfoWithStorage[192.168.0.5:9866,DS-266d72b4-db30-47ab-8bca-52cf6505d339,DISK]]

上述信息表示文件/tmp/output4/part-00023总大小为3字节,占用1个block,存在着3个副本, 3个副本分别位于上述列出的DataNode上。该文件在所有的3个节点上的实际物理存储地址可能是/data/hadoop/hdfs/data/current/BP-1893721203-192.168.0.45-1736402046661/current/finalized/subdir*/subdir*/blk_1073742691*,区别在于subdir路径可能不同。

HDFS的API操作

NameNode和SecondaryNameNode的机制

NameNode元数据是存储在运行内存当中的。NameNode的内存为128GB,一个Block占元数据的150B。

  1. NameNode启动,加载编辑日志edits和镜像文件fsimage到运行内存中。

  2. 客户端向NameNode发送增/删/查/改请求。

  3. edits记录操作日志、更新滚动日志。

  4. 增/删/查/改操作完成。

  5. Secondary NameNode请求NameNode是否需要CheckPoint,若需要则执行CheckPoint。CheckPoint的触发条件为:1.定时时间到了;2.Edits中的数据满了。

  6. edits滚动,从edits_inprogress_001变为edits_001,新增edits_inprogress_002。

  7. NameNode中的edits和fsimage被拷贝到Secondary NameNode中并生成fsimage.chkpoint然后拷贝到NameNode中,该fsimage.chkpoint被重命名为fsimage

fsimage和edits

在NameNode被初始化后,在Hadoop336/data/dfs/name/current中会生成下述四个文件:

fsimage_0000000000000000000
fsimage_0000000000000000000.md
seen_txid
VERSION

fimage文件是HDFS元数据中的一个永久性检查点,包含HDFS文件系统的所有目录和文件inode的序列化信息。可以使用下述命令查看每一个fsimage文件。

hdfs oiv -p XML -i fsimage_0000000000000000000 -o /my/path/to/store/myfsimage.xml

edits是存放HDFS所有更新操作的文件,客户端请求的所有操作首先会被记录到edits中。可以使用下述命令查看每一个edits文件。

hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /my/path/to/store/myedits.xml

seen_txid文件保存的是一个数字,即最后一个edits_的数字。我曾今在HDFS上做了随意一个修改,然后删除了记录了该修改的Edits Log文件edits_inprogress_0000000000000000731。此时seen_txid里面记录的值为731。之后我重启HDFS集群发现namenode启动失败,报错显示txid校验失败,然后我手动将seen_txid中的731改为730后,namenode就可以正常启动了,最后我发现我一开始所做的哪个随意HDFS修改不见了。

设置CheckPont默认触发

hdfs-default.xml中添加下述配置,设置NameNode的CheckPoint的默认触发时间是1小时,默认edits满操作次数是10000次,检查edits操作次数的时间间隔为1分钟。

<property>
    <name>dfs.namenode.checkpoint.period</name>
    <value>3600s</value>
</property>
<property>
    <name>dfs.namenode.checkpoint.txns</name>
    <value>10000</value>
</property>
<property>
    <name>dfs.namenode.checkpoint.check.period</name>
    <value>60s</value>
</property>

DataNode工作机制

  1. DataNode在每个Block中存储数据+数据长度+校验和+时间戳

  2. DataNode启动后向NameNode注册,NameNode向DataNode返回注册成功的信息。以后每隔一定时间DataNode向NameNode上报所有的Block信息。

  3. 心跳3秒一次,用于确保每个NameNode和DataNode之间的通信连接正常。若NameNode超过10分钟+30秒没有收到DataNode的心跳,则认为该DataNode不可用。

参数设置:

<property>
    <name>dfs.blockreport.intervalMsec</name>
    <value>21600000</value>
    <description>Determines block reporting interval in milliseconds.</description>
</property>
<property>
    <name>dfs.datanode.directoryscan.interval</name>
    <value>21600s</value>
    <description>Interval in seconds for Datanode to scan its data directories and reconcile the difference between blocks in memory and on the disk.</description>
</property>

数据完整性

数据在上传到HDFS中时会在本地进行数据封装,给数据增加一个校验位(如crc校验位)。HDFS接收到数据时会根据校验位对数据进行检查。校验位如果与数据不匹配,则表明数据在上传过程中出现损坏。

掉线时限

如果DataNode与NameNode超过\(2\times \text{recheck_interval} + 10 \times \text{heart_beat_interval}=10\text{mins}\ 30\text{seconds}\),则NameNode与DataNode断开通讯,认为DataNode不可用。

参数设置:

<property>
    <name>dfs.datanode.heartbeat.recheck-interval</name>
    <value>300000</value>
    <description>millisecond.</description>
</property>
<property>
    <name>dfs.heartbeat.interval</name>
    <value>3</value>
    <description>second.</description>
</property>

HDFS的读写数据流程

客户端写数据的逻辑流程

  1. 客户端向NameNode请求上传目标文件。NameNode检查权限和目录结构,返回允许上传的响应。该过程需要创建DistributedFileSystem对象

  2. 客户端请求上传第一个Block(0-128MB)。NameNode元数据进行副本存储节点的选择,返回DataNode1, DataNode2, DataNode3这三个存储数据的节点。

  3. 客户端HDFS数据流向DataNode1传输文件,DataNode1向DataNode2传输文件,DataNode2向DataNode3传输文件。DataNode3向DataNode2响应传输成功,DataNode2向DataNode1响应传输成功,DataNode1向客户端响应传输成功。为提高并发性,DataNode1开始被写入文件的同时已经向DataNode2节点开始传输文件了。数据流会将目标文件缓存为一个个chunk(512B)+chunksum(4B),当所有chunk512B+chunksum4B足够大了之后它们会被封装成一个Packet(64KB),Packet是传输数据的最小单元。该过程需要创建FSDataOutputStream对象

客户端读数据的逻辑流程

  1. 客户端向NameNode请求下载文件。NameNode检查权限以及文件是否存在,返回目标文件的元数据(包含目标文件的存储目录、所在的Block)。该过程需要创建DistributedFileSystem对象

2.客户端HDFS数据流向节点距离最近的DataNode请求数据,若当前最近DataNode节点负载过大,则数据流会向别的DataNode请求数据。读取DataNode文件时采取串型读取方式,即读完目标文件的Block1后再读取Block2。该过程需要创建FSDataInputStream对象

网络拓扑节点距离计算

设有一台服务器,其结构如下:

\[ 服务器 \begin{cases} 集群d1 \begin{cases} 机架r1: \text{DataNode1}, \text{DataNode2}, \text{DataNode3}\\ 机架r2: \text{DataNode4}, \text{DataNode5}, \text{DataNode6}\\ 机架r3: \text{DataNode7}, \text{DataNode8}, \text{DataNode9} \end{cases}\\ 集群d2 \begin{cases} 机架r4: \text{DataNode10}, \text{DataNode11}, \text{DataNode12}\\ 机架r5: \text{DataNode13}, \text{DataNode14}, \text{DataNode15}\\ 机架r6: \text{DataNode16}, \text{DataNode17}, \text{DataNode18} \end{cases} \end{cases} \]

DataNode7和DataNode12的节点距离为:\(2+2+2=6\)

DataNode1和DataNode2的节点距离为:\(2\)

HDFS的默认副本存储方式:加入副本因子为\(3\),那么HDFS会在当前DataNode下存储一份副本,然后在随机另一个机架的随机DataNode中存储一份副本(出于安全性考虑),最后再在这个随机机架中选择一个随机DataNode存储一份副本(机架传输速度更快)。

MapReduce

MapReude概述

MapReduce不擅长实时计算,不能实现像MySQL那种毫秒级查询。

MapReduce不擅长流式计算,不能像Sparkstreaming和flink那样处理新出现的一条一条的数据。

MapReduce不擅长有向无环图计算(迭代式串联计算),而Spark的RDD适合做这样的计算。

Map阶段对原始数据进行切分,然后分布式计算,保存计算结果到磁盘分区。

Reduce阶段从磁盘分区中拿取数据并汇总,然后输出最终结果。

MapReduce进程:

  1. MrAppMaster:负责整个程序的过程调度和状态协调。

  2. MapTask:负责Map阶段的整个数据处理流程。

  3. ReduceTask:负责Reduce阶段的整个数据处理流程。

Map和Reduce的原理

以WordCount任务为例。

在Map阶段,原始输入文本wordcount.txt会被分割成3个数据块(input splits)发送到三个Hadoop节点上。KeyInput为偏移量,其数据类型为LongWritable。ValueInput为每一行文本,其数据类型为Text。KeyOutput为单词,其数据类型为Text。ValueOutput为频次1,其数据类型为IntWritable。三个节点独立并行运行。最终节点1的Map结果为:(“hello”, 1), (“hello”, 1), (“world”, 1);节点2的结果为:(“hello”, 1), (“hadoop”, 1);节点3的结果为:(“hadoop”, 1)。

在Reduce阶段,先对各个节点的Map结果进行shuffle排序,然后对属于同一个Reduce任务的数据进行数据传输(传输过程涉及序列化)。比如节点2的1个(“hello”, 1)记录会被传送到节点1上。三个”hello”属于同一个reduce任务,reduce任务执行计算\(1+1+1\)

MapReduce编程

MapReduce编程代码中需要涉及:1. Mapper;2. Reducer;3. Driver三个部分。样例代码存放于practice_files/Mapreduce_WordCount中。

程序在Hadoop集群上的运行命令为:

hadoop jar Mapreduce_WordCount-1.0-SNAPSHOT-jar-with-dependencies.jar /threekingdoms/shuinput /threekingdoms/shuoutput

其中HDFS路径/threekingdoms/shuinput下存放的是待统计词频的一个.txt文件。在这个程序中是不需要指定这个.txt文件本身的,而只需指定它的上级文件夹。输出文件夹shuoutput不能预先存在,这个文件夹只能由程序自己创建,程序执行完毕后里面存放的是“_SUCCESS”和“part-r-00000”两个文件,后者存储了词频统计的结果。

MapReduce序列化

序列化技术用于解决不同节点之间内存数据的传输问题。Hadoop序列化可以减轻待传输数据的校验信息,方便节点之间的通信。

将一个节点内存中的数据序列化为字节码到磁盘,然后传输字节码到另一个节点,字节码反序列化加载到另一个节点的内存中。

可序列化的数据才能在Reduce阶段在不同的节点中进行网络传输。

自定义序列化类FlowBean.java位于practice_files\mapreduce_files\src\main\java\com\zt\mapreduce\mobiledata中。

InputFormat

MapTask数量(即并行度)由数据存储在HDFS中的切片数量决定。切片大小默认为Block_Size。切片只针对于单个数据文件,比如wordcount.txt而不是对wordcount.txt和mobiledata.txt视作一个整体切片。

例如我有一个400MB的文件分布式存储在下述三个节点中(设Block大小为128MB,切片大小默认为128MB):

节点1:140MB(\(140\text{MB} < 128\text{MB}\times1.1\)

节点2:200MB

节点3:60MB

则在Map操作中,节点1中的所有140MB数据会被划分为一个切片,因为它小于切片大小的1.1倍(\(128\times1.1=140.8\));节点2中的200MB数据会被划分为两个切片(128MB+72MB);节点3中的60MB数据会被划分为一个切片。

FileInputFormat源码数据流程

  1. 程序先找到输入数据存储目录。

  2. 遍历输入数据存储目录下的每一个文件。

    • 获取文件大小
    • 计算切片大小
    • 将切片信息写到切片规划文件中
  3. 切片规划提交到YARN,由YARN开启MapTask个数。

CombineTextInputFormat

FileInputFormat常见的接口实现类:TextInputFormat, KeyValueInputFormat, NLineInputFormat, CombineTextInputFormat, 自定义InputFormat等。

CombineTextInputFormat是解决大量小文件的方法,将多个小文件从逻辑上规划到一个切片中。

设input文件夹下有4个文件:a.txt(1.7MB), b.txt(5.1MB), c.txt(3.4MB), d.txt(6.8MB)

1.7在0~1个最大分割大小内,故分1块1.7MB

5.1在1~2个最大分割大小内,故分2块2.55MB

3.4在0~1个最大分割大小内,故分1块3.4MB

6.8在1~2个最大分割大小内,故分2块3.4MB

切片过程:从上到下对块累加,如果大于最大分割大小则单独形成一个切片。

最终的切片情况,形成3个切片:1.7MB, 2.55MB, 2.55MB, 3.4MB, 3.4MB, 3.4MB

// 在driver.java文件中的“设置最终输出KV类型”代码之后,“设置输入输出路径”代码之前添加如下内容。
job.setInputFormatClass(CombineTextInputFormat.class)
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304)  // 设置最大分割大小为4MB

Shuffle

OutputFormat

ETL(Extract-Transform-Load)

ETL用来描述将数据从源端经过抽取、转换、加载到目的端的过程。

YARN

YARN工作流

YARN相当于是一个分布式操作系统。YARN由ResourceManager、NodeManager、Container、ApplicationMaster等组件构成。

ResourceManager的作用

  1. 接收客户端提交任务的请求。

  2. 监控NodeManager。

  3. 启动、监控ApplicationMaster。

  4. 资源的分配与调度。

NodeManager的作用:

  1. 管理单个节点上的资源。

  2. 处理来自ResourceManager的命令。

  3. 处理来自ApplicationMaster的命令。

ApplicationMaster的作用:

  1. 为应用程序申请资源并分配给内部的任务。

  2. 任务的监控和容错。

Container的作用:

Container是YARN资源的抽象,它封装了一个节点上的如内存、CPU、磁盘、网络等多个维度的资源。例如一个节点有4个物理CPU核心,每一个核心被映射成一个vCore,每一个Container使用一个vCore,那么这个节点上最多可以拥有的Container的数量是4。

YARN 调度器

FIFO调度器:先来的job先被服务。例如job0中有4个MapTask任务,集群首先最多同时处理3个MapTask任务,然后再处理job0中剩下的那一个MapTask任务,之后job0才能被处理完成,然后才处理之后的job。FIFO效率非常差。

容量调度器

  1. 多队列,每个队列采用FIFO调度策略

  2. 可以为每个队列配置资源最低保证和资源上限。

  3. 一个队列中的资源有剩余时,剩余的这部分资源可以共享给别的队列。

  4. 每个队列支持多用户。多个用户可以在同一个队列中运行应用程序。调度器可以对同一个用户所使用的资源总量进行限定。

(1). 使用深度优先算法,优先选择实际资源占用率最低的队列分配资源;(2). 按照队列中作业的优先级和提交时间顺序分配资源;(3). 依次按照作业中容器的优先级、数据本地性原则分配资源。

公平调度器(大公司常用):

YARN常用命令

# 查看所有正在运行的application
yarn application -list
# 查看某特定状态的application(状态选项:ALL, NEW, NEW_SAVING, SUBMITTED, ACCEPTED, RUNNING, FINISHED, FAILED, KILLED)
yarn application -lis -appStates FINISHED
# 结束某applicatin,-kill 后的参数是application ID
yarn application -kill <application_ID>
# 查看yarn日志
yarn logs -applicationId <application_ID>
# 查看某个应用所对应的某个Container的信息
yarn logs -applicationId <application_ID> -containerId <container_ID>
# 查看尝试运行的任务及其容器ID
yarn applicationattempt -list <application_ID>
# 查看某个application所对应的所有Container的信息,只有在运行状态中才能查看到Container的信息。
yarn container -list <application_ID>
# 查看某个Container的状态
yarn container -status <container_ID>
# 查看yarn节点状态
yarn node -list -all
# 重新加载修改之后的队列参数
yarn rmadmin -refreshQueues
# 查看队列信息
yarn queue -status <QueueName>

YARN参数配置

配置:我的VMWare的每一个节点都是2GB内存+2个CPU(每个CPU拥有2个核心)。

需求:处理1GB的文件。设置Block大小为128GB,则要分为8个MapTask,至少1个ReduceTask,1个mrAppMaster。

$HADOOP_HOME/etc/hadoop/yarn-site.xml。如果集群各个节点性能一致,则可以直接分发下述的YARN配置,否则需要对不同的性能的节点单独配置。

(在配置下述参数之前,我已经在VMWare上将所有节点都设置了快照名为YARN1)

<!-- 配置容量调度器 -->
<property>
    <description>The class to use as the resource scheduler.</description>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>
<!-- ResourceManager最大能处理调度器请求的线程数量。在我的VMWare集群中同一时间最多提供3×2×2=12个线程,减去4个线程用于其他服务,因此我在这里设置RM最大处理线程数量为8 -->
<property>
    <description>Number of threads to handle scheduler interface.</description>
    <name>yarn.resourcemanager.scheduler.client.thread-count</name>
    <value>8</value>
</property>
<!-- 关闭YARN的自动检测硬件配置功能 -->
<property>
    <description>Enable auto-detection of node capabilities such as memory and CPU.</description>
    <name>yarn.nodemanager.resource.detect-hardware-capabilities</name>
    <value>false</value>
</property>
<!-- 是否虚拟化CPU Core为vCore。当集群中个节点CPU型号不同时可以启动次选项 -->
<property>
    <name>yarn.nodemanager.resource.count-logical-processors-as-cores</name>
    <value>false</value>
</property>
<!-- 单个CPU Core映射成多少个vCore -->
<property>
    <name>yarn.nodemanager.resource.pcores-vcores-multiplier</name>
    <value>1.0</value>
</property>
<!-- NodeManager使用当前节点的内存大小为2GB(默认配置是8GB) -->
<property>
    <name>yarn.nodemanager.resource.memory-mb</name>
    <value>2048</value>
</property>
<!-- NodeManager的CPU核心数量(默认是8个) -->
<property>
    <name>yarn.nodemanager.resource.cpu-vcores</name>
    <value>4</value>
</property>
<!-- 一个Container的最小占用内存大小设置为512MB -->
<property>
    <name>yarn.scheduler.minimum-allocation-mb</name>
    <value>512</value>
</property>
<!-- 一个Container的最大占用内存大小设置为2GB。当我设置为1GB时去运行example wordcount任务会出现Container请求了1536MB的大内存而报错-->
<property>
    <name>yarn.scheduler.maximum-allocation-mb</name>
    <value>2048</value>
</property>
<!-- 一个Container的最小CPU核心数量 -->
<property>
    <name>yarn.scheduler.maximum-allocation-vcores</name>
    <value>1</value>
</property>
<!-- 一个Container的最大CPU核心数量 -->
<property>
    <name>yarn.scheduler.maximum-allocation-vcores</name>
    <value>2</value>
</property>
<!-- 关闭虚拟内存检测。因为Java8和CentOS7系统对虚拟内存的不适配性,Java8实际没有充分利用CentOS为它预留的内存而去使用有限的Java堆内存,可能会导致虚拟内存经常性爆掉。-->
<property>
    <name>yarn.nodemanager.vmem-check-enabled</name>
    <value>false</value>
</property>
<!-- 物理内存到虚拟内存的映射比例。2×2.1=4.2GB-->
<property>
    <name>yarn.nodemanager.vmem-pmem-ratio</name>
    <value>2.1</value>
</property>

YARN队列配置(容量调度器)

设有两个队列:default和hive。

需求1:default队列期望使用40%的集群资源,限制其最大能使用60%的集群资源;hive队列期望使用40%的集群资源,限制其最大能使用60%的集群资源。

需求2:配置队列优先级。

$HADOOP_HOME/etc/hadoop/capacity-scheduler.xml中修改下述内容:

修改完毕后执行yarn rmadmin -refreshQueues刷新,不必重启集群。

配置好后,在HADOOP_HOME中执行hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount -D mapreduce.job.queuename=hive /threekingdoms/shukingdom.txt /output333即可指定只在hive队列上运行该实例程序。

通过在WordCountDriver.java中添加下述内容也可以实现指定需要提交到的队列。

Configuration conf = new Configuration();
conf.set("mapreduce.job.queuename", "hive");
<property>
    <name>yarn.scheduler.capacity.root.queues</name>
    <value>default,hive</value>
    <description>
      The queues at the this level (root is the root queue).
    </description>
</property>

<property>
    <name>yarn.scheduler.capacity.root.default.capacity</name>
    <value>40</value>
    <description>Default queue target capacity.</description>
</property>

<!--添加下述内容-->
<property>
    <name>yarn.scheduler.capacity.root.hive.capacity</name>
    <value>60</value>
    <description>Default queue target capacity.</description>
</property>

<!-- 一个用户使用的最大的default队列的资源比例 -->
<property>
    <name>yarn.scheduler.capacity.root.default.user-limit-factor</name>
    <value>1</value>
    <description>
      Default queue user limit a percentage from 0.0 to 1.0.
    </description>
</property>

<!-- 一个用户使用的最大的hive队列的资源比例 -->
<property>
    <name>yarn.scheduler.capacity.root.hive.user-limit-factor</name>
    <value>1</value>
    <description>
      hive queue user limit a percentage from 0.0 to 1.0.
    </description>
</property>

<property>
    <name>yarn.scheduler.capacity.root.default.maximum-capacity</name>
    <value>60</value>
    <description>
      The maximum capacity of the default queue.
    </description>
</property>

<property>
    <name>yarn.scheduler.capacity.root.hive.maximum-capacity</name>
    <value>80</value>
    <description>
      The maximum capacity of the hive queue.
    </description>
</property>

<!--队列保持运行状态-->
<property>
    <name>yarn.scheduler.capacity.root.default.state</name>
    <value>RUNNING</value>
    <description>
      The state of the default queue. State can be one of RUNNING or STOPPED.
    </description>
</property>
<property>
    <name>yarn.scheduler.capacity.root.hive.state</name>
    <value>RUNNING</value>
    <description>
      The state of the hive queue. State can be one of RUNNING or STOPPED.
    </description>
</property>

<!--设置让哪些用户可以在某队列上提交任务,*表示所有用户-->
<property>
    <name>yarn.scheduler.capacity.root.default.acl_submit_applications</name>
    <value>*</value>
    <description>
      The ACL of who can submit jobs to the default queue.
    </description>
</property>
<property>
    <name>yarn.scheduler.capacity.root.hive.acl_submit_applications</name>
    <value>*</value>
    <description>
      The ACL of who can submit jobs to the hive queue.
    </description>
</property>

<!--设置让哪些用户可以操作某队列,*表示所有用户-->
<property>
    <name>yarn.scheduler.capacity.root.default.acl_administer_queue</name>
    <value>*</value>
    <description>
      The ACL of who can administer jobs on the default queue.
    </description>
</property>
<property>
    <name>yarn.scheduler.capacity.root.hive.acl_administer_queue</name>
    <value>*</value>
    <description>
      The ACL of who can administer jobs on the hive queue.
    </description>
</property>

<!--设置让哪些用户可以设置队列中任务的优先级,*表示所有用户-->
<property>
    <name>yarn.scheduler.capacity.root.default.acl_application_max_priority</name>
    <value>*</value>
    <description>
      The ACL of who can submit applications with configured priority.
      For e.g, [user={name} group={name} max_priority={priority} default_priority={priority}]
    </description>
</property>
<property>
    <name>yarn.scheduler.capacity.root.hive.acl_application_max_priority</name>
    <value>*</value>
    <description>
      The ACL of who can submit applications with configured priority.
      For e.g, [user={name} group={name} max_priority={priority} default_priority={priority}]
    </description>
</property>

<!--默认YARN任务最大生命周期,-1表示不设时间限制。(还有一个default-application-lifetime,我不太清楚为什么还需要这样一个参数。)-->
<property>
    <name>yarn.scheduler.capacity.root.default.maximum-application-lifetime
    </name>
    <value>-1</value>
</property>
<property>
    <name>yarn.scheduler.capacity.root.hive.maximum-application-lifetime
    </name>
    <value>-1</value>
</property>

YARN任务优先级

$HADOOP_HOME/etc/hadoop/yarn-site.xml

<!-- 设置5个任务优先级 -->
<property>
    <name>yarn.cluster.max-application-priority</name>
    <value>5</value>
</property>

运行下述命令,模拟提交长任务直至资源紧张。优先级越大的任务优先执行。

cd $HADOOP_HOME
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 5 50000

hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi -D mapreduce.job.priority=1 5 50000

YARN队列配置(公平调度器)

需求:新建root.group.testroot.group.zt两个队列。若提交任务未指定队列,则由test用户提交的任务提交到root.group.test队列上,zt用户提交的任务提交到root.group.zt队列上。

$HADOOP_HOME/etc/hadoop/yarn-site.xml文件的末尾追加下述内容。

<property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
    <description>配置使用公平调度器</description>
</property>
<property>
    <name>yarn.scheduler.fair.allocation.file</name>
    <value>/home/zt/development/Hadoop336/hadoop-3.3.6/etc/hadoop/fair-scheduler.xml</value>
    <description>指定公平调度器的配置文件</description>
</property>
<property>
    <name>yarn.scheduler.fair.preemption</name>
    <value>false</value>
    <description>禁止队列之间的资源抢占</description>
</property>

创建fair-scheduler.xml文件。

<?xml version="1.0"?>
<allocations>
    <!--单个队列中Application Master占用这个队列的资源的最大比例,企业一般配置0.1-->
    <queueMaxAMShareDefault>0.5</queueMaxAMShareDefault>
    <!--单个队列最大资源的默认值-->
    <queueMaxResourcesDefault>2048mb,4vcores</queueMaxResourcesDefault>
    <!--新增队列test-->
    <queue name="test">
        <!--队列最小资源-->
        <minResources>1024mb,2vcores</minResources>
        <!--队列最大资源-->
        <maxResources>2048mb,4vcores</maxResources>
        <!--队列中最多同时运行的应用数,默认50,根据线程数配置-->
        <maxRunningApps>4</maxRunningApps>
        <!--队列中Application Master占用资源的最大比例-->
        <maxAMShare>0.5</maxAMShare>
        <!--该队列的资源权重,默认值为1.0-->
        <weight>1.0</weight>
        <!--队列内部的资源分配策略-->
        <schedulingPolicy>fair</schedulingPolicy>
    </queue>
    <!--新增队列zt-->
    <queue name="zt" type="parent">
        <!--队列最小资源-->
        <minResources>1024mb,2vcores</minResources>
        <!--队列最大资源-->
        <maxResources>2048mb,4vcores</maxResources>
        <!--队列中最多同时运行的应用数,默认50,根据线程数配置-->
        <maxRunningApps>4</maxRunningApps>
        <!--队列中Application Master占用资源的最大比例-->
        <!--maxAMShare>0.5</maxAMShare-->
        <!--该队列的资源权重,默认值为1.0-->
        <weight>1.0</weight>
        <!--队列内部的资源分配策略-->
        <schedulingPolicy>fair</schedulingPolicy>
    </queue>
    <!--任务队列分配策略,可配置多层的规则,从第一个规则开始匹配,直到匹配成功-->
    <queuePlacementPolicy>
        <rule name="specified" create="false"/>
        <rule name="nestedUserQueue" create="true">
            <rule name="primaryGroup" create="false"/>
        </rule>
        <rule name="reject"/>
    </queuePlacementPolicy>
</allocations>

运行下述命令测试新建的队列。其中,下述第二个提交任务的命令会根据Linux系统当前的用户名称提交到对应的用户队列上面去(但是好像由于fair-scheduler.xml文件的问题,导致我无法实现这条功能)。(此外,由于每个队列的最大内存只分配了2048MB,这个容量大小执行下述命令是不满足的。)

cd $HADOOP_HOME

hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi -Dmapreduce.job.queuename=root.test 1 1

hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 1 1

Kerberos安全系统

  1. 客户端要访问服务端,需要先向Kerberos KDC (Key Distribution Center)发送请求。(?这个过程如何完成)

  2. Kerberos的认证服务器(AS, Authentication Server)负责校验客户端的身份信息(客户端名称、客户端IP、时间戳),认证成功后向客户端发送可以访问票证授予服务器ticket(包含有效时间、客户端可以解密和不可以解密的信息)。

  3. 客户端收到ticket,然后带上要访问的服务端名称访问票证授予服务器票证授予服务器返回客户端访问服务端的另外有一个ticket

病毒入侵情况

由于没有配置安全系统,在云公网上暴露HDFS_IP:9870和YARN_IP:8088曾让我的集群受到病毒入侵。

病毒被存在在路径/var/tmp下:

这些文件会启动一个异常ssh连接。即使是我重启整个集群,每个节点的CPU内存占用率莫名其妙高居不下,且通过htop命令发现不了异常进程。

Kerberos安装与配置

Kerberos安装

Kerberos官网

在Hadoop02上安装Kerberos服务端:

apt-get install krb5-kdc krb5-admin-server

服务端安装完成后会生成文件:``。

在Hadoop102, Hadoop103, Hadoop104上安装Kerberos客户端:

sudo apt-get install -y krb5-user libkrb5-dev libpam-krb5 libpam-ccreds

客户端安装完成后会生成文件:/etc/krb5.conf

查看Kerberos版本(纪录此笔记时的Kerberos版本为1.19.2):

klist -V

Kerberos服务端配置

vim /etc/krb5kdc/kdc.conf

原文件内容如下:

[kdcdefaults]
    kdc_ports = 750,88

[realms]
    EXAMPLE.COM = {
        database_name = /var/lib/krb5kdc/principal
        admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
        acl_file = /etc/krb5kdc/kadm5.acl
        key_stash_file = /etc/krb5kdc/stash
        kdc_ports = 750,88
        max_life = 10h 0m 0s
        max_renewable_life = 7d 0h 0m 0s
        #master_key_type = aes256-cts
        #supported_enctypes = aes256-cts:normal aes128-cts:normal
        default_principal_flags = +preauth
    }
  • kdc_ports:KDC服务的监听端口,这里配置的是750和88两个端口。一定要保证防火墙至少允许其中一个端口,比如ufw allow 88/tcpufw allow 88/udp,通过ufw status查看是否已经开放了目标端口。否则其他Kerberos客户端连接不上服务端的KDC。

  • EXAMPLE.COM:realm,大写。

  • master_key_type:Kerberos主秘钥的加密算法类型。

  • acl_file:Kerberos通过acl_file来确定Principal具有哪些权限。

  • admin_keytab:用于认证管理员密码。

  • supported_enctypes:支持的加密算法类型。

Kerberos客户端配置

vim /etc/krb5.conf

krb5.conf修改后的内容

[logging]
    default=FILE:/var/log/krb5libs.log
    kdc=FILE:/var/log/krb5kdc.log
    admin.server=FILE:/var/log/kadmind.log
[libdefaults]
    dns_lookup_realm=false
    ticket_lifetime=24h
    renew_lifetime=7d
    forwardable=true
    rdns=false
    pkinit_anchors=FILE:/etc/pki/tls/certs/ca-bundle.crt
    default_realm=EXAMPLE.COM
    #default_ccache_name=KEYRING:persistent:%{uid}
[realms]
    EXAMPLE.COM = {
    kdc=hadoop102
    admin_server=hadoop102
    }
[domain_realm]
    #.example.com=EXAMPLE.COM
    #example.com=EXAMPLE.COM
  • ticket_lifetime:客户端拿到的Kerberos服务端的票据的有效时间。

  • renew_lifetime:客户端可以向Kerberos服务端重新请求票据的时间。

  • ****:

分发/etc/krb5.conf到其他节点,让其他节点(Kerberos客户端)可以访问到hadoop102(Kerberos服务端)。

krb5.conf修改前的内容

krb5.conf  krb5kdc/   
root@hadoop102:/etc# vim /etc/krb5.conf
root@hadoop102:/etc# cat /etc/krb5.conf 
[libdefaults]
        default_realm = ATHENA.MIT.EDU

# The following krb5.conf variables are only for MIT Kerberos.
        kdc_timesync = 1
        ccache_type = 4
        forwardable = true
        proxiable = true

# The following encryption type specification will be used by MIT Kerberos
# if uncommented.  In general, the defaults in the MIT Kerberos code are
# correct and overriding these specifications only serves to disable new
# encryption types as they are added, creating interoperability problems.
#
# The only time when you might need to uncomment these lines and change
# the enctypes is if you have local software that will break on ticket
# caches containing ticket encryption types it doesn't know about (such as
# old versions of Sun Java).

#       default_tgs_enctypes = des3-hmac-sha1
#       default_tkt_enctypes = des3-hmac-sha1
#       permitted_enctypes = des3-hmac-sha1

# The following libdefaults parameters are only for Heimdal Kerberos.
        fcc-mit-ticketflags = true

[realms]
        ATHENA.MIT.EDU = {
                kdc = kerberos.mit.edu
                kdc = kerberos-1.mit.edu
                kdc = kerberos-2.mit.edu:88
                admin_server = kerberos.mit.edu
                default_domain = mit.edu
        }
        ZONE.MIT.EDU = {
                kdc = casio.mit.edu
                kdc = seiko.mit.edu
                admin_server = casio.mit.edu
        }
        CSAIL.MIT.EDU = {
                admin_server = kerberos.csail.mit.edu
                default_domain = csail.mit.edu
        }
        IHTFP.ORG = {
                kdc = kerberos.ihtfp.org
                admin_server = kerberos.ihtfp.org
        }
        1TS.ORG = {
                kdc = kerberos.1ts.org
                admin_server = kerberos.1ts.org
        }
        ANDREW.CMU.EDU = {
                admin_server = kerberos.andrew.cmu.edu
                default_domain = andrew.cmu.edu
        }
        CS.CMU.EDU = {
                kdc = kerberos-1.srv.cs.cmu.edu
                kdc = kerberos-2.srv.cs.cmu.edu
                kdc = kerberos-3.srv.cs.cmu.edu
                admin_server = kerberos.cs.cmu.edu
        }
        DEMENTIA.ORG = {
                kdc = kerberos.dementix.org
                kdc = kerberos2.dementix.org
                admin_server = kerberos.dementix.org
        }
        stanford.edu = {
                kdc = krb5auth1.stanford.edu
                kdc = krb5auth2.stanford.edu
                kdc = krb5auth3.stanford.edu
                master_kdc = krb5auth1.stanford.edu
                admin_server = krb5-admin.stanford.edu
                default_domain = stanford.edu
        }
        UTORONTO.CA = {
                kdc = kerberos1.utoronto.ca
                kdc = kerberos2.utoronto.ca
                kdc = kerberos3.utoronto.ca
                admin_server = kerberos1.utoronto.ca
                default_domain = utoronto.ca
        }

[domain_realm]
        .mit.edu = ATHENA.MIT.EDU
        mit.edu = ATHENA.MIT.EDU
        .media.mit.edu = MEDIA-LAB.MIT.EDU
        media.mit.edu = MEDIA-LAB.MIT.EDU
        .csail.mit.edu = CSAIL.MIT.EDU
        csail.mit.edu = CSAIL.MIT.EDU
        .whoi.edu = ATHENA.MIT.EDU
        whoi.edu = ATHENA.MIT.EDU
        .stanford.edu = stanford.edu
        .slac.stanford.edu = SLAC.STANFORD.EDU
        .toronto.edu = UTORONTO.CA
        .utoronto.ca = UTORONTO.CA

服务端配置kadmin.acl文件

vim /etc/krb5kdc/kadm5.acl

添加如下内容:

*/admin@EXAMPLE.COM *

上述第一个*代表任何用户,第二个*代表任何的权限。

初始化Kadmin数据库

kdb5_util create -s -r EXAMPLE.COM

密码设置为:123456

上述命令执行成功后,会在路径/etc/krb5kdc中生成由/etc/krb5kdc.conf所指定的stash文件,以及在路径/var/lib/krb5kdc中生成四个以principal为前缀的文件。

之后务必执行下述命令以启动Kerberos服务端:

systemctl start krb5-kdc
systemctl start krb5-admin-server

Kerberos服务端的启动、关闭、状态

systemctl start krb5-kdc
systemctl start krb5-admin-server
systemctl stop krb5-kdc
systemctl stop krb5-admin-server
systemctl status krb5-kdc
systemctl status krb5-admin-server

Kerberos常用命令

执行kadmin.loacl(Kerberos服务端登录)登录服务端Kerberos-KDC数据库。

执行?查看所有命令列表。

  • 展示所有的principal:
listprincs
  • 添加一个principal:
add_principal mytest/admin@EXAMPLE.COM
  • 删除一个principal:
delprinc mytest/admin@EXAMPLE.COM
  • 将一个principal加入到一个新的mytest.keytab文件中(/root/mytest.keytab不存在则会被创建):[-norandkey]可选参数表示不新生成随机密码
ktadd [-norandkey] -kt /root/mytest.keytab mytest/admin@EXAMPLE.COM

在Shell命令中输入klist -kt /root/mytest.keytab可以查看到里面已经有的principal。

  • 将一个principal从mytest.keytab中移除:
ktremove -kt /root/mytest.keytab mytest/admin@EXAMPLE.COM

Kerberos客户端向服务端认证

通过密码认证拿到服务端ticket

通过下述命令在Kerberos客户端(例如hadoop103)上登录创建的principal:“”。(通过命令klist可以查看登录状态以及客户端所获取的票据详情。)

kinit mytest/admin@EXAMPLE.COM

通过keytab认证拿到服务端ticket

在Hadoop103节点上执行下述命令,可以直接登录Kerberos服务端:

kinit -kt /root/mytest.keytab mytest/admin@EXAMPLE.COM

在拿到服务端ticket之后,通过命令kadmin就可以登录服务端的kerberos了。为了避免再次输入密码,记得在ktadd命令中加上-norandkey

一些关于Kerberos认证的说明

  • 对Kerberos的任意Principal认证后均可以访问到HDFS集群。在HDFS中的用户名为认证的Principal的全称(或在core-site.xml中配置的映射的名称),用户组是什么不明朗(好像是supergroup,且supergroup可以转变为父目录的组)。

  • 不对Kerberos认证则完全不能访问HDFS系统。

创建Linux用户及用户组hadoop

在每个节点上新增Linux用户组hadoop。创建hdfs_user,创建yarn_user,创建mapred_user创建密码。

groupadd hadoop
useradd -g hadoop -m hdfs_user

useradd -g hadoop -m yarn_user

useradd -g hadoop -m mapred_user

在kerberos中创建principal 并设置密码123456,然后就可以在任一的节点上使用passwd hdfs_user并输入current kerberos password: 123456可以修改密码。来实现修改用户密码了。在一个节点上修改的hdfs_user的用户密码直接适用于其他节点上的用户hdfs_user。遇到了一个很奇怪的bug,上述修改Linux用户的密码的过程可能不会顺利。在按照上述步骤修改密码前,。如果不能,需要执行kadmin.local -q "change_password hdfs_user"来重新修改密码为123456.虽然这个步骤看起来很奇怪,但确实能解决问题。

之后登录进各个节点的hdfs_user用户,相互设置ssh免密登录。yarn_user、mapred_user用户同理。先在各个节点的root用户下执行下述命令,之后再创建ssh免密登录。( ssh-copy-id hadoop103 。)

mkdir -p /home/hdfs_user/.ssh
mkdir -p /home/yarn_user/.ssh
mkdir -p /home/mapred_user/.ssh

chown -R hdfs_user:hadoop /home/hdfs_user/
chown -R yarn_user:hadoop /home/yarn_user/
chown -R mapred_user:hadoop /home/mapred_user/

运行HDFS任务时,切换到hdfs_user用户(或root用户)、认证任一与HDFS相关的Principal;运行YARN任务时,切换到yarn_user用户(或root用户)、认证任一与YARN相关的Principal;运行MapReduce任务时,切换到mapred_user用户(或root用户)、认证任一与MapReduce相关的Principal。

Hadoop本地目录权限

参照官方文档Permissions for both HDFS and local fileSystem paths

在hadoop102上执行:

# 下述两个文件夹由第一次启动HDFS集群时自动生成
chmod 700 $HADOOP_HOME/data/dfs/name
chown -R hdfs_user:hadoop $HADOOP_HOME/data/dfs/name
chmod 700 $HADOOP_HOME/data/dfs/data
chown -R hdfs_user:hadoop $HADOOP_HOME/data/dfs/data

# 下面这个文件夹为$HADOOP_LOG_DIR和$YARN_LOG_DIR的默认路径
chmod 775 $HADOOP_HOME/logs
chown -R hdfs_user:hadoop $HADOOP_HOME/logs

# 下面这个文件夹为yarn.nodemanager.local-dirs,它由第一次启动yarn集群时自动生成
chmod 755 $HADOOP_HOME/data/nm-local-dir
chown -R yarn_user:hadoop $HADOOP_HOME/data/nm-local-dir

# 下面这个文件夹为yarn.nodemanager.log-dirs,默认位置为$YARN_LOG_DIR/userlogs
chmod 755 $HADOOP_HOME/logs/userlogs
chown -R yarn_user:hadoop $HADOOP_HOME/logs/userlogs

# 下面这个文件是啥?
chown -R yarn_user:hadoop /tmp/hadoop-yarn-yarn_user

在hadoop103上执行:

chmod 700 $HADOOP_HOME/data/dfs/data
chown -R hdfs_user:hadoop $HADOOP_HOME/data/dfs/data

chmod 775 $HADOOP_HOME/logs
chown -R hdfs_user:hadoop $HADOOP_HOME/logs

chmod 755 $HADOOP_HOME/data/nm-local-dir
chown -R yarn_user:hadoop $HADOOP_HOME/data/nm-local-dir

chmod 755 $HADOOP_HOME/logs/userlogs
chown -R yarn_user:hadoop $HADOOP_HOME/logs/userlogs

chown -R yarn_user:hadoop /tmp/hadoop-yarn-yarn_user/

在hadoop104上执行:

chmod 700 $HADOOP_HOME/data/dfs/data
chown -R hdfs_user:hadoop $HADOOP_HOME/data/dfs/data
chmod 700 $HADOOP_HOME/data/dfs/namesecondary
chown -R hdfs_user:hadoop $HADOOP_HOME/data/dfs/namesecondary

chmod 775 $HADOOP_HOME/logs
chown -R hdfs_user:hadoop $HADOOP_HOME/logs

chmod 755 $HADOOP_HOME/data/nm-local-dir
chown -R yarn_user:hadoop $HADOOP_HOME/data/nm-local-dir

chmod 755 $HADOOP_HOME/logs/userlogs
chown -R yarn_user:hadoop $HADOOP_HOME/logs/userlogs

chown -R yarn_user:hadoop /tmp/hadoop-yarn-yarn_user/

在视频教程中,我还剩一个journalnode没有配置。

创建Hadoop principal账户

Kerberos服务规划(必须要认证相应的principal才能够操作hadoop组件):

创建每个hadoop组件的kerberos账户(principal):

下述代码在Kerberos服务端上创建了12个principal(即12个Hadoop用户),并将12个principal的keytab文件分别发送到对应节点上的/etc/security/keytabs的文件夹中。在后续的hdfs-site.xml配置文件中,NameNode就会使用Principal ,必须认证此账户才能操作NameNode。

ssh hadoop102
mkdir /etc/security/keytabs
kadmin.local -q "add_principal -randkey nn/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/nnService.keytab nn/hadoop102@EXAMPLE.COM"
kadmin.local -q "add_principal -randkey dn/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/dnService.keytab dn/hadoop102@EXAMPLE.COM"
kadmin.local -q "add_principal -randkey nm/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/nmService.keytab nm/hadoop102@EXAMPLE.COM"
kadmin.local -q "add_principal -randkey jhs/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/jhsService.keytab jhs/hadoop102@EXAMPLE.COM"
kadmin.local -q "add_principal -randkey https/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/httpsService.keytab https/hadoop102@EXAMPLE.COM"
chmod 400 /etc/security/keytabs/*
chmod 440 /etc/security/keytabs/httpsService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/nnService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/dnService.keytab
chown yarn_user:hadoop /etc/security/keytabs/nmService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/httpsService.keytab
chown mapred_user:hadoop /etc/security/keytabs/jhsService.keytab


ssh hadoop103
mkdir /etc/security/keytabs
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey dn/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/dnService.keytab dn/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey rm/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/rmService.keytab rm/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey nm/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/nmService.keytab nm/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey https/hadoop103@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/httpsService.keytab https/hadoop103@EXAMPLE.COM"
chmod 400 /etc/security/keytabs/*
chmod 440 /etc/security/keytabs/httpsService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/dnService.keytab
chown yarn_user:hadoop /etc/security/keytabs/rmService.keytab
chown yarn_user:hadoop /etc/security/keytabs/nmService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/httpsService.keytab

ssh hadoop104
mkdir /etc/security/keytabs
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey dn/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/dnService.keytab dn/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey nm/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/nmService.keytab nm/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey snn/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/snnService.keytab snn/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "add_principal -randkey https/hadoop104@EXAMPLE.COM"
kadmin -p mytest/admin@EXAMPLE.COM -w123456 -q "ktadd -kt /etc/security/keytabs/httpsService.keytab https/hadoop104@EXAMPLE.COM"
chmod 400 /etc/security/keytabs/*
chmod 440 /etc/security/keytabs/httpsService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/dnService.keytab
chown yarn_user:hadoop /etc/security/keytabs/nmService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/snnService.keytab
chown hdfs_user:hadoop /etc/security/keytabs/httpsService.keytab

修改Hadoop配置文件

修改配置文件以建立Hadoop集群组件与Principal账户之间的对应关系。

修改core-site.xml

vim $HADOOP_HOME/etc/hadoop/core-site.xml
    <!-- 启用Kerberos安全认证 -->
    <property>
        <name>hadoop.security.authentication</name>
        <value>kerberos</value>
    </property>
    <!-- 启用Hadoop集群授权管理 -->
    <property>
        <name>hadoop.security.authorization</name>
        <value>true</value>
    </property>
    <!-- Linux系统用户映射Kerberos principal的映射规则-->
    <property>
        <name>hadoop.security.auth_to_local.mechanism</name>
        <value>MIT</value>
    </property>
    <!-- 具体映射规则 -->
    <property>
        <name>hadoop.security.auth_to_local</name>
        <value>
            RULE:[2:$1/$2@$0]([dn]n\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0](snn\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0]([rn]m\/.*@EXAMPLE\.COM)s/.*/yarn_user/
            RULE:[2:$1/$2@$0](jhs\/.*@EXAMPLE\.COM)s/.*/mapred_user/
        </value>
    </property>

上述的hdfs_user、yarn_user、mapred_user与Linux用户hdfs_user、yarn_user、mapred_user应该没有关系。

修改hdfs-site.xml

vim $HADOOP_HOME/etc/hadoop/hdfs-site.xml
    <!-- 开启DataNode数据块的Kerberos认证 -->
    <property>
        <name>dfs.block.access.token.enable</name>
        <value>true</value>
    </property>
    <!-- NameNode对应的Kerberos的Principal,这个值也可以设置为nn/_HOST@EXAMPLE.COM -->
    <property>
        <name>dfs.namenode.kerberos.principal</name>
        <value>nn/hadoop102@EXAMPLE.COM</value>
    </property>
    <!-- NameNode服务对应的keytab文件 -->
    <property>
        <name>dfs.namenode.keytab.file</name>
        <value>/etc/security/keytabs/nnService.keytab</value>
    </property>
    <!-- DataNode对应的Kerberos的Principal -->
    <property>
        <name>dfs.datanode.kerberos.principal</name>
        <value>dn/_HOST@EXAMPLE.COM</value>
    </property>
    <!-- DataNode服务对应的keytab文件 -->
    <property>
        <name>dfs.datanode.keytab.file</name>
        <value>/etc/security/keytabs/dnService.keytab</value>
    </property>
    <!-- Secondary NameNode 对应的 Kerberos 的 Principal,这个值也可以设置为 snn/_HOST@EXAMPLE.COM -->
    <property>
        <name>dfs.secondary.namenode.kerberos.principal</name>
        <value>snn/hadoop104@EXAMPLE.COM</value>
    </property>
    <!-- Secondary NameNode 服务对应的 keytab 文件 -->
    <property>
        <name>dfs.secondary.namenode.keytab.file</name>
        <value>/etc/security/keytabs/snnService.keytab</value>
    </property>
    <!-- HDFS支持的HTTPS协议 -->
    <property>
        <name>dfs.http.policy</name>
        <value>HTTPS_ONLY</value>
    </property>
    <!-- DataNode的数据传输保护策略为仅认证模式-->
    <property>
        <name>dfs.data.transfer.protection</name>
        <value>authentication</value>
    </property>
    <!-- HDFS WebUI服务认证主体,这个值也可以设置为https/_HOST@EXAMPLE.COM -->
    <property>
        <name>dfs.web.authentication.kerberos.principal</name>
        <value>https/_HOST@EXAMPLE.COM</value>
    </property>
    <!-- HDFS WebUI服务对应的keytab文件 -->
    <property>
        <name>dfs.web.authentication.kerberos.keytab</name>
        <value>/etc/security/keytabs/httpsService.keytab</value>
    </property>

修改yarn-site.xml

vim $HADOOP_HOME/etc/hadoop/yarn-site.xml
    <!-- ResourceManager服务principal,这个值也可以设置为rm/_HOST@EXAMPLE.COM -->
    <property>
        <name>yarn.resourcemanager.principal</name>
        <value>rm/hadoop103@EXAMPLE.COM</value>
    </property>
    <!-- ResourceManager服务的keytab文件路径 -->
    <property>
        <name>yarn.resourcemanager.keytab</name>
        <value>/etc/security/keytabs/rmService.keytab</value>
    </property>
    <!-- NodeManager服务principal -->
    <property>
        <name>yarn.nodemanager.principal</name>
        <value>nm/_HOST@EXAMPLE.COM</value>
    </property>
    <!-- NodeManager服务的keytab文件路径 -->
    <property>
        <name>yarn.nodemanager.keytab</name>
        <value>/etc/security/keytabs/nmService.keytab</value>
    </property>

修改mapred-site.xml

vim $HADOOP_HOME/etc/hadoop/mapred-site.xml
    <!-- 配置JobHistoryServer的Principal和密码 -->
    <property>
        <name>mapreduce.jobhistory.principal</name>
        <value>jhs/hadoop102@EXAMPLE.COM</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.keytab</name>
        <value>/etc/security/keytabs/jhsService.keytab</value>
    </property>
    
    <property>
        <name>mapreduce.jobhistory.http.policy</name>
        <value>HTTPS_ONLY</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.spnego-principal</name>
        <value>https/_HOST@EXAMPLE.COM</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.spnego-keytab-file</name>
        <value>/etc/security/keytabs/httpsService.keytab</value>
    </property>

配置Hadoop的HTTPS访问

HTTPS=HTTP+SSL

生成自签名证书

HTTPS的访问需要有安全证书,下述是生成自签名证书和对应私钥的命令。生成有效期为100年的自签名证书保存在/root/hdfs_ca_cert中,私钥保存在/root/hdfs_ca_key中。两个文件夹都要分发到其他节点。

设置PEM pass phrase为:123456。

openssl req -new -x509 -keyout /root/hdfs_ca_key -out /root/hdfs_ca_cert -days 36500 -subj '/C=CN/ST=beijing/L=haidian/O=devA/OU=devB/CN=devC'

生成keystore文件和truststore文件

keystore文件存储了SSL握手所涉及的私钥以及证书信息。

输入下述命令,设置密码123456,然后在Enter key password for <hadoop102>时直接输入回车。

ssh hadoop102
keytool -keystore /root/keystore/ -alias hadoop102 -genkey -keyalg RSA -dname "CN=hadoop102,OU=dev,O=dev,L=dev,ST=dev,C=CN"
ssh hadoop103
keytool -keystore /root/keystore/ -alias hadoop103 -genkey -keyalg RSA -dname "CN=hadoop103,OU=dev,O=dev,L=dev,ST=dev,C=CN"
ssh hadoop104
keytool -keystore /root/keystore/ -alias hadoop104 -genkey -keyalg RSA -dname "CN=hadoop104,OU=dev,O=dev,L=dev,ST=dev,C=CN"

truststore文件存储了可信任的根证书,用于验证服务器证书链中的证书是否可信。在每个节点上分别执行下述命令。之后输入两遍密码123456,输入Y表示信任。

keytool -keystore /root/truststore -alias CARoot -import -file /root/hdfs_ca_cert

从keystore中导出cert

在各个节点上执行:

ssh hadoop102
keytool -certreq -alias hadoop102 -keystore /root/keystore -file /root/cert
ssh hadoop103
keytool -certreq -alias hadoop103 -keystore /root/keystore -file /root/cert
ssh hadoop104
keytool -certreq -alias hadoop104 -keystore /root/keystore -file /root/cert

生成自签名证书

使用hdfs_ca_cert和hdfs_ca_key对cert文件进行签名,生成自签名证书。分别在各个节点执行下述命令。密码123456

openssl x509 -req -CA /root/hdfs_ca_cert -CAkey /root/hdfs_ca_key -in /root/cert -out /root/cert_signed -days 36500 -CAcreateserial

将CA证书导入到keystore

将hdfs_ca_cert证书文件导入到keystore中。在每个节点执行下述命令。密码123456,Y确认。

keytool -keystore /root/keystore -alias CARoot -import -file /root/hdfs_ca_cert

将自签名证书导入到keystore

输入密码123456

ssh hadoop102
keytool -keystore /root/keystore -alias hadoop102 -import -file /root/cert_signed
ssh hadoop103
keytool -keystore /root/keystore -alias hadoop103 -import -file /root/cert_signed
ssh hadoop104
keytool -keystore /root/keystore -alias hadoop104 -import -file /root/cert_signed

将keystore和truststore文件导入到/home目录

在每个节点上执行下述命令:

cp keystore truststore /home/
chown -R root:hadoop /home/keystore
chown -R root:hadoop /home/truststore
chmod 770 /home/keystore
chmod 770 /home/truststore

配置ssl-server.xml文件

mv $HADOOP_HOME/etc/hadoop/ssl-server.xml.example $HADOOP_HOME/etc/hadoop/ssl-server.xml
vim $HADOOP_HOME/etc/hadoop/ssl-server.xml
<property>
  <name>ssl.server.truststore.location</name>
  <value>/home/truststore</value>
  <description>Truststore to be used by NN and DN. Must be specified.
  </description>
</property>

<property>
  <name>ssl.server.truststore.password</name>
  <value>123456</value>
  <description>Optional. Default value is "".
  </description>
</property>

<property>
  <name>ssl.server.keystore.location</name>
  <value>/home/keystore</value>
  <description>Keystore to be used by NN and DN. Must be specified.
  </description>
</property>

<property>
  <name>ssl.server.keystore.password</name>
  <value>123456</value>
  <description>Must be specified.
  </description>
</property>

<property>
  <name>ssl.server.keystore.keypassword</name>
  <value>123456</value>
  <description>Must be specified.
  </description>
</property>

配置完成后分发。

配置ssl-client.xml文件

mv $HADOOP_HOME/etc/hadoop/ssl-client.xml.example $HADOOP_HOME/etc/hadoop/ssl-client.xml
vim $HADOOP_HOME/etc/hadoop/ssl-client.xml
<property>
  <name>ssl.client.truststore.location</name>
  <value>/home/truststore</value>
  <description>Truststore to be used by clients like distcp. Must be
  specified.
  </description>
</property>

<property>
  <name>ssl.client.truststore.password</name>
  <value>123456</value>
  <description>Optional. Default value is "".
  </description>
</property>

<property>
  <name>ssl.client.keystore.location</name>
  <value>/home/keystore</value>
  <description>Keystore to be used by clients like distcp. Must be
  specified.
  </description>
</property>

<property>
  <name>ssl.client.keystore.password</name>
  <value>123456</value>
  <description>Optional. Default value is "".
  </description>
</property>

<property>
  <name>ssl.client.keystore.keypassword</name>
  <value>123456</value>
  <description>Optional. Default value is "".
  </description>
</property>

配置完成后分发。

Yarn配置LinuxContainExecutor

安装LinuxContainerExecutor

LinuxContainerExecutor能有助于提升对YARN访问的安全性。

安装libcrypto.so库(此库华为云Ubuntu22.04已经预装)。libcrypto.so库提供了加密算法,隶属于OpenSSL库。

查看Ubuntu系统是否是已经安装了libcrypto.so库:

ldconfig -p | grep libcrypto

出现类似libcrypto.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libcrypto.so.3表明已经安装。

华为云Ubuntu22.04系统中预装了该库,之后需要将该库路径添加到系统共享搜索路径中:

# 找到该库的位置:/snap/core20/1587/usr/lib/x86_64-linux-gnu/libcrypto.so.1.1
find / -name libcrypto.so.1.1 2>/dev/nul
echo "/snap/core20/1587/usr/lib/x86_64-linux-gnu/" | sudo tee -a /etc/ld.so.conf
sudo ldconfig

如果不配置成功这一步骤,会导致Yarn集群的DataNode都无法正常启动。

修改container-executor权限

修改每个节点中container-executor的权限:

chown root:hadoop $HADOOP_HOME/bin/container-executor
chmod 40750 $HADOOP_HOME/bin/container-executor

修改下述文件并分发:

vim $HADOOP_HOME/etc/hadoop/container-executor.cfg
yarn.nodemanager.linux-container-executor.group=hadoop
# 禁止使用容器执行器的用户
banned.users=hdfs_user,yarn_user
# 系统用户的ID小于1000,普通用户的ID大于等于1000
min.user.id=1000
# 允许使用容器执行器的系统用户的列表
allowed.system.users=
feature.tc.enabled=false

在每个节点上修改container-executor.cfg权限:

chown root:hadoop $HADOOP_HOME/etc/hadoop/container-executor.cfg
chown root:hadoop $HADOOP_HOME/etc/hadoop
chown root:hadoop $HADOOP_HOME/etc
chown root:hadoop $HADOOP_HOME
chown root:hadoop /opt/Hadoop336
chown root:hadoop /opt
chmod 400 $HADOOP_HOME/etc/hadoop/container-executor.cfg

配置yarn-site.xml并分发:

vim $HADOOP_HOME/etc/hadoop/yarn-site.xml
    <!-- 开启YARN的https -->
    <property>
        <name>yarn.http.policy</name>
        <value>HTTPS_ONLY</value>
    </property>
    <!-- 配置NodeManager使用LinuxContainerExecutor管理Container -->
    <property>
        <name>yarn.nodemanager.container-executor.class</name>
        <value>org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor</value>
    </property>
    <!-- 配置NodeManager的启动用户所属组 -->
    <property>
        <name>yarn.nodemanager.linux-container-executor.group</name>
        <value>hadoop</value>
    </property>
    <!-- LinuxContainerExecutor-->
    <property>
        <name>yarn.nodemanager.linux-container-executor.path</name>
        <value>/opt/Hadoop336/hadoop-3.3.6/bin/container-executor</value>
    </property>

修改Hadoop集群的启停文件

HDFS启停脚本

vim $HADOOP_HOME/sbin/start-dfs.sh

在文件的头部添加,并分发:

HDFS_DATANODE_USER=hdfs_user
HDFS_DATANODE_SECURE_USER=hdfs_user
HDFS_NAMENODE_USER=hdfs_user
HDFS_SECONDARYNAMENODE_USER=hdfs_user
HDFS_ZKFC_USER=hdfs_user
vim $HADOOP_HOME/sbin/stop-dfs.sh

在文件的头部添加,并分发:

这里的ZKFC好像是要配置ZooKeeper。

HDFS_DATANODE_USER=hdfs_user
HDFS_DATANODE_SECURE_USER=hdfs_user
HDFS_NAMENODE_USER=hdfs_user
HDFS_ZKFC_USER=hdfs_user

YARN启停脚本

vim $HADOOP_HOME/sbin/start-yarn.sh

在文件的头部添加,并分发:

YARN_RESOURCEMANAGER_USER=yarn_user
YARN_NODEMANAGER_USER=yarn_user
vim $HADOOP_HOME/sbin/stop-yarn.sh

在文件的头部添加,并分发:

YARN_RESOURCEMANAGER_USER=yarn_user
YARN_NODEMANAGER_USER=yarn_user

HDFS目录权限

官方建议的权限设置规则

在首次使用kerberos安全系统启动HDFS集群后,我尝试以任何用户进入HDFS文件系统都失败。HDFS的初始文件夹只有一个/tmp,属于root:supergroup。为了在HDFS上创建hdfs_user:hadoop用户所能操作的文件夹。我需要先在Linux上创建一个全新的用户和组以及pricipal用于首次进入HDFS。

这里有一个疑问,新建任意Principal都可以进入HDFS修改根目录权限,这应该会导致安全问题?

groupadd supergroup
useradd -g supergroup -m zt
kadmin.local -q "add_principal zt"

设置好principal zt的密码后,在core-site.xml:supergroup的映射。

    <property>
        <name>hadoop.security.auth_to_local</name>
        <value>
            RULE:[2:$1/$2@$0]([dn]n\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0](snn\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0]([rn]m\/.*@EXAMPLE\.COM)s/.*/yarn_user/
            RULE:[2:$1/$2@$0](jhs\/.*@EXAMPLE\.COM)s/.*/mapred_user/
            RULE:[1:$1@$0](zt@EXAMPLE\.COM)s/.*/zt/
        </value>
    </property>

重启HDFS,使用kinit zt,即可使用hadoop fs -mkdir /usr在HDFS上创建目录了。拿到zt的票之后执行下述命令:

hadoop fs -chown -R hdfs_user:hadoop /tmp
hadoop fs -chmod -R 777 /tmp

之后才能顺利启动jobhistoryserver,因为jobhistoryserver的启动需要以jhs的principal向/tmp文件夹里面写入东西。

按照官网规则,还应该配置下述hadoop目录和权限(我单独为/user目录递归增加了用户组写权限,为了顺利启动Spark On Yarn):

hadoop fs -chown hdfs_user:hadoop /id
hadoop fs -chmod u=rwx,g=rx,o=rx /

hadoop fs -mkdir /user
hadoop fs -chown hdfs_user:hadoop /user
hadoop fs -chmod -R u=rwx,g=rwx,o=rx /user

hadoop fs -mkdir /nmLogs
hadoop fs -chown yarn_user:hadoop /nmLogs
hadoop fs -chmod 777 /nmLogs

hadoop fs -mkdir -p /tmp/hadoop-yarn/staging/history/done
hadoop fs -chown mapred_user:hadoop /tmp/hadoop-yarn/staging/history/done
hadoop fs -chmod 750 /tmp/hadoop-yarn/staging/history/done

hadoop fs -mkdir -p /tmp/hadoop-yarn/staging/history/done_intermediate
hadoop fs -chown mapred_user:hadoop /tmp/hadoop-yarn/staging/history/done_intermediate
hadoop fs -chmod 777 /tmp/hadoop-yarn/staging/history/done_intermediate

hadoop fs -chown mapred_user:hadoop /tmp/hadoop-yarn/staging/history
hadoop fs -chown mapred_user:hadoop /tmp/hadoop-yarn/staging
hadoop fs -chown mapred_user:hadoop /tmp/hadoop-yarn
hadoop fs -chmod 770 /tmp/hadoop-yarn/staging/history
hadoop fs -chmod 770 /tmp/hadoop-yarn/staging
hadoop fs -chmod 770 /tmp/hadoop-yarn

成功启动基于Kerberos安全模式的Hadoop集群后,访问地址https://NameNode的公网IP:9871来访问HDFS的Web UI。通过地址https://ResourceManager的公网IP:8090来访问Yarn的Web UI。

Hive

  1. 准备Principal和linux用户,并配置权限:
# 在每个节点上都要执行下面这一行创建hive_user的代码,并相互设置免密ssh登录
useradd -g hadoop -m hive_user 

chown -R hive_user:hadoop /opt/Hive313/apache-hive-3.1.3-bin
kadmin.local -q "add_principal hive_user"

kadmin.local -q "add_principal -randkey hive/hadoop102@EXAMPLE.COM"
kadmin.local -q "ktadd -kt /etc/security/keytabs/hiveService.keytab hive/hadoop102@EXAMPLE.COM"
chown hive_user:hadoop /etc/security/keytabs/hiveService.keytab
chmod 400 /etc/security/keytabs/hiveService.keytab
  1. 修改hive-env.sh文件
mv /opt/Hive313/apache-hive-3.1.3-bin/conf/hive-env.sh.template /opt/Hive313/apache-hive-3.1.3-bin/conf/hive-env.sh
vim /opt/Hive313/apache-hive-3.1.3-bin/conf/hive-env.sh

追加下述内容:

export JAVA_HOME=/opt/JDK8/jdk1.8.0_202
export HADOOP_HOME=/opt/Hadoop336/hadoop-3.3.6
export HIVE_HOME=/opt/Hive313/apache-hive-3.1.3-bin
export HIVE_CONF_DIR=/opt/Hive313/apache-hive-3.1.3-bin/conf
  1. 修改hive-site.xml文件
vim /opt/Hive313/apache-hive-3.1.3-bin/conf/hive-site.xml

添加下述Kerberos安全相关配置:

    <property>
        <name>hive.server2.authentication</name>
        <value>KERBEROS</value>
    </property>
    <property>
        <name>hive.server2.authentication.kerberos.principal</name>
        <value>hive/_HOST@EXAMPLE.COM</value>
    </property>
    <property>
        <name>hive.server2.authentication.kerberos.keytab</name>
        <value>/etc/security/keytabs/hiveService.keytab</value>
    </property>

    <property>
        <name>hive.metastore.sasl.enabled</name>
        <value>true</value>
    </property>
    <property>
        <name>hive.metastore.kerberos.principal</name>
        <value>hive/_HOST@EXAMPLE.COM</value>
    </property>
    <property>
        <name>hive.metastore.kerberos.keytab.file</name>
        <value>/etc/security/keytabs/hiveService.keytab</value>
    </property>

上述内容配置好后,执行hive后,

  1. $HADOOP_HOME/etc/hadoop/core-site.xml中新增hive_user的映射规则,以及允许hive在任何主机上模拟属于hadoop组的用户:
    <property>
        <name>hadoop.security.auth_to_local</name>
        <value>
            RULE:[2:$1/$2@$0]([dn]n\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0](snn\/.*@EXAMPLE\.COM)s/.*/hdfs_user/
            RULE:[2:$1/$2@$0]([rn]m\/.*@EXAMPLE\.COM)s/.*/yarn_user/
            RULE:[2:$1/$2@$0](jhs\/.*@EXAMPLE\.COM)s/.*/mapred_user/
            RULE:[2:$1/$2@$0](hive\/.*@EXAMPLE\.COM)s/.*/hive_user/
            RULE:[1:$1@$0](zt@EXAMPLE\.COM)s/.*/zt/
        </value>
    </property>

<!-- 按照视频里的教程,这里还加了一堆hive, hdfs, HTTP的* -->

分发到所有节点。

本地Windows访问HDFS Web

注意:因为遇到了Hadoop本身的Bug,Hadoop-3.1.x及以后版本无法完成下述功能的实现。

安装配置Kerberos的Windows客户端

下载地址。下载MIT Kerberos for Windows 4.1 kfw-4.1-amd64.msi。在安装过程中选择“Typical”,默认的安装地址为:C:Files:Files。安装完毕后需要重启。

在系统环境变量中Path找到最后一行的C:\Program Files\MIT\Kerberos\bin,把它提前以掩盖Java中可能含有的kinit和klist命令。

配置Kerberos认证

  1. 从服务端把/etc/krb5.conf复制到Windows中的C:\ProgramData\MIT\Kerberos5\krb5.ini。然后把其中的kdcadmin_server都改成公网IP。

  2. 从服务端把/etc/security/keytabs/nnService.keytab复制到Windows中的C:\ProgramData\MIT\Kerberos5目录下。

操作Kerberos客户端

打开Windows命令行,在C:\Program Files\MIT\Kerberos\bin路径下正常输入Kerberos命令即可。

在火狐浏览器中,在网址框输入about:config,然后修改以下两项:

network.negotiate-auth.trusted-uris 改为服务器域名,集群用逗号分割
network.auth.use-sspi 改为fales

然后访问:https://NameNode公网IP:9871

但是通过上述步骤依然不能实现本地Windows通过Web UI访问HDFS文件系统,并出现报错:Failed to obtain user group information: java.io.IOException: Security enabled but user not authenticated by filter。这个报错好像是hadoop-3.1.x之后版本的Bug,详见https://issues.apache.org/jira/browse/HDFS-16441

配置Windwos Kerberos客户端

其他问题的解决方案

网络通信问题

hadoop集群各个组件要通过网络的特定端口建立通信连接。

建议在各个节点上允许私网的所有端口的访问,否则会出现各个hadoop组件都正常启动而相互不能感知的情况:

# 下述命令中的私有IP网段通过命令ip addr show查看本机的网段
ufw all from 192.168.1.0/24

另外记得开放hadoop的一些web页面的端口访问,例如HDFS的web页面端口:

ufw allow 9870/tcp
ufw allow 9870/udp

VMWare虚拟机间歇性宕机

跟一个网络相关的程序有关

未关闭集群直接关机节点

此操作会可能会造成再次启动hdfs时datanode的clusterID与NameNode的clusterID不匹配。导致后续无法正常启动datanode。

可以在每一个节点上使用rm -rf /home/zt/development/Hadoop336/data/dfs/data/*来使datanode可以被正常启动。

Windows系统运行MapReduce代码

对于hadoop3.3.6版本,可以下载https://github.com/ruslanmv/How-to-install-Hadoop-on-Windows/tree/master。然后在新增系统环境变量HADOOP_HOME为上述文件中的winutils\hadoop-3.3.1。然后在系统Path中 新增一个%HADOOP_HOME%\bin

之后在Windows平台测试MapReduce代码将不会再报找不到HADOOP_HOME之类的错误。