滴滴弹性云基于 K8S 的调度实践

上篇文章详细介绍了弹性云混部的落地历程,弹性云是滴滴内部提供给网约车等核心服务的容器平台,其基于 k8s 实现了对海量 node 的管理和 pod 的调度。本文重点介绍弹性云的调度能力,分为以下部分:

  • 调度链路图:介绍当前弹性云调度体系链路,对架构体系有一个初步的认知

  • k8s 调度能力的运用:整体介绍弹性云现在用到的 k8s 调度能力和对其的增强

  • k8s 版本的升级:介绍到从 k8s 1.12 到 1.20 跨版本升级的方案

  • 服务画像/真实使用率调度:原生的 request 调度存在着和真实使用率之间的 gap 等缺陷,尝试通过对 node 上业务做数据画像来让调度做出更符合真实情况的调度

  • 重调度:由于调度只能依据当前数据,随着业务的增长、集群机器的下线等,调度效果可能不达预期,比如会产生一些宿主机热点等,介绍重调度对这块的处理能力

  • 调度规则引擎:通用的调度策略不能满足所有业务场景,直接在调度主流程中做适配比较 hack,介绍 Galahad+Webhook 对调度资源的灵活注入从而影响最终的调度结果的能力

  • 调度稳定性建设:介绍整体调度的稳定性保障体系

调度链路图

4a5523f309907d4bcbf2b4687b651e92.png

  • kube-rescheduler:重调度模块,自动轮询集群状态,并发起异常 pod 的漂移

  • kube-odin:弹性云的上游 pass 层,用户通过 kube-odin 接入弹性云

  • galahad:调度规则引擎,按标签选取业务 pod 或 node 的调度策略存储,主要解决物理机集群差异化,快速变化的需求与 k8s 集群管理的灵活度匹配

  • kube-hook:k8s 的 mutatingwebhook 和对应服务,用于将 galahad 注入 pod 或 node

  • master:原生的 k8s 三大件

  • ipam:弹性云的 pod ip 分配模块

  • IRMAS:内核组件,包括 Odin-Agent 监控数据上报、pod quota 分配等能力

  • kube-agent:node 组件,做真实使用率数据的获取和写入

  • zhivago:基于 Prometheus 的服务画像引擎,涉及数据清洗,数据分析,数据存储,大盘展示等多个模块

k8s 调度能力的运用

涉及资源

  • 无状态服务(Deployment):弹性云最开始使用的 workload,但因为历史原因,比如要兼容业务在物理机时代的使用方式等,目前已废弃。

  • 有状态服务(StatefulSet):弹性云目前支持的主 workload,优化了控制器逻辑,实现了自定义部署顺序策略、无序删除策略、打散策略等等。

  • 静态容器(Pod):直接管理 pod,在现有的 Kubernetes 原生调度编排之上,提供和 VM 类似的资源交付手段,是为了兼容类似物理机使用方式的妥协,不  cloudnative,不建议新业务接入

  • Endponit:接流组件监听其变化,在pod漂移后自动接上dsi/lvs的流量

  • Deamonset:node 上 agent 的 workload

k8s 调度器的实现

kube-scheduler 的根本工作任务是根据各种调度算法将 Pod 绑定(bind)到最合适的工作节点,整个调度流程分为两个阶段:预选策略(Predicates)和优选策略(Priorities),这里不再赘述。

弹性云调度预选卡点

在 k8s 原生的预选卡点上我们做了很多增强:

e3923ca00ee9e3ca70b832f90583d337.png

3a312699f27c96333564bc077eda06a6.png

弹性云调度优选算分

重点介绍两个优选算法:  

dd4739953d4a9efbc00fc51794917fe6.png

301f4edcdfdb1d9f4e42c7243369b057.png

很长一段时间我们一直使用如下的优选打分权重,也就是 Balanced 和 Least 并重,但在资源紧张的情况下,重调度效果显示调度在即使有比较低利用率 cpu 的 node,也会选取另一个利用率较高但比较均衡的 node,而这个 node 更可能成为热点,这就是我们说的“调度bad case”。

9294b4d909d05c5d6d6215b30d52b36c.png

为此,当前在某些峰值利用率较高、热点较大的大机房,做了如下优选权重的调整,更加关注资源的使用率而不是平铺度。

92dd052d3d80c6cc51a2d64acfb6af66.png

调度器框架

在将 k8s 版本升级到 1.20 后,我们将上述弹性云的预选卡点和优选算法全部通过 SchedulerFramework 做了重新实现,主要拆解为 PreFilter、Filter 和 Score 三种扩展点。

abc3be297803f8973c10149599bf90b3.png

k8s 版本的升级

不久之前弹性云部分机房 k8s 版本还是 1.12,升级到 1.20 的难点如下:

  • 集群体量大,最大集群规模已经远远超出了社区推荐的5千个 node 上限,有问题的爆炸半径大;

  • 场景复杂多样,隔离集群、有状态服务容器、不可变更ip漂移容器、静态容器等;

  • 周边涉及范围广,kube-odin、各种 operator 和控制器等诸多上游组件。

替换升级方案介绍:

  • 两个 k8s 集群,1.12 和 1.20,直接搭建新的一套新的 1.20 master 和周边组件;

  • 1.20 集群中创建和 1.12 和等量的业务负载,也就是 sts 和 pod;

  •  通过上游的流量管控应用决定流量分布,一开始流量都在 1.12 的 sts 的 pod中,逐步切流到 1.20 的 sts 的 pod;

  • 有问题的时候可以快速回切流量。

原地升级方案介绍:

  • 只有一个 k8s 集群,将 master 和周边组件直接从 1.12 升级到 1.20;

  • 逐步将集群中的 node 也就是 kubelet 从 1.12 版本升级到 1.20;

  • 不做任何业务负载相关的操作,也就是 sts 和 pod 无需重建,其实的流量分布也不做操作,随着 node 升级流量天然就逐步从 1.12 切到 1.20 了;

  • 有问题的时候需要部分回滚 node 的 kubelet,当出现全局性风险的时候需要全量回滚 master 和周边组件。

两个方案对比如下:

058176c5c62a079940cd1d31ebe57daa.png

从方案可落地以及成本角度最终选取了原地升级

核心实现

  • 有状态服务,比如 redis 等需要 kubelet 升级容器不重启:修改原生的kubelet 实现,按hash值是否变化来决定是否重建容器的策略。

a01fa5d7313bc7171c2841c018172235.png

  • 升级中间态风险规避:master 1.20 +部分 Kubelet 1.12 的场景,梳理和关注高版本 apiserver 中资源或协议的变化,推动周边系统使用非 alpha 或 beta版本资源。

目前已无损升级滴滴所有核心机房万级别的 node 和十万级别 pod ,且升级过程中业务完全无感,未发生一次故障。

服务画像/真实使用率调度

从全局资源优化角度看,调度是动词,是策略的运用,关键是这个策略从何而来、怎么优化?我们知道 k8s 原生使用 request 做装箱,而一般为了成本考虑都会做 node 上的超卖,这在 node 上所有 pod 利用率都较高的情况下会产生热点和资源争抢。

服务画像的核心目标是探索不影响业务情况下的单机使用率上限,以及寻找真实资源的全局分布最优解。其核心能力包括:元信息完备的多维数据(map能力),灵活的聚合计算语言 promQL(reduce 能力),配置化的数据可视化大盘 (可观测性能力),以及灵活可配置的数据->策略通路(画像能力)。

数据分析 MapReduce

MapReduce 是一种数据处理思想,Map 代表数据预处理,包括归一化、格式化、补全数据元信息等,Reduce 代表数据聚合,可以进一步分为四种运算操作:过滤(filters)、聚合(aggregations)、集合操作(set operations)和联结(joins)。

举个例子,如何用MapReduce的思想分析数据:

北京机房下,X2234节点、wyc产品线的所有8核规格容器中,所有可漂移的容器过去七天消耗的最大CPU核数按宿主机维度求和。

表达式:

sum(max_over_time(ddcloud_pod_cpu_used{region="beijing",set="X2234",prod_line="wyc",cpu_limit="8",flexible="true"}[7d]))by(m_host)

结论:

依据该分析,可得出 wyc 产品线的容器在北京机房部分物理机上的分布情况,并据此人工或自动消除 wyc 产品线容器热点。

真实使用率调度

在过去一段时间,整个弹性云公共集群一直处于超卖较大的情况,而真实利用率的限制是真正保证高峰期宿主不会被打爆的主要策略。

如何算出真实使用率?算法是取当前宿主所有 pod,对其进行拟合取七天内最大的点作为调度参考值。当 pod 发生漂移时值也随之改变。zhivago 会将 pod 和 node 的7天最大值提前计算好保存到对应的配置中,此配置会被调度读取并作为调度决策的重要参考,每1分钟为更新周期。

为什么取七天最大值?因为从当前的数据的同比、环比可以看到,滴滴的业务特性7天为循环周期。取7天最大值可以在最坏的情况下尽量不产生热点。调度会保证宿主上所有 pod 7天真实使用率之和不超过真实资源的一定比例,包括cpu、内存、io 和磁盘等。

重调度

kubernetes 调度 pod 是一次性决策的,实时状态决策,但业务的压力是变化的,所以长期运行后 node 的状态不一定合理。我们的服务画像/真实使用率调度是一个偏“保守”的调度策略,如果业务流量保持和前7天完全一致,可以确保各个 node 在高峰期的 cpu 利用率都不会超过一定比例。但在很多场景下,业务流量会创新高,比如服务先扩容,后切流:

ad812eea28f6710449040ba5ceac8a99.png


还有,比如集群新增了亲和/互斥的策略,如何将策略作用于存量 pod ?集群修改超卖规则,超卖系数压缩怎么对存量 pod 生效?怎么处理不满足新调度规则的 pod?对这些场景,我们都希望用重调度来再做一次调度:

f016ac0b267d26a0e5c821459f2bde00.png

核心流程

  • 定时任务1:每10分钟做一次筛选,对不满足调度 Good Case 的 pod 入队列;

  • 定时任务2:每分钟做一次筛选,对满足漂移条件的 pod 做漂移;

  • 定时任务3:每分钟做一次漂移失败的 pod 的检查,重新入队列;

  • 定时任务4:每天捞一轮数据以生成重调度的成功率、失败数以及未恢复 pod 的日报。

紧急均衡

由于 CPU 属于抖动性最强的资源,在利用率较高的情况下一般业务都会受到影响,所以提供了紧急情况立即均衡的能力,不区分业务高峰期。紧急均衡触发条件为:物理机实际利用率高于一定的水位,可配置调整。

兜底策略

重调度是统一的再调度中心,具备自动化能力,但调度本身较重,需要规避不可控的爆炸半径。因此有如下重调度的限制:

f676e8564305d4efb511fc63ec6701e0.png

效果说明

在流量和核数相同的情况下,重处理前后的集群热点率对比:

1c1c741d238959030d6d0f01b84ed831.png

调度规则引擎

一直以来我们努力的方向是完善调度策略精确性或者说是完善基础能力,虽有一定的效果,但已经无法满足日益复杂的调度需求。为了让调度的管理能力可配置化,而不再依赖于代码的上线,需要一个单独的组件对涉及到的如下属性做修改和使用:

  • Annotations (各种开关、阈值、附加属性等)

  • Labels (阈值、标签)

  • Resource (pod所需资源、node总资源)

  • Taints/Tolerations (node's taint and pod's tolerations)

  • Affinity (node's and pod's soft and hard [anti]affnity)

修改的对象可以是 Pod、Node、Pod Controller(如Statefulset)等。这就是 Galahad,它是核心调度组件,负责集群资源调度与管控,寻找集群调度过程中的圣杯。

Galahad 全景图如下图所示:

fda8602930a036362923703d71203e35.png

核心实现

通过 Galahad 可以提供对 k8s 各个资源的自定义配置,如容器维度的调度打散,亲和性、物理机维度的资源超卖,资源限制、业务集群维度的物理机亲和性等等。具体是和 webhook 配合使用,基于 k8s 的 mutatingwebhook 实现对新建 pod 或者修改 node 的资源注入。   

421dacdf7a435a891a69f2b27e5cf0cb.png

效果说明

支持以下场景:

  • 为了满足混合调度的需求,尽量减少调度器的修改,通过 Galahad 区分 pod 在不同集群的调度策略;

  • 在隔离集群等需要特殊打散配置的场景下,直接通过 Galahad 注入 pod 打散策略;

  • 在 map 等公共集群重数据服务上云的场景下,通过 Galahad 注入 pod 的跨 sts 平铺。

调度稳定性

稳定性体系

1、调度可观测:

  • 调度链路的可观测:apiserver->hook->etcd 的 trace 串联 

19ae966858ee526ecadc14a4accaa9c1.png

  • 调度现场的可观测:调度失败详情、优选打分日志、调度现场的pod和node快照

2b0ed698b01e7de342672126d5b59467.png

  • 集群状态的可观测,包括集群 node 和 pod 详情、剩余套餐数、热点和可解释大盘等

2、核心指标保障:周调度成功率、堆积和延迟,重调度成功率和延迟,各组件指标比如apiserver qps等,通过核心指标大盘体现。

3、功能点自动化回归验证:自动化巡检能力。

4、放火能力失败验证:故障注入和系统自我止损能力。

组件稳定性

1、apiserver 稳定性

  • 请求均衡:基于 go away 的长连接请求打平

  • 限流保护:原生的 apf 限流,对内部组件请求豁免的优化

  • 来源感知:复杂请求链路梳理、推动调用方增加 client 信息

  • 应急预案:master 备机和快速扩容 sop

2、调度器性能优化

  • 在离线 pod 调度优先级差异化处理和退避策略,保障在线调度性能

  • percentageOfNodesToScore 等调度器参数优化

3、控制器性能优化

  • endponit 控制器同步缓存数据优化,时间复杂度从n*n->1,发布封线时间缩短4/5

  • 1.12版本 taintController 直接从 etcd list pod 请求的优化

4、galahad+webhook 稳定性

  • 1.12 默认的 apiserver 对 webhook 长连接优化为短连接

  • webhook 部署优化,容器和物理机冷备

  • galahad 操作工单化,防止误操作

  • 强弱依赖不可用演练

5、etcd 稳定性

  • apiserver 对 etcd 的前置限流保护

  • event 和主 etcd 的拆分

  • 穿透到 etcd 的 list 请求治理

  • 客户端定时压缩优化为服务端自己主动压缩

  • 高峰期前的 deflag 磁盘碎片整理

故障治理能力

1、事前

  • 监控和告警补全

  • 周边依赖梳理,强依赖稳定性保障

  • 压测环境单集群容量上限摸底

2、事中

  • 护堤手册和大盘

  • 通用问题排查手册

  • 故障场景止损 SOP

3、事后

  • 复盘和改进落地流程

总结

滴滴弹性云基于 k8s 实现了容器调度,支持多云调度、在线隔离、在离混部的多种复杂业务场景,目前在线公共集群的平均利用率已经接近50%,未出现明显异常。

在这个过程中,基于对 k8s 的理解完善了画像、重调度、规则引擎能力,提升了调度效果,并通过稳定性治理保证了系统99.99%的可用。后续会进一步在业务精细化调度、隔离能力完善、多调度器支持以及可观测提升等方面做深耕。  



云原生夜话

聊聊看,您对k8s的使用落地有什么心得,怎么解决集群资源分配的问题呢?如需与我们进一步交流探讨,也可直接私信后台。

作者将选取1则最有意义的留言,送出滴滴200元打车券。10月24日晚9点开奖。

原文地址:https://blog.csdn.net/DiDi_Tech/article/details/133896977

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读942次。kube-controller-manager 和 kubelet 是异步工作的,这意味着延迟可能包括任何的网络延迟、apiserver 的延迟、etcd 延迟,一个节点上的负载引起的延迟等等。当 Kubernetes 中 Node 节点出现状态异常的情况下,节点上的 Pod 会被重新调度到其他节点上去,但是有的时候我们会发现节点 Down 掉以后,Pod 并不会立即触发重新调度,这实际上就是和 Kubelet 的状态更新机制密切相关的,Kubernetes 提供了一些参数配置来触发重新调度的时间。_node-monitor-period
文章浏览阅读3.8k次。上篇文章详细介绍了弹性云混部的落地历程,弹性云是滴滴内部提供给网约车等核心服务的容器平台,其基于 k8s 实现了对海量 node 的管理和 pod 的调度。本文重点介绍弹性云的调度能力,分为以下部分:调度链路图:介绍当前弹性云调度体系链路,对架构体系有一个初步的认知k8s 调度能力的运用:整体介绍弹性云现在用到的 k8s 调度能力和对其的增强k8s 版本的升级:介绍到从 k8s 1.12 到 1...._滴滴机房 腾讯
文章浏览阅读897次。对于cpu来说,这种分配方式并不会有太大问题,因为cpu可以灵活调度,numa调度时我们只计算绑定了numa cpu的pod是可以接受的,但是对于内存来说,numa node上申请了的内存无法做到随时迁移,这就会导致调度器视角numa node的mem资源足够,但是等到pod真正使用时,由于没有绑定numa node的pod申请的内存,导致numa node的mem资源不足,造成swap中断或者远端内存申请,这会对绑定mem的pod来带来性能损耗。忽略了没有绑定numa node的pod资源。_kubectl numa
文章浏览阅读796次,点赞17次,收藏15次。只要在Service定义中设置了ClusterIp:None,就定义了一个HeadLess Service, 它与普通的Service关键区别在于它没有ClusterIp地址,如果解析HeadLess Service的DNS域名,则会返回该Service对应的全部Pod的EndPoint列表,这就意味着客户端是直接与后端的pod建立了TCP/IP链接进行通信的。一个Label是一个键值对。注解:属于资源对象的元数据,可以被理解为一种特殊的标签,不过更多的是与程序挂钩,通常用于实现资源对象属性的自定义扩展。
文章浏览阅读763次。但是此时如果配置成 NONE, 租户创建成功了,但是无法创建资源文件,也就是无法上传文件,可能 dolphinscheduler 团队就想着将文件上传到 hdfs,暂不支持本地。需要将 resource.storage.type 置为 NONE, 因为我之前用的 1.3.6 版本的时候,即使资源文件存在本地文件也需要配置成 hdfs。_[error] 2023-10-24 18:10:43.762 +0800 org.apache.dolphinscheduler.api.servic
文章浏览阅读2.7k次,点赞2次,收藏13次。公司使用的是交老的k8s版本(1.16),由于老版本的K8s对于现在很多新特性不支持,所以需要升级到新版本。目前2023年7月11日最新版本的k8s是v1.27.3。通过参考官方文档进行k8s部署工作。其中涉及到操作系统配置、防火墙配置、私有镜像仓库等。_k8s最新版本
文章浏览阅读1.8w次,点赞14次,收藏27次。能节省你在kubeadm init 时遇到问题的排错时间⌚️。整合了网上大佬
文章浏览阅读1.1k次,点赞2次,收藏7次。具体操作步骤可以参考之前的教程,建议是先安装一台,然后克隆虚拟机,这样速度快。注意:在克隆时记得修改Mac地址、IP地址、UUID和主机名。(最后别忘了保存下快照~)_部署k8s集群
文章浏览阅读863次,点赞23次,收藏16次。当部署完 Kubernetes,便拥有了一个完整的集群。一组工作机器,称为节点, 会运行容器化应用程序。每个集群至少有一个工作节点。工作节点会 托管Pod ,而 Pod 就是作为应用负载的组件。控制平面管理集群中的工作节点和Pod。说人话版本:集群:cluster,多个几点被组织到一起共同为系统提供服务过程称之为集群。本质上是将承载同一个软件服务节点组织到一起,称之为该软件(服务)的集群,当然集群中的节点身份地位是不一样的。k8s集群也是如此,他也是多个节点组成。
文章浏览阅读943次。Rancher是一个开源的企业级多集群Kubernetes管理平台,实现了Kubernetes集群在混合云+本地数据中心的集中部署与管理,以确保集群的安全性,加速企业数字化转型。Rancher 1.0版本在2016年就已发布,时至今日,Rancher已经成长为企业在生产环境中运行容器和Kubernetes的首要选择。_rancher管理k8s
文章浏览阅读742次,点赞2次,收藏3次。本篇来讲解如何在centos下安装部署高可用k8s集群。_kubeadm ha keepalived + nginx
文章浏览阅读1.9k次,点赞21次,收藏25次。那么这个空间设置成内存的2倍大小。点击IPv4设置--手动--添加--设置ip--设置DNS服务器,最后点击--“保存”;首先选中--“本地标准磁盘”,存储配置--自定义分区,点击--“完成”;在--主机名--设置主机名:(例如k8s-master01),点击--点击+,设置--挂载点/boot--期望容量,点击--添加挂载点;点击--+--挂载点swap--期望容量,点击--“添加挂载点”;默认选择--亚洲--上海,并调整日期和时间,点击--“完成”;设备类型--确认--LVM,卷组--选择“修改”;_euler 服务器搭建
文章浏览阅读1k次。在1.25版本的k8s集群中部署gpu-manage时,虽然显示gpu节点上gpu-manage的pod实例都是running状态,但是给pod申领。既可以用源码的Makefile自动编译打包成新的镜像,但是源码的。说明gpu-manager和容器运行时接口通信失败了。编译后的镜像在1.25版本的k8s中可以正常使用。,但是在k8s1.23版本之后,接口路径已经改为。资源时,却始终找不到有资源的节点。,另外有一些依赖需要国际上的支持。可以看到这里用的运行时接口是。查看节点的详情时,返回的。_launch gpu manager 报错 can't create container runtime manager: context dead
文章浏览阅读1k次,点赞18次,收藏16次。SelfLink:API的资源对象之一,表示资源对象在集群当中自身的一个连结,self-Link是一个唯一的标识号,可以用于标识k8s集群当中的每个资源的对象。容器里使用的配置,在provisioner当中定义好环境变量,传给容器,storageclass的名称,NFS服务器的地址,NFS的目录。NFS的provisionner的客户端以pod的方式运行在集群当中,监听k8s集群当中PV的请求,然后动态的创建于NFS相关的PV。命名为 nfs-client-provisioner-clusterrole。
文章浏览阅读6.3k次,点赞2次,收藏20次。k8s证书过期解决方案之替换证书_k8s证书过期如何更换
文章浏览阅读1k次。KMS,Key Management Service,即密钥管理服务,在K8S集群中,以驱动和插件的形式启用对Secret,Configmap进行加密。以保护敏感数据
文章浏览阅读888次。exporter对于云服务的监控还是很不完美,毕竟每家都有自己的护城河。自动发现多实例这样的借助consul 阿波罗这样的会简单一些。aws可以借助cloudwatch这样的导入模板到grafana中。还是希望能将类似腾讯云云监控中的这些指标采集到prometheus中,但是这过程应该还很遥远grafana出图 prometheus查询语法这些东西有时间的好好研究一下。报警有必要进行分级别,收敛配置一下!_command: - "-redis.password-file=/redis_passwd.json
文章浏览阅读1k次。可以在此处(https://cloud.google.com/kubernetes-engine/docs/how-to/kube-dns)和此处(https://www.digitalocean.com/community/tutorials/an-introduction-to-the-kubernetes-dns-service)找到更多的详细信息。-or-ipvs/)和此处(https://arthurchiao.art/blog/cracking-k8s-node-proxy/)。_k8s默认命名空间
文章浏览阅读4.9k次,点赞11次,收藏32次。如果运行runc命令时提示:runc: error while loading shared libraries: libseccomp.so.2: cannot open shared object file: No such file or directory,则表明runc没有找到libseccomp,需要检查libseccomp是否安装,本次安装默认就可以查询到。所有主机均需要操作。所有主机均需要操作。所有主机均需要操作。所有主机均需要操作。所有主机均需要操作。所有主机均需要操作。_kubernetes 1.28
文章浏览阅读3.6w次,点赞118次,收藏144次。Canal 提供了网络功能,使得 Kubernetes 集群中的 Pod 可以相互通信,并与集群外部的服务进行通信。它通过网络插件的方式,为每个 Pod 分配唯一的 IP 地址,并管理网络流量的路由和转发。此外,Canal 还支持网络策略,用于定义 Pod 之间的通信规则和安全策略。Canal 基于 Calico 和 Flannel 项目,结合了二者的优点。它使用 Calico 的数据平面,提供高性能的网络转发和安全特性,同时使用 Flannel 的控制平面,实现 IP 地址管理和网络策略的配置。_k8s canal