为啥在 Kubernetes cron 作业中可能会创建两个作业,或者可能不会创建作业?

Posted

技术标签:

【中文标题】为啥在 Kubernetes cron 作业中可能会创建两个作业,或者可能不会创建作业?【英文标题】:Why in kubernetes cron job two jobs might be created, or no job might be created?为什么在 Kubernetes cron 作业中可能会创建两个作业,或者可能不会创建作业? 【发布时间】:2018-05-21 08:05:26 【问题描述】:

在 k8s 中Cron Job Limitations 提到不能保证一个作业会执行一次:

一个 cron 作业大约每执行一次就创建一个作业对象 日程。我们说“约”是因为在某些情况下 可能会创建两个作业,也可能不会创建作业。我们试图 使这些变得罕见,但不要完全阻止它们。因此,就业 应该是幂等的

谁能解释一下:

为什么会发生这种情况? 这可能发生的概率/统计数据是多少? 它会在未来的某个合理的时间在 k8s 中修复吗? 是否有任何解决方法来防止这种行为(如果正在运行的作业不能实现为幂等)? 其他cron 相关 服务是否遇到同样的问题?也许这是一个核心 cron 问题?

【问题讨论】:

【参考方案1】:

控制器:

https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/cronjob/cronjob_controller.go

从为解释奠定基础的评论开始:

I did not use watch or expectations. Those add a lot of corner cases, and we aren't expecting a large volume of jobs or scheduledJobs. (We are favoring correctness over scalability.)  

If we find a single controller thread is too slow because there are a lot of Jobs or CronJobs, we we can parallelize by Namespace. If we find the load on the API server is too high, we can use a watch and UndeltaStore.) 

Just periodically list jobs and SJs, and then reconcile them.

Periodically 表示每 10 秒一次:

https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/cronjob/cronjob_controller.go#L105

在引用的限制之后的文档在某些情况下也有一些有用的颜色,在特定的时间表上可能会启动 2 个作业或没有作业:

If startingDeadlineSeconds is set to a large value or left unset (the default) and if concurrentPolicy is set to AllowConcurrent, the jobs will always run at least once.

Jobs may fail to run if the CronJob controller is not running or broken for a span of time from before the start time of the CronJob to start time plus startingDeadlineSeconds, or if the span covers multiple start times and concurrencyPolicy does not allow concurrency. For example, suppose a cron job is set to start at exactly 08:30:00 and its startingDeadlineSeconds is set to 10, if the CronJob controller happens to be down from 08:29:00 to 08:42:00, the job will not start. Set a longer startingDeadlineSeconds if starting later is better than not starting at all.

更高层次,在分布式系统中只求解一次很难:

https://bravenewgeek.com/you-cannot-have-exactly-once-delivery/

分布式系统中的时钟和时间同步也很困难:

https://8thlight.com/blog/rylan-dirksen/2013/10/04/synchronization-in-a-distributed-system.html

问题:

为什么会发生这种情况?

例如,托管 CronJobController 的节点在作业应该运行时失败。

这可能发生的概率/统计数据是多少?

对于任何给定的运行来说都不太可能。对于足够多的运行,很难避免不得不面对这个问题。

它会在 k8s 的某个合理的未来修复吗?

k8s repo 中的 area/batch 标签下没有与幂等性相关的问题,所以人们猜不到。

https://github.com/kubernetes/kubernetes/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Fbatch

是否有任何解决方法来防止这种行为(如果正在运行的作业不能实现为幂等)?

更多地考虑幂等的具体定义,以及工作中存在提交的特定点。例如,如果作业将状态保存到暂存区,则可以使作业支持多次执行,然后有一个选举过程来确定谁的工作获胜。

其他与 cron 相关的服务也会遇到同样的问题吗?也许是核心 cron 问题?

是的,这是一个核心分布式系统问题。

对于大多数用户来说,k8s 文档提供的答案可能比需要的更精确和细致入微。如果您计划的工作是控制一些关键的医疗程序,那么计划失败案例非常重要。如果它只是进行一些系统清理,那么错过预定的运行并不重要。根据定义,几乎所有 k8s CronJobs 的用户都属于后者。

【讨论】:

看起来很清晰,非常感谢。至于“当作业控制器失败时作业可能不会启动” - 这很明显,尽管它为什么可以多次启动更难理解。 我一直在一个 cron 执行点运行多个作业。但似乎只有这些作业的运行时间很短。知道为什么会发生这种情况以及如何防止它吗?我使用 concurrencyPolicy: Forbid、backoffLimit: 0 和 restartPolicy: Never。 我们长期运行夜间作业,有时会重复。我们真的不经常看到失误。我们可以很容易地提醒和检查。然而,重复是一个问题。我正在研究解决方案。 @nroose 你找到解决方案了吗? @khichar.anil 不是真的。我通过我们的 mysql 完成了一个互斥锁,以确保这些骗子不会同时运行。这些已经不再经常发生了。

以上是关于为啥在 Kubernetes cron 作业中可能会创建两个作业,或者可能不会创建作业?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 kubernetes cron 作业中启动 rails rake 任务

为啥成功执行 Cron 作业会停止将日志进一步附加到文件中

为啥我的 sidekiq 计划的 cron 作业没有在 heroku 上运行?

Kubernetes 通过基于时间的触发器扩展 pod

Kubernetes - 作业调度 API

Kubernetes Cron Job 在创建下一个计划之前终止 Pod