From 380d1890e7ff64e726fb5ce57543b92142f29b25 Mon Sep 17 00:00:00 2001 From: Qiming Teng Date: Wed, 29 Jul 2020 10:14:13 +0800 Subject: [PATCH] [zh] Translate Job into Chinese --- .../concepts/workloads/controllers/job.md | 861 ++++++++++++++++++ 1 file changed, 861 insertions(+) create mode 100644 content/zh/docs/concepts/workloads/controllers/job.md diff --git a/content/zh/docs/concepts/workloads/controllers/job.md b/content/zh/docs/concepts/workloads/controllers/job.md new file mode 100644 index 0000000000..0a5dfd8291 --- /dev/null +++ b/content/zh/docs/concepts/workloads/controllers/job.md @@ -0,0 +1,861 @@ +--- +title: Jobs +content_type: concept +feature: + title: 批量执行 + description: > + 除了服务之外,Kubernetes 还可以管理你的批处理和 CI 工作负载,在期望时替换掉失效的容器。 +weight: 60 +--- + + + + +Job 会创建一个或者多个 Pods,并确保指定数量的 Pods 成功终止。 +随着 Pods 成功结束,Job 跟踪记录成功完成的 Pods 个数。 +当数量达到指定的成功个数阈值时,任务(即 Job)结束。 +删除 Job 的操作会清除所创建的全部 Pods。 + +一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 +当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job +对象会启动一个新的 Pod。 + +你也可以使用 Job 以并行的方式运行多个 Pod。 + + + +## 运行示例 Job {#running-an-example-job} + +下面是一个 Job 配置示例。它负责计算 π 到小数点后 2000 位,并将结果打印出来。 +此计算大约需要 10 秒钟完成。 + +{{< codenew file="controllers/job.yaml" >}} + + +你可以使用下面的命令来运行此示例: + +```shell +kubectl apply -f https://kubernetes.io/examples/controllers/job.yaml +``` + +输出类似于: + +``` +job.batch/pi created +``` + + +使用 `kubectl` 来检查 Job 的状态: + +```shell +kubectl describe jobs/pi +``` + +输出类似于: + +``` +Name: pi +Namespace: default +Selector: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c +Labels: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c + job-name=pi +Annotations: kubectl.kubernetes.io/last-applied-configuration: + {"apiVersion":"batch/v1","kind":"Job","metadata":{"annotations":{},"name":"pi","namespace":"default"},"spec":{"backoffLimit":4,"template":... +Parallelism: 1 +Completions: 1 +Start Time: Mon, 02 Dec 2019 15:20:11 +0200 +Completed At: Mon, 02 Dec 2019 15:21:16 +0200 +Duration: 65s +Pods Statuses: 0 Running / 1 Succeeded / 0 Failed +Pod Template: + Labels: controller-uid=c9948307-e56d-4b5d-8302-ae2d7b7da67c + job-name=pi + Containers: + pi: + Image: perl + Port: + Host Port: + Command: + perl + -Mbignum=bpi + -wle + print bpi(2000) + Environment: + Mounts: + Volumes: +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal SuccessfulCreate 14m job-controller Created pod: pi-5rwd7 +``` + + +要查看 Job 对应的已完成的 Pods,可以执行 `kubectl get pods`。 + +要以机器可读的方式列举隶属于某 Job 的全部 Pods,你可以使用类似下面这条命令: + +```shell +pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}') +echo $pods +``` + +输出类似于: + +``` +pi-5rwd7 +``` + + +这里,选择算符与 Job 的选择算符相同。`--output=jsonpath` 选项给出了一个表达式, +用来从返回的列表中提取每个 Pod 的 name 字段。 + +查看其中一个 Pod 的标准输出: + +```shell +kubectl logs $pods +``` + + +输出类似于: + +``` +3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901 +``` + +## 编写 Job 规约 + +与 Kubernetes 中其他资源的配置类似,Job 也需要 `apiVersion`、`kind` 和 `metadata` 字段。 +Job 的名字必须时合法的 [DNS 子域名](/zh/docs/concepts/overview/working-with-objects/names#dns-subdomain-names)。 + +Job 配置还需要一个[`.spec` 节](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status)。 + + +### Pod 模版 + +Job 的 `.spec` 中只有 `.spec.template` 是必需的字段。 + +字段 `.spec.template` 的值是一个 [Pod 模版](/zh/docs/concepts/workloads/pods/#pod-templates)。 +其定义规范与 {{< glossary_tooltip text="Pod" term_id="pod" >}} +完全相同,只是其中不再需要 `apiVersion` 或 `kind` 字段。 + +除了作为 Pod 所必需的字段之外,Job 中的 Pod 模版必需设置合适的标签 +(参见[Pod 选择算符](#pod-selector))和合适的重启策略。 + +Job 中 Pod 的 [`RestartPolicy`](/zh/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy) +只能设置为 `Never` 或 `OnFailure` 之一。 + + +### Pod 选择算符 {#pod-selector} + +字段 `.spec.selector` 是可选的。在绝大多数场合,你都不需要为其赋值。 +参阅[设置自己的 Pod 选择算符](#specifying-your-own-pod-selector). + + +### Job 的并行执行 {#parallel-jobs} + +适合以 Job 形式来运行的任务主要有三种: + +1. 非并行 Job + - 通常只启动一个 Pod,除非该 Pod 失败 + - 当 Pod 成功终止时,立即视 Job 为完成状态 +1. 具有 *确定完成计数* 的并行 Job + - `.spec.completions` 字段设置为非 0 的正数值 + - Job 用来代表整个任务,当对应于 1 和 `.spec.completions` 之间的每个整数都存在 + 一个成功的 Pod 时,Job 被视为完成 + - **尚未实现**:每个 Pod 收到一个介于 1 和 `spec.completions` 之间的不同索引值 +1. 带 *工作队列* 的并行 Job + - 不设置 `spec.completions`,默认值为 `.spec.parallelism` + - 多个 Pod 之间必须相互协调,或者借助外部服务确定每个 Pod 要处理哪个工作条目。 + 例如,任一 Pod 都可以从工作队列中取走最多 N 个工作条目。 + - 每个 Pod 都可以独立确定是否其它 Pod 都已完成,进而确定 Job 是否完成 + - 当 Job 中 _任何_ Pod 成功终止,不再创建新 Pod + - 一旦至少 1 个 Pod 成功完成,并且所有 Pod 都已终止,即可宣告 Job 成功完成 + - 一旦任何 Pod 成功退出,任何其它 Pod 都不应再对此任务执行任何操作或生成任何输出。 + 所有 Pod 都应启动退出过程。 + + +对于 _非并行_ 的 Job,你可以不设置 `spec.completions` 和 `spec.parallelism`。 +这两个属性都不设置时,均取默认值 1。 + +对于 _确定完成计数_ 类型的 Job,你应该设置 `.spec.completions` 为所需要的完成个数。 +你可以设置 `.spec.parallelism`,也可以不设置。其默认值为 1。 + +对于一个 _工作队列_ Job,你不可以设置 `.spec.completions`,但要将`.spec.parallelism` +设置为一个非负整数。 + +关于如何利用不同类型的 Job 的更多信息,请参见 [Job 模式](#job-patterns)一节。 + + +#### 控制并行性 {#controlling-parallelism} + +并行性请求(`.spec.parallelism`)可以设置为任何非负整数。 +如果未设置,则默认为 1。 +如果设置为 0,则 Job 相当于启动之后便被暂停,直到此值被增加。 + +实际并行性(在任意时刻运行状态的 Pods 个数)可能比并行性请求略大或略小, +原因如下: + + +- 对于 _确定完成计数_ Job,实际上并行执行的 Pods 个数不会超出剩余的完成数。 + 如果 `.spec.parallelism` 值较高,会被忽略。 +- 对于 _工作队列_ Job,有任何 Job 成功结束之后,不会有新的 Pod 启动。 + 不过,剩下的 Pods 允许执行完毕。 +- 如果 Job {{< glossary_tooltip text="控制器" term_id="controller" >}} 没有来得及作出响应,或者 +- 如果 Job 控制器因为任何原因(例如,缺少 `ResourceQuota` 或者没有权限)无法创建 Pods。 + Pods 个数可能比请求的数目小。 +- Job 控制器可能会因为之前同一 Job 中 Pod 失效次数过多而压制新 Pod 的创建。 +- 当 Pod 处于体面终止进程中,需要一定时间才能停止。 + + + +## 处理 Pod 和容器失效 + +Pod 中的容器可能因为多种不同原因失效,例如因为其中的进程退出时返回值非零, +或者容器因为超出内存约束而被杀死等等。 +如果发生这类事件,并且 `.spec.template.spec.restartPolicy = "OnFailure"`, +Pod 则继续留在当前节点,但容器会被重新运行。 +因此,你的程序需要能够处理在本地被重启的情况,或者要设置 +`.spec.template.spec.restartPolicy = "Never"`。 +关于 `restartPolicy` 的更多信息,可参阅 +[Pod 生命周期](/zh/docs/concepts/workloads/pods/pod-lifecycle/#example-states)。 + + +整个 Pod 也可能会失败,且原因各不相同。 +例如,当 Pod 启动时,节点失效(被升级、被重启、被删除等)或者其中的容器失败而 +`.spec.template.spec.restartPolicy = "Never"`。 +当 Pod 失败时,Job 控制器会启动一个新的 Pod。 +这意味着,你的应用需要处理在一个新 Pod 中被重启的情况。 +尤其是应用需要处理之前运行所触碰或产生的临时文件、锁、不完整的输出等问题。 + + +注意,即使你将 `.spec.parallelism` 设置为 1,且将 `.spec.completions` 设置为 +1,并且 `.spec.template.spec.restartPolicy` 设置为 "Never",同一程序仍然有可能被启动两次。 + +如果你确实将 `.spec.parallelism` 和 `.spec.completions` 都设置为比 1 大的值, +那就有可能同时出现多个 Pod 运行的情况。 +为此,你的 Pod 也必须能够处理并发性问题。 + + +### Pod 回退失效策略 + +在有些情形下,你可能希望 Job 在经历若干次重试之后直接进入失败状态,因为这很 +可能意味着遇到了配置错误。 +为了实现这点,可以将 `.spec.backoffLimit` 设置为视 Job 为失败之前的重试次数。 +失效回退的限制值默认为 6。 +与 Job 相关的失效的 Pod 会被 Job 控制器重建,并且以指数型回退计算重试延迟 +(从 10 秒、20 秒到 40 秒,最多 6 分钟)。 +当 Job 的 Pod 被删除时,或者 Pod 成功时没有其它 Pod 处于失败状态,失效回退的次数也会被重置(为 0)。 + + +{{< note >}} +如果你的 Job 的 `restartPolicy` 被设置为 "OnFailure",就要注意运行该 Job 的容器 +会在 Job 到达失效回退次数上限时自动被终止。 +这会使得调试 Job 中可执行文件的工作变得非常棘手。 +我们建议在调试 Job 时将 `restartPolicy` 设置为 "Never", +或者使用日志系统来确保失效 Jobs 的输出不会意外遗失。 +{{< /note >}} + + +## Job 终止与清理 + +Job 完成时不会再创建新的 Pod,不过已有的 Pod 也不会被删除。 +保留这些 Pod 使得你可以查看已完成的 Pod 的日志输出,以便检查错误、警告 +或者其它诊断性输出。 +Job 完成时 Job 对象也一样被保留下来,这样你就可以查看它的状态。 +在查看了 Job 状态之后删除老的 Job 的操作留给了用户自己。 +你可以使用 `kubectl` 来删除 Job(例如,`kubectl delete jobs/pi` +或者 `kubectl delete -f ./job.yaml`)。 +当使用 `kubectl` 来删除 Job 时,该 Job 所创建的 Pods 也会被删除。 + + +默认情况下,Job 会持续运行,除非某个 Pod 失败(`restartPolicy=Never`) +或者某个容器出错退出(`restartPolicy=OnFailure`)。 +这时,Job 基于前述的 `spec.backoffLimit` 来决定是否以及如何重试。 +一旦重试次数到达 `.spec.backoffLimit` 所设的上限,Job 会被标记为失败, +其中运行的 Pods 都会被终止。 + +终止 Job 的另一种方式是设置一个活跃期限。 +你可以为 Job 的 `.spec.activeDeadlineSeconds` 设置一个秒数值。 +该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。 +一旦 Job 运行时间达到 `activeDeadlineSeconds` 秒,其所有运行中的 Pod +都会被终止,并且 Job 的状态更新为 `type: Failed` +及 `reason: DeadlineExceeded`。 + + +注意 Job 的 `.spec.activeDeadlineSeconds` 优先级高于其 `.spec.backoffLimit` 设置。 +因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达 +`activeDeadlineSeconds` 所设的时限即不再部署额外的 Pod,即使其重试次数还未 +达到 `backoffLimit` 所设的限制。 + +例如: + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: pi-with-timeout +spec: + backoffLimit: 5 + activeDeadlineSeconds: 100 + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never +``` + +注意 Job 规约和 Job 中的 +[Pod 模版规约](/zh/docs/concepts/workloads/pods/init-containers/#detailed-behavior) +都有 `activeDeadlineSeconds` 字段。 +请确保你在合适的层次设置正确的字段。 + +还要注意的是,`restartPolicy` 对应的是 Pod,而不是 Job 本身: +一旦 Job 状态变为 `type: Failed`,就不会再发生 Job 重启的动作。 +换言之,由 `.spec.activeDeadlineSeconds` 和 `.spec.backoffLimit` 所触发的 Job 终结机制 +都会导致 Job 永久性的失败,而这类状态都需要手工干预才能解决。 + + +## 自动清理完成的 Job + +完成的 Job 通常不需要留存在系统中。在系统中一直保留它们会给 API +服务器带来额外的压力。 +如果 Job 由某种更高级别的控制器来管理,例如 +[CronJobs](/zh/docs/concepts/workloads/controllers/cron-jobs/), +则 Job 可以被 CronJob 基于特定的根据容量裁定的清理策略清理掉。 + +### 已完成 Job 的 TTL 机制 {#ttl-mechanisms-for-finished-jobs} + +{{< feature-state for_k8s_version="v1.12" state="alpha" >}} + + +自动清理已完成 Job (状态为 `Complete` 或 `Failed`)的另一种方式是使用由 +[TTL 控制器](/zh/docs/concepts/workloads/controllers/ttlafterfinished/)所提供 +的 TTL 机制。 +通过设置 Job 的 `.spec.ttlSecondsAfterFinished` 字段,可以让该控制器清理掉 +已结束的资源。 + +TTL 控制器清理 Job 时,会级联式地删除 Job 对象。 +换言之,它会删除所有依赖的对象,包括 Pod 及 Job 本身。 +注意,当 Job 被删除时,系统会考虑其生命周期保障,例如其 Finalizers。 + +例如: + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: pi-with-ttl +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: pi + image: perl + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + restartPolicy: Never +``` + + +Job `pi-with-ttl` 在结束 100 秒之后,可以成为被自动删除的标的。 + +如果该字段设置为 `0`,Job 在结束之后立即成为可被自动删除的对象。 +如果该字段没有设置,Job 不会在结束之后被 TTL 控制器自动清除。 + +注意这种 TTL 机制仍然是一种 Alpha 状态的功能特性,需要配合 `TTLAfterFinished` +特性门控使用。有关详细信息,可参考 +[TTL 控制器](/zh/docs/concepts/workloads/controllers/ttlafterfinished/)的文档。 + + +## Job 模式 {#job-patterns} + +Job 对象可以用来支持多个 Pod 的可靠的并发执行。 +Job 对象不是设计用来支持相互通信的并行进程的,后者一般在科学计算中应用较多。 +Job 的确能够支持对一组相互独立而又有所关联的 *工作条目* 的并行处理。 +这类工作条目可能是要发送的电子邮件、要渲染的视频帧、要编解码的文件、NoSQL +数据库中要扫描的主键范围等等。 + + +在一个复杂系统中,可能存在多个不同的工作条目集合。这里我们仅考虑用户希望一起管理的 +工作条目集合之一 — *批处理作业*。 + +并行计算的模式有好多种,每种都有自己的强项和弱点。这里要权衡的因素有: + + +- 每个工作条目对应一个 Job 或者所有工作条目对应同一 Job 对象。 + 后者更适合处理大量工作条目的场景; + 前者会给用户带来一些额外的负担,而且需要系统管理大量的 Job 对象。 +- 创建与工作条目相等的 Pod 或者令每个 Pod 可以处理多个工作条目。 + 前者通常不需要对现有代码和容器做较大改动; + 后者则更适合工作条目数量较大的场合,原因同上。 +- 有几种技术都会用到工作队列。这意味着需要运行一个队列服务,并修改现有程序或容器 + 使之能够利用该工作队列。 + 与之比较,其他方案在修改现有容器化应用以适应需求方面可能更容易一些。 + + +下面是对这些权衡的汇总,列 2 到 4 对应上面的权衡比较。 +模式的名称对应了相关示例和更详细描述的链接。 + +| 模式 | 单个 Job 对象 | Pods 数少于工作条目数? | 直接使用应用无需修改? | 在 Kube 1.1 上可用?| +| ----- |:-------------:|:-----------------------:|:---------------------:|:-------------------:| +| [Job 模版扩展](/zh/docs/tasks/job/parallel-processing-expansion/) | | | ✓ | ✓ | +| [每工作条目一 Pod 的队列](/zh/docs/tasks/job/coarse-parallel-processing-work-queue/) | ✓ | | 有时 | ✓ | +| [Pod 数量可变的队列](/zh/docs/tasks/job/fine-parallel-processing-work-queue/) | ✓ | ✓ | | ✓ | +| 静态工作分派的单个 Job | ✓ | | ✓ | | + + +当你使用 `.spec.completions` 来设置完成数时,Job 控制器所创建的每个 Pod +使用完全相同的 [`spec`](https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status)。 +这意味着任务的所有 Pod 都有相同的命令行,都使用相同的镜像和数据卷,甚至连 +环境变量都(几乎)相同。 +这些模式是让每个 Pod 执行不同工作的几种不同形式。 + +下表显示的是每种模式下 `.spec.parallelism` 和 `.spec.completions` 所需要的设置。 +其中,`W` 表示的是工作条目的个数。 + +| 模式 | `.spec.completions` | `.spec.parallelism` | +| ----- |:-------------------:|:--------------------:| +| [Job 模版扩展](/zh/docs/tasks/job/parallel-processing-expansion/) | 1 | 应该为 1 | +| [每工作条目一 Pod 的队列](/zh/docs/tasks/job/coarse-parallel-processing-work-queue/) | W | 任意值 | +| [Pod 个数可变的队列](/zh/docs/tasks/job/fine-parallel-processing-work-queue/) | 1 | 任意值 | +| 基于静态工作分派的单一 Job | W | 任意值 | + + +## 高级用法 {#advanced-usage} + +### 指定你自己的 Pod 选择算符 {#specifying-your-own-pod-selector} + +通常,当你创建一个 Job 对象时,你不会设置 `.spec.selector`。 +系统的默认值填充逻辑会在创建 Job 时添加此字段。 +它会选择一个不会与任何其他 Job 重叠的选择算符设置。 + +不过,有些场合下,你可能需要重载这个自动设置的选择算符。 +为了实现这点,你可以手动设置 Job 的 `spec.selector` 字段。 + + +做这个操作时请务必小心。 +如果你所设定的标签选择算符并不唯一针对 Job 对应的 Pod 集合,甚或该算符还能匹配 +其他无关的 Pod,这些无关的 Job 的 Pod 可能会被删除。 +或者当前 Job 会将另外一些 Pod 当作是完成自身工作的 Pods, +又或者两个 Job 之一或者二者同时都拒绝创建 Pod,无法运行至完成状态。 +如果所设置的算符不具有唯一性,其他控制器(如 RC 副本控制器)及其所管理的 Pod +集合可能会变得行为不可预测。 +Kubernetes 不会在你设置 `.spec.selector` 时尝试阻止你犯这类错误。 + + +下面是一个示例场景,在这种场景下你可能会使用刚刚讲述的特性。 + +假定名为 `old` 的 Job 已经处于运行状态。 +你希望已有的 Pod 继续运行,但你希望 Job 接下来要创建的其他 Pod +使用一个不同的 Pod 模版,甚至希望 Job 的名字也发生变化。 +你无法更新现有的 Job,因为这些字段都是不可更新的。 +因此,你会删除 `old` Job,但 _允许该 Job 的 Pod 集合继续运行_。 +这是通过 `kubectl delete jobs/old --cascade=false` 实现的。 +在删除之前,我们先记下该 Job 所使用的选择算符。 + +```shell +kubectl get job old -o yaml +``` + +输出类似于: + +``` +kind: Job +metadata: + name: old + ... +spec: + selector: + matchLabels: + controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002 + ... +``` + + +接下来你会创建名为 `new` 的新 Job,并显式地为其设置相同的选择算符。 +由于现有 Pod 都具有标签 `controller-uid=a8f3d00d-c6d2-11e5-9f87-42010af00002`, +它们也会被名为 `new` 的 Job 所控制。 + +你需要在新 Job 中设置 `manualSelector: true`,因为你并未使用系统通常自动为你 +生成的选择算符。 + +``` +kind: Job +metadata: + name: new + ... +spec: + manualSelector: true + selector: + matchLabels: + controller-uid: a8f3d00d-c6d2-11e5-9f87-42010af00002 + ... +``` + + +新的 Job 自身会有一个不同于 `a8f3d00d-c6d2-11e5-9f87-42010af00002` 的唯一 ID。 +设置 `manualSelector: true` 是在告诉系统你知道自己在干什么并要求系统允许这种不匹配 +的存在。 + + +## 替代方案 {#alternatives} + +### 裸 Pod {#bare-pods} + +当 Pod 运行所在的节点重启或者失败,Pod 会被终止并且不会被重启。 +Job 会重新创建新的 Pod 来替代已终止的 Pod。 +因为这个原因,我们建议你使用 Job 而不是独立的裸 Pod, +即使你的应用仅需要一个 Pod。 + + +### 副本控制器 {#replication-controller} + +Job 与[副本控制器](/docs/user-guide/replication-controller)是彼此互补的。 +副本控制器管理的是那些不希望被终止的 Pod (例如,Web 服务器), +Job 管理的是那些希望被终止的 Pod(例如,批处理作业)。 + +正如在 [Pod 生命期](/zh/docs/concepts/workloads/pods/pod-lifecycle/) 中讨论的, +`Job` 仅适合于 `restartPolicy` 设置为 `OnFailure` 或 `Never` 的 Pod。 +注意:如果 `restartPolicy` 未设置,其默认值是 `Always`。 + + +### 单个 Job 启动控制器 Pod + +另一种模式是用唯一的 Job 来创建 Pod,而该 Pod 负责启动其他 Pod,因此扮演了一种 +后启动 Pod 的控制器的角色。 +这种模式的灵活性更高,但是有时候可能会把事情搞得很复杂,很难入门, +并且与 Kubernetes 的集成度很低。 + + +这种模式的实例之一是用 Job 来启动一个运行脚本的 Pod,脚本负责启动 Spark +主控制器(参见 [Spark 示例](https://github.com/kubernetes/examples/tree/{{< param "githubbranch" >}}/staging/spark/README.md)), +运行 Spark 驱动,之后完成清理工作。 + +这种方法的优点之一是整个过程得到了 Job 对象的完成保障, +同时维持了对创建哪些 Pod、如何向其分派工作的完全控制能力, + + +## Cron Jobs {#cron-jobs} + +你可以使用 [`CronJob`](/zh/docs/concepts/workloads/controllers/cron-jobs/) +创建一个在指定时间/日期运行的 Job,类似于 UNIX 系统上的 `cron` 工具。 +