Job是什么?

对于ReplicaSet、ReplicationController等类型的控制器而言,它希望pod保持预期数目、持久运行下去,除非用户明确删除,否则这些对象一直存在,它们针对的是耐久性任务,如web服务等。对于非耐久性任务,比如压缩文件,任务完成后,pod需要结束运行,不需要pod继续保持在系统中,这个时候就要用到Job。因此说Job是对ReplicaSet、ReplicationController等持久性控制器的补充。

Job与其它控制器的细微差别

Job定义方法与ReplicaSet等控制器相似,只有细微差别,如下:

  1. Job中的restart policy必需是"Never"或者"OnFailure",这个很好理解,因为pod要运行到结束,而不是反复重新启动。
  2. Job不需要选择器,其中的pod也不需要标签,系统在创建Job时会自动添加相关内容。当然用户也可以出于资源组织的目的添加标签,但这个与Job本身的实现没有关系。
  3. Job新增加两个字段:.spec.completions、.spec.parallelism。详细用法在示例中说明。
  4. backoffLimit字段:示例中说明

示例1:非并发Job

非并发Job的含义是,Job启动后,只运行一个pod,pod运行结束后整个Job也就立刻结束。

以下是简单的Job配置文件,只包含一个pod,输出圆周率小数点后2000位,运行时间大概为10s:

apiVersion: batch/v1
kind: Job
metadata:name: pi
spec:template:spec:containers:- name: piimage: perlcommand: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]restartPolicy: NeverbackoffLimit: 4

以上示例无需设置选择器、pod标签。无需设置.spec.completions、.spec.parallelism,这两个字段的默认值都是1。backoffLimit=4,表示允许pod失败的次数。将以上内容保存成文件并创建Job:

$ kubectl create -f https://k8s.io/examples/controllers/job.yaml
job "pi" created

确认Job状态:

$ kubectl describe jobs/pi
Name:             pi
Namespace:        default
Selector:         controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495
Labels:           controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495job-name=pi
Annotations:      <none>
Parallelism:      1
Completions:      1
Start Time:       Tue, 07 Jun 2016 10:56:16 +0200
Pods Statuses:    0 Running / 1 Succeeded / 0 Failed
Pod Template:Labels:       controller-uid=b1db589a-2c8d-11e6-b324-0209dc45a495job-name=piContainers:pi:Image:      perlPort:Command:perl-Mbignum=bpi-wleprint bpi(2000)Environment:        <none>Mounts:             <none>Volumes:              <none>
Events:FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message---------    --------    -----    ----            -------------    --------    ------            -------1m           1m          1        {job-controller }                Normal      SuccessfulCreate  Created pod: pi-dtn4q

从以上输出可以看到系统自动添加的Selector、Pod labels。注意Events的输出,全程只创建了一个pod。

列出Job的所有pod:

$ pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath={.items..metadata.name})
$ echo $pods
pi-aiw0a

查看pod输出:

$ kubectl logs $pods
3.14159265358979323846264......

以上是最简单的Job应用示例,输出圆周率小数点后2000位。但是,考虑另外一种情况,假如我们需要计算圆周率小数点后3000、4000、5000位怎么办?一种方案是先将上例中的2000改成3000,创建并运行。然后再改成4000,再创建并运行,一直到5000。显然这种方案并不高明:麻烦、资源利用率低。另外一种方法是同时创建4个Job,分别计算2000、3000、4000、5000。注意4个Job的name字段不能冲突分别是pi-2000、pi-3000、pi-4000、pi-5000,
文件名分别为pi-2000.yaml、pi-3000.yaml、pi-4000.yaml、pi-5000.yaml,并保存在目录/tmp/pi下。利用kubectl对目录的支持一次性创建4个Job:

$ kubectl create -f /tmp/pi
job "pi-2000" created
job "pi-3000" created
job "pi-4000" created
job "pi-5000" created

以上方法是伪并发,只适用于任务量少的情况。假如我们需要处理的任务是从pi-1到pi-10000,那么以上方法就不适用了:

  1. 需要同时创建太多的Job与pod,不好管理。
  2. 资源配额限制。

示例2:粗并发Job

本例创建一个Job,但Job要创建多个pod。了解完示例后就明白为什么叫“粗并发”。

本示例需要一个消息队列服务的配合,不详细描述如何部署、填充消息队列服务。假设我们有一个RabbitMQ服务,集群内访问地址为:amqp://guest:guest@rabbitmq-service:5672。其有一个名为job1的队列,队列内有apple banana cherry date fig grape lemon melon共8个成员。

另外假设我们有一个名为gcr.io/<project>/job-wq-1的image,其功能是从队列中读取出一个元素并打印到标准输出,然后结束。注意,它只处理一个元素就结束了。接下来创建如下Job:

apiVersion: batch/v1
kind: Job
metadata:name: job-wq-1
spec:completions: 8parallelism: 2template:metadata:name: job-wq-1spec:containers:- name: cimage: gcr.io/<project>/job-wq-1env:- name: BROKER_URLvalue: amqp://guest:guest@rabbitmq-service:5672- name: QUEUEvalue: job1restartPolicy: OnFailure

上例中,completions的值为8,等于job1队列中元素的个数。因为每个成功的pod处理一个元素,所以需要成功8次,job1中的所有成员就会被处理完成。在粗并发模式下,completions的值必需指定,否则其默认值为1,整个Job只处理一个成员就结束了。

上例中,parallelism的值是2。虽然需要pod成功8次,但在同一时间,只允许有两个pod并发。一个成功结束后,再启动另一个。这个参数的主要目的是控制并发pod的个数,可根据实际情况调整。当然可以不指定,那么默认的并发个数就是1。

env中的内容告诉image如何访问队列。

将以上内容保存在job.yaml文件中,运行Job:

kubectl create -f ./job.yaml

稍等片刻Job运行完成,查看结果:

$ kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11fjob-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2017 16:42:02 +0800
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11fjob-name=job-wq-1Containers:c:Image:      gcr.io/causal-jigsaw-637/job-wq-1Port:Environment:BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672QUEUE:            job1Mounts:             <none>Volumes:              <none>
Events:FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam527s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

查看Events,可以看到总共创建了8个pod。在本例中,每处理队列中的一个成员都需要创建一个pod,开销很大。如果队列中的成员个数非常庞大,那么这种处理方式就不适用。我们希望少创建pod、每个pod能处理多条记录,请看示例3。

示例3:细并发Job

并发处理都需要某种队列作为协调工具,本例换成redis,同样如何部署、填充redis队列不描述。只需要明确redis有一个job2队列,其内有如下9个成员:

redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

接下来创建image,详细过程不描述。只需确定这个image运行如下名为worker.py的python程序:

#!/usr/bin/env pythonimport time
import rediswqhost="redis"
# Uncomment next two lines if you do not have Kube-DNS working.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")q = rediswq.RedisWQ(name="job2", host="redis")
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():item = q.lease(lease_secs=10, block=True, timeout=2) if item is not None:itemstr = item.decode("utf=8")print("Working on " + itemstr)time.sleep(10) # Put your actual work here instead of sleep.q.complete(item)else:print("Waiting for work")
print("Queue empty, exiting")

首先连接到redis的job2队列。然后是一个while循环,每次读job2中的一条记录并输出,然后sleep 10s。循环退出的条件是job2队列为空。这个image与示例2不同,示例2只处理一条记录这就结束,而这个可以处理多条一直到队列为空。

接下来定义Job:

apiVersion: batch/v1
kind: Job
metadata:name: job-wq-2
spec:parallelism: 2template:metadata:name: job-wq-2spec:containers:- name: cimage: gcr.io/myproject/job-wq-2restartPolicy: OnFailure

上例中,无需像示例2一样指定 completions的值,因为结束条件是job2为空,已经内嵌在image的逻辑中。parallelism=2表示可以并发两个pod,不设置默认为1,在实际应用中可据实际情况自行调整。

运行Job:

kubectl create -f ./job.yaml

过一会查看Job运行状况:

$ kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11fjob-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2016 17:07:59 -0800
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11fjob-name=job-wq-2Containers:c:Image:              gcr.io/exampleproject/job-wq-2Port:Environment:        <none>Mounts:             <none>Volumes:              <none>
Events:FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message---------    --------    -----    ----            -------------    --------    ------            -------33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

虽然允许的最大并发数是2,但Events显示只创建成功一个pod,这个是正常情况,最大并非必需,可能系统因为资源问题达不到最大。

查看pod输出:

$ kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

细并发相比与粗并发,减少了创建pod的开销,使每个pod能处理多条记录,但是pod要自己决定退出条件,如果不退出,那么Job永远无法结束。

关于资源回收

Job创建的pod在结束运行后,无论是成功还是失败,不会默认删除,仍然保留在系统中,这样用户才可以查看其日志、状态信息、排除错误。用户需要手动运行kubectl delete删除所有运行结束的pod,为了方便组织资源,一次性删除会部pod,可以被pod自定义标签。Job在运行完成后也仍然保留在系统中,由用户删除。所以使用Job,用户应注意资源回收,避免资源被耗尽。

参考:https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/

Kubernetes之Job相关推荐

  1. Kubernetes 中 设置pod不部署在同一台节点上

    在k8s中,节点的调度主要由亲和性和污点来进行控制的.   而在亲和性部分由分为了节点亲和性和节点反亲和性.   节点亲和性是指在pod部署时,尽量(软策略)或者必须满足(硬策略)部署在某些节点上. ...

  2. 【CentOS】利用Kubeadm部署Kubernetes (K8s)

    [CentOS]利用Kubeadm部署Kubernetes (K8s)[阅读时间:约10分钟] 一.概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 三.具体实验流程 1 系统准 ...

  3. 【Kubernetes】如何使用Kubeadm部署K8S集群

    一 . 准备机器 本次环境采用华为云ECS弹性云服务器部署(也可以使用VMware) vm01(2V4G): Ubuntu_18.04作为K8S master节点 vm02(1V1G): Ubuntu ...

  4. 自定义Kubernetes调度程序来编排高可用性应用程序

    自定义Kubernetes调度程序来编排高可用性应用程序 只要愿意遵守规则,在Kubernetes上进行部署和乘飞机旅行就可以很愉快.通常,事情会"正常工作".但是,如果有兴趣与必 ...

  5. Kubernetes 网络排错指南

    本文介绍各种常见的网络问题以及排错方法,包括 Pod 访问异常.Service 访问异常以及网络安全策略异常等. 说到 Kubernetes 的网络,其实无非就是以下三种情况之一 Pod 访问容器外部 ...

  6. 使用Netsil监控Kubernetes上的微服务

    ubernetes是容器编排和调度领域的王者,它击败了竞争对手Docker Swarm和Apache Mesos,开启了闪耀的未来,微服务可以自修复,可以自动扩展,可以跨zone,region甚至跨云 ...

  7. e.V4p.C0/index.php,php-fpm进程在Kubernetes中接收SIGKILL信号

    我已经在其中配置了Nginx,PHP和php-fpm创建了ubuntu docker镜像 . 当我在Docker实例上运行它时工作正常 . 但是当我在kubernetes中运行相同的图像时,php-f ...

  8. kubectl常用命令_《蹲坑学kubernetes》之十五:kubectl命令详解

    kubectl用于运行Kubernetes集群命令的管理工具.本章节主要讲了kubectl基本语法和使用方法.在以后的实际工作中,使用越来越多,也会越来越熟悉. 1.kubectl语法 kubectl ...

  9. K8S - Kubernetes简介

    Kubernetes Kubernetes(简称K8s,用8代替8个字符"ubernete")是Google开源的一个容器编排引擎,支持自动化部署.大规模可伸缩.应用容器化管理. ...

  10. Kubernetes 架构(下)【转】

    上一节我们讨论了 Kubernetes 架构 Master 上运行的服务,本节讨论 Node 节点. Node 是 Pod 运行的地方,Kubernetes 支持 Docker.rkt 等容器 Run ...

最新文章

  1. 人工智能:看看BAT三巨头怎么说
  2. [学习笔记]电磁场与电磁波
  3. 算法设计与分析——回溯法——装载问题
  4. poj 2828 线段树
  5. SENTAURUS_2018_06
  6. html点击复制一段文字内容,js实现点击按钮复制文本功能
  7. 论文解读笔记:基于深度学习的行为分析综述
  8. 理财十问:1.你知道自己的风险偏好吗?
  9. Android持久化存储——(包含操作SQLite数据库)
  10. 双非二本计算机学生是应该考研还是就业
  11. 阚俊青少机器人_长春中医药大学附属医院、中医学院阚俊明副书记一行调研我校中医学类学生培养工作...
  12. 【翻译】GRAIL-手写识别 1
  13. 垃圾收集器总结--CMS垃圾收集器
  14. Python练手小项目(11)用户名密码验证的初步探索
  15. Python采集全国各地百度地图上店铺POI数据(母婴、美食等)
  16. 各行业商业数据分析报告网站汇总
  17. 通达OA应用中心操作手册
  18. 蓝色巨人(上)——IBM公司发展史
  19. 【沐风老师】3DMAX地板生成器插件FloorGenerator使用教程
  20. 2D和3D游戏动作区分

热门文章

  1. AVA设计模式初探之组合模式
  2. 人脸识别实名认证在景区门禁系统中的应用
  3. Python爬虫是什么?怎么分辨善意爬虫跟恶意爬虫?
  4. 程序猿生存指南-22 分手快乐
  5. anaconda下终止命令执行的快捷键
  6. 第二题:编写程序,将华氏度转换为摄氏度
  7. leetcode_977
  8. 2022版 Tangible Software Solutions 功能齐全的源代码转换器
  9. 龙芯智龙开发板:驱动步进电机实战入门
  10. 2021/12/14 nginx包下载安装步骤记录