作業(yè)幫kubernetes serverless在大規(guī)模任務(wù)場(chǎng)景下落地和優(yōu)化
一、背景
在作業(yè)幫的云原生容器化改造進(jìn)程中,各個(gè)業(yè)務(wù)線原本部署在虛擬機(jī)上的定時(shí)任務(wù)逐漸遷移到kubernetes集群cronjob上。開始cronjob規(guī)模較小,數(shù)量在1000以下時(shí),運(yùn)行正常,但是隨著cronjob規(guī)模的擴(kuò)大到上萬個(gè)后,問題就逐漸顯現(xiàn)出來。
二、問題
當(dāng)時(shí)主要面臨著兩個(gè)問題:一是集群內(nèi)節(jié)點(diǎn)穩(wěn)定性問題,二是集群的資源利用率不高。
第一個(gè)問題:集群內(nèi)節(jié)點(diǎn)穩(wěn)定性
由于業(yè)務(wù)上存在很多分鐘級(jí)執(zhí)行的定時(shí)任務(wù),導(dǎo)致pod的創(chuàng)建和銷毀非常頻繁,單個(gè)節(jié)點(diǎn)上平均每分鐘有上百個(gè)容器創(chuàng)建和銷毀,機(jī)器的穩(wěn)定性問題頻繁出現(xiàn)。
一個(gè)典型的問題是頻繁的創(chuàng)建pod導(dǎo)致節(jié)點(diǎn)上cgroup過多,特別是memory cgroup不能及時(shí)回收,讀取/sys/fs/cgroup/memory/memory.stat變慢,由于kubelet會(huì)定期讀取該文件來統(tǒng)計(jì)各個(gè)cgroup namespace的內(nèi)存消耗,cpu內(nèi)核態(tài)會(huì)逐漸上升,上升到一定程度時(shí),部分cpu核心會(huì)長時(shí)間陷入內(nèi)核態(tài),導(dǎo)致明顯的網(wǎng)絡(luò)收發(fā)包延遲。
在節(jié)點(diǎn)上 perf record cat /sys/fs/cgroup/memory/memory.stat 和 perf report 會(huì)發(fā)現(xiàn),cpu主要消耗在memcg_stat_show上:
而cgroup-v1的memcg_stat_show函數(shù)會(huì)對(duì)每個(gè)cpu核心遍歷多次memcg tree,而在一個(gè)memcg tress的節(jié)點(diǎn)數(shù)量達(dá)到幾十萬級(jí)別時(shí),其帶來的耗時(shí)是災(zāi)難性的。
為什么memory cgroup沒有隨著容器的銷毀而立即釋放呢?主要是因?yàn)閙emory cgroup釋放時(shí)會(huì)遍歷所有緩存頁,這可能很慢,內(nèi)核會(huì)在這些內(nèi)存需要用到時(shí)才回收,當(dāng)所有內(nèi)存頁被清理后,相應(yīng)的memory cgroup才會(huì)釋放。整體來看,這個(gè)策略是通過延遲回收來分?jǐn)傊苯诱w回收的耗時(shí),一般情況下,一臺(tái)機(jī)器上創(chuàng)建容器不會(huì)太多,通常幾百到幾千基本都沒什么問題,但是在大規(guī)模定時(shí)任務(wù)場(chǎng)景下,一臺(tái)機(jī)器每分鐘都有上百個(gè)容器被創(chuàng)建和銷毀,而節(jié)點(diǎn)并不存在內(nèi)存壓力,memory cgroup沒有被回收,一段時(shí)間后機(jī)器上的memory cgroup數(shù)量達(dá)到了幾十萬,讀取一次memory.stat,耗時(shí)達(dá)到了十幾秒,cpu內(nèi)核態(tài)大幅上升,導(dǎo)致了明顯的網(wǎng)絡(luò)延遲。
除此之外,還有dockerd負(fù)載過高,響應(yīng)變慢、kubelet PLEG超時(shí)導(dǎo)致節(jié)點(diǎn)unready等問題。
第二個(gè)問題:集群的節(jié)點(diǎn)資源利用率
由于我們使用的智能卡的CNI網(wǎng)絡(luò)模式,單個(gè)節(jié)點(diǎn)上是有的pod數(shù)量上限的,節(jié)點(diǎn)有幾乎一半的pod數(shù)量是為定時(shí)任務(wù)的pod保留的,而定時(shí)任務(wù)的pod運(yùn)行的時(shí)間普遍很短,資源使用率很低,這就導(dǎo)致了集群為定時(shí)任務(wù)預(yù)留的資源產(chǎn)生了較多的閑置,不利于整體的機(jī)器資源使用率提升。
其他問題:調(diào)度速度、服務(wù)間隔離性
在某些時(shí)段,比如每天的0點(diǎn),會(huì)同時(shí)產(chǎn)生幾千個(gè)JOB需要運(yùn)行。而原生調(diào)度器是k8s調(diào)度pod本身是對(duì)集群資源的分配,反應(yīng)在調(diào)度流程上則是預(yù)選和打分階段是順序進(jìn)行的,也就是串行。幾千個(gè)JOB調(diào)度完成需要幾分鐘,而大部分業(yè)務(wù)是要求00:00:00準(zhǔn)時(shí)運(yùn)行或者業(yè)務(wù)接受誤差在3s內(nèi)。
有些服務(wù)POD是計(jì)算或者IO密集型,這種服務(wù)會(huì)大量搶占節(jié)點(diǎn)CPU或者IO,而cgroup的隔離并不徹底,所以會(huì)干擾其他正常在線服務(wù)運(yùn)行。
三、在k8s集群中使用serverless
所以對(duì)CRONJOB型任務(wù)我們需要一個(gè)更徹底的隔離方式,更細(xì)粒度的節(jié)點(diǎn),更快的的調(diào)度模式。
為了解決上述問題我們考慮將定時(shí)任務(wù)POD和普通在線服務(wù)的POD隔離開,但是由于很多定時(shí)任務(wù)需要和集群內(nèi)服務(wù)互通,最終確定了一種將定時(shí)任務(wù)的pod在集群內(nèi)隔離開來的解決辦法 —— k8s serverless。我們引入了虛擬節(jié)點(diǎn),來實(shí)現(xiàn)在現(xiàn)有的k8s體系下使用k8s serverless。部署在虛擬節(jié)點(diǎn)上的 POD 具備與部署在集群既有節(jié)點(diǎn)上的 POD 一致的安全隔離性、網(wǎng)絡(luò)連通性,又具有無需預(yù)留資源,按量計(jì)費(fèi)的特性。如圖所示:
任務(wù)調(diào)度器
所有cronjob型workload都使用任務(wù)調(diào)度器,任務(wù)調(diào)度器批量并行調(diào)度任務(wù)POD到serverless的節(jié)點(diǎn),調(diào)度上非串行,實(shí)現(xiàn)完整并行,調(diào)度速度ms級(jí)。也支持serverless節(jié)點(diǎn)故障時(shí)或者資源不足時(shí)調(diào)度回正常節(jié)點(diǎn)。
解決和正常節(jié)點(diǎn)上POD差異
在使用k8s serverless前首先要解決serverless pod和運(yùn)行在正常節(jié)點(diǎn)上的POD的差異,做到對(duì)業(yè)務(wù)研發(fā)無感。
1.日志采集統(tǒng)一
在日志采集方面,由于虛擬節(jié)點(diǎn)是云廠商維護(hù)的,無法運(yùn)行DaemonSet,而我們的日志采集組件是以DaemonSet的形式運(yùn)行的,這就需要對(duì)虛擬節(jié)點(diǎn)上的日志做單獨(dú)的采集方案。云廠商將容器的標(biāo)準(zhǔn)輸出收集到各自的日志服務(wù)里,各個(gè)云廠商日志服務(wù)的接口各不一樣,所以我們自研了日志消費(fèi)服務(wù),通過插件的形式集成云廠商日志client,消費(fèi)各個(gè)云廠商的日志,和集群統(tǒng)一的日志組件采集的日志打平后放到統(tǒng)一kafka集群里以供后續(xù)消費(fèi)。
2.監(jiān)控報(bào)警統(tǒng)一
在監(jiān)控方面,我們對(duì)serverless上的pod 做了實(shí)時(shí)的cpu/內(nèi)存/磁盤/網(wǎng)絡(luò)流量等監(jiān)控,做到了和普通節(jié)點(diǎn)上的pod一致,暴露pod sanbox 的export接口。我們的promethus負(fù)責(zé)統(tǒng)一采集。遷移到serverless時(shí)做到了業(yè)務(wù)完全無感。
提升啟動(dòng)性能
Serverless JOB 需要具備秒級(jí)的啟動(dòng)速度才能滿足定時(shí)任務(wù)對(duì)啟動(dòng)速度的要求,比如業(yè)務(wù)要求00:00:00準(zhǔn)時(shí)運(yùn)行或者業(yè)務(wù)接受誤差在3s內(nèi)。
主要耗時(shí)在以下兩個(gè)步驟:
1. 底層sanbox的創(chuàng)建或者運(yùn)行環(huán)境初始化
2. 業(yè)務(wù)鏡像拉取
主要是做到同一個(gè)workload的sanbox能夠被復(fù)用,這樣主要耗時(shí)就在服務(wù)啟動(dòng)時(shí)長,除了首次耗時(shí)較長,后續(xù)基本在秒級(jí)啟動(dòng)
四、總結(jié)
通過自定義job調(diào)度器、解決和正常節(jié)點(diǎn)上POD的差異、提升serverless POD啟動(dòng)性能措施,做到了業(yè)務(wù)無感的切換到serverless,有效的利用serverless免運(yùn)維、強(qiáng)隔離、按量計(jì)費(fèi)的特性,既實(shí)現(xiàn)了和普通業(yè)務(wù)pod隔離,使得集群不用再為定時(shí)任務(wù)預(yù)留機(jī)器資源,釋放了集群內(nèi)自有節(jié)點(diǎn)的上萬個(gè)pod,約占總量的10%;同時(shí)避免了節(jié)點(diǎn)上pod創(chuàng)建過于頻繁引發(fā)的問題,業(yè)務(wù)對(duì)定時(shí)任務(wù)的穩(wěn)定性也有了更好的體驗(yàn)。定時(shí)任務(wù)遷移到serverless,釋放了整個(gè)集群約10%的機(jī)器,定時(shí)任務(wù)的資源成本降低了70%左右。