Job负责批处理任务,即只执行一次的任务,它保证批处理任务的一个或多个Pod

说明:

  • spce.template格式同Pod
  • RestartPolicy仅支持Never或OnFailur
  • 单个Pod时,默认Pod成功运行后Job即结束
  • spec.completions标志Job结束需要成功运行的Pod个数,默认为1
  • spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试


Job

准备

通过计算π来演示job

计算2000位的pi,然后发送给指定web服务器

main.sh

pi=$(echo "scale=2000; 4*a (1)" | bc -l)
pi=$(echo $pi| sed 's/\\ //g')
curl http://192.168.0.221:5000/pi?pi=$pi

创建包含bc命令与curl命令。以及main.sh脚本的镜像并推送到自有仓库

dockerfile

FROM ubuntu:22.04
RUN apt update && apt install -y curl bc
WORKDIR /tmp
COPY main.sh /tmp/main.sh
ENTRYPOINT ["/bin/sh", "/tmp/main.sh"]

构建并推送到自有仓库

docker build -t harbor.tangotz.com/os/ubutnu-pi:0.1 .
docker push harbor.tangotz.com/os/ubutnu-pi:0.1

构建job资源文件

j.yml

apiVersion: batch/v1
kind: Job
metadata:
  name: gen-pi
spec:
  template:
    metadata:
      name: pi
    spec:
      containers:
       - name: pi-pod
         image:  harbor.tangotz.com/os/ubutnu-pi:0.2
         lifecycle:
           postStart:
             exec:
               command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/start"]
           preStop:
             exec:
               command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/stop"]

      restartPolicy: Never

这里添加了lifecycle。在Job资源中,会有非预期结果。为了验证lifecycle,这里用一个简单的flask应用来处理http请求

app.py

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/pi', methods=['GET'])
def pi_endpoint():
    pi = request.args.get('pi', None)

    # 返回响应]
    print(pi)
    return 'pi OK'

@app.route('/start', methods=['GET'])
def start_endpoint():
    name = request.args.get('name', None)

    # 返回响应]
    print(name)
    return 'start OK'


@app.route('/stop', methods=['GET'])
def stop_endpoint():
    name = request.args.get('name', None)

    # 返回响应]
    print(name)
    return 'stop OK'


if __name__ == '__main__':
    app.run("0.0.0.0", debug=True)

验证

运行flask应用

python3 app.py

创建Job

kubectl apply -f j.yml

此时观察app.py输出结果,可以看到定义的postStart钩子可以正常运行,稍后pi数值也会输出,但是没有preStop钩子的运行结果。

从0开始搞K8S:Job CronJob_cronjob

预期外

Job 控制器的主要目的是成功完成指定的任务(即Pod中的容器)。一旦Pod成功完成其任务(即主容器退出码为0),Job就会认为该任务已完成,并且不会重新启动Pod(即使设置了 restartPolicy: Never 也一样,因为这个设置是在Pod层面,用于定义容器退出后Pod是否重启)。因此,preStop 钩子可能在Pod结束前不会执行,因为它预期在容器正常终止之前执行,但Job的Pod通常只运行到任务完成。


另外,如果job运行时间很短,比如只计算10位的圆周率,postStart和preStop都会出错,由于 Job 任务运行得很快,有可能 postStart 和 preStop 钩子尝试在容器还没有完全启动或立即要停止的时候运行,这可能导致它们无法正确执行。

从0开始搞K8S:Job CronJob_job_02


CronJob

管理基于时间的Job,即:

  • 在给定时间只运行一次
  • 周期性的在给定时间运行

说明:

  • spec.template格式同Pod
  • RestartPlicy仅支持Never和OnFailure
  • 单个pod时,默认Pod成功后Job即结束
  • .spec.completions标志Jpb结束需要成功运行的Pod个数,默认位1
  • .spec.parallelism标志并行运行的Pod的个数,默认位1
  • spec.actibeDeadlinesSeconds标志失败Pod的重试最大时间, 超过这个时间不会继续重试。

典型用法

  • 在给定时间调度Job运行
  • 创建周期性运行的Job,例如数据库备份、发送邮件

资源清单关键属性

  • .spec.schedule 调度 指定运行周期,格式同cron
  • .spec.jobTemplate Job模板,格式同Job
  • .spec.startingDeadlineSecond 启动job的期限(秒级)。默认为无限制。若错过调度时间,则认为job失败
  • .spec.concurrentyPolicy 并发策略。用于指定处理被CronJob创建的Job并发执行的策略

Allow(默认):允许并发运行Job

Forbid :禁止并发 ,如果前一个没有完成,则直接跳过下一个

Replace:取消当前正在运行的Job,用一个新的来替换

示例

设定一个每分钟发送时间的CronJob

与上文类似,设定一个

main.sh

t=$(date +"%Y-%m-%d_%H:%M:%S")
curl http://192.168.0.221:5000/time?time=$t

生成镜像

dockerfile

FROM ubuntu:22.04
RUN apt update && apt install -y curl bc
WORKDIR /tmp
COPY main.sh /tmp/main.sh
ENTRYPOINT ["/bin/sh", "/tmp/main.sh"]

推送到harbor

docker build -t harbor.tangotz.com/os/u-time:0.1 .
docker push harbor.tangotz.com/os/u-time:0.1

编写资源文件

cj.yml

apiVersion: batch/v1
kind: CronJob
metadata:
  name: time
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: timer
            image:  harbor.tangotz.com/os/u-time:0.1
            lifecycle:
              postStart:
                exec:
                  command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/start"]
              preStop:
                exec:
                  command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/stop"]
          restartPolicy: Never

观察结果

每分钟都会发送一个请求

从0开始搞K8S:Job CronJob_job_03

但是也有一个小问题,通过CronJob调用Job,再由Job生成的Pod并不会在任务结束后就立刻清理

从0开始搞K8S:Job CronJob_cronjob_04

在使用 CronJob 生成的 Job 时,Pod 的存活时间并不是直接由 CronJob 控制的,而是由 Job 的配置和 Kubernetes 的默认行为共同决定的。Pod 的存活时间通常取决于以下几个因素:

  1. Job 的完成状态:如果 Job 成功完成(即 .spec.completions 指定的 Pod 数量都成功执行并退出),那么这些 Pod 通常会在短时间内被自动删除。Kubernetes 会监控这些 Pod 的状态,一旦 Job 被标记为完成(即所有 Pod 都成功完成或失败达到了一定的次数限制),它就会开始清理过程。
  2. ttlSecondsAfterFinished:这是 Kubernetes 1.12+ 版本中引入的一个特性,允许指定 Job 完成后 Pod 保持存活的秒数。如果设置了这个字段,Pod 会在指定的时间后自动被删除。但默认情况下,这个字段是不设置的,因此 Pod 可能会根据 Kubernetes 的垃圾收集机制来决定何时被删除。
  3. 垃圾收集机制:Kubernetes 的节点控制器(Node Controller)和 API 服务器(API Server)会定期清理不再需要的 Pod。这些 Pod 可能是已经完成的 Job 的一部分,或者是因为其他原因(如节点故障)而不再需要的。但是,这个清理过程并不是实时的,可能会有一定的延迟。
  4. 其他因素:Pod 的存活时间还可能受到其他因素的影响,如网络延迟、API 服务器的负载等。这些因素都可能导致 Pod 的删除操作被延迟。


如果希望更精确地控制 Pod 的存活时间,可以考虑在 Job 的配置中设置 ttlSecondsAfterFinished 字段。这样,就可以指定一个明确的时间段,在 Job 完成后自动删除 Pod。请注意,这个字段是在 Kubernetes 1.12+ 版本中引入的,因此请确保集群版本支持这个特性。

存活时间

根据上文添加存活时间参数

apiVersion: batch/v1
kind: CronJob
metadata:
  name: time
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      ttlSecondsAfterFinished: 30  # 设置Job完成后Pod的存活时间为30秒  
      template:
        spec:
          containers:
          - name: timer
            image:  harbor.tangotz.com/os/u-time:0.1
            lifecycle:
              postStart:
                exec:
                  command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/start"]
              preStop:
                exec:
                  command: ["/bin/sh", "-c", "curl 192.168.0.221:5000/stop"]
          restartPolicy: Never

再次查看pod,可以看到大约是在32s的时候pod被清理

从0开始搞K8S:Job CronJob_job_05