使用工作队列的 Kubernetes 作业调度

Posted

技术标签:

【中文标题】使用工作队列的 Kubernetes 作业调度【英文标题】:Kubernetes job scheduling using work queue 【发布时间】:2018-12-29 21:16:00 【问题描述】:

我们正在使用 Kubernetes/go-lang 编写一个包裹递送跟踪器应用程序。一切都很好,直到我们遇到了我们有某些调度需求的路障,并且无法弄清楚该使用什么。用例是:

    一旦包裹被分配并送出,应跟踪其位置。 如果包裹到达仓库3小时后仍未分配,我们应将其标记为“明天发货”。 一些常见的 cron 类型的作业。

对于#1,我们计划安排一个作业,该作业将在每 10 秒后轮询一次包裹的位置。但这是一次性的工作。包裹送达后。它应该会消失。

对于#2,我们计划安排一个定时/cron 作业,该作业将由我们的 API 触发。此作业将安排在仓库收到包裹后的 3 小时内完成。

我们上面的 #3 是用于各种内部目的的常用 cron 作业。

我们希望使用单一调度平台来满足所有这些类型的需求。

由于我们专门使用 Kubernetes,我们希望利用它的作业调度功能。但我们对此有一定的怀疑。

    我们可以从源代码创建这些工作吗?如here 所述,这是可能的。但我不确定我们的系统管理员是否会允许我们这样做。

    我读过 Kubernetes 中的工作队列,我们​​可以将工作推送到工作队列,消费者将为这些工作项创建工作。

    但是,我不知道如何为该工作队列创建一个永久消费者守护进程,它将轮询该队列并为每个工作项创建作业。

    另一个疑问是如何在这里安排简单的 cron 作业(上面的#3)。

我也听说过 Kala,但不确定这在 Kubernetes 世界中是否适合。

任何参考、指针、链接或建议都将受到高度赞赏,因为我对 Kubernetes/Golang 很陌生,而且在谷歌上也找不到任何具体的东西。

【问题讨论】:

【参考方案1】:

有多少包?你提到的时间间隔有多重要?我想到了几种方法:

Kubernetes cron jobs 可以按计划运行,想必可以解决你的第三个需求。如果您有“一些”包(我不一定会尝试超过“数百个”),那么您可以为每个包创建一个新的 cron 作业,该作业运行一次然后自行删除。 Cron 作业的运行频率不能超过每分钟一次。如果您可以将“每 10 秒轮询一次”的要求放宽为“每分钟轮询一次”,并且包的数量很少,那么您可以这样做。

对于第二个要求,您可以每隔一段时间检查每个包裹是否过期。如果“3 小时后”的要求不是特别严格,这会更好。编写一个查看每个包的程序,如果三个小时的延迟已经过去,则更改其状态,然后编写一个每 15 分钟运行一次的 cron 作业。围绕这个的扩展问题是不同的,但是“查找过期包”听起来像是一个 SQL 查询;这取决于您的后端技术。

您可以编写一个长时间运行的程序来执行所有基于间隔的检查。您暗示使用 Go,并且可以设置每 10 秒触发一次的time.Timer(一个用于整个程序,或者一个用于每个包)。我可能会让程序在内存中知道每个包 ID、它的当前状态以及何时需要将状态更改为“明天交付”,并在一个进程中运行所有这些。一个普通的部署会管理这个。您还应该将此状态保存在数据库中,这可能会引入一些扩展限制。

最后,您可以使用消息队列系统。 RabbitMQ 是一个流行的开源选择。您可以安排延迟消息(发送消息以交换 package.in,该消息路由到队列 @9​​87654327@,TTL 为 10 秒,将该队列的 dead-letter exchange 设置为 package.ready,将其发送到队列package.out,并让应用程序从该队列中消费)。同样,您将编写一个长时间运行的过程;这个从队列中获取项目,从数据库中检索它们的状态,轮询它们的当前位置,并将它们标记为“明天交付”或在package.in 中适当地重新排队。这是最可扩展的答案(您可以通过 Kubernetes 部署启动许多工作人员),但也最少使用内置 Kubernetes 部件。

【讨论】:

我们的扩展需求并没有那么大。我们暂时不会超过 200 个包裹。 您是否建议对作业类型 1 和 2 使用消息队列,对作业类型 3 使用通常的 cron 作业?对于上面的类型 2,如果我使用 RabbitMQ,我是否会在 3 小时后将这些作业推送到队列中,或者消息将立即放入队列但会在 3 小时后被使用?同样对于上面的类型 1,我们是否会立即将作业推送到队列中?并且每个 goroutine 都会在睡眠 10 秒后轮询位置?你有什么资源可以让我在 Kafka/Redis 中实现这种死信交换吗? (3) 如果您对某件事的最佳描述是“常规 cron 作业”,那么 Kubernetes CronJob 可能是一个很好的匹配。 在 (1) 和 (2) 上,如果您有一个作业每 10 秒运行一次,直到包裹送达,那么如果包裹还没有送达,它将在 3 小时后运行。所以我的几个解决方案结合了这些。特别是我对 RabbitMQ 设置的描述基本上只有一个队列,其中消息在 10 秒延迟后传递,这两个队列都完成了。如果您愿意,您可以实现一个延迟 3 小时的类似队列。 RabbitMQ 代理本身实现延迟(通过 TTL/DLX 技巧),作业将在消息准备就绪时不断消耗消息。 你可以很容易地在 Redis 上实现一个简单的作业队列,特别是如果你使用一个排序集,其中“分数”是作业需要下一次执行时的 Unix 时间戳。 Kafka 并不是为这样的模式设计的:您不能像 RabbitMQ 提供的那样添加延迟或消息重新排队,也不能选择性地拒绝消息(如果轮询特定包的位置失败会发生什么?)。在你描述的规模上,任何基本的数据库支持系统都可以正常工作,如果我这样做,我会使用 PostgreSQL,主要是出于个人喜好。

以上是关于使用工作队列的 Kubernetes 作业调度的主要内容,如果未能解决你的问题,请参考以下文章

在没有数据库队列驱动程序的情况下完成流明作业调度

Kubernetes 资源拓扑感知调度优化

Kubernetes - 作业调度 API

「首席架构师推荐」工作流引擎哪家强?首席架构帮你挑

作业帮 Kubernetes 原生调度器优化实践

Android Work Manager:如何每月将作业排入队列