一次 Spark Operator 问题的救火
Posted 莫比乌斯的code
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次 Spark Operator 问题的救火相关的知识,希望对你有一定的参考价值。
这个是一个由问题推动的知识点收集的旅程,记录了问题本身和问题的定位 (Google) 的点滴。在解决问题时,尝试去 Dive Deeper,就会有一些乐趣产生。
你也许刚好没有接触这块的知识点,对,刚好。没有关系,我尝试输出的是一种定位和解决问题的快乐方法。
背景
你听过 Spark, Maybe 听过 Spark on K8S,但是 Almost not 听过 Spark Operator。
Just Remember 这是个车厂多于轮胎厂的时代,组合创新盛行而有效。
Spark Operator 就是 Google Cloud Native 方式在 K8S 上运行 Spark 的组装车。不多说,上车。
现象
在升级 Spark Operator 后遇到了 Spark Application 无法解析 HDFS namenode service name,报 "Unknown Host" 的错误。前方说我们 十万火急,需要立刻解决,不解决就影响项目进度了。耳熟?对,我们遇到的问题 4 个 9 以上面的话结尾。First,calm down。
线索
从表象上看,最直接的问题是在 Spark Driver 中没有 HDFS 的配置文件:
hdfs-site.xml
从 Pod 的信息中可以看到 HDFS 配置 Volume 未挂载
查看 Spark Operator 的 Log,有一行可疑信息"Bad Certificate"
信息
因为 "Bad Certificate" 能给我们的信息可能很关键,但是并不直观,没有直接说明是谁和谁之间通信的证书不对,所以本次定位是采用从 Volume 挂载的地方入手。平常喜欢的方式是 从现象到理论到代码 的方式进行递进。先顺着线索收集了相关的理论信息。
1. Mounting volume
Spark Operator 的说明文档中有指出Mounting volume
需要Enable webhook
,这个点比较难找。
https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/blob/master/docs/user-guide.md#mounting-volumes
检查了下当前的环境,Webhook 已经 Enable,存在 Spark Operator Controller (CRD) 中。如果不是专业的 K8S 运维,比较难理解 Mutating admission webhook 的含义,嘿,刚好我不是,那就继续挖呗。
2. Mutating admission webhook
这个概念属于 K8S 的范畴,这张图说明了 Mutating Admission Webhook 插件化应用的位置。
直观来说:在 Api server
收到 API 请求后,可以在这个 Hook 点进行一些特殊化的改写动作。Spark Operator 就是在这个点挂载 HadoopConfiguration
。当 Api Server 访问 Mutating Admission Webhook 时需要采用 Https 方式进行访问,也就是在此处用到了认证信息。
图片来源于这篇对 Admission Webhook 的介绍:https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/, (敲黑板,贴出来原链接的意思就是值得看)。
在K8S中,Service Account 通常我们只能使用公钥,如果自定义服务之间需要访问,就需要自己生成密钥对、创建 K8s Secret,这个过程在 Spark-web-hook-init 中调用源码里面 hack 目录下的脚本完成。
3. Web-hook-init
从这个 Job 的 yaml 文件中可以看到,在早期的版本中干的事情是清除密钥和创建密钥。但是遇到了 Update 会清除密钥但不创建新密钥的问题,后面就将这两个功能拆分成两个 yaml。见
issues:https://github.com/GoogleCloudPlatform/spark-on-k8s-operator/issues/875
很可惜,这个问题和我们遇到的问题还不一样,并不能拿来就用,美梦没有得逞。
猜测
从现象和前面收集的信息来看,最大的可能是 Web-hook-init 产生的密钥并没有更新到 Spark Api Server。很大的可能性是升级是先升级了 Spark-operator 的 API Server,然后重新执行了web-hook-init。基于原理的猜想,大概不那么玄学了吧,有效驳斥了老程序定位问题的直觉说。
验证
即然是顺序问题,那么我们重启 Spark-operator 服务,功能正常。嗯,对,还是重启大法好啊。说的没错,同时咱们是有理有据的重启。
Deeper
我起初怀疑是我们内部没有采用 Helm 部署导致的问题,翻开 Yaml 却发现并没有关于 Install Order 的控制,Helm 的默认控制顺序和预期的不一样,Deployment 先部署,Secret 后部署。
https://github.com/helm/helm/blob/release-2.10/pkg/tiller/kind_sorter.go#L61
Trust me,Coder 很难信服非标操作但是理论上Ok的这种东西,大概率还是会保持怀疑,It's OK。
思考
为什么部署的时候没事,反而 update 的时候有事呢?。这里有关于如果pod 创建时, Secret 还没有创建是如何运作的介绍。
https://kubernetes.io/docs/concepts/configuration/secret/
也就是说如果 Secret 的创建在 Pod 的创建之后是没事的。那么更新是如何运作的呢?
在上面的 Note 中已经说明了 Update 对 Spark Operator 这种将 Secret mount 到 Pod 中的玩法无效。具体的Mount 方式是在 spark-operator-deployment.yaml 中控制的:
为什么 helm update 没事,自己update 服务有事呢?
这个纯粹和我们 Update 的方式相关,操作人员在 Update 前执行了 Delete 操作。理论上 K8S 走的是Volidate - Diff - Apply的操作模式,只会更新 Diff 的地方。单纯的 Update 是不会更新 Secret 的。
所以,包含这么多知识点的一个问题,归根到底却是一个操作问题。又有什么关系呢?有收获就 OK了。
后记
我是个经常救火,又立志决不当 Fire man 的人,所以保持借假修真的心态,探究的更加深入,找出知识点和乐趣。
提前祝各位Coder 中秋快乐。
以上是关于一次 Spark Operator 问题的救火的主要内容,如果未能解决你的问题,请参考以下文章
五年java开发生涯中一次吐血的项目救火(程序员何苦难为程序员)
Spark:Dataframe“except”方法引发错误:“unresolved operator:'Except;”