确保最多在 Kubernetes 上执行单个作业实例并写入 Postgresql

Ensuring at most a single instance of job executing on Kubernetes and writing into Postgresql

我有一个 Python 程序,我每 2 小时 运行 将其作为 Kubernetes 集群上的作业。我还有一个网络服务器,只要用户单击页面上的按钮就会启动作业。

我需要确保在任何给定时间集群上最多只有一个作业实例 运行ning。

鉴于我正在使用 Kubernetes 运行 作业并从作业内部连接到 Postgresql,解决方案应该以某种方式利用这两者。我虽然有点了解它并提出了以下想法:

  1. 在 Kubernetes 中找到设置此限制的设置,然后尝试启动第二个实例将失败。我找不到这个设置。
  2. 创建共享锁或互斥锁。缺点是如果工作崩溃,我可能在退出前无法解锁。
    1. Kubernetes 是 运行ning etcd,也许我可以使用它
    2. 在 Postgresql 中创建一个 'lock' table,当新实例连接时,它会检查它是否是唯一的 运行ning。以某种方式使用交易,以便一个人获胜并获得收益,而其他人则退出。我还没有想到这个,但应该可以。
  3. 查询kubernetes API我在工作中使用的标签,看看是否有一些实例。这可能不是原子的,因此可能会漏掉不止一个实例。

考虑到我选择的平台,这个问题的通常解决方案是什么?我该怎么做,才不会重新发明轮子,有靠谱的东西?

一种完全不同的方法是 运行 执行作业功能的(网络)服务器。在高层次上,这个想法是网络服务器可以联系这个新的作业服务器来执行功能。此外,这个新的作业服务器将有一个内部 cron,每 2 小时触发一次相同的功能。

可能有 2 种实现方法:

  1. 你可以把检查机制放在jobserver代码里面,保证即使2个API调用同时发生在job server上,也只有一个执行,另一个等待。您可以使用语言平台的锁定功能来实现此目的,或者使用消息队列。
  2. 您可以将检查机制放在作业服务器代码之外(在数据库中)以确保只执行一个 API 调用。类似于你的建议。如果你使用 postgres 事务,你不必担心你的作业崩溃和锁的值保持不变。

这两种方法的 pros/cons 都很简单。在我看来,1 和 2 之间的主要区别在于,如果您更新作业服务器代码,那么您可能会遇到 2 个作业服务器可能同时 运行ning 的情况。这会破坏你想要的隔离属性。因此,数据库可能工作得更好,或者在 k8s 意义上更符合习惯(所有服务器都是无状态的,因此所有 k8s 好东西都可以工作;将任何共享状态放在可以处理并发的数据库中)。

针对您的想法,以下是我的想法:

  1. 在 k8s 中找到一个设置来限制这一点:k8s 不会以相同的名称启动事物(在规范的元数据中)。但是其他任何工作都可以,k8s 将开始另一份工作。

  2. a) etcd3 支持分布式锁定原语。但是,我从来没有用过这个,我真的不知道要注意什么。

  3. b) postgres 锁值应该有效。即使在作业崩溃的情况下,您也不必担心剩余设置的锁的值。

  4. 向 k8s API 服务器查询应该是原子的东西并不是像你说的那样的好主意。我使用了一个对 k8s 事件做出反应的系统(比如对象规范上的注释更改),但是我有一些错误,我的 'operator' 突然停止获取 k8s 事件并且需要重新启动,或者再次,如果我想向事件处理程序服务器推送更新,那么可能同时存在 2 个事件处理程序。

我建议坚持使用您最熟悉的内容。在我的例子中,这将实现像 k8s 部署这样的作业服务器,运行s 作为服务器并监听 events/API 调用。