1.环境简述
上从在docker上装完hadoop集群后,发现有很多问题,又重新建了一个,如下:
主机名 | IP | 角色 |
---|---|---|
hadoop1 | 172.18.0.11 | NN1 ZK RM |
hadoop2 | 172.18.0.12 | NN2 ZK RM JOBHISTORY |
hadoop3 | 172.18.0.13 | DN ZK ND |
hadoop4 | 172.18.0.14 | DN QJM1 ND |
hadoop5 | 172.18.0.15 | DN QJM2 ND |
hadoop6 | 172.18.0.16 | DN QJM3 ND |
目前已经安装了hdfs yarn zookeeper
2.QJM HA简述
2.1为什么要做HDFS HA?
在hadoop2.0之前,集群中只能有一个namenode,如果这个namenode宕机或者需要对namenode进行升级,那么整个集群的服务将不可用。因此要做HA。
2.2 HDFS HA的方式
目前支持两种HA方式:
- NFS namenode和standby namenode共享一个NFS磁盘,所以namenode的元数据变更立即同步到standby中。缺点是如果namenode或者standby namenode与NFS磁盘之间的网络出了问题,HA即失效,即把namenode单独故障转移到了NFS上,NFS同样存在单点故障问题。
- QJM namenode和standby namenode分别连接到一组Journal节点中,如果namenode出了故障,可以把standby namenode切换到activer状态,不影响集群使用。本方式配合zookeeper可以实现自动切换。
2.2 HSFS HA的结构
以上是QJM HA的典型的结构图。集群中共有两个namenode(简称NN),其中只有一个是active状态,另一个是standby状态。active 的NN负责响应DN(datanode)的请求,为了最快的切换为active状态,standby状态的NN同样也连接到所有的datenode上获取最新的块信息(blockmap)。active NN会把元数据的修改(edit log)发送到多数的journal节点上(2n+1个journal节点,至少写到n+1个上),standby NN从journal节点上读取edit log,并实时的合并到自己的namespace中。另外standby NN连接所有DN,实时的获取最新的blockmap。这样,一旦active的NN出现故障,standby NN可以立即切换为active NN.
注意:同一时刻只能有一个NAMENODE写edit log,否则将hdfs 元数据将会"脑裂"
2.3 机器要求
- 两个配置完全一样的namenode节点
- 2n+1个 journal节点,journal节点不需要很好的配置,可以与集群中的其它角色一起。
3.部署HDFS HA
3.1 详细配置
HDFS HA中用,nameserivce ID来标识一个HDFS服务,为了标识每个NN,还要加上namenode id。
在hdfs-site.xml中: 1.设置集群的标识dfs.nameservice<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
这里修改为dockercluster
2.设置namenode名称 dfs.ha.namenodes.[nameservice ID]
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
nn1 nn2为namenode的标识。
注意:当前只支持两个namenode的HA3.设置namenode对外提供服务的RPC地址 dfs.namenode.rpc-address.[nameservice ID].[name node ID]
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>machine1.example.com:8020</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>machine2.example.com:8020</value>
</property>
这个RPC地址实际就是 dfs.defaultFS地址
4.设置HDFS web页面地址 dfs.namenode.http-address.[nameservice ID].[name node ID]
<property>
<name>dfs.namenode.http-address.mycluster.nn1</name>
<value>machine1.example.com:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.mycluster.nn2</name>
<value>machine2.example.com:50070</value>
</property>
如果启用的hdfs的安全机制,要设置 https-address
5.设置journal上edit log共享目录 dfs.namenode.shared.edits.dir
格式是:qjournal://host1:port1;host2:port2;host3:port3/journalId 所有节点上路径要保持一致<property>
<name>dfs.namenode.shared.edits.dir</name><value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster</value>
</property>
这里我们改成: qjournal://hadoop4:8485;hadoop5:8485;hadoop6:8485/dockercluster
6.设置实现集群HA的类 dfs.client.failover.proxy.provider.[nameservice ID]
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
当前仅支持这个类
7.设置切换时执行的程序 dfs.ha.fencing.methods
当namenode发生切换时,原来active的NN可能依然在写edit log,这时如果standby 也开始写edit log,元数据会"脑裂"。为了防止"脑裂",必须要切换之前杀掉原来active 的NN,这个脚本就是实现这个目的。当前支持两中fencing.method:shell 和 sshfence。另外,可能自定义org.apache.hadoop.ha.NodeFence来实现自己的保护程序。7.1.sshfence(默认)
通过SSH登录到原来active的NN,并使用fuser命令KILL掉NN进程。要使用SSH,必须配置rsa-key参数:dfs.ha.fencing.ssh.private-key-files<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/hdfs/.ssh/id_rsa</value>
</property>
也可以用其它用户登录,同样可以配置超时参数:
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence([[username][:port]])</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
7.2.shell
自定义一个shell脚本业杀死NAMENODE<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh arg1 arg2 ...)</value>
</property>
shell脚本可以读取到当前已经配置的HDFS变量,将"."替换为"_" 即可。对于某些共用的条目,如dfs_namenode_rpc-address可以自动的指向特定节点如dfs.namenode.rpc-address.ns1.nn1。以下变量也可以使用:
$target_host |
$target_port |
$target_address |
$target_namenodeid |
示例:
<property>
<name>dfs.ha.fencing.methods</name>
<value>shell(/path/to/my/script.sh --nameservice=$target_nameserviceid $target_host:$target_port)</value>
</property>
如果shell返回0,表示执行成功。如果不为0,则继续执行其它的fencing.method.shell方式没有timeout.
这时里,我们也用ssh方式,比较简单,只需要生成key就行了.在NN1 NN2上执行:
[hdfs@hadoop1 ~]$ ssh-keygen -t rsa
Generatingpublic/private rsa key pair.
Enter file in which to save the key (/home/hdfs/.ssh/id_rsa):
Enter passphrase (empty forno passphrase):
Enter same passphrase again:
Your identification has been saved in/home/hdfs/.ssh/id_rsa.
Yourpublic key has been saved in/home/hdfs/.ssh/id_rsa.pub.
The key fingerprint is:
6b:de:13:b7:55:ba:43:1c:28:ef:2e:b8:b7:0a:e0:15 hdfs@hadoop1
The key's randomart image is:
+--[ RSA 2048]----+
| |
| |
| E . |
8.在core-site.xml中设置hdfs 服务 fs.defaultFS
一旦使用了HDFS HA,那么fs.defaultFS就不能写成host:port文件,而要写成服务方式,写上nameservice id:<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
这里改成hdfs://dockercluster
9.journal节点守护进程自己的数据目录 dfs.journalnode.edits.dir
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/path/to/journal/node/local/data</value>
</property>
3.2 部署HDFS HA
1.启动所有journal节点hadoop-daemon.sh start journalnode
2.如果是新建的集群,在其中一个NN上执行hdfs format命令hdfs namenode -format
hdfs namenode -bootstrapStandby
,该命令会让journal节点做好连接两个namenode的准备。 4.如果将非HA的NN切换为HA,执行hdfs namenode -initializeSharedEdits
将本地的edit log初始化到journal中。 执行完以上后,像平时一样启动每个NN。 在每个NN的web页面中,将会显示NN的状态是active或者standby ————————————————-更改—————————————–
后续更新: 在做实验时发现,官网这个步骤有问题! 正确的步骤是: 1.启动所有journal节点hadoop-daemon.sh start journalnode
2.执行journal 节点初始化hdfs namenode -initializeSharedEdits
3.启动原来的namenode $HADOOP_HOME/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR --script hdfs start namenode
4.初始化standby hdfs namenode -bootstrapStandby
, 5.启动standby $HADOOP_HOME/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR --script hdfs start namenode
————————————————-更改—————————————– .
4. HDFS HA的管理
HDFS HA的管理主要靠hdfs haadmin命令实现:
[hdfs@hadoop1 ~]$ hdfs haadmin
Usage: haadmin
[-transitionToActive [--forceactive]<serviceId>]
[-transitionToStandby <serviceId>]
[-failover [--forcefence][--forceactive]<serviceId><serviceId>]
[-getServiceState <serviceId>]
[-checkHealth <serviceId>]
[-help <command>]
Generic options supported are
-conf <configuration file> specify an application configuration file
-D <property=value>use value for given property
-fs <local|namenode:port> specify a namenode
-jt <local|resourcemanager:port> specify a ResourceManager
-files <comma separated list of files> specify comma separated files to be copied to the map reduce cluster
-libjars <comma separated list of jars> specify comma separated jar files to include in the classpath.
-archives <comma separated list of archives> specify comma separated archives to be unarchived on the compute machines.
The general command line syntax is
bin/hadoop command [genericOptions][commandOptions]
- transitionToActive 和 transitionToStandby 切换为active或者standby。注意:不会使用任务fencing措施,因此一般不使用这两个命令,用
hdfs haadmin -failover
- failover 对hdfs做一次主备切换。在切换前,如果原来active的NN现在是standby状态,那么会直接把原来standby的NN切换为active状态。如果原来active状态的NN还是acitve状态,则会先将该NN切换为standby状态,如果切换为standby失败,则会调用
dfs.ha.fencing.methods
里指定的fencing方式来确保原来的acitve NN被干掉。如果没有定义fencing方式,或者fencing执行失败,则会抛出异常,同时不会切换原来的standby 为acitve - getServiceState 获取指定NN的状态是active还是standby
- checkHealth 检查指定NN的状态。NN会对自己的服务做检查,返回0表示正常,0之外的为异常。注意:当前这个功能还没有实现(形同虚设),只要不停机就会返回0
5.自动切换
以上讲了如何手动切换HA,现在来说说实在切换HA
5.1 使用zookeeper实现HA原理
自动切换的HA需要用到zookeeper中的两个组件ZooKeeper quorum和ZKFailoverController process (ZKFC)。
zoookeeper会做以下两件事:- Failure detection 失效检测 每个namenode都会在zookeeper里保存一个持久会话,一旦某个namenode挂了,zookeeper中的会话就会过期,zookeeper检测机制会通知另外一个namenode需要做failover了
- Active NameNode election 选择新的acitve namenode zookeeper可以通过简单的机制来选出唯一一个acitve的namdenode.当active的namenode挂了后,另外一个namenode会在zookeeper中保存一个独占锁来标明自己将会是下一个active的namenode. ZKFC是同namenode在同一台机器上的zookeeper客户端。ZKFC负责以下事情:
- Health monitoring 健康检测 ZKFC使用health-check命令定时去pingnamenode,如果在超时时间内得到了响应并且状态是健康的(当前不是只能返回0吗?),就认为namenode是健康的,如果超过时间没有响应,则认为namenode宕机。
- ZooKeeper session management 会话管理 当namenode是健康的时间,ZKFC会和zookeeper保持一个会话,对于acitve的namenode节点,还会在zookeeper上创建一个临时的znode(如/hdfs/active.lock),如果会话过期,则znode会被自动删除
- ZooKeeper session management 在active的节点上ZKFC会在zookeeper里创建一个临时的znode并创建文件(如果/hdfs/acitve.lock),这时其它的namenode节点就不能创建同样一个znode,那么其它节点就会监控这个znode,一旦原来active的namenode宕机,则znode被删除,原来standby的namenode上的ZKFC就可以创建znode并切换standby namenode为acitve
5. 部署hdfs自动切换
5.1 关闭集群
将手动的HA切换为自动的HA必须先关闭集群
5.2 添加HA配置
1.修改hdfs-site.xml
添加:<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
2.修改core-site.xml
添加zookeer的server列表:<property>
<name>ha.zookeeper.quorum</name>
<value>zk1.example.com:2181,zk2.example.com:2181,zk3.example.com:2181</value>
</property>
这里我修改为 hadoop1 hadoop2 hadoop3
注意,如果使用了hdfs federation,需要加上nameservice-id,如dfs.ha.automatic-failover.enabled.my-nameservice-id.
5.3 在zookeeper中初始化HA状态
在其中一台namenode上执行:
[hdfs]HADOOP_PREFIX/bin/hdfs zkfc -formatZK
5.4 开启集群
- 使用start-dfs.sh启动集群 如果配置了SSH,使用start-dfs.sh启动集群,该脚本会自动启动ZKFC,然后ZKFC选择出一个active的namenode
- 手动启动集群 在每个namenode机器上执行:
[hdfs]HADOOP_PREFIX/sbin/hadoop-daemon.sh - -script $HADOOP_PREFIX/bin/hdfs start zkfc
5.5 使用zookeeper时的安全机制
略,有兴趣的自己上官网看吧。
6.FAQ
1.ZKFC和NAMENODE有没有特定的启动顺序
2.需要对ZKFC进程做监控,某些时候自动切换失效是因为ZKFC挂了 3.如果zookeeper挂了,则自动failover失效,但不会到HDFS服务有影响。当zookeeper启动后,自动failover功能恢复正常 4.当前并不技能人为的设置某个namenode为primary或者preferred 5.在自动HA的情况下,可以人为的切换namenode,执行hdfs hadmin命令。7.做了HA后HDFS的升级、回滚
注意**在升级、回滚、finalization中必须保持所有journal节点是开启的(至关重要!)
7.1 升级
1.停止所有的namenode,并安装新的软件
2.开启所有的journal节点,注意在升级、回滚、finalization中必须保持所有journal节点是开启的(至关重要!) 3.以-upgrade方式启动一台namenode 4.启动后,一般该namenode会直接进入active状态,然后执行本地元数据和JNs上edit log的升级 5.另外一台要namenode如果使用-upgrade启动会报错,正确方式是重新初始化-bootstrapStandby7.2 finalize
注意:如果做回滚或者finalization,则以正常方式重启所有的namenode(不带其它参数)
在active的namenode上执行hdfs dfsadmin -finalizeUpgrade
,这里active的NN将会定稿edit log并在本地上目录中删除旧版本的元数据 7.3 回滚
注意:如果做回滚或者finalization,则以正常方式重启所有的namenode(不带其它参数)
关闭所有的NN,在其中一台namenode上执行roll back命令,然后启动这个NN。在另外一个NN上执行-bootstrapStandby
命令来同步状态