spark on hive 模式导致读写 hdfs 失败

spark sql 操作 hive 表,底下的支撑其实还是 hdfs,之前的集群,hdfs 没有做 HA,倒也相安无事,不过最新 spark sql 的计算任务迁移到了一个新的集群,刚迁移过去的时候,计算任务是能够正常跑的,但是,后来这个集群上的 hdfs 做了 HA,问题就来了

Caused by: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.ipc.StandbyException): Operation category READ is not supported in state standby. Visit https://s.apache.org/sbnn-error
	at org.apache.hadoop.hdfs.server.namenode.ha.StandbyState.checkOperation(StandbyState.java:88)
	at org.apache.hadoop.hdfs.server.namenode.NameNode$NameNodeHAContext.checkOperation(NameNode.java:1826)
	at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.checkOperation(FSNamesystem.java:1431)
	at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getFileInfo(FSNamesystem.java:4235)
	at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.getFileInfo(NameNodeRpcServer.java:895)
	at org.apache.hadoop.hdfs.server.namenode.AuthorizationProviderProxyClientProtocol.getFileInfo(AuthorizationProviderProxyClientProtocol.java:527)
	at org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolServerSideTranslatorPB.getFileInfo(ClientNamenodeProtocolServerSideTranslatorPB.java:824)
	at org.apache.hadoop.hdfs.protocol.proto.ClientNamenodeProtocolProtos$ClientNamenodeProtocol$2.callBlockingMethod(ClientNamenodeProtocolProtos.java)
	at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:617)
	at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1073)
	at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2086)
	at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:2082)
	at java.security.AccessController.doPrivileged(Native Method)
	at javax.security.auth.Subject.doAs(Subject.java:415)
	at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1693)
	at org.apache.hadoop.ipc.Server$Handler.run(Server.java:2080)

可以看到报了一个错,叫做 Operation category READ is not supported in state standby,先看了一下集群的情况,原来是在做 HA 的过程 hdfs 的 active 节点发生了变化,原来是 node1 作为 hdfs 的 active 节点,现在 node1 变成了 standby 节点,而 node2 变成了 active 节点。

这个变化其实没有任何问题,因为在 HA 的情况,任何节点都应该被允许成为 active,而上层应用程序也应该能够处理 HA 的 hdfs集群的 active 节点发生转移的时候的情况。所以,初步的猜想是,我们的 spark sql 任务在读写 hive 的时候固定的从 node1 去操作 hdfs 文件了,这个问题应该是由某一个配置控制的,那么就在 cdh 的配置页面中找,但是并没有特别清晰的选项用于控制这个,于是直接上机器去看配置文件,cdh 的 spark 读写 hdfs 的配置在 /etc/spark/conf.cloudera.spark_on_yarn/yarn-conf/hdfs-site.xml

<property>
    <name>dfs.ha.namenodes.nameservice1</name>
    <value>namenode50,namenode84</value>
  </property>

可以看到其中有配置了多个 nameservice 的,按理说应该没有问题,于是上网一通 google,但是找到的都是牛头不对马嘴的解决方案,于是回来自己思索。。。

手贱查看了一下集群中其他节点的同名配置,发现集群中其他机器的配置文件不一致,猜想是配置 HA 的时候没有下发配置文件到集群中,于是在 cdh 页面上手工下发配置并重启集群,然而。。这并没有什么卵用。。

出于排插的目的,我试了一下 beeline 客户端,用 hdfs 用户启动 beeline,发现是可以正常的 create table 和 drop table 的,在 hdfs 的 web ui 中可以看到 /hive/warehouse 目录下能够相应的创建和删除对应的表的文件夹,这就说明起码在 beeline 这个环节上是没有问题的,那么就应该是在调用 spark sql 的时候使用的配置有问题,那么又尝试了一下不用 -f 命令传入脚本,而是直接用 /opt/cloudera/parcels/CDH/lib/spark/bin/spark-sql 启动终端,然后执行 create 命令,发现也是正常的

然后又是一顿搜,看到这个,https://community.hortonworks.com/questions/9790/orgapachehadoopipcstandbyexception.html

I know turning the nn1 back to ACTIVE solves the issue. Looking for workarounds which doesn’t require this manual operation. Thanks in advance.

于是先把这个问题绕过去,然后把 spark sql 脚本跑起来,但是后来用 beeline 去执行 select 语句的时候又报错:

Error: Error while compiling statement: FAILED: SemanticException Unable to determine if hdfs://.... is encrypted: java.lang.IllegalArgumentException: Wrong FS: hdfs://..., expected: hdfs://nameservice1 (state=42000,code=40000

转而尝试使用 /opt/cloudera/parcels/CDH/lib/spark/bin/spark-sql 的命令行可以正常执行,这里其实背后的原因是 beeline 连接的是 hive 的 thriftserver,而不是 spark thrift server,看 ps 命令的输出

hive      6075 24337  0 16:53 ?        00:01:09 /usr/java/jdk1.7.0_67-cloudera/bin/java -Xmx256m -Dhadoop.log.dir=/opt/cloudera/parcels/CDH-5.7.2-1.cdh5.7.2.p0.18/lib/hadoop/logs -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=/opt/cloudera/parcels/CDH-5.7.2-1.cdh5.7.2.p0.18/lib/hadoop -Dhadoop.id.str= -Dhadoop.root.logger=INFO,console -Djava.library.path=/opt/cloudera/parcels/CDH-5.7.2-1.cdh5.7.2.p0.18/lib/hadoop/lib/native -Dhadoop.policy.file=hadoop-policy.xml -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Stack=true -Xms856686592 -Xmx856686592 -XX:MaxPermSize=512M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:OnOutOfMemoryError=/opt/cm-5.7.2/lib64/cmf/service/common/killparent.sh -Dhadoop.security.logger=INFO,NullAppender org.apache.hadoop.util.RunJar /opt/cloudera/parcels/CDH-5.7.2-1.cdh5.7.2.p0.18/lib/hive/lib/hive-service-1.1.0-cdh5.7.2.jar org.apache.hive.service.server.HiveServer2

这个地方其实有两个事情要做,首先,应该把 10000 这个端口让出来由 spark thrift server 来提供服务,这块涉及到 cdh 的配置,需要把 hive thrift server 的角色去掉,然后看看怎么加上 spark thrift sevrver 的服务。另外一方面,退一步说,即使是 hive thrift server,也应该能正确的找到这些 hdfs 上的文件,这个坑我感觉应该是由于我们先在 hive 上建立了一部分的表结构并导入了数据之后,再回头去开通 HA 导致的。理想的做法应该是在部署期间配置好 HA,然后再建表结构和导入数据的。

但是 cdh 官方本身对 spark 是打压的态度的(估计是为了推广自己的 impala),所以 cdh 页面上并不能配置 spark thrift server,我们的做法是把 hive thrift server 的角色去掉,然后手工启动 spark thrift server:

/opt/cloudera/parcels/CDH/lib/spark/sbin/start-thriftserver.sh  \
--master yarn-client \
--num-executors 3  \
--executor-cores 3 \
--executor-memory 2G   \
--jars /opt/cloudera/parcels/CDH-5.7.2-1.cdh5.7.2.p0.18/lib/hive/lib/mysql-connector-java-5.1.34.jar \
--driver-java-options "-Dlog4j.configuration=file:///opt/cloudera/parcels/CDH/lib/spark/conf/log4j.properties" 

但是这样做的恶心之处在于没有纳入 cdh 的统一管理,所以以后凡是需要用到 beeline 的时候,都需要先去看看 spark thrift server 启动了没,如果没有启动,还需要手工启动,如果过程中挂了,还需要手工去重启等等,也是烦人

想要从 cdh 上删除 hive server2 的时候还报错,说至少需要有一个 hive server2,算了,也就保留,但是手工把他停止掉了,这样 10000 端口才能让出来给 spark thrift server 用。不过还是觉得这样恶心,索性把 hive thrift server 改成 10001,这样就不会有端口冲突的问题了。

启动了 spark thrift sever 之后发现的另外一个事情是发现 yarn 上的可用内存不够了,整个集群才 12G,这个不符合道理啊,实际上一个节点有 32G 的内存,哪怕主节点不干活,也有 64G,起码应该配置 50G 给 yarn 才差不多,于是在页面上把这个值改成 20G。然后重新下发了配置。

———————

2016-11-30 00:05:07 追加

在这里,https://github.com/mattshma/bigdata/issues/44,提到

HDFS设置HA后,hive报错如下:

FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:java.lang.IllegalArgumentException: Wrong FS: , expected: )
在HA设置前的表,在设置HA后,无法删除。原因是hive metastore中关于namenode的信息没更新。操作如下:

停Hive服务
在Hive metastore标签中, 点击Actions,执行Update Hive Metastore NameNodes
启动hive服务
本身在HA设置后,CDH也会说需要更新hive元数据的。

通过这个设置,可以解决 Wrong FS 的问题

—————————

2016-12-4 11:38:54 一并的,上文提到的 namenode 转移的问题也可以解决了

2 thoughts on “spark on hive 模式导致读写 hdfs 失败

Leave a Reply

Your email address will not be published. Required fields are marked *