12

DockOne微信分享(二四六):同程艺龙大数据在Kubernetes的实践

 4 years ago
source link: http://dockone.io/article/9663
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【编者的话】同程艺龙是中国在线旅游行业的创新者和市场领导者,秉持 "让旅行更简单、更快乐" 的使命。作为同程艺龙的数据底层支撑部门,我们在2018年推动所有组件容器化部署,2019年推动所有服all in Kubernetes战略,在这个过程中我们也遇到了一些问题,比如存储组件是否应该上Kubernetes,外部世界如何和Kubernetes集群打通,本文将向大家介绍我们使用Kubernetes部署分布式系统的一些经验和思考。

同程艺龙数据中心容器化背景

同程艺龙是中国在线旅游行业的创新者和市场领导者,秉持 "让旅行更简单、更快乐" 的使命。作为同程艺龙的数据底层支撑部门,我们每天会产生海量的数据进行存储和计算,为了更好的支撑业务,我们在2018年推动所有的服务都采用了Docker Host模式部署 。 我们一开始采用脚本管理容器服务,虽然能够做到自动化部署,但是不同组件的资源池没有打通,机器剩余资源的把控不到位,服务的滚动更新和故障转移也处于一个人工处理的状态,这个时候我们急需一个统一调度和管理集群资源的平台,所以2019年我们开始尝试将所有服务部署到Kubernetes上,已经投产的存储服务有Elasticsearch,Tikv,Kudu,Kafka等一些节点类型少的服务,计算组件有Hive,Spark SQL等服务,在这条艰辛的on Kubernetes之路,我们也遇到了很多问题,今天向大家分享一下。

Kubernetes集群如何和外部世界打通

首先看下我们Kubernetes集群的架构图:

eYN32au.jpg!web

Kubernetes集群和外部机器通信,我们采用的OVS虚拟交换机的方式将物理网络和容器网络做成一个大二层的网络,这样Kubernetes集群外部的机器也能访问到容器。因为我们会部署分布式系统,有些分布式系统要求IP不变,所以Pod我们做了IP Local,根据Namespace和pod name来记录每个Pod的IP,IP保留时间为三天。为了防止极端情况下的需求,我们给Pod做了指定IP的功能,但是由于我们部署使用的是自定义对象或者StatefulSet对象,这个功能还需要在上层对象的Controller中支持。上面这些功能我们都是基于Contiv的netplugin插件,在使用过程中我们遇到节点veth pair删除不干净的情况,我们采用定时检查删除无用veth pair来保证网络的干净。

OVS架构图如下:

7VruUnA.jpg!web

Pod网络的问题解决以后,Service的负载我们希望和Pod网络一样可以直接访问。这里我们使用Kubernetes Java Client Watch Service和Endpoints Event事件,将EndPoints和Service Cluster IP同步到同程艺龙的基础设施组件TVS上,TVS是一个四层负载均衡,我们通过HTTP的方式进行同步。在刚开始上线的时候,我们发现Kubernetes Client切换leader节点的时候会重新消费之前的Service event事件,所以同步一定要保证幂等性,Service删除事件同步要加上Kubernetes Service是否存在的校验,确认Service真的不在的时候,再去删除TVS的VIP。这种方式将我们的路由规则更集中式的管理,不会像kube-proxy将所有的规则同步到每台物理机器上。

Service网络解决以后,由于分布式系统的一些特性,有些组件是一定要通过域名才能启动和访问。所以我们会把Kubernetes集群内的节点域名全部注册到公司的DNS服务上,这里我们只要将Kubernetes内部域名改成公司提供的域名尾缀并重启集群和CoreDNS,然后将CoreDNS的域名同步一份到公司的DNS服务上。

做了上面这些支持后,我们觉得这样一个集群才符合生产环境的使用,既可以使用Kubernetes的很多特性,也和公司内部组件做了整合,用户的体验感也没有太大的变化,反而借助于Service和域名的自动同步,Kubernetes的编排能力,体验更佳。

存储计算组件上Kubernetes集群的实践

因为Kubernetes设计的初衷是为了部署计算型的组件,所以我们觉得部署计算节点对我们来说难度不大,我们部署了 Hive,Spark SQL,Tensorflow server等计算组件,不同组件我们采用不同的Namespace进行管理,不同组件的机器资源我们打上标签,做好资源池的划分。针对Hive这种两种及以上类型的节点,一开始我们会采用Helm写好模板做好一键部署,然后我们会投入人力做Hive的Operator,开发Operator可以将我们的一些人工容灾的思想用代码去变现出来,更新多节点类型的组件也会简单很多。

我们都会遇到存储组件应不应该上Kubernetes,我到底应不应该用共享存储的问题。我们认为存储组件是可以上Kubernetes的,这里我们采用的 Local PV+Ceph的方式,针对大数据存储组件,比如ES,TiKV,Kafka等组件,我们采用Local PV的方式,像物理机一样将数据存在本地磁盘,保证性能。针对Jupyter一些非重要的组件我们会采用Ceph RBD Image的方式,用来节省本地磁盘的空间。刚开始的时候我们采用批量创建Local PV的方式,后来我们希望控制磁盘的使用,一个或者两个存储组件配一些计算组件做到资源的混部,我们将Local PV的创建做成按需分配,合理的控制磁盘的使用。

存储组件我们第一个部署的组件是ElasticSearch,ES是非常容易支持的,因为客户端是http访问,对外只需要提供 ES client节点类型,不需要暴露集群内部节点信息,利用Service只需要提供一个TVS的VIP给到用户。刚开始我们使用StatefulSet部署Elasticsearch,由于Statefulset顺序性的限制,我们不能指定下掉某个节点,于是我们开发了一个高级版的AdvanceStatefulSet,通过指定序号就可以下线Pod。

像这样配置即可:

offlineStrategy:

podOrdinals:

- 0

我们将ES部署方式用Helm模板写好,做到自动化部署,每一个节点类型的容错还局限于AdvanceStatefulSet的Controller。ES官方也提供了Operator,因为处于beta版本,自定义资源还有很多问题,没有基于Sts做CRD,所以我们没有选择这种方式。我们觉得在Sts上层再建立一层CRD,可以更方便去管理多种节点类型,但是人力有限,我们对这个需求的优先级还不是很高,当前基于AdvanceSts管理也能够运维起来。

在部署Kudu组件的时候,我们延用ES的套路,发现节点域名解析不到的问题,当时我们还没有将Kubernetes内部域名同步到公司层面的DNS。在分布式系统中,很常见的场景是客户端连接到Master节点,Master节点会返回Worker节点域名,在Kubernetes集群外部的机器是没法访问到Worker的域名的。在没解决DNS问题之前,我们先采用Host模式部署,注意一下port冲突问题就好了。

因为我们在2018年所有的组件已经on Docker,我们只要将组件的部署方式迁移到Kubernetes上来管理,所以像Kafka,TiKV,PG,ZK这些组件,我们都可以轻松的上Kubernetes。不过在大数据组件当中,机器资源最多就属Hadoop组件,在以前Docker Host部署的时候,DataNode和yarn节点会挂载sata的12块盘,TiKV EleatiscSearch我们采用的是ssd raid50单块盘的方式,我们发现Local PV的方式不能很好的挂载多块盘,如果强行的挂载12个PV,scheduler需要支持每个PV在不同的磁盘,才能符合我们利用多块盘读写的目的。Local PV没法解决我们的需求,我们开始尝试HostPath,HostPath现在没有一个好的资源管理器,HostPath不能和机器属性绑定在一起,简单来说就是让HostPath具备Local PV机器绑定的特性。所以我们正在开发一个将HostPath也转变成PV资源进行管理,具备和Local PV一样的特性的Agent。当前我们采用的折中方案是AdvanceStatefulSet+nodeSelector+Affinity来部署yarn和DataNode,不过我们没有大规模使用,因为不够优雅。

Kubernetes集群部署分布式系统总结

我们认为在Kubernetes上部署存储或者计算型的组件是可行的,做好外部世界和Kubernetes集群内部的通信,或者将所有的组件都放在Kubernetes集群内部。针对性能要求比较高的组件,采用Host模式+Local PV的方式部署。在没有人力和开源成熟的Operator的时候,可以先用Helm模板化部署方式,先解决自动化部署。提供可视化的运维操作,通过人工组合操作的方式先代替Operator自动化运维。将一些通用的功能搬到Operator之上,并将Operator定制化能力提供给用户,让用户成为你的开发者,共同构建Kubernetes生态。当前我们维护了自己的AdvanceStatefulSet Controller,提供镜像原地升级,指定下线节点的功能,维护一个基础的Base Operator,我们希望将一些功能通用的覆盖所有组件,针对Hive这种多节点类型的服务开发独立的Operator。在社区我们积极参与TiDB Operator的开发,我们觉得当前TiDB的云原生方案是比较成熟,利用TiDB Operator部署的TiKV我们已经上生产了。这些支持得益于我们对于Service Pod DNS的支持,赋予Kubernetes的网络和磁盘静态分配绑定的能力。

以前我们会部署一个大的分布式系统业务混用,比如ES,现在我们可以利用Kubernetes切成小集群,每个集群都做好日志和监控的功能,业务可以申请自己的集群,资源上是隔离的,不会因为资源互相影响。懂组件的业务也可以根据自己的场景,不断优化自己集群的配置。当我们对组件性能把控不到位的时候,采用小集群是规避风险的一种方式。

关于监控,我们使用一个集群配一个Prometheus+Grafana的方案,做到监控隔离,Prometheus我们采用Thanos架构,提供统一的监控查询。

监控参考:

FN73quv.jpg!web

关于日志,我们采用sidecar注入到Pod的方式,收集到Kafka,Flink落地ES,保留七天,我们可以针对日志做一些报警策略。

日志参考:

uU77ri2.jpg!web

一些天生支持云原生的组件探索

除了已有组件尝试部署到Kubernetes,我们也在探索新的组件,比如JupyterHub,JupyterHub我们采用的是Ceph共享存储。基于JupyterHub接口能力,提供自定义镜像和定制化框架的能力,每个用户可以申请自己的Jupyter,分配固定的资源配比。我们也尝试部署了KubeFlow,想借助于TensorFlow Operator 部署分布式任务能力提升算法侧的能力,不过因为KubeFlow要绑定Istio组件,我们觉得太大了,所以没有将其放到线上。我们会基于Jupyter提供更好的数据分析层面的交互能力。

我们还部署了TensorFlow Server,打通HDFS和TensorFlow Server,用户使用Spark训练的TF model生成到HDFS之上,可以自动部署成TF server,做到模型的自动部署和滚动更新。还有Dask等等服务。

我们发现已有组件官方都在做云原生的支持,新组件会自带Kubernetes的支持,所以on Kubernetes未来一定可以走的更远。

遇到的问题和未来规划

在on Kubernetes这条路上,Kubernetes不能拿来即用,要根据自己的场景适配。在不影响已有业务的情况下,提供不改变用户行为的场景。原本用户可以很方便看到日志,知道请求落到哪个点上,那么你就需要提供很好的日志收集服务,提供更稳定的服务,才能让用户相信你的能力。

在网络方面,我们会尝试DPDK来加速容器网络以及其他硬件方案,将大数据服务本地性需求弱化,做到存储和计算的分离,提升扩展性。

在磁盘方面,我们会提供不同的规格的尝试,当前我们是混部,我们会提供LVS+混部+分区的方案选择,提供更灵活的部署方式。

在平台层面,我们会将机器和底层研发用户分开,可视化的运维集群,提升安全性和便捷。

在技术变革的快车道上,我们要制定好如何在行驶中换轮胎,甚至是换引擎。

Q&A

Q:存储集群,出现Pod迁移怎么解决数据盘问题?

A:Local PV,只要你不删除PV和PVC,不会产生Pod偏移。你可以使用Sts volume Template试试。

Q:以前做过Zeppelin(类似Jupyter的工具)+Spark的应用,经常因为各种队列资源计算不足需要调整配置那些,如果上Docker的话,这种方便操作吗?就是担心容器集群会不会不稳定?

A:方便的。当前我们使用JupyterHub来创建Jupyter,Jupyter和Spark集群打通,提供用户直接写代码和集群交互。不过要注意资源控制。我们直接和Spark交互用的是Apache的Livy。Kubernetes集群你提高稳定性就可以了,我们现在用的很稳定。

Q:日志监控保留一周,那资源利用的数据保留多久?比如CPU内存网络硬盘这些数据?

A:容器的监控我们保留12天。历史数据我们用的Thanos架构,保存到Ceph中。物理机的监控我们是放在小米的Falcon。

Q:如果Pod挂了,出现Pod自动漂移到其他节点,挂载的Local PV数据是否也要做迁移?

A:如果Pod挂了无法拉起,不删除PV和PVC,Pod是不会漂移到其他节点。这是Local PV调度决定的。如果需要漂移到其他节点,需要手动的删除PV和PVC,Pod漂移到其他节点,然后将有问题那个物理机上的数据迁移到新的PV上来。

Q:Thanos监控数据如何存放?查询速度比原生的Prometheus如何?

A:Thanos是Prometheus的高可用架构,我们用来做一个集中查询。历史归档数据是存储在Ceph中,Prometheus存储比如七天的数据,还是和原来一样存储在本地。查询速度我们使用下来还可以,查询速度取决于你的监控数据的体量,你可以做一些横向扩展,数据量大的时候该慢还是慢的。

Q:请问一下Pod指定IP是怎么实现的?

A:Kubernetes是支持CNI接口的,我们使用Contiv的netplugin插件,当Pod调度起来,会走CNI接口申请一个IP。根据Namespace+podname对应一个唯一标识,这个标识和IP是存储在一个单独的etcd中。podname用deployment部署是随机的,所以我们基本上都会用我们开发的AdvanceSts来部署,这样名字是固定的。

Q:Kubernetes是直接部署在物理机还是虚拟机上的?

A:物理机,我们OVS网络没办法在虚拟机上部署。

以上内容根据2020年1月14日晚微信群分享内容整理。 分享人 程威,同程艺龙数据中心Kubernetes负责人 。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK