彭涛,同程艺龙架构师,负责同程艺龙容器平台架构设计和开发工作。Kubernetes 代码贡献者,Flink 代码贡献者,曾就职于百度基础架构部,微博研发中心。长期从事云计算行业。在百度期间负责百度公有云的虚拟网络开发工作,作为微博容器弹性扩缩容平台开发负责人,带领团队完成的微博容器弹性扩缩容平台,成功应对春晚等热点峰值流量压力。

董艳龙,同程艺龙高级开发工程师,主要负责同程艺龙的容器云平台资源调度,离线在线混部以及 Kubernetes 在同程艺龙的落地工作。致力于构建公司高效的私有云平台。

马永真,同程艺龙高级开发工程师,负责同程艺龙容器平台的开发工作,曾就职于百度智能云事业部。力求提升公司集群资源利用率,专注于优化公司容器平台调度算法,精准描绘资源使用情况。



背景

一年前,同程艺龙开启了从裸容器迁移基于 Kubernetes 的云原生探索之路,先回顾一下容器化时代之前遇到的一些问题。在物理机时代,机器资源交付周期过长,资源利用率低不能很好的支撑业务发展,到了虚拟机时代大量采用虚拟化的技术向前探索更高的资源利用率和交付效率。随着 Docker 技术的兴起,开始使用 Docker 来构建统一的轻量、快速、高效的服务发布平台。到了云原生的时代,通过 Kubernetes 来支撑业务的快速发展和服务高效接入。

我们的目标有两个:

一是提高资源利用率。

二是通过构建标准化的云原生平台给业务更好的赋能。

同程艺龙容器平台的基本架构(架构图)

  • 应用:运行在容器平台上的应用按类别分成了五类

  • 前端:根据使用习惯和权限判定拆分成了。

    • Jean 供在线/离线任务使用

    • Furt 系统主要供运维使用

    • 兜率系统是基于 Kubeflow 开发的机器学习平台。

  • 接口汇聚:和上游(页面、业务)与下游(子系统)交互,提供统一接口服务

  • 集群管理:通过抽象实际的运行组件,组织多种资源模型对外提供统一的容器服务生态

从公司的角度来看,集群管理与调度平台面向全公司服务,有多个主要业务线、子公司、和外界有合作关系的合作伙伴。集群平台不可能针对不同的使用方提供定制化接口和解决方案,只有将业务的模型进行收拢抽象后,统一为在线/离线、AI/大数据、云原生应用、Serverless、音/视频最终通过 Service API 来屏蔽容器平台系统的细节,做到与不同的业务解耦。

解决了上层的问题后,还需要支持多种资源类型的的调度:纯 Docker 调度、VM 调度、裸金属任务调度、Kubernetes 集群任务调度。Kubeflow 的分布式训练、自动化调参等。

这里使用 wangler 来统一收拢调度资源类型之后,提供给上层 Service API 来进行使用。再来看与下层平台的解耦。不同的业务类型通常会部署不同的 Wangler 进行管理,Wangler 的代码是支持通过 plugin 的方式支持不同集群。

接到上层资源请求后,首先要进行会做参数校验、资源余量、调度细分、IP 分配、Numa 节点绑定等等操作,完成之后再转换成相应的集群可识别的参数去具体执行,例如向 Kubernetes 平台申请扩容和缩容机器,是通过把 Wangler 里面的逻辑概念(ServiceDescribeInfo)转换成 Kubernetes 的 Pod、Job、Deployement 对象去实际申请分配机器资源,最终将完整的服务交付给用户。

一些比较新的特性或者特殊的需求,会优先使用 Kubernetes 集群来完成,理由是 Kubernetes 有良好的设计和优秀的插件系统、丰富的云原生应用可以快速的实现自己的业务需求。同时可以借助社区和生态的力量,共同建设和探索。

遇到的一些问题

在日常的使用 Kubernetes 和 Docker 过程中主要遇到了四类问题:隔离、调度问题、资源利用率和推广。

  • 隔离问题:

    • 容器能不能认识到自己在容器里面;

    • 运行在同一台服务器上的容器会不会和系统的某些默认配置产生影响。

    • 在部署过程中的资源隔离。

  • 调度问题:

    • 如何评估目前的调度策略能提升资源利用率

    • 如何支持更多的资源进行调度

  • 资源利用率问题:如何进一步的提高资源利用率

  • 推广:当卡住主流程的问题解决了,剩下的往往是层出不穷的细枝末节。在推广的阶段要如何迎难而上取得业务同学的认可。

在同程艺龙的架构下是如何解决

隔离问题:

接入业务的过程中,发现从艺龙上一代容器服务平台迁移到基于 Kubernetes 的容器平台的容器经常会出现 CPU 跑满、机器负载升高的情况、尤其集中在 Java 应用的容器里面,出现服务启动失败的情况。

原因是在于之前的 Docker 平台是没有限制 CPU,而迁移到 Kubernets 平台后使用了 Cgroup 限制了 CPU 的使用。而 Java 在 1.8.0_191 版本之前不能识别到自己跑在容器中,看到全部物理机的资源。191 版本之后开启了 UseContainerSupport 参数,可以避免这类问题。

本着替用户着想提供无感迁移的方案,分析原因是因为数量很多的 GC 线程集中到被限制的 CPU 上时会出现类似的问题,大家都知道 GC 通常不会占用太多时间,那为什么不能短暂的让这个容器超过现有规格而使用机器上的冗余资源,然后 GC 完成之后再回到之前的规格呢?

基于这个想法开发了自动化扩缩容的单机版本-动态容器规格调整。通过这个功能,相应的机器负载变高的情况降低到几乎等于0,这个问题,同时也出现在 Dotnet.core 的容器里,都可以通过这个技术来解决。除了这个同样还存在着 Pid 限制、文件句柄数、网络 IO 限制。部分使用了社区的功能,部分进行了二次开发。

除了上述的限制问题,还有容器和物理机上的一些限制需要规避。在接入的过程中存在一些对性能要求比较高的客户会要求绑定 Core, 也就是独占某几个 CPU 不允许其他人使用。同时为了避免被绑到 0 号核上,通常还有规避特定核的需求。

在 Kubernetes 的目前容器的隔离策略中,绑 CPU 是通过 Kubernetes Topology Manager 申请特定核数的 CPU,Kubernetes 给按照 Topology Manager 计算的最优结果绑定特定的 CPU。无法满足特定的绑定 Core 需求,同时也没法满足 Numa Node 就近访问内存。

可以通过扩展 Pod 级别的 API 完成参数传递,并在 Kubelet 完成了绑定 Core 和 Numa 节点就近访问内存功能。同时可以在 Kubelet 上设置保留的Core(例如 0 号核)来满足用户的需求。经过实际测试,这个功能提升了容器性能 50% 左右。

在实际的接入过程中,还有些奇葩的需求例如‘资源预留’,这里可以和大家分享一下,业务在容器的启停,或者是重新发布上线的过程中要求资源必须预留。

例如 A 业务在机器 a 上开了一个 8Core8G 的容器,但是在容器停止和启动之间,假如有 B 业务也申请了 8Core8G 的容器,那么会导致 A 业务的容器会启动失败(资源不足了)。这个需求要是按照原本的 Kubernetes 的设计倒也是好实现,因为原本 Kubernetes 的资源申请机制就是预申请(例如机器有 8Core8G,你也就只能申请 8Core8G 的实例)把调度给 Hook 到外围来做,然后在 pod 创建之前把物理机的资源做一个预处理就好。但是叠加上了 IP 固定的需求、CPU 绑定 Core 需求、资源超卖需求就变得有点复杂了。绑定 Core 需求上面已经提到,本质上用户希望容器当成微型物理机来使用。

为了避免频繁变更 IP 导致上游负载均衡性能颠簸,做了 IP 固定的功能。在 Kubernetes 里面 IP 分配的主逻辑是 Kubelet+CNI 插件来完成的。CNI 插件只支持少量的参数传递。需要 Hook 相关代码把 IP 给带到CNI插件里面完成了IP固定的功能。然后把 Kubernetes 调度里面加入了 IP 调度的逻辑才完成了相关功能。同时由于用户往往无法精准评估容器所使用的资源,而为了提升利用率会使用资源超卖来解决,但是超卖本质上会加剧资源冲突的概率,这里需要引入服务画像功能,该功能后面会有详述。

调度问题:

调度是提升资源利用率的重中之重,很多时候调度的需求往往是相互背离的,例如有的服务要求必须和其他的业务容器绑定在一起部署,有的则要求必须不能和 CPU 密集型的一起部署。加上需要调度的资源不一,如 Docker 容器级别、机器级别、大数据业务等。所以调度在这里不是一个功能性的问题,而是必须考虑一个调度框架。这里参考了 Kubernetes 的实现在 Wangler 这一层也实现一个调度框架。

以 Kubernetes的一次 pod 创建为例,在 Kube-Schedule 开始调度的时候会通过本身的扩展机制访问 Wangler-Schedule 的三个接口,依次完成预选、打分、绑定的过程。这个过程中抽象的 Wangler-schedule 提供这三个接口都是纯计算无状态且可以随意组合调度需求的。整个调度逻辑都是链式计算的方式来完成,这样做有一个好处,这套调度可以被使用在所有符合相关 Label 计算的资源上。以此完成了 Docker、机器、计算任务等的 Label 标签改造,同时也在框架内实现了节点亲和性、容器亲和性、库存容量计算。

抽象出来一个都可以用的调度框架意义在于可以方便的进行调度效果的评估,大家关注的点在于如何尽可能的提升资源利用率。而每往框架中添加一种调度的算法都有可能会导致节点资源利用率的下降。那如何准确的来评估新添加的调度算法可以提升或者是降低资源利用率呢?

简单举例说明下:首先需要收集每一次的调度创建需求,例如在 8 点 30 分 A业务申请一个 8Core8G 的容器,在数据库上记录这一条调度的请求,同时会记录当时整个集群的状态。例如集群里面有 50 台机器,每台机器上面现在部署了多少容器,占用了多少资源。把这些都记录下来之后。用该记录调用调度框架,按照调度算法计算出应该 Bind 的物理机,然后把那台物理机上的资源减掉申请的资源,再看剩下的集群全部资源可以开启一个特定规格的容器(例如最常用的 8Core8G)的容器的数量是多少,作为这次调度的得分。通过回放相关数据,和增删任意所需维度可以把这个做成一个调度模拟器,从而解决高维调度需求(通过机器学习等工具)。

资源利用率问题:

当前给业务容器进行资源分配都是采 用预分配加上 Cgroup 限制的手段,但是在实际进行服务利用率统计的时候,发现大多数业务并不了解自己的服务是什么样子,这就会导致一些问题:

  1. 实际资源利用低。根据统计两周内对服务的最高资源使用数据表示,百分之八十的业务都存在百分之五十以上的资源浪费(CPU 和内存) 。

  2. 特定资源集群的利用率低,公司内有专门的 Flink(流计算-在线与批处理-离线)...等集群,这部分集群的资源利用在闲时能有 80% 的资源是处于空闲状态。

  3. 夜间和白天的资源使用存在波峰波谷,根据统计,CPU 白天和晚上的使用率是10:1 、内存是3:1的使用率

相应的解决手段是:

资源超售:

超售有两种手段实现,第一种是通过压缩 Pod 的 Request 和 Limit 值,节点设定压缩比的标签。这样的方式有个问题在于,如果用户设定了 8Core8G,但是按照节点压缩比设定为 50%,那么 Pod 的 Limit 和 Request 就会被设定为 4Core4G。但是如果用户程序启动的时候如果真能用到 8CoreCPU,那么按照 Cgroup 的限制会直接以 4Core 的方式提供给用户,如果真能用到 8G 的内存,那么按照 Cgroup 的限制会在 4G 的时候直接 OOM Kill。压缩的方式比较不能被接受。而且存在与用户认知不符的情况。

第二种方式是超售。调整节点资源的超卖比,在可用资源上乘上一个系数来呈现一个超卖之后的节点。目前 Kubernetes 是没有这样的接口,通过 Kubernetes 的扩展机制,Hook 了 Kubelet 向上汇报的过程,实现了该功能。这样虽然没有被 Kill 的问题,但是会出现资源在用满的时候出现争抢严重,性能下降的情况。

同程艺龙这边的方案是在测试和预发布环境打开超卖系数,在生产环境下打开 CPU 超卖,而内存的超卖需要服务画像的数据支持。会根据预发环境和测试环境下这个服务一周的实际负载给出一个调度建议值。然后部署了该服务的节点会结合超卖比和实际部署在节点上的服务的调度建议值,来确定新的容器是否能调度到该物理机上。还是应该调度到其他的物理机上。等于做了一个简单的服务画像。

通过上述手段节省的资源要部署哪些容器呢?这个也有讲究,一种是离线计算的服务可以占用这部分资源,离线的服务处于随时可停止的状态。另外一种就是通过调度计算出来的互补性服务,例如有个服务是 CPU 密集型的,那么能搭配部署的就是内存占用大的服务等。这块依赖调度的节点亲和性和服务亲和性。

云原生应用:

在公司所有的机器中,有一部分的机器是独立部署单一服务的,例如 Flink 集群、Hadoop 集群、Spark 集群等。这部分资源很多时候都是独立部署,且资源利用率低。但是这部分资源并不像在线资源那样只需要提供 Docker 镜像和启动参数就能完整的迁移上去。

以 Flink 为例。Flink 在目前的模式下迁移到 Kubernetes 有两种模式,一种是 Job Cluster,一种是 Session Cluster。相比于传统的 Yarn 调度来说,迁移到 Kubernetes 有几个好处,第一,资源可以很方便的做隔离(CPU、绑定 Core、内存、网络、硬盘.....),依托于 Linux 强大的隔离机制,服务可以不用造轮子就能使用上。第二迁移到 Kubernetes上,利用线上的冗余资源,能更快速的扩大集群规模。而且单位资源的成本也会快速下降。

Flink 在 Session Cluster模式下,需要 Config-Map、Service、Job-Manager-Deployment,Task-Manager-Deployment. 如果要做 HA,还需要引入 ZooKeeper。在这种情况下,如果想要 Flink 迁移到 Kubernetes,就不只是提供一个容器平台,而是基于云原生的模式提供一整套的解决方案。这部分需要专门的团队去维护这部分集群。同时也要紧密关注社区的动态,例如即将在 Flink 1.11 版本中完成的 Native Kubernetes,会让接入变更更加简单。

混合部署应用:

混合部署分两个阶段,第一个是阶段主要解决白天和晚上利用率波峰波谷的问题。这部分通常采用在凌晨 1 点到早晨 8 点部署离线计算或者机器学习的任务到在线集群机器上来提升整体资源利用率。取决于离线任务的压力需求,可能会选择下掉部分业务的流量负载(调用容器的下负载接口或者从注册中心进行服务摘除)。然后在凌晨 8 点开始把离线的计算任务回退,把在线业务的负载加回去。

第二个阶段还处在预研中,计划是白天进行离线和在线业务的实时混合部署。初步的想法是要首先解决在线业务的自动化扩缩容和降级,做完这个之后才可以在不影响在线业务的情况下进行混合部署。然后做任务的优先级(方便在资源压力过大时把离线任务自动剔除)

推广问题:

有人说推广最好的时机是当业务出现故障的时候,然后你能给出解决问题的方案和产品。这样你是最好推广的了,也有人说最好的推广是上头直接压红头文件,得有技术委员会或者 CTO 的角色来给你背书。上述两种都是各家友商在推广过程中的心路历程。在同程艺龙这边推广主要面对的问题有如下几个:

  1. 我们迁移到你这有啥收益啊?

  2. 你们是不是又重复造轮子了,天天折腾我们迁来迁去的?

  3. 我们这边有优先级更高的事情,你们迁移的事情往后排吧。

  4. 这个迁移我们做的东西有点多啊,你们能不能提供无感迁移的方案?

推广容器的时候业务方往往先考虑的迁移到你们的容器平台有啥收益,收益如下:

  1. 成本更低

  2. 更高效的发布效率

  3. 更方便的服务接入速度(配套的DNS、负载均衡、SLA、健全的云原生服务)

  4. 更完善的自运维体系(DEVOPS)

从业务的角度,能获取更快的发布速度和更方便的服务接入,从运维角度能够更轻松的管理大规模集群和单位资源成本的降低。迈过这一步基本上业务方和运维会同意和你考虑迁移的事情。

随后需要解决的迁移过程过长的问题。虽然业务方和运维都觉得这个东西不错,但是为了业务的稳定性,同程会需要很长的观察周期。如果迁移过程中涉及到业务方需要配合迁移去做变更的,这个周期会更加长。往往这个时候负责迁移的运维同学就会向你抱怨没有无感迁移方案。这里也走了不少弯路,总结起来的经验有如下几个:

  • 要提供简洁好用的服务

  • 要提供好充分的资源

  • 如果可能的话,进行无感迁移

  • 如果业务要适配的服务,适配过程中的修改做到最低

  • 配套的提示,工具要无脑化可执行

  • 积极解决迁移过程中的任何问题,获取业务和运维同学的信任

  • 确定排期后,新增业务得收口,老旧业务也得给出迁移计划

总而言之就是一句话,比你的用户多做一步,多想一步。设身处地的解决问题。

总结

在如今的时代,各种新的架构和云的使用模式层出不穷。上面支撑的业务更是百花齐放。本文主要分享了同程艺龙在容器上做的一些探索和实践。和内部推动业务上平台的一些想法,同程艺龙的容器团队还是一支很年轻的团队,欢迎大家跟我们交流、探讨。也请大家多提意见和建议,最后打个广告,招人,对玩容器、Kubernetes、资源调度 Topic 感兴趣的请联系 tao3.peng@ly.com。

参考阅读:

  • dubbo-go 1.4.0 版本发布,支持 K8s 注册中心、rest 协议

  • Kubernetes实战——谈谈微博应对春晚等突发峰值流量的经验

  • 是时候考虑Spring非阻塞编程模式?R2DBC pk JDBC 和 WebFlux pk Web MVC 评测数据

  • 分布式事务如何实现?深入解读 Seata 的 XA 模式

  • 打造高效交付团队心得

  • 无服务器场景(serverless)的容错怎么做?我们的设计

技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式


长按二维码 关注「高可用架构」公众号

同程艺龙云原生 K8s 落地实践相关推荐

  1. 百度BaikalDB在同程艺龙的成功应用实践剖析

    导读:文章主要介绍 BaikalDB在同程艺龙的完整落地实践,文章把BaikalDB总结为六个核心特性,分别是<BaikalDB高可用与HTAP特性实践>.<BaikalDB 高性能 ...

  2. 技术沙龙 | 云时代下的架构演进—企业云及云原生技术落地实践

    云改变了IT行业的形态和市场格局,催生了应用的发展.随着云计算技术的不断演进,作为一名优秀的架构师,必须深入了解云计算平台的特点及架构设计,包括构建数据库.大规模落地微服务.Service Mesh和 ...

  3. 干货 | 携程Dynamo风格存储的落地实践

    作者简介 根泰,携程高级后端开发工程师,关注数据存储和数据库领域. 遐龄,携程研发总监,关注大数据存储.性能调优. Dynamo风格数据库来源于亚马逊的Dynamo: Amazon's Highly ...

  4. K8s落地实践之旅 —— Pod(豌豆荚)

    文章目录 ✨ 前言 1. 认识k8s

  5. Apache Flink 在同程艺龙实时计算平台的研发与应用实践

    本文主要介绍 Apache Flink 在同程艺龙的应用实践,从当前同程艺龙实时计算平台现状.建设过程.易用性提升.稳定性优化四方面分享了同城艺龙实时计算平台的建设经验,供大家参考. 1.背景介绍 在 ...

  6. 解读 Service Mesh 的实现方式与同程艺龙的具体实践

    当互联网架构面临数据量,高并发.高可用场景几何增长的情况,Service Mesh 可以在其中发挥什么样的作用?什么样的场景适合使用 Service Mesh?如果有需要使用,又将如何来实现 Serv ...

  7. 云原生周报 | K8s 官方推出纪录片;BFE 2021开源总结;服务网格在联通的落地实践

    [百度云原生]祝您新春快乐,虎年大吉! 业界要闻 1. Kubernetes 社区推出官方纪录片 摘要:近日,Kubernetes 社区发布官方纪录片的第一部分.受到 2013 年 Docker 开源 ...

  8. Flink集成Iceberg在同程艺龙的实践

    简介:本文由同城艺龙大数据开发工程师张军分享,主要介绍同城艺龙 Flink 集成 Iceberg 的生产实践. 本文由同城艺龙大数据开发工程师张军分享,主要介绍同城艺龙 Flink 集成 Iicebe ...

  9. 干货 | 携程基于BookKeeper的延迟消息架构落地实践

    作者简介 本文作者magiccao.littleorca,来自携程消息队列团队.目前主要从事消息中间件的开发与弹性架构演进工作,同时对网络/性能优化.应用监控与云原生等领域保持关注. 一.背景 QMQ ...

最新文章

  1. python datetime timedelta函数_Python Pandas DatetimeIndex.to_perioddelta()用法及代码示例
  2. NSURLSessionDataTask与NSOperationQueue实现多文件断点下载(任意时刻终止进程,重启应用,自动重启下载)...
  3. 裕-C#过滤DataTable中的空数据和重复数据
  4. WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.
  5. skinmagic对VC中程序窗口的换肤
  6. Python学习之路day3-文件操作
  7. STM32学习(1)-资料查找,STM32简介,STM32选型以及芯片内部结构图
  8. 【故障处理】ORA-12162: TNS:net service name is incorrectly specified
  9. 43岁读NLP博士,一位70后的励志人生
  10. 华师大计算机在线测试,华东师大:180道心理测试题面试免费师范生
  11. c语言恶搞小程序自动关机,C语言的自动关机程序及捉弄人的小程序.doc
  12. php java 单点登录_用cas来实现php的单点登陆
  13. 大一新生必看,自学必看,里昂详解数据结构之二叉树
  14. 寒江独钓NDIS驱动学习总结
  15. java计算机毕业设计中国民航酒店分销系统源码+系统+lw+数据库+调试运行
  16. 论文阅读5 Cv-CapsNet:Complex-Valued Capsule Network
  17. android模拟器检测方案优化,逍遥模拟器优化方案 - 新手引导 - 逍遥安卓论坛 - Powered by Discuz!...
  18. java 调用 su,java调用外部应用程序
  19. 基于ZigBee和STM32的智能家居控制系统的设计与实现(二)
  20. 数据治理体系化思考与实践

热门文章

  1. 三步教你Excel如何分别设置中英文字体
  2. Python语言的优点和缺点
  3. 站在庄家的角度看股票
  4. 【满分】【华为OD机试真题2023 JAVAJS】红黑图
  5. 关于STM32中使用printf通过串口发送数据
  6. 无垠的太空第三季在线观看_百度云迅雷下载
  7. PSO-CNN模型研究与实现-PSO优化模型内部超参数
  8. [01背包] 宠物小精灵之收服(01背包+二维费用背包+思维)
  9. win32编程中创建窗口时CW_USEDEFAULT的作用
  10. python基础 day13 运维堡垒机开发