Kubernetes 设计模式笔记 —— Job & CronJob

Batch Job

Batch Job 模式适合处理隔离的原子化的工作任务,能够在分布式的环境中,可靠地运行 short-lived Pods,直到工作任务成功地结束。

在 Kubernetes 中,可以通过不同的方式创建 Pod:

  • Bare Pod:可以手动创建 Pod 来运行容器应用,但是当此类 Pod 所在的节点失效时,Pod 不会自动重启。除非用于开发或测试目的,此类方式并不推荐
  • ReplicaSet:当 Pod 应该长时间持续运行时(比如 web server),就适合用此方式来创建 Pod 和管理其生命周期。它会确保在任意时刻,运行着的 Pod 副本数量都是稳定的
  • DaemonSet:负责在每一个节点上都部署一个 Pod。通常情况下用于平台管理工作,比如监控、日志聚合、存储等

上述 Pod 有一个共同点,它们都代表着长时间运行的进程,并不是在一段时间后就需要被关掉。但是在某些场景下,仍需要执行一类预先定义好的、有限的工作流,当该工作流程可靠地完成后,再关闭对应的容器。

Kubernetes Job 类似于 ReplicaSet,它也会创建 1 个或者多个 Pods 并确保它们成功运行。区别在于,当特定数量的 Pods 成功终止后,Job 就变为完成状态,不会再有额外的 Pod 被启动。

apiVersion: batch/v1
kind: Job
metadata:
  name: random-generator
spec:
  completions: 5
  parallelism: 2
  template:
    metadata:
      name: random-generator
    spec:
      restartPolicy: OnFailure
      containers:
      - image: k8spatterns/random-generator:1.0
        name: random-generator
        command: [ "java", "-cp", "/", "RandomRunner", "/numbers.txt", "10000" ]

比如上面配置的 Job,会确保有 5 个 Pod 成功执行完毕,可以有两个 Pod 同时运行。此外,Job 配置文件中的 restartPolicy 是必需的,且其值只能是 OnFailureNever,不能是 Always

为什么不通过 bare Pods 来执行 Job 对应的任务呢?因为 Job 相比于 bare Pods,能够提供更多可靠性和扩展性方面的好处。

  • Job 并不是临时的 in-memory 任务,而是一个持久化的能够在集群重启后幸存的任务
  • Job 完成后并不会被删除,而是继续保留,方便以后追踪问题。只有当 bare Pods 是 restartPolicy: OnFailure 时,其才会拥有同样的特性
  • Job 可能需要执行多次,可以通过 .spec.completions 指定
  • 当任务确实需要完成多次时,Job 还支持扩展,即同一时间开启多个 Pods。可以通过 .spec.parallelism 指定
  • 若节点失效,或者 Pod 正在运行时因为某些原因被移除,由 Job 创建的 Pods 会被 scheduler 重新分配给健康的节点

两个字段对控制 Job 的行为发挥着关键作用:

  • .spec.completions:指定 Pod 的数量。当特定数量的 Pod 执行完毕后,当前 Job 才算完成
  • .spec.parallelism:指定可以并行执行的 Pod 副本数量
Parallel Batch Job with a fixed completion count

基于上述两个参数,Job 可以分为如下几种类型:

  • Single Pod Job:不设置 .spec.completions.spec.parallelism 的值,或者将它们设置为默认值 1。此类 Job 只会启动一个 Pod,当 Pod 成功退出后,Job 完成
  • Fixed completions count Jobs:.spec.completions 的值大于 1。当特定数量(.spec.completions)的 Pod 执行完毕后,Job 完成
  • Work queue Job:.spec.completions 不设置或者设为默认值,.spec.parallelism 大于 1。适用于工作队列中的 Job。当至少有一个 Pod 成功终止时,所有其他 Pod 也会自行终止。比如,一堆固定数量的待处理项目保存在某个队列中,并行的 Pod 可以按顺序获取并处理它们,当某个 Pod 检测到队列为空并成功退出后,Job controller 等待其他 Pod 终止运行
总结

Job 帮助我们将隔离的工作单元变成一个可靠的、可扩展的执行单元。并不是所有的服务都需要一直运行,比如某些服务可能需要按需运行,某些必须在特定的时间窗口运行,某些必须按照计划重复执行。
通过 Job 可以只在需要的时候运行 Pod,且任务完成后就退出。使用 Job 处理 short-lived 任务可以节约系统资源。

Periodic Job

Periodic Job 是对 Batch Job 的扩展,为其添加了时间维度,同时允许临时的事件触发工作流的执行。
在分布式系统的世界里,有一种比较清晰的倾向,借助 HTTP 和轻量的消息系统实现实时、事件驱动的应用。不考虑软件开发中的此类倾向,计划任务仍然是一种历史悠久且至今常用的手段。
它们通常用于自动化的系统维护工作或者管理员任务,在商业应用方面的场景比如文件同步、发送邮件、清理和归档旧文件等。

传统的处理 Periodic Job 的方式是借助专门的计划任务软件比如 Cron。但是 Cron jobs 运行在单一的服务器上,难以维护且有发生单点故障的风险。
这也是为什么很多开发者会尝试实现自己的方案,比如 Java 中的 Quartz、Spring Batch 等。但是类似于 Cron,它们也会遇到弹性和高可用性方面的挑战,导致较高的资源使消耗。此外在这类方案里,Job 调度器是应用的一部分,为了获得高可用,通常就需要运行多个应用实例,同时还需要确保同一时刻下只有一个实例是活跃的。从而引入 leader election 等分布式系统问题。

面对以上的一些问题,Kubernetes 实现了 CronJob,允许开发者以广为熟知的 Cron 格式将 Job 设置为计划任务。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: random-generator
spec:
  # Every three minutes
  schedule: "*/3 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - image: k8spatterns/random-generator:1.0
            name: random-generator
            command: [ "java", "-cp", "/", "RandomRunner", "/numbers.txt", "10000" ]
restartPolicy: OnFailure

与 Job 相比,CronJob 有一些额外的字段:

  • .spec.schedule:指定 Job 的 schedule 模式(如 0 * * * * 表示每个小时触发一次)
  • .spec.startingDeadlineSeconds:Job 启动时的截止时间。有些时候由于资源不够或者缺少其他依赖,Job 错过了预定的触发时间。此字段用于指定错过多少秒后就直接跳过此次执行
  • .spec.concurrencyPolicy:用于控制同一个 CronJob 的并发执行。默认值为 Allow,即使前一个 Job 并未结束,也允许新的 Job 实例被创建;可以指定为 Forbid,若当前 Job 并未结束,则跳过下一次执行;或者改为 Replace,取消当前还未结束的 Job 并启动一个新的 Job 实例
  • .spec.suspend:暂停所有后续执行,但不影响已经开始的执行
  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit:应保留多少已完成和失败的 Job 作为审计数据

总结

CronJob 其实是一个非常简单的原语,在现有的 Job 定义中添加类似 Cron 的行为。但是当它与 Kubernetes 提供的其他原语比如 Pods、资源隔离结合起来时,就成为一个非常强大的任务调度系统。
它的调度行为是平台的一部分,实现在应用的外部,使得开发者能够专注于应用的业务逻辑,无需在应用内部额外设计一套调度逻辑。同时提供了高可用、高弹性、高容积以及由策略驱动的 Pod 部署等特性。
当然,和 Job 一样,CronJob 容器在部署时,也需要考虑所有的特殊情况,比如重复执行、未触发、并发执行和任务取消等。

参考资料

Kubernetes Patterns

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容