共计 25250 个字符,预计需要花费 64 分钟才能阅读完成。
1 Hadoop HA 架构详解
1.1 HDFS HA 背景
HDFS 集群中 NameNode 存在单点故障(SPOF)。对于只有一个 NameNode 的集群,如果 NameNode 机器出现意外情况,将导致整个集群无法使用,直到 NameNode 重新启动。
影响 HDFS 集群不可用主要包括以下两种情况:一是 NameNode 机器宕机,将导致集群不可用,重启 NameNode 之后才可使用;二是计划内的 NameNode 节点软件或硬件升级,导致集群在短时间内不可用。
为了解决上述问题,Hadoop 给出了 HDFS 的高可用 HA 方案:HDFS 通常由两个 NameNode 组成,一个处于 active 状态,另一个处于 standby 状态。Active NameNode 对外提供服务,比如处理来自客户端的 RPC 请求,而 Standby NameNode 则不对外提供服务,仅同步 Active NameNode 的状态,以便能够在它失败时快速进行切换。
1.2 HDFS HA 架构
一个典型的 HA 集群,NameNode 会被配置在两台独立的机器上,在任何时间上,一个 NameNode 处于活动状态,而另一个 NameNode 处于备份状态,活动状态的 NameNode 会响应集群中所有的客户端,备份状态的 NameNode 只是作为一个副本,保证在必要的时候提供一个快速的转移。
为了让 Standby Node 与 Active Node 保持同步,这两个 Node 都与一组称为 JNS 的互相独立的进程保持通信 (Journal Nodes)。当 Active Node 上更新了 namespace,它将记录修改日志发送给 JNS 的多数派。Standby noes 将会从 JNS 中读取这些 edits,并持续关注它们对日志的变更。Standby Node 将日志变更应用在自己的 namespace 中,当 failover 发生时,Standby 将会在提升自己为 Active 之前,确保能够从 JNS 中读取所有的 edits,即在 failover 发生之前 Standy 持有的 namespace 应该与 Active 保持完全同步。
为了支持快速 failover,Standby node 持有集群中 blocks 的最新位置是非常必要的。为了达到这一目的,DataNodes 上需要同时配置这两个 Namenode 的地址,同时和它们都建立心跳链接,并把 block 位置发送给它们。
任何时刻,只有一个 Active NameNode 是非常重要的,否则将会导致集群操作的混乱,那么两个 NameNode 将会分别有两种不同的数据状态,可能会导致数据丢失,或者状态异常,这种情况通常称为“split-brain”(脑裂,三节点通讯阻断,即集群中不同的 Datanodes 却看到了两个 Active NameNodes)。对于 JNS 而言,任何时候只允许一个 NameNode 作为 writer;在 failover 期间,原来的 Standby Node 将会接管 Active 的所有职能,并负责向 JNS 写入日志记录,这就阻止了其他 NameNode 基于处于 Active 状态的问题。
基于 QJM 的 HDFS HA 方案如上图所示,其处理流程为:集群启动后一个 NameNode 处于 Active 状态,并提供服务,处理客户端和 DataNode 的请求,并把 editlog 写到本地和 share editlog(这里是 QJM)中。另外一个 NameNode 处于 Standby 状态,它启动的时候加载 fsimage,然后周期性的从 share editlog 中获取 editlog,保持与 Active 节点的状态同步。为了实现 Standby 在 Active 挂掉后迅速提供服务,需要 DataNode 同时向两个 NameNode 汇报,使得 Stadnby 保存 block to DataNode 信息,因为 NameNode 启动中最费时的工作是处理所有 DataNode 的 blockreport。为了实现热备,增加 FailoverController 和 Zookeeper,FailoverController 与 Zookeeper 通信,通过 Zookeeper 选举机制,FailoverController 通过 RPC 让 NameNode 转换为 Active 或 Standby。
1.3 HDFS HA 配置要素
NameNode 机器:两台配置对等的物理机器,它们分别运行 Active 和 Standby Node。
JouralNode 机器:运行 JouralNodes 的机器。JouralNode 守护进程相当的轻量级,可以和 Hadoop 的其他进程部署在一起,比如 NameNode、DataNode、ResourceManager 等,至少需要 3 个且为奇数,如果你运行了 N 个 JNS,那么它可以允许 (N-1)/ 2 个 JNS 进程失效并且不影响工作。
在 HA 集群中,Standby NameNode 还会对 namespace 进行 checkpoint 操作(继承 Backup Namenode 的特性),因此不需要在 HA 集群中运行 SecondaryNameNode、CheckpointNode 或者 BackupNode。
1.4 HDFS HA 配置参数
需要在 hdfs.xml 中配置如下参数:
dfs.nameservices:HDFS NN 的逻辑名称,例如 myhdfs。
dfs.ha.namenodes.myhdfs:给定服务逻辑名称 myhdfs 的节点列表,如 nn1、nn2。
dfs.namenode.rpc-address.myhdfs.nn1:myhdfs 中 nn1 对外服务的 RPC 地址。
dfs.namenode.http-address.myhdfs.nn1:myhdfs 中 nn1 对外服务 http 地址。
dfs.namenode.shared.edits.dir:JournalNode 的服务地址。
dfs.journalnode.edits.dir:JournalNode 在本地磁盘存放数据的位置。
dfs.ha.automatic-failover.enabled:是否开启 NameNode 失败自动切换。
dfs.ha.fencing.methods:配置隔离机制,通常为 sshfence。
1.5 HDFS 自动故障转移
HDFS 的自动故障转移主要由 Zookeeper 和 ZKFC 两个组件组成。
Zookeeper 集群作用主要有:一是故障监控。每个 NameNode 将会和 Zookeeper 建立一个持久 session,如果 NameNode 失效,那么此 session 将会过期失效,此后 Zookeeper 将会通知另一个 Namenode,然后触发 Failover;二是 NameNode 选举。ZooKeeper 提供了简单的机制来实现 Acitve Node 选举,如果当前 Active 失效,Standby 将会获取一个特定的排他锁,那么获取锁的 Node 接下来将会成为 Active。
ZKFC 是一个 Zookeeper 的客户端,它主要用来监测和管理 NameNodes 的状态,每个 NameNode 机器上都会运行一个 ZKFC 程序,它的职责主要有:一是健康监控。ZKFC 间歇性的 ping NameNode,得到 NameNode 返回状态,如果 NameNode 失效或者不健康,那么 ZKFS 将会标记其为不健康;二是 Zookeeper 会话管理。当本地 NaneNode 运行良好时,ZKFC 将会持有一个 Zookeeper session,如果本地 NameNode 为 Active,它同时也持有一个“排他锁”znode,如果 session 过期,那么次 lock 所对应的 znode 也将被删除;三是选举。当集群中其中一个 NameNode 宕机,Zookeeper 会自动将另一个激活。
1.6 YARN HA 架构
YARN 的 HA 架构和 HDFSHA 类似,需要启动两个 ResourceManager,这两个 ResourceManager 会向 ZooKeeper 集群注册,通过 ZooKeeper 管理它们的状态(Active 或 Standby)并进行自动故障转移。
2 高可用集群规划
2.1 集群规划
根据 Hadoop 的 HA 架构分析,规划整个集群由 5 台主机组成,具体情况如下表所示:
主机名 |
IP 地址 |
安装的软件 |
JPS |
hadoop-master1 |
172.16.20.81 |
Jdk/hadoop |
Namenode/zkfc/resourcemanager/ JobHistoryServer |
hadoop-master2 |
172.16.20.82 |
Jdk/hadoop |
Namenode/zkfc/resourcemanager/ WebProxyServer |
hadoop-slave1 |
172.16.20.83 |
Jkd/hadoop/zookeepe |
Datanode/journalnode/nodemanager/ quorumPeerMain |
hadoop-slave2 |
172.16.20.84 |
Jkd/hadoop/zookeeper |
Datanode/journalnode/nodemanager/ quorumPeerMain |
hadoop-slave3 |
172.16.20.85 |
Jkd/hadoop/zookeeper |
Datanode/journalnode/nodemanager/ quorumPeerMain |
需要说明以下几点:
HDFS HA 通常由两个 NameNode 组成,一个处于 Active 状态,另一个处于 Standby 状态。Active NameNode 对外提供服务,而 Standby NameNode 则不对外提供服务,仅同步 Active NameNode 的状态,以便能够在它失败时快速进行切换。
Hadoop 2.0 官方提供了两种 HDFS HA 的解决方案,一种是 NFS,另一种是 QJM。这里我们使用简单的 QJM。在该方案中,主备 NameNode 之间通过一组 JournalNode 同步元数据信息,一条数据只要成功写入多数 JournalNode 即认为写入成功。通常配置奇数个 JournalNode,这里还配置了一个 Zookeeper 集群,用于 ZKFC 故障转移,当 Active NameNode 挂掉了,会自动切换 Standby NameNode 为 Active 状态。
YARN 的 ResourceManager 也存在单点故障问题,这个问题在 hadoop-2.4.1 得到了解决:有两个 ResourceManager,一个是 Active,一个是 Standby,状态由 zookeeper 进行协调。
YARN 框架下的 MapReduce 可以开启 JobHistoryServer 来记录历史任务信息,否则只能查看当前正在执行的任务信息。
Zookeeper 的作用是负责 HDFS 中 NameNode 主备节点的选举,和 YARN 框架下 ResourceManaer 主备节点的选举。
2.2 软件版本
操作系统:CentOS Linux release 7.0.1406
JDK:Java(TM)SE Runtime Environment (build 1.7.0_79-b15)
Hadoop:Hadoop 2.6.0-cdh5.7.1
ZooKeeper:zookeeper-3.4.5-cdh5.7.1
3 Linux 环境准备
集群各节点进行如下修改配置:
3.1 创建用户并添加权限
// 切换 root 用户
$ su root
// 创建 hadoop 用户组
# groupadd hadoop
// 在 hadoop 用户组中创建 hadoop 用户
# useradd -g hadoop hadoop
// 修改用户 hadoop 密码
# passwd hadoop
// 修改 sudoers 配置文件给 hadoop 用户添加 sudo 权限
# vim /etc/sudoers
hadoop ALL=(ALL) ALL
// 测试是否添加权限成功
# exit
$ sudo ls /root
3.2 修改 IP 地址和主机名
// 切换 root 用户
$ su root
// 修改本机 IP 地址
# vim /etc/sysconfig/network-scripts/ifcfg-eth0
// 重启网络服务
# service network restart
// 修改主机名
# hostnamectl set-hostname 主机名
// 查看主机名
# hostnamectl status
3.3 设置 IP 地址与主机名映射
// 切换 root 用户
$ su root
// 编辑 hosts 文件
# vim /etc/hosts
172.16.20.81 hadoop-master1
172.16.20.82 hadoop-master2
172.16.20.83 hadoop-slave1
172.16.20.84 hadoop-slave2
172.16.20.85 hadoop-slave3
3.4 关闭防火墙和 Selinux
// 切换 root 用户
$ su root
// 停止 firewall 防火墙
# systemctl stop firewalld.service
// 禁止 firewall 开机启动
# systemctl disable firewalld.service
// 开机关闭 Selinux
# vim /etc/selinux/config
SELINUX=disabled
// 重启机器后 root 用户查看 Selinux 状态
# getenforce
3.5 配置 SSH 免密码登录
// 在 hadoop-master1 节点生成 SSH 密钥对
$ ssh-keygen -t rsa
// 将公钥复制到集群所有节点机器上
$ ssh-copy-id hadoop-master1
$ ssh-copy-id hadoop-master2
$ ssh-copy-id hadoop-slave1
$ ssh-copy-id hadoop-slave2
$ ssh-copy-id hadoop-slave3
// 通过 ssh 登录各节点测试是否免密码登录成功
$ ssh hadoop-master2
备注:在其余节点上执行同样的操作,确保集群中任意节点都可以 ssh 免密码登录到其它各节点。
3.6 安装 JDK
// 卸载系统自带的 openjdk
$ suroot
# rpm-qa | grep java
# rpm-e –nodeps java-1.7.0-openjdk-1.7.0.75-2.5.4.2.el7_0.x86_64
# rpm-e –nodeps java-1.7.0-openjdk-headless-1.7.0.75-2.5.4.2.el7_0.x86_64
# rpm-e –nodeps tzdata-java-2015a-1.el7_0.noarch
# exit
// 解压 jdk 安装包
$ tar-xvf jdk-7u79-linux-x64.tar.gz
// 删除安装包
$ rmjdk-7u79-linux-x64.tar.gz
// 修改用户环境变量
$ cd ~
$ vim.bash_profile
exportJAVA_HOME=/home/hadoop/app/jdk1.7.0_79
exportPATH=$PATH:$JAVA_HOME/bin
// 使修改的环境变量生效
$ source.bash_profile
// 测试 jdk 是否安装成功
$ java-version
4 集群时间同步
如果集群节点时间不同步,可能会出现节点宕机或引发其它异常问题,所以在生产环境中一般通过配置 NTP 服务器实现集群时间同步。本集群在 hadoop-master1 节点设置 ntp 服务器,具体方法如下:
// 切换 root 用户
$ su root
// 查看是否安装 ntp
# rpm -qa | grep ntp
// 安装 ntp
# yum install -y ntp
// 配置时间服务器
# vim /etc/ntp.conf
# 禁止所有机器连接 ntp 服务器
restrict default ignore
# 允许局域网内的所有机器连接 ntp 服务器
restrict 172.16.20.0 mask 255.255.255.0 nomodify notrap
# 使用本机作为时间服务器
server 127.127.1.0
// 启动 ntp 服务器
# service ntpd start
// 设置 ntp 服务器开机自动启动
# chkconfig ntpd on
集群其它节点通过执行 crontab 定时任务,每天在指定时间向 ntp 服务器进行时间同步,方法如下:
// 切换 root 用户
$ su root
// 执行定时任务,每天 00:00 向服务器同步时间,并写入日志
# crontab -e
0 0 * * * /usr/sbin/ntpdate hadoop-master1>> /home/hadoop/ntpd.log
// 查看任务
# crontab -l
更多详情见请继续阅读下一页的精彩内容 :http://www.linuxidc.com/Linux/2016-08/134180p2.htm
5 Zookeeper 集群安装
Zookeeper 是一个开源分布式协调服务,其独特的 Leader-Follower 集群结构,很好的解决了分布式单点问题。目前主要用于诸如:统一命名服务、配置管理、锁服务、集群管理等场景。大数据应用中主要使用 Zookeeper 的集群管理功能。
本集群使用 zookeeper-3.4.5-cdh5.7.1 版本。首先在 Hadoop-slave1 节点安装 Zookeeper,方法如下:
// 新建目录
$ mkdir app/cdh
// 解压 zookeeper 安装包
$ tar -xvf zookeeper-3.4.5-cdh5.7.1.tar.gz -C app/cdh/
// 删除安装包
$ rm -rf zookeeper-3.4.5-cdh5.7.1.tar.gz
// 配置用户环境变量
$ vim .bash_profile
export ZOOKEEPER_HOME=/home/hadoop/app/cdh/zookeeper-3.4.5-cdh5.7.1
export PATH=$PATH:$ZOOKEEPER_HOME/bin
// 使修改的环境变量生效
$ source.bash_profile
// 修改 zookeeper 的配置文件
$ cd app/cdh/zookeeper-3.4.5-cdh5.7.1/conf/
$ cp zoo_sample.cfg zoo.cfg
$ vim zoo.cfg
# 客户端心跳时间 (毫秒)
tickTime=2000
# 允许心跳间隔的最大时间
initLimit=10
# 同步时限
syncLimit=5
# 数据存储目录
dataDir=/home/hadoop/app/cdh/zookeeper-3.4.5-cdh5.7.1/data
# 数据日志存储目录
dataLogDir=/home/hadoop/app/cdh/zookeeper-3.4.5-cdh5.7.1/data/log
# 端口号
clientPort=2181
# 集群节点和服务端口配置
server.1=hadoop-slave1:2888:3888
server.2=hadoop-slave2:2888:3888
server.3=hadoop-slave3:2888:3888
# 以下为优化配置
# 服务器最大连接数,默认为 10,改为 0 表示无限制
maxClientCnxns=0
# 快照数
autopurge.snapRetainCount=3
# 快照清理时间,默认为 0
autopurge.purgeInterval=1
// 创建 zookeeper 的数据存储目录和日志存储目录
$ cd ..
$ mkdir -p data/log
// 在 data 目录中创建一个文件 myid,输入内容为 1
$ echo “1” >> data/myid
// 修改 zookeeper 的日志输出路径 (注意 CDH 版与原生版配置文件不同)
$ vim libexec/zkEnv.sh
if [“x${ZOO_LOG_DIR}” = “x” ]
then
ZOO_LOG_DIR=”$ZOOKEEPER_HOME/logs”
fi
if [“x${ZOO_LOG4J_PROP}” = “x” ]
then
ZOO_LOG4J_PROP=”INFO,ROLLINGFILE”
fi
// 修改 zookeeper 的日志配置文件
$ vim conf/log4j.properties
zookeeper.root.logger=INFO,ROLLINGFILE
// 创建日志目录
$ mkdir logs
将 hadoop-slave1 节点上的 Zookeeper 目录同步到 hadoop-slave2 和 hadoop-slave3 节点,并修改 Zookeeper 的数据文件。此外,不要忘记设置用户环境变量。
// 在 hadoop-slave1 中将 zookeeper 目录复制到其它节点
$ cd ~
$ scp -r app/cdh/zookeeper-3.4.5-cdh5.7.1hadoop-slave2:/home/hadoop/app/cdh
$ scp -r app/cdh/zookeeper-3.4.5-cdh5.7.1 hadoop-slave3:/home/hadoop/app/cdh
// 在 hadoop-slave2 中修改 data 目录中的 myid 文件
$ echo “2” >app/cdh/zookeeper-3.4.5-cdh5.7.1/data/myid
// 在 hadoop-slave3 中修改 data 目录中的 myid 文件
$ echo “3” >app/cdh/zookeeper-3.4.5-cdh5.7.1/data/myid
最后,在安装了 Zookeeper 的各节点上启动 Zookeeper,并查看节点状态,方法如下:
// 启动
$ zkServer.sh start
// 查看状态
$ zkServer.sh status
// 关闭
$ zkServer.sh stop
6 Hadoop HA 配置
// 在 hadoop-master1 节点解压 hadoop 安装包
$ tar-xvf hadoop-2.6.0-cdh5.7.1.tar.gz -C /home/hadoop/app/cdh/
// 删除安装包
$ rmhadoop-2.6.0-cdh5.7.1.tar.gz
// 修改 hadoop-env.sh 文件
$ cd/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/etc/hadoop
$ vimhadoop-env.sh
exportJAVA_HOME=/home/hadoop/app/jdk1.7.0_79
// 配置 core-site.xml 文件
$ vim core-site.xml
<configuration>
<!-- 指定 hdfs 的 nameservices 名称为 mycluster,与 hdfs-site.xml 的 HA 配置相同 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<!-- 指定缓存文件存储的路径 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/data/tmp</value>
</property>
<!-- 配置 hdfs 文件被永久删除前保留的时间(单位:分钟),默认值为 0 表明垃圾回收站功能关闭 -->
<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>
<!-- 指定 zookeeper 地址,配置 HA 时需要 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>hadoop-slave1:2181,hadoop-slave2:2181,hadoop-slave3:2181</value>
</property>
</configuration>
// 配置 hdfs-site.xml 文件
$ vim hdfs-site.xml
<configuration>
<!-- 指定 hdfs 元数据存储的路径 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/data/namenode</value>
</property>
<!-- 指定 hdfs 数据存储的路径 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/data/datanode</value>
</property>
<!-- 数据备份的个数 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 关闭权限验证 -->
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
<!-- 开启 WebHDFS 功能(基于 REST 的接口服务)-->
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<!-- ////////////// 以下为 HDFS HA 的配置 ////////////// -->
<!-- 指定 hdfs 的 nameservices 名称为 mycluster -->
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<!-- 指定 mycluster 的两个 namenode 的名称分别为 nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<!-- 配置 nn1,nn2 的 rpc 通信端口 -->
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>hadoop-master1:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>hadoop-master2:8020</value>
</property>
<!-- 配置 nn1,nn2 的 http 通信端口 -->
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>hadoop-master1:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>hadoop-master2:50070</value>
</property>
<!-- 指定 namenode 元数据存储在 journalnode 中的路径 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://hadoop-slave1:8485;hadoop-slave2:8485;hadoop-slave3:8485/mycluster</value>
</property>
<!-- 指定 journalnode 日志文件存储的路径 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/data/journal</value>
</property>
<!-- 指定 HDFS 客户端连接 active namenode 的 java 类 -->
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制为 ssh -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!-- 指定秘钥的位置 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/hadoop/.ssh/id_rsa</value>
</property>
<!-- 开启自动故障转移 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
</configuration>
// 配置 mapred-site.xml 文件
$ vim mapred-site.xml
<configuration>
<!-- 指定 MapReduce 计算框架使用 YARN -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<!-- 指定 jobhistory server 的 rpc 地址 -->
<property>
<name>mapreduce.jobhistory.address</name>
<value>hadoop-master1:10020</value>
</property>
<!-- 指定 jobhistory server 的 http 地址 -->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>hadoop-master1:19888</value>
</property>
<!-- 开启 uber 模式(针对小作业的优化)-->
<property>
<name>mapreduce.job.ubertask.enable</name>
<value>true</value>
</property>
<!-- 配置启动 uber 模式的最大 map 数 -->
<property>
<name>mapreduce.job.ubertask.maxmaps</name>
<value>9</value>
</property>
<!-- 配置启动 uber 模式的最大 reduce 数 -->
<property>
<name>mapreduce.job.ubertask.maxreduces</name>
<value>1</value>
</property>
</configuration>
// 配置 yarn-site.xml 文件
$ vim yarn-site.xml
<configuration>
<!-- NodeManager 上运行的附属服务,需配置成 mapreduce_shuffle 才可运行 MapReduce 程序 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!-- 配置 Web Application Proxy 安全代理(防止 yarn 被攻击)-->
<property>
<name>yarn.web-proxy.address</name>
<value>hadoop-master2:8888</value>
</property>
<!-- 开启日志 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 配置日志删除时间为 7 天,- 1 为禁用,单位为秒 -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
<!-- 修改日志目录 -->
<property>
<name>yarn.nodemanager.remote-app-log-dir</name>
<value>/logs</value>
</property>
<!-- 配置 nodemanager 可用的资源内存 -->
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>2048</value>
</property>
<!-- 配置 nodemanager 可用的资源 CPU -->
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>2</value>
</property>
<!-- ////////////// 以下为 YARN HA 的配置 ////////////// -->
<!-- 开启 YARN HA -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 启用自动故障转移 -->
<property>
<name>yarn.resourcemanager.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 指定 YARN HA 的名称 -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yarncluster</value>
</property>
<!-- 指定两个 resourcemanager 的名称 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- 配置 rm1,rm2 的主机 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>hadoop-master1</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>hadoop-master2</value>
</property>
<!-- 配置 YARN 的 http 端口 -->
<property>
<name>yarn.resourcemanager.webapp.address.rm1</name>
<value>hadoop-master1:8088</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address.rm2</name>
<value>hadoop-master2:8088</value>
</property>
<!-- 配置 zookeeper 的地址 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>hadoop-slave1:2181,hadoop-slave2:2181,hadoop-slave3:2181</value>
</property>
<!-- 配置 zookeeper 的存储位置 -->
<property>
<name>yarn.resourcemanager.zk-state-store.parent-path</name>
<value>/rmstore</value>
</property>
<!-- 开启 yarn resourcemanager restart -->
<property>
<name>yarn.resourcemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- 配置 resourcemanager 的状态存储到 zookeeper 中 -->
<property>
<name>yarn.resourcemanager.store.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
</property>
<!-- 开启 yarn nodemanager restart -->
<property>
<name>yarn.nodemanager.recovery.enabled</name>
<value>true</value>
</property>
<!-- 配置 nodemanager IPC 的通信端口 -->
<property>
<name>yarn.nodemanager.address</name>
<value>0.0.0.0:45454</value>
</property>
</configuration>
// 配置 slaves 文件
$ vimslaves
hadoop-slave1
hadoop-slave2
hadoop-slave3
// 创建配置文件中涉及的目录
$ cd/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/
$ mkdir-p data/tmp
$ mkdir-p data/journal
$ mkdir-p data/namenode
$ mkdir-p data/datanode
// 将 hadoop 工作目录同步到集群其它节点
$ scp-r /home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/hadoop-master2:/home/hadoop/app/cdh/
scp -r/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/ hadoop-slave1:/home/hadoop/app/cdh/
scp -r/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/ hadoop-slave2:/home/hadoop/app/cdh/
scp -r/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/ hadoop-slave3:/home/hadoop/app/cdh/
// 在集群各节点上修改用户环境变量
$ vim .bash_profile
export HADOOP_HOME=/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
// 使修改的环境变量生效
$ source.bash_profile
// 解决本地库文件不存在的问题
在 apache 官网下载 hadoop-2.6.0.tar.gz,解压后将 lib/native 下所有文件复制到 $HADOOP_HOME/lib/native 中。
7 Hadoop 集群的初始化
// 启动 zookeeper 集群(分别在 slave1、slave2 和 slave3 上执行)
$ zkServer.shstart
// 格式化 ZKFC(在 master1 上执行)
$ hdfszkfc -formatZK
// 启动 journalnode(分别在 slave1、slave2 和 slave3 上执行)
$ hadoop-daemon.shstart journalnode
// 格式化 HDFS(在 master1 上执行)
$ hdfsnamenode -format
// 将格式化后 master1 节点 hadoop 工作目录中的元数据目录复制到 master2 节点
$ scp-r app/cdh/hadoop-2.6.0-cdh5.7.1/data/namenode/*hadoop-master2:/home/hadoop/app/cdh/hadoop-2.6.0-cdh5.7.1/data/namenode/
// 初始化完毕后可关闭 journalnode(分别在 slave1、slave2 和 slave3 上执行)
$ hadoop-daemon.shstop journalnode
1 Hadoop HA 架构详解
1.1 HDFS HA 背景
HDFS 集群中 NameNode 存在单点故障(SPOF)。对于只有一个 NameNode 的集群,如果 NameNode 机器出现意外情况,将导致整个集群无法使用,直到 NameNode 重新启动。
影响 HDFS 集群不可用主要包括以下两种情况:一是 NameNode 机器宕机,将导致集群不可用,重启 NameNode 之后才可使用;二是计划内的 NameNode 节点软件或硬件升级,导致集群在短时间内不可用。
为了解决上述问题,Hadoop 给出了 HDFS 的高可用 HA 方案:HDFS 通常由两个 NameNode 组成,一个处于 active 状态,另一个处于 standby 状态。Active NameNode 对外提供服务,比如处理来自客户端的 RPC 请求,而 Standby NameNode 则不对外提供服务,仅同步 Active NameNode 的状态,以便能够在它失败时快速进行切换。
1.2 HDFS HA 架构
一个典型的 HA 集群,NameNode 会被配置在两台独立的机器上,在任何时间上,一个 NameNode 处于活动状态,而另一个 NameNode 处于备份状态,活动状态的 NameNode 会响应集群中所有的客户端,备份状态的 NameNode 只是作为一个副本,保证在必要的时候提供一个快速的转移。
为了让 Standby Node 与 Active Node 保持同步,这两个 Node 都与一组称为 JNS 的互相独立的进程保持通信 (Journal Nodes)。当 Active Node 上更新了 namespace,它将记录修改日志发送给 JNS 的多数派。Standby noes 将会从 JNS 中读取这些 edits,并持续关注它们对日志的变更。Standby Node 将日志变更应用在自己的 namespace 中,当 failover 发生时,Standby 将会在提升自己为 Active 之前,确保能够从 JNS 中读取所有的 edits,即在 failover 发生之前 Standy 持有的 namespace 应该与 Active 保持完全同步。
为了支持快速 failover,Standby node 持有集群中 blocks 的最新位置是非常必要的。为了达到这一目的,DataNodes 上需要同时配置这两个 Namenode 的地址,同时和它们都建立心跳链接,并把 block 位置发送给它们。
任何时刻,只有一个 Active NameNode 是非常重要的,否则将会导致集群操作的混乱,那么两个 NameNode 将会分别有两种不同的数据状态,可能会导致数据丢失,或者状态异常,这种情况通常称为“split-brain”(脑裂,三节点通讯阻断,即集群中不同的 Datanodes 却看到了两个 Active NameNodes)。对于 JNS 而言,任何时候只允许一个 NameNode 作为 writer;在 failover 期间,原来的 Standby Node 将会接管 Active 的所有职能,并负责向 JNS 写入日志记录,这就阻止了其他 NameNode 基于处于 Active 状态的问题。
基于 QJM 的 HDFS HA 方案如上图所示,其处理流程为:集群启动后一个 NameNode 处于 Active 状态,并提供服务,处理客户端和 DataNode 的请求,并把 editlog 写到本地和 share editlog(这里是 QJM)中。另外一个 NameNode 处于 Standby 状态,它启动的时候加载 fsimage,然后周期性的从 share editlog 中获取 editlog,保持与 Active 节点的状态同步。为了实现 Standby 在 Active 挂掉后迅速提供服务,需要 DataNode 同时向两个 NameNode 汇报,使得 Stadnby 保存 block to DataNode 信息,因为 NameNode 启动中最费时的工作是处理所有 DataNode 的 blockreport。为了实现热备,增加 FailoverController 和 Zookeeper,FailoverController 与 Zookeeper 通信,通过 Zookeeper 选举机制,FailoverController 通过 RPC 让 NameNode 转换为 Active 或 Standby。
1.3 HDFS HA 配置要素
NameNode 机器:两台配置对等的物理机器,它们分别运行 Active 和 Standby Node。
JouralNode 机器:运行 JouralNodes 的机器。JouralNode 守护进程相当的轻量级,可以和 Hadoop 的其他进程部署在一起,比如 NameNode、DataNode、ResourceManager 等,至少需要 3 个且为奇数,如果你运行了 N 个 JNS,那么它可以允许 (N-1)/ 2 个 JNS 进程失效并且不影响工作。
在 HA 集群中,Standby NameNode 还会对 namespace 进行 checkpoint 操作(继承 Backup Namenode 的特性),因此不需要在 HA 集群中运行 SecondaryNameNode、CheckpointNode 或者 BackupNode。
1.4 HDFS HA 配置参数
需要在 hdfs.xml 中配置如下参数:
dfs.nameservices:HDFS NN 的逻辑名称,例如 myhdfs。
dfs.ha.namenodes.myhdfs:给定服务逻辑名称 myhdfs 的节点列表,如 nn1、nn2。
dfs.namenode.rpc-address.myhdfs.nn1:myhdfs 中 nn1 对外服务的 RPC 地址。
dfs.namenode.http-address.myhdfs.nn1:myhdfs 中 nn1 对外服务 http 地址。
dfs.namenode.shared.edits.dir:JournalNode 的服务地址。
dfs.journalnode.edits.dir:JournalNode 在本地磁盘存放数据的位置。
dfs.ha.automatic-failover.enabled:是否开启 NameNode 失败自动切换。
dfs.ha.fencing.methods:配置隔离机制,通常为 sshfence。
1.5 HDFS 自动故障转移
HDFS 的自动故障转移主要由 Zookeeper 和 ZKFC 两个组件组成。
Zookeeper 集群作用主要有:一是故障监控。每个 NameNode 将会和 Zookeeper 建立一个持久 session,如果 NameNode 失效,那么此 session 将会过期失效,此后 Zookeeper 将会通知另一个 Namenode,然后触发 Failover;二是 NameNode 选举。ZooKeeper 提供了简单的机制来实现 Acitve Node 选举,如果当前 Active 失效,Standby 将会获取一个特定的排他锁,那么获取锁的 Node 接下来将会成为 Active。
ZKFC 是一个 Zookeeper 的客户端,它主要用来监测和管理 NameNodes 的状态,每个 NameNode 机器上都会运行一个 ZKFC 程序,它的职责主要有:一是健康监控。ZKFC 间歇性的 ping NameNode,得到 NameNode 返回状态,如果 NameNode 失效或者不健康,那么 ZKFS 将会标记其为不健康;二是 Zookeeper 会话管理。当本地 NaneNode 运行良好时,ZKFC 将会持有一个 Zookeeper session,如果本地 NameNode 为 Active,它同时也持有一个“排他锁”znode,如果 session 过期,那么次 lock 所对应的 znode 也将被删除;三是选举。当集群中其中一个 NameNode 宕机,Zookeeper 会自动将另一个激活。
1.6 YARN HA 架构
YARN 的 HA 架构和 HDFSHA 类似,需要启动两个 ResourceManager,这两个 ResourceManager 会向 ZooKeeper 集群注册,通过 ZooKeeper 管理它们的状态(Active 或 Standby)并进行自动故障转移。
2 高可用集群规划
2.1 集群规划
根据 Hadoop 的 HA 架构分析,规划整个集群由 5 台主机组成,具体情况如下表所示:
主机名 |
IP 地址 |
安装的软件 |
JPS |
hadoop-master1 |
172.16.20.81 |
Jdk/hadoop |
Namenode/zkfc/resourcemanager/ JobHistoryServer |
hadoop-master2 |
172.16.20.82 |
Jdk/hadoop |
Namenode/zkfc/resourcemanager/ WebProxyServer |
hadoop-slave1 |
172.16.20.83 |
Jkd/hadoop/zookeepe |
Datanode/journalnode/nodemanager/ quorumPeerMain |
hadoop-slave2 |
172.16.20.84 |
Jkd/hadoop/zookeeper |
Datanode/journalnode/nodemanager/ quorumPeerMain |
hadoop-slave3 |
172.16.20.85 |
Jkd/hadoop/zookeeper |
Datanode/journalnode/nodemanager/ quorumPeerMain |
需要说明以下几点:
HDFS HA 通常由两个 NameNode 组成,一个处于 Active 状态,另一个处于 Standby 状态。Active NameNode 对外提供服务,而 Standby NameNode 则不对外提供服务,仅同步 Active NameNode 的状态,以便能够在它失败时快速进行切换。
Hadoop 2.0 官方提供了两种 HDFS HA 的解决方案,一种是 NFS,另一种是 QJM。这里我们使用简单的 QJM。在该方案中,主备 NameNode 之间通过一组 JournalNode 同步元数据信息,一条数据只要成功写入多数 JournalNode 即认为写入成功。通常配置奇数个 JournalNode,这里还配置了一个 Zookeeper 集群,用于 ZKFC 故障转移,当 Active NameNode 挂掉了,会自动切换 Standby NameNode 为 Active 状态。
YARN 的 ResourceManager 也存在单点故障问题,这个问题在 hadoop-2.4.1 得到了解决:有两个 ResourceManager,一个是 Active,一个是 Standby,状态由 zookeeper 进行协调。
YARN 框架下的 MapReduce 可以开启 JobHistoryServer 来记录历史任务信息,否则只能查看当前正在执行的任务信息。
Zookeeper 的作用是负责 HDFS 中 NameNode 主备节点的选举,和 YARN 框架下 ResourceManaer 主备节点的选举。
2.2 软件版本
操作系统:CentOS Linux release 7.0.1406
JDK:Java(TM)SE Runtime Environment (build 1.7.0_79-b15)
Hadoop:Hadoop 2.6.0-cdh5.7.1
ZooKeeper:zookeeper-3.4.5-cdh5.7.1
3 Linux 环境准备
集群各节点进行如下修改配置:
3.1 创建用户并添加权限
// 切换 root 用户
$ su root
// 创建 hadoop 用户组
# groupadd hadoop
// 在 hadoop 用户组中创建 hadoop 用户
# useradd -g hadoop hadoop
// 修改用户 hadoop 密码
# passwd hadoop
// 修改 sudoers 配置文件给 hadoop 用户添加 sudo 权限
# vim /etc/sudoers
hadoop ALL=(ALL) ALL
// 测试是否添加权限成功
# exit
$ sudo ls /root
3.2 修改 IP 地址和主机名
// 切换 root 用户
$ su root
// 修改本机 IP 地址
# vim /etc/sysconfig/network-scripts/ifcfg-eth0
// 重启网络服务
# service network restart
// 修改主机名
# hostnamectl set-hostname 主机名
// 查看主机名
# hostnamectl status
3.3 设置 IP 地址与主机名映射
// 切换 root 用户
$ su root
// 编辑 hosts 文件
# vim /etc/hosts
172.16.20.81 hadoop-master1
172.16.20.82 hadoop-master2
172.16.20.83 hadoop-slave1
172.16.20.84 hadoop-slave2
172.16.20.85 hadoop-slave3
3.4 关闭防火墙和 Selinux
// 切换 root 用户
$ su root
// 停止 firewall 防火墙
# systemctl stop firewalld.service
// 禁止 firewall 开机启动
# systemctl disable firewalld.service
// 开机关闭 Selinux
# vim /etc/selinux/config
SELINUX=disabled
// 重启机器后 root 用户查看 Selinux 状态
# getenforce
3.5 配置 SSH 免密码登录
// 在 hadoop-master1 节点生成 SSH 密钥对
$ ssh-keygen -t rsa
// 将公钥复制到集群所有节点机器上
$ ssh-copy-id hadoop-master1
$ ssh-copy-id hadoop-master2
$ ssh-copy-id hadoop-slave1
$ ssh-copy-id hadoop-slave2
$ ssh-copy-id hadoop-slave3
// 通过 ssh 登录各节点测试是否免密码登录成功
$ ssh hadoop-master2
备注:在其余节点上执行同样的操作,确保集群中任意节点都可以 ssh 免密码登录到其它各节点。
3.6 安装 JDK
// 卸载系统自带的 openjdk
$ suroot
# rpm-qa | grep java
# rpm-e –nodeps java-1.7.0-openjdk-1.7.0.75-2.5.4.2.el7_0.x86_64
# rpm-e –nodeps java-1.7.0-openjdk-headless-1.7.0.75-2.5.4.2.el7_0.x86_64
# rpm-e –nodeps tzdata-java-2015a-1.el7_0.noarch
# exit
// 解压 jdk 安装包
$ tar-xvf jdk-7u79-linux-x64.tar.gz
// 删除安装包
$ rmjdk-7u79-linux-x64.tar.gz
// 修改用户环境变量
$ cd ~
$ vim.bash_profile
exportJAVA_HOME=/home/hadoop/app/jdk1.7.0_79
exportPATH=$PATH:$JAVA_HOME/bin
// 使修改的环境变量生效
$ source.bash_profile
// 测试 jdk 是否安装成功
$ java-version
4 集群时间同步
如果集群节点时间不同步,可能会出现节点宕机或引发其它异常问题,所以在生产环境中一般通过配置 NTP 服务器实现集群时间同步。本集群在 hadoop-master1 节点设置 ntp 服务器,具体方法如下:
// 切换 root 用户
$ su root
// 查看是否安装 ntp
# rpm -qa | grep ntp
// 安装 ntp
# yum install -y ntp
// 配置时间服务器
# vim /etc/ntp.conf
# 禁止所有机器连接 ntp 服务器
restrict default ignore
# 允许局域网内的所有机器连接 ntp 服务器
restrict 172.16.20.0 mask 255.255.255.0 nomodify notrap
# 使用本机作为时间服务器
server 127.127.1.0
// 启动 ntp 服务器
# service ntpd start
// 设置 ntp 服务器开机自动启动
# chkconfig ntpd on
集群其它节点通过执行 crontab 定时任务,每天在指定时间向 ntp 服务器进行时间同步,方法如下:
// 切换 root 用户
$ su root
// 执行定时任务,每天 00:00 向服务器同步时间,并写入日志
# crontab -e
0 0 * * * /usr/sbin/ntpdate hadoop-master1>> /home/hadoop/ntpd.log
// 查看任务
# crontab -l
更多详情见请继续阅读下一页的精彩内容 :http://www.linuxidc.com/Linux/2016-08/134180p2.htm
8 Hadoop 集群的启动
8.1 集群启动步骤
// 启动 zookeeper 集群(分别在 slave1、slave2 和 slave3 执行)
$ zkServer.shstart
// 启动 HDFS(在 master1 执行)
$ start-dfs.sh
备注:此命令分别在 master1/master2 节点启动了 NameNode 和 ZKFC,分别在 slave1/slave2/slave3 节点启动了 DataNode 和 JournalNode,如下图所示。
// 启动 YARN(在 master2 执行)
$ start-yarn.sh
备注:此命令在 master2 节点启动了 ResourceManager,分别在 slave1/slave2/slave3 节点启动了 NodeManager。
// 启动 YARN 的另一个 ResourceManager(在 master1 执行,用于容灾)
$ yarn-daemon.sh start resourcemanager
// 启动 YARN 的安全代理(在 master2 执行)
$ yarn-daemon.sh start proxyserver
备注:proxyserver 充当防火墙的角色,可以提高访问集群的安全性
// 启动 YARN 的历史任务服务(在 master1 执行)
$ mr-jobhistory-daemon.sh starthistoryserver
备注:yarn-daemon.sh start historyserver 已被弃用;CDH 版本似乎有个问题,即 mapred-site.xml 配置的 mapreduce.jobhistory.address 和 mapreduce.jobhistory.webapp.address 参数似乎不起作用,实际对应的端口号是 10200 和 8188,而且部需要配置就可以在任意节点上开启历史任务服务。
8.2 集群启动截图
hadoop-master1 开启了 NameNode、ResourceManager、HistoryServer 和 ZKFC,如下图所示:
hadoop-master2 开启了 NameNode、ResourceManager、ProxyServer 和 ZKFC,如下图所示:
hadoop-slave1、hadoop-slave2 和 hadoop-slave3 分别开启了 DataNode、JournalNode、NodeManager 和 ZooKeeper,如下图所示:
8.3 Web UI
下图为 http://hadoop-master1:50070,可看到 NameNode 为 active 状态:
下图为 http://hadoop-master2:50070,可看到 NameNode 为 standby 状态:
HDFS 还有一个隐藏的 UI 页面 http://hadoop-master1:50070/dfshealth.jsp 比较好用:
下图为 http://hadoop-master2:8088,可看到 ResourceManager 为 active 状态:
下图为 http://hadoop-master1:8088,可看到 ResourceManager 为 standby 状态,它会自动跳转到 http://hadoop-master2:8088:
下图为 http://hadoop-master1:19888,可查看历史任务信息:
9 功能测试
// 向 HDFS 上传数据
$ hadoopfs -put webcount.txt /input
// 查看 HDFS 上的数据
$ hadoopfs -ls /input
$ hadoopfs -cat /input/webcount.txt
// 向 YARN 提交 MapReduce 任务,该任务用于分析网站日志文件 webcount.txt 统计每小时的点击次数
$ hadoopjar mr-webcount-0.0.1-SNAPSHOT.jar com.mr.demo.WebCountDriver/input/webcount.txt /output/webcount 1 1
// 在 HDFS 查看结果
$ hadoopfs -ls /output/webcount
$ hadoopfs -cat /output/webcount/part-r-00000
// 通过 Web UI 查看任务信息和历史任务信息
更多 Hadoop 相关信息见 Hadoop 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=13
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-08/134180.htm