cron每2天跑一次_我们如何学会大规模改善Kubernetes CronJobs(第1部分,共2部分)...

9176f4f76a62da8802eb2d51e60078ee.png

在Lyft,我们选择将服务器基础架构移至Kubernetes,Kubernetes是分布式容器编排系统,以利用自动化的优势,拥有可构建的可靠平台,并通过提高效率来降低总体成本。

分布式系统可能难以推理和理解,Kubernetes也不例外。尽管Kubernetes有很多好处,但我们在采用Kubernetes内置的CronJob作为运行重复的计划任务的平台时发现了一些痛点。在这个分为两部分的博客系列中,我们将深入探讨Kubernetes CronJob的技术和操作缺陷,并分享我们为克服这些缺陷所做的工作。

本系列的第1部分(本文)详细讨论了使用Lyft的Kubernetes CronJob所遇到的缺点。在第2部分中,我们分享了我们为解决Kubernetes堆栈中的这些问题所做的工作,以提高可用性和可靠性。

1 这是给谁的看的

  • Kubernetes CronJob的用户
  • 任何人在Kubernetes上构建平台
  • 任何对在Kubernetes上运行分布式的计划任务感兴趣的人
  • 有兴趣了解现实世界中大规模使用Kubernetes的任何人
  • Kubernetes贡献者

2 阅读你将会得到什么?

  • 深入了解Kubernetes的各个部分(尤其是CronJob)在现实世界中的行为。
  • 在Lyft这样的公司中使用Kubernetes作为平台的经验教训,以及我们如何解决这些缺点。

3 先决条件

  • 基本了解cron概念
  • 对CronJob的工作原理有基本的了解,特别是CronJob控制器,它创建的Jobs和它们的底层Pod之间的关系,以便更好地理解CronJob的深层次以及与Unix cron的比较。
  • 熟悉sidecar容器模式及其用途。在Lyft,我们利用sidecar容器排序来确保打包为sidecar容器的运行时依赖项(如Envoy,statsd等)在应用程序容器本身之前启动并运行。

4 背景和术语

  • 这cronjobcontroller是Kubernetes控制面板中协调CronJobs的代码段
  • 据说cron在某些机器执行时会被调用(通常根据其时间表)
  • Lyft Engineering在平台基础架构模型上运行,该模型中有一个基础架构团队(以下称为平台团队,平台工程师或平台基础架构),平台的客户是Lyft的其他工程师(以下称为开发人员,服务开发人员,用户或客户)。Lyft的工程师拥有,操作和维护其构建的内容,因此本文通篇使用“操作”。

5 Lyft的CronJobs

今天,在Lyft,我们在多租户生产Kubernetes环境中运行了将近500个cron任务,每小时执行1500多次调用。

Lyft在各种用例中广泛使用了重复的计划任务。在采用Kubernetes之前,这些都是在Linux机器上直接使用Unix cron执行的。开发团队负责crontab 使用平台基础架构团队维护的基础架构代码(IaC)管道编写其定义并提供运行它们的实例。

作为将工作负载容器化和迁移到我们内部Kubernetes平台的更大努力的一部分,我们选择采用Kubernetes CronJob *来代替Unix cron作为这种新的容器化环境中的cron执行程序。像许多人一样,我们选择Kubernetes是因为它具有许多理论上的好处,其中之一就是有效地利用资源。考虑一个每周运行15分钟的cron。在我们旧的环境中,运行cron的机器空闲时间为99.85%。使用Kubernetes CronJob,仅在cron调用的生命周期内使用计算资源(CPU,内存)。在其余时间里,Kubernetes可以有效地使用这些资源来运行其他CronJob或一起缩减集群。鉴于以前的执行cron任务的方法,通过过渡到临时创建工作的模型,可以获得很多好处。

215aff09ab5cd916f27bb54a7116c624.png
                    Lyft K8s堆栈中的平台和开发人员所有权边界

自从采用Kubernetes作为平台以来,开发团队不再配置和操作自己的计算实例。取而代之的是,平台工程团队负责维护和操作Kubernetes堆栈中使用的计算资源和运行时依赖项,以及自己生成Kubernetes CronJob对象。开发人员只需配置其cron计划和应用程序代码。

在纸上这一切听起来都不错,但是在实践中,我们发现了使用CronJob将cron从易于理解的传统Unix cron环境迁移到Kubernetes的分布式临时环境的几个痛点。

*虽然CronJob曾经是(并且至今仍是)(从Kubernetes v1.18起)是一个beta API,但我们发现它符合我们当时的要求,而且与其他Kubernetes很好地契合我们已经构建的基础架构工具。

6 Kubernetes CronJob(与Unix cron相比)有什么不同?

72dbc6d1815b2acdf70ff2151cd452a1.png

执行Kubernetes CronJob涉及的事件和K8s软件组件的简化序列

为了更好地理解为什么在生产环境中很难使用Kubernetes CronJobs,我们必须首先讨论CronJob与众不同的原因。Kubernetes CronJobs承诺可以像cron任务一样在Linux或Unix系统上运行;但是,它们的行为与Unix cron相比有一些关键差异:启动性能和故障处理。

7 启动表现

我们首先将启动延迟定义为从预期的cron启动到实际执行的应用代码的时间。也就是说,如果cron预计在处运行00:00:00,而应用程序代码实际上在处开始执行00:00:22,则特定cron调用的启动延迟为22秒。

传统的Unix crons的启动延迟非常短。当需要调用Unix cron时,只需运行指定的命令。为了说明这一点,请考虑以下cron定义:

#每天晚上在午夜运行date命令
0 0 * * * date >> date-cron.log

使用此cron定义,可以期待以下输出:

#date-cron.log
Mon Jun 22 00:00:00 PDT 2020
Tue Jun 23 00:00:00 PDT 2020

另一方面,Kubernetes CronJobs可能会经历严重的启动延迟,因为它们需要在任何应用程序代码开始运行之前发生多个事件。仅举几个:

  • cronjobcontroller 处理并决定调用CronJob
  • cronjobcontroller 根据CronJob的Job规范创建Job
  • jobcontroller 注意到新创建的Job并创建一个Pod
  • Kubernetes准入控制器将Sidecar Container规范注入Pod规范*
  • kube-scheduler 安排Pod到kubelet上
  • kubelet 运行Pod(拉出所有容器图像)
  • kubelet 启动所有杂物箱*
  • kubelet 启动应用程序容器*
  • Lyft的Kubernetes堆栈独有

在Lyft,我们发现,一旦在Kubernetes环境中达到一定规模的CronJobs,启动延迟就会特别受到#1,#5和#7的影响。

8 Cronjobcontroller 处理延迟

为了更好地了解这种延迟的来源,让我们深入了解内建的源代码cronjobcontroller。通过Kubernetes 1.18,它cronjobcontroller仅每10秒列出所有CronJob,并对每个CronJob执行一些控制器逻辑。在cronjobcontroller实施这样做同步,发行至少API调用每一个的cronjob。当CronJob的数量超过一定数量时,这些API调用将开始受速率限制。10秒轮询周期的延迟和API客户端速率限制的延迟加起来并导致CronJobs的启动延迟明显。

9 计划Cron Pod

由于Cron时间表的性质,大多数的Cron都将在分钟的顶部运行(XX:YY:00)。例如,@hourly cron预计在01:00:00,等执行02:00:00。在多租户cron平台中,每小时,每15分钟,每5分钟等等都计划运行许多cron,这会产生热点,需要同时调用许多cron。在Lyft,我们注意到一个这样的热点就是小时的高峰(XX:00:00)。这些热点会给CronJob执行的快乐路径中涉及的控制平面组件施加压力并暴露出额外的客户端速率限制kube-scheduler,kube-apiserver从而导致启动延迟显着增加。

此外,如果你没有为高峰需求配置计算(和/或为计算实例使用云提供商),而是使用诸如群集自动缩放器之类的工具动态扩展节点,则节点启动时间会给启动CronJob Pods带来额外的延迟。

10 Pod执行:非应用容器

一旦CronJob Pod成功调度到上kubelet,就kubelet需要提取并执行所有sidecar和应用程序本身的容器镜像。由于Lyft使用Sidecar排序方式控制应用程序容器的方式,如果这些Sidecar容器中的任何一个启动缓慢或需要重新启动,它们将传播额外的启动延迟。

总而言之,在多租户环境中,在实际执行应用程序代码之前发生的所有这些事件,再加上CronJob的规模,都可能引入明显且不可预测的启动延迟。稍后我们将看到,此启动延迟会导致CronJob错过运行,从而对CronJob在现实世界中的行为产生负面影响。

11 集装箱故障处理

监视cron的执行是一个好习惯。使用Unix cron,这样做非常简单。Unix crons使用指定的解释给定的命令$SHELL,并且,当命令退出时(无论成功与否),都会完成特定的调用。监视Unix cron的一种基本方法是引入命令包装器脚本,如下所示:

#!/bin/sh
my-cron-command
exitcode=$?

if [[ $exitcode -ne 0 ]]; then
    # stat-and-log is pseudocode for emitting metrics and logs
    stat-and-log "failure"
else
    stat-and-log "success"
fi

exit $exitcode

使用Unix cronstat-and-log时,无论每次cron调用都将完全执行一次$exitcode。然后,可以将这些指标用于执行失败的简单警报。

使用Kubernetes CronJob时,默认情况下会重试故障,并且执行过程可能具有多个故障状态(Job故障和容器故障),因此监控并不那么简单。

在应用程序容器中使用类似的脚本,并且将Jobs配置为在失败时重新启动,CronJob会重复执行并喷出度量标准,并在失败时记录多达BackoffLimit次数,从而给尝试调试它的开发人员带来很多噪音。此外,由于应用程序容器可能会自行恢复并成功完成,因此使用包装脚本中的第一个失败的幼稚警报可能是无法采取行动的噪音。

或者,你可以使用API层指标针对Job失败(例如 kube_job_status_failed来自kube-state-metrics)在Job级别而非应用程序容器级别发出警报。这种方法的缺点是,一旦作业到达终端故障状态,就不会发出呼叫通知BackoffLimit,这可能比第一次应用程序容器故障要晚得多。

12 是什么导致CronJobs间歇性失败?

不可忽略的启动延迟和失败重试循环会导致额外的延迟,这些延迟可能会干扰Kubernetes CronJobs的重复执行。对于频繁的CronJob或与空闲时间相关的应用程序执行时间较长的CronJob,此额外的延迟可以延续到下一个计划的调用中。如果CronJobConcurrencyPolicy: Forbid设置为禁止并发运行,则此残留会导致将来的调用无法按时执行并得到备份。

ec29541aa3e862d1861bd5e85627b448.png

示例时间线(从的角度来看cronjobcontroller)在startingDeadlineSeconds特定的每小时CronJob中超出了该时间线-CronJob错过了运行,直到下一个预定时间才会被调用

我们在Lyft观察到的一个更加险恶的场景是CronJobstartingDeadlineSeconds设置之后,CronJobs可能会完全丢失调用。在这种情况下,当启动延迟超过时startingDeadlineSeconds,CronJob将完全错过运行。另外,如果CronJob也ConcurrencyPolicy设置为Forbid,则先前调用的重试失败循环也可能延迟下一次调用,从而导致CronJob也丢失。

13 Kubernetes CronJobs在现实世界中的运营负担

自开始将这些重复的计划任务移至Kubernetes以来,我们发现开箱即用地使用CronJob从开发人员和平台团队的角度都引入了多个痛点,这些痛点开始抵消了收益和成本。为了节省开支,我们最初选择了Kubernetes CronJob。我们很快意识到,我们的开发人员和平台团队都没有配备必要的工具来操作和理解CronJobs的复杂生命周期。

Lyft的开发人员在尝试操作和调试其Kubernetes CronJobs时遇到了许多问题和投诉,例如:

  • “为什么我的cron不运行?”
  • “我认为我的cron停止了运行。我如何知道我的cron是否正在运行?”
  • “我不知道cron不在运行,我只是以为它在运行!”
  • “我该如何补救X失败的Cron?我不能只是自己输入和运行命令。”
  • “你能解释一下为什么这个Cron似乎错过了X和Y [时间段]之间的一些时间表吗?”
  • “我们有X(大量)corn,每个corn都有自己的警报,维护它们全都变得乏味/痛苦。”
  • “这是工作,豆荚和杂物这是什么?”

作为平台团队,我们没有能力回答以下问题:

  • 我们如何量化Kubernetes Cron平台的性能特征?
  • 在我们的Kubernetes环境中加入更多CronJobs有什么影响?
  • 与单租户Unix cron相比,运行多租户Kubernetes CronJobs的性能如何?
  • 我们如何开始定义与客户沟通的服务水平目标(SLO)?
  • 作为平台运营商,我们要监视什么并发出警报,以确保在不影响客户的情况下迅速解决平台范围的问题?

调试CronJob故障并不是一件容易的事,并且通常需要对故障发生的位置以及寻找证据的位置进行直观了解。有时,很难找到这些证据,例如cronjobcontroller仅以高详细日志级别记录的日志。或者,这些痕迹只是在特定时间段后消失,并调试“打hack”游戏,例如CronJob,Job和Pod对象本身上的Kubernetes Events,默认情况下仅保留一小时。这些方法都不容易使用,而且从平台上越来越多的CronJobs的支持角度来看,它们的伸缩性也不佳。

另外,有时当CronJob错过了太多运行时,Kubernetes只会退出,需要有人手动“松开” CronJob。在实际使用中,这种情况发生的频率比您想像的要高,并且每次手动修复都变得很痛苦。

总结了我们大规模使用Kubernetes CronJob所遇到的技术和运营问题。在第2部分中,我们分享了我们为解决Kubernetes堆栈中的这些问题所做的工作,以提高CronJobs的可用性和可靠性。

辛勤的蜜蜂永没有时间悲哀。  「——来自匿名」

ead67fad5b8e920b8e72eab62e0d898e.png

如果喜欢文章的话,点点关注,就差你的关注了,更多好玩有趣的云原生前沿技术尽在云原生CTO,如果对你有帮助,欢迎分享给更多人

                              记得点赞分享哦
4ba1b64e0a64ba7c4f2b1a83b91cdb5a.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值