HA下的Spark集群工作原理解密

本文详细介绍如何在 Spark 中配置基于 ZooKeeper 的高可用性 (HA) 集群,包括搭建 ZooKeeper 集群、配置 Spark 以支持 ZooKeeper 下的 HA、启动和验证 Spark 集群的过程。

默认情况下StandaloneSpark集群是Master-Slaves架构的集群模式,由一台master来调度资源,这就和大部分的Master-Slaves结构集群一样,存在着Master单点故障的问题。如何解决这个单点故障的问题呢Spark提供了两种方案:基于文件系统的单点恢复(Single-Node Recovery with Local File system)和基于zookeeperStandby Masters(Standby Masters with ZooKeeper)。其中ZooKeeper是生产环境下的最佳选择。

ZooKeeper提供了一个Leader Election机制,利用这个机制你可以在集群中开启多个master并使它们都注册ZooKeeper实例ZooKeeper管理使其中只有一个是Active的,其他的都是Standby的,Active状态的master可以提供服务,standby状态的则不可以。ZooKeeper保存集群的状态信息,该信息包括所有的WorkerDriver Application。当ActiveMaster出现故障时,ZooKeeper从其他standby的master中选举出一台,然后该新选举出来的master会恢复挂掉了master的状态信息,之后该Master就可以正常提供调度服务。整个恢复过程只需要12分钟。需要注意的是,1到2分钟内,只会影响新程序的提交那些在master崩溃时已经运行在集群中的程序并不会受影响。

下面实战如何配置ZooKeeperspark HA。

1. 下载zookeeper,解压并配置环境变量

a. 推荐从官网下载http://www.apache.org/dyn/closer.cgi/zookeeper/

 

 

b. 解压:tar -xzvf zookeeper-3.4.6.tar.gz

c. 修改环境变量: vim ~/.bashrc:

export ZOOKEEEPER_HOME=/usr/local/spark/zookeeper-3.4.6

export PATH=$ZOOKEEPER_HOME/bin:$PATH

d. source ~/.bashrc

2. 配置ZooKeeper

a. 首先创建datadir和datalogdir,可以以下命令创建: mkdir data; mkdir logs:

 

b. 修改配置文件zoo.cfg:

如果$ZOOKEEPER_HOME/conf文件夹下没有zoo.cfg的话,可以使用命令创建一个:scp zoo_sample.cfg zoo.cfg

vim zoo.cfg

 

注:

dataDir指定的是用来存储内存里数据的快照文件的目录;(在不指定dataLogDir,该目录也是数据库更改事务日志的存储目录)

dataLogDir指定的是数据库的事务日志的存储目录

server.0,server.1,server.2指定的是ZooKeeper集群的各个节点,这里的数字012对应该节点datadirmyid文件的内容(在此强调:必须在这里指定所有ZooKeeper节点!)。

两个端口" 2888" and "3888",前者ZooKeeper各个节点相互连接通信所用,后者leader 选举所用

更详细的信息请参阅:

http://zookeeper.apache.org/doc/trunk/zookeeperStarted.html

 

c. 在datadir下建立myid文件并修改内容为0:echo 0 > myid

 

注意myid文件的内容对应zoo.cfg中指定的该节点的id.

 

d. 使用scp命令将ZooKeeper拷贝到另外2台机器上(生产环境的最佳实践是用3台机器做HA),配置另外2台机器的环境变量并source使之生效:

scp -r zookeeper-3.4.6/ root@worker1:/usr/local/spark

 

vim ~/.bashrc:

export ZOOKEEEPER_HOME=/usr/local/spark/zookeeper-3.4.6

export PATH=$ZOOKEEPER_HOME/bin:$PATH

source ~/.bashrc

e. 在另外2台机器上将data 目录下的文件myid的内容分别修改为1和2。

3. 启动ZooKeeper集群:

a. 在安装了zk的各个节点上分别执行: zkServer.sh start

b. jps验证启动成功与否,若见‘QuorumPeerMain’表明进程已启动

注意:进程启动成功不代表ZooKeeper已经可以开始提供服务。

c. zkServer.sh status验证启动成功与否,并查看哪台机器被选为leader.

master节点启动并验证ZooKeeper状态:

 

注意: 上图使用命令'zkServer.sh status' 可见如下错误信息,‘Error contacting service. It is probably not running.’ 但这是正常的一旦另外2台节点上启动了ZooKeeper,该错误信息将不会再出现。一个简单的事实是,只有超过一半的ZooKeeper节点启动后,ZooKeeper才可以正常提供服务

另外2台机器上启动并验证ZooKeeper

 

 

master上验证ZooKeeper状态

 

可见,现在master节点的错误信息不见了。

现在3台节点ZooKeeper都已经启动成功,且一台(worker2是Leader,其它都是follower. ZooKeeper已经启动成功,并正常对外提供服务!

4. 修改spark配置使其支持ZooKeeper下的HA:

cd $SPARK_HOME/conf;

vim spark-env.sh:

注释掉export SPARK_MASTER_IP=MASTER

添加export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER

-Dspark.deploy.zookeeper.url=master:2181,worker1:2181,worker2:2181 -Dspark.deploy.zookeeper.dir=/spark"

注意所有准备用来做master的节点都需要做这个修改。

 

 

5. 在正式开始启动spark集群之前,我们先来看看启动spark集群的相关命令:

a. start-all.sh其实质是先后执行了start-master.sh和start-slaves.sh:

 

b. start-master.sh会首先尝试从spark_env.sh中获取spark_master_ip,获取到的话,就在该参数对应的节点上(可以是当前节点也可以不是当期节点)启动master进程;如果没有获取到的话,则会在当前节点上启动master进程:

 

事实上,如果在spark-env.sh中指定了spark_master_ip,但它的值不是你当前执行start-master.sh或start-all.sh的节点的hostname的话,会报如下错误:

16/02/24 00:21:07 WARN util.Utils: Service 'sparkMaster' could not bind on port 7077. Attempting port 7078.

Exception in thread "main" java.net.BindException: Cannot assign requested address: Service 'sparkMaster' failed after 16 retries!

c. start-slaves.sh会在slaves 文件中指定的每个节点上分别调用start-slave.sh来启动worker进程,并尝试注册到特定的master上。这个master通过以下方式获取:首先尝试从spark-env.sh中获取spark_master_ip,获取到的话,该参数对应的节点(可以是当前节点也可以不是当期节点)就是master节点;如果没有获取到的话,则会视当前节点为master节点。该命令不能从命令行接受参数:

 

d. start-slave.sh: start-slave.sh必须在命令行指定master(s). 该命令会从命令行的第一个参数中取得master节点。

 

 

事实上,该命令常用来在集群中动态添加worker节点,即:整个集群已经启动并正常提供服务,这时有新的节点可用,我们要把它动态添加到集群中,而不必关闭正在运行的集群,就可以使用该命令。

e. 不论你用哪种方式启动worker(start-slaves.sh 或start-slave.sh),当对应的Master没有启动或启动了但不是active状态的话,该worker进程仍然可以启动成功(即Jps可见该进程),但由于没能向master注册成功,它并不能提供服务,它会一直尝试向master注册!

6. 在部署了ZooKeeper的一台host上启动spark:

通过step5中的分析,我们知道当spark_env.sh中没有配置spark_master_ip,提交命令start-all.sh, start-master.sh或start-slaves.sh)的host视为master节点在ZooKeeper下所有的ZooKeeper节点都必须step4 中的修改,即注释SPARK_MASTER_IP的配置,并添加SPARK_DAEMON_JAVA_OPTS.

事实上,我们可以在任意一个已经启动了ZooKeeper且ZooKeeper正常提供服务的节点上通过命令start-all.sh启动spark集群不管该节点在ZooKeeper中的状态是leader还是follower. (当然你也可以只在该节点上start-master.sh,后续通过start-slaves.sh再启动workers.)

在这里我们在master节点上用start-all.sh来启动spark集群。

因为spark只是一个计算框架,在这里我们还是使用hadoophdfs作为文件系统,故需要首先需要确保hdfs已经启动并正常提供服务:

 

确保ZooKeeper已经启动并正常提供服务:

Master节点处于follower状态

 

Worker1处于leader状态

 

Worker2处于follower状态

 

master节点上用start-all.sh来启动spark集群:当然你也可以在worker1worker2上使用start-all.sh来启动整个集群,或只是start-master.sh来启动master进程

 

注:若你正确地配置了某个节点使用ZooKeeper,(即配置了SPARK_DAEMON_JAVA_OPTS且注释掉了SPARK_MASTER_IP),但在没有成功启动ZooKeeper的情况下使用命令start-master.sh启动master的话master仍能成功启动,但会处于standby不能提供服务:

 

 

再次强调:需要成功地启动了ZooKeeper集群的基础上(至少在一半以上ZooKeeper节点上成功启动了ZooKeeper进程),才能去启动spark集群

7. jps验证哪些节点有master进程

master:

 

Worker1:

 

Worker2:

 

可见只有手动提交了start-all.sh或start-master.sh的master节点有MASTER进程,其他节点则没有

注意:即使有了master进程,也不代表master已经启动并能提供服务,这还要看master状态是否是ACTIVE.

8. WebUI查看master状态.

可见master处于ALIVE状态,在正常提供服务。

事实上, 第一台手动启动了master进程的节点一定是alive状态的,不管该节点在zk上是leader还是follower!

9. 在其他ZooKeeper节点上手动启动master,并用jps查看Master进程是否启动成功:

再次强调: 这些节点的spark-env.sh中一定要正确配置为使用ZooKeeper, 即正确配置了SPARK_DAEMON_JAVA_OPTS,否则的话这些启动了的多个masters不能发觉彼此的存在,从而都将自己视为ACTIVE状态,这将置于个集群于不健康状态(因为这些masters都会独立提供资源调度服务)。这个错误如此常见,以至于在官网上也有说明:

 

worker1上启动master并jps查看进程:

 

worker2上启动master并jps查看进程:

 

 

10. WebUI查看所有master的状态。

Master:

 

Worker1:

 

 

Worker2:

 

总结:手动启动的第一个master是ACTIVE状态,后续启动的masters是STANDBY状态, 这与哪个节点是leader,哪个节点是follower没有关系。

注:这些节点有的在zk中可能是leader,但这不影响。

 

11. 如果你step6中只是采用start-master.sh启动master而不是使用start-all.sh启动masterconf/slaves文件中指定的workers的话,需要通过start-slaves.sh启动所有conf/slaves文件中指定的workers.

由于我们是使用的start-all.sh启动了整个集群,这里不再需要单独启动workers

12. 使用ZooKeeper HA下的spark:

在没有做HA时,当我们提交新程序或动态添加worker时,需要提供系统唯一的master的ip; 而在ZooKeeper HA状态下的spark集群,由于有多个master,我们需要提供这些所有的masterip, Spark程序会依次尝试所有提供master,知道找到当前正在提供服务master(ACTIVE状态的master)。即:Spark程序会跟所有MASTER通信,因为要注册,但最后只跟作为ACTIVE级别的MASTER交互。

验证:$SPARK_HOME/bin/spark-shell --master spark://Master:7077,Worker1:7077,Worker2:7077

 

 

Jps 可见该程序:

 

Webui 查看该程序:

 

 

 

13. 验证HA下的系统故障与恢复:

a. 在当前ACTIVE状态master上,用命令stop-master.shstop master,模拟实际环境中的机器故障。发布命令后jps,可见master进程确实没有了,spark-shell程序的进程SparkSubmit一直存在,不受影响:

b. 此时spark-shell终端可见如下Log:

 

c. webui查看各个master状态:

Master节点发生故障无法连接:

 

Worker1standby状态变为active状态,且已经开始正常提供服务,running application可见提交的spark-shell程序

 

Worker2状态仍是standby状态

 

事实上,提交新程序和动态添加新的worker,它们需要找到并注册到当前提供服务的处于ACTIVE状态master,一旦注册成功后,它们‘在系统中’了:相关信息保存在ZooKeeper中了)。后续一旦发生故障,ZooKeeper选举出来的leaderZooKeeper保存的信息中恢复发生了故障的master状态信息,然后开始提供服务,联系所有的已经注册了的applicationworker,告诉它们新的ACTIVEmasterip。这些applicationworker在最初启动时甚至不需知道这些后续转为active状态master的存在!也正因为如此,新的master可以任何时候被动态创建

d. 在已经发生了故障的master节点上,重新启动master进程,并用JpswebUI查看其状态:

 

 

 

14. 常见问题与解决方法

 

 

if you config as following and start-master on the master host without starting zk:

#export SPARK_MASTER_IP=master

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=master:2181,worker1:2181,worker2:2181 -Dspark.deploy.zookeeper.dir=/spark"

 

then below will occur:

 

1.6.0 Spark Master at spark://master:7077

 

    URL: spark://master:7077

    REST URL: spark://master:6066 (cluster mode)

    Alive Workers: 0

    Cores in use: 0 Total, 0 Used

    Memory in use: 0.0 B Total, 0.0 B Used

    Applications: 0 Running, 0 Completed

    Drivers: 0 Running, 0 Completed

    Status: STANDBY

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值