作业帮kubernetes serverless在大规模任务场景下落地和优化

云计算
在作业帮的云原生容器化改造进程中,各个业务线原本部署在虚拟机上的定时任务逐渐迁移到kubernetes集群cronjob上。开始cronjob规模较小,数量在1000以下时,运行正常,但是随着cronjob规模的扩大到上万个后,问题就逐渐显现出来。

一、背景

在作业帮的云原生容器化改造进程中,各个业务线原本部署在虚拟机上的定时任务逐渐迁移到kubernetes集群cronjob上。开始cronjob规模较小,数量在1000以下时,运行正常,但是随着cronjob规模的扩大到上万个后,问题就逐渐显现出来。

二、问题

当时主要面临着两个问题:一是集群内节点稳定性问题,二是集群的资源利用率不高。

第一个问题:集群内节点稳定性

由于业务上存在很多分钟级执行的定时任务,导致pod的创建和销毁非常频繁,单个节点上平均每分钟有上百个容器创建和销毁,机器的稳定性问题频繁出现。
一个典型的问题是频繁的创建pod导致节点上cgroup过多,特别是memory cgroup不能及时回收,读取/sys/fs/cgroup/memory/memory.stat变慢,由于kubelet会定期读取该文件来统计各个cgroup namespace的内存消耗,cpu内核态会逐渐上升,上升到一定程度时,部分cpu核心会长时间陷入内核态,导致明显的网络收发包延迟。

在节点上 perf record cat /sys/fs/cgroup/memory/memory.stat 和 perf report 会发现,cpu主要消耗在memcg_stat_show上:

而cgroup-v1的memcg_stat_show函数会对每个cpu核心遍历多次memcg tree,而在一个memcg tress的节点数量达到几十万级别时,其带来的耗时是灾难性的。

为什么memory cgroup没有随着容器的销毁而立即释放呢?主要是因为memory cgroup释放时会遍历所有缓存页,这可能很慢,内核会在这些内存需要用到时才回收,当所有内存页被清理后,相应的memory cgroup才会释放。整体来看,这个策略是通过延迟回收来分摊直接整体回收的耗时,一般情况下,一台机器上创建容器不会太多,通常几百到几千基本都没什么问题,但是在大规模定时任务场景下,一台机器每分钟都有上百个容器被创建和销毁,而节点并不存在内存压力,memory cgroup没有被回收,一段时间后机器上的memory cgroup数量达到了几十万,读取一次memory.stat,耗时达到了十几秒,cpu内核态大幅上升,导致了明显的网络延迟。

除此之外,还有dockerd负载过高,响应变慢、kubelet PLEG超时导致节点unready等问题。

第二个问题:集群的节点资源利用率

由于我们使用的智能卡的CNI网络模式,单个节点上是有的pod数量上限的,节点有几乎一半的pod数量是为定时任务的pod保留的,而定时任务的pod运行的时间普遍很短,资源使用率很低,这就导致了集群为定时任务预留的资源产生了较多的闲置,不利于整体的机器资源使用率提升。

其他问题:调度速度、服务间隔离性

在某些时段,比如每天的0点,会同时产生几千个JOB需要运行。而原生调度器是k8s调度pod本身是对集群资源的分配,反应在调度流程上则是预选和打分阶段是顺序进行的,也就是串行。几千个JOB调度完成需要几分钟,而大部分业务是要求00:00:00准时运行或者业务接受误差在3s内。

有些服务POD是计算或者IO密集型,这种服务会大量抢占节点CPU或者IO,而cgroup的隔离并不彻底,所以会干扰其他正常在线服务运行。

三、在k8s集群中使用serverless

所以对CRONJOB型任务我们需要一个更彻底的隔离方式,更细粒度的节点,更快的的调度模式。

为了解决上述问题我们考虑将定时任务POD和普通在线服务的POD隔离开,但是由于很多定时任务需要和集群内服务互通,最终确定了一种将定时任务的pod在集群内隔离开来的解决办法 —— k8s serverless。我们引入了虚拟节点,来实现在现有的k8s体系下使用k8s serverless。部署在虚拟节点上的 POD 具备与部署在集群既有节点上的 POD 一致的安全隔离性、网络连通性,又具有无需预留资源,按量计费的特性。如图所示:

任务调度器

所有cronjob型workload都使用任务调度器,任务调度器批量并行调度任务POD到serverless的节点,调度上非串行,实现完整并行,调度速度ms级。也支持serverless节点故障时或者资源不足时调度回正常节点。

解决和正常节点上POD差异

在使用k8s serverless前首先要解决serverless pod和运行在正常节点上的POD的差异,做到对业务研发无感。

1.日志采集统一

在日志采集方面,由于虚拟节点是云厂商维护的,无法运行DaemonSet,而我们的日志采集组件是以DaemonSet的形式运行的,这就需要对虚拟节点上的日志做单独的采集方案。云厂商将容器的标准输出收集到各自的日志服务里,各个云厂商日志服务的接口各不一样,所以我们自研了日志消费服务,通过插件的形式集成云厂商日志client,消费各个云厂商的日志,和集群统一的日志组件采集的日志打平后放到统一kafka集群里以供后续消费。

2.监控报警统一

在监控方面,我们对serverless上的pod 做了实时的cpu/内存/磁盘/网络流量等监控,做到了和普通节点上的pod一致,暴露pod sanbox 的export接口。我们的promethus负责统一采集。迁移到serverless时做到了业务完全无感。

提升启动性能

Serverless JOB 需要具备秒级的启动速度才能满足定时任务对启动速度的要求,比如业务要求00:00:00准时运行或者业务接受误差在3s内。

主要耗时在以下两个步骤:
1. 底层sanbox的创建或者运行环境初始化
2. 业务镜像拉取

主要是做到同一个workload的sanbox能够被复用,这样主要耗时就在服务启动时长,除了首次耗时较长,后续基本在秒级启动

四、总结

通过自定义job调度器、解决和正常节点上POD的差异、提升serverless POD启动性能措施,做到了业务无感的切换到serverless,有效的利用serverless免运维、强隔离、按量计费的特性,既实现了和普通业务pod隔离,使得集群不用再为定时任务预留机器资源,释放了集群内自有节点的上万个pod,约占总量的10%;同时避免了节点上pod创建过于频繁引发的问题,业务对定时任务的稳定性也有了更好的体验。定时任务迁移到serverless,释放了整个集群约10%的机器,定时任务的资源成本降低了70%左右。

 

责任编辑:鸢玮 来源: 作业帮
相关推荐

2021-11-05 15:55:35

作业帮Kubernetes调度器

2020-06-10 10:00:53

Serverless数据处理函数

2023-04-25 08:01:23

JavaQuarkusKubernetes

2022-06-30 15:26:35

区块链司法配套设施

2020-02-17 08:00:02

云计算云开发Kubernetes

2024-04-26 09:38:36

2023-02-17 07:41:18

KubernetePrometheus

2020-12-01 11:09:14

2023-02-20 13:45:31

数据分析腾讯 Alluxio

2023-09-21 07:52:55

Flink CEP复杂事件处理

2022-08-10 09:02:03

风控Flink阿里云

2019-09-17 12:06:56

微服务网络容器

2022-06-09 13:45:18

vivoK8S集群Kubernetes

2023-05-26 08:39:44

深度学习Alluxio

2023-01-06 11:05:36

人工智能作业帮语音技术

2021-10-07 16:45:44

存储网络场景

2016-01-29 20:23:23

华为

2010-09-01 15:16:49

WLAN交换机结构

2009-04-09 09:32:00

VoWLANWLAN
点赞
收藏

51CTO技术栈公众号