乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

  • DevOps

  • Kubernetes

    • 什么是云原生?
    • Docker容器技术
    • Kubernetes简介
    • Kubernetes核心对象
    • Kubernetes集群管理
    • Kubernetes权限管理
    • Kubernetes工作负载管理
      • Deployment
        • 水平扩展/收缩
        • 1、通过 kubectl scale 命令进行扩缩
        • 2、通过 kubectl edit 直接编辑 Deployment
        • 3、通过修改本地 YAML 文件,使用 kubectl apply 更新
        • 滚动更新/回滚
        • 滚动更新
        • 回滚
        • 总结
      • DaemonSet
      • Job/CronJob
        • Job
        • 基本操作
        • 并行控制
        • CronJob
      • 最后
    • Kubernetes调度管理
    • Kubernetes应用质量管理
    • Kubernetes数据持久化管理
    • Kubernetes应用访问管理
    • Kubernetes应用配置管理
    • Kubernetes有状态应用管理
    • Kubernetes 网络管理
    • Helm 应用包管理
  • Prometheus

  • ELK

  • 专栏
  • Kubernetes
乔克
2025-07-19
目录

Kubernetes工作负载管理

在 Kubernetes 中,Pod 是最小的管理单元,是一组紧密关联的容器组合。

但是,单独的 Pod 并不能保障总是可用,比如我们创建一个 nginx 的 Pod,因为某些原因,该 Pod 被意外删除,我们希望其能够自动新建一个同属性的 Pod。很遗憾,单纯的 Pod 并不能满足需求。

为此,Kubernetes 实现了一系列控制器来管理 Pod,使 Pod 的期望状态和实际状态保持一致。目前常用的控制器有:

  • Deployment
  • StatefulSet
  • DaemonSet
  • Job/CronJob

这里只介绍 Deployment、DaemonSet、Job/CronJob。StatefulSet 留到后面Kubernetes有状态应用管理章节再来介绍,因为它涉及到很多其他的知识点,比如 Service、PV/PVC,等这些知识点介绍完成过后再来说 StatefulSet 要好一点。

# Deployment

在说 Deployment 之前,先来了解一下 ReplicaSet(RS)。

在 Kubernetes 初期,是使用 RC(Replication Controller)来控制 Pod,保证其能够按照用户的期望运行,但是后面因为各种原因淘汰了 RC,转而使用 RS 来替代它。从功能上看 RC 和 RS 没多大的变化,唯一的区别 RS 支持集合的 Selector,可以方便定义更复杂的条件。

我们可以定义一个简单的 ReplicaSet 来感受一下:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-set
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

创建结果如下:

kubectl get po
NAME              READY   STATUS              RESTARTS   AGE
nginx-set-hmtq4   0/1     ContainerCreating   0          2s
nginx-set-j2jpr   0/1     ContainerCreating   0          2s
kubectl get rs
NAME        DESIRED   CURRENT   READY   AGE
nginx-set   2         2         0       5s
1
2
3
4
5
6
7

可以看到我们期望replicas: 2创建 2 个 Pod,所以通过kubectl get pod的时候可以看到有 2 两个 Pod 正在创建,这时候如果我们删除一个 Pod,RS 会立马给我们重新拉一个 Pod,以满足我们的期望。

不过,在实际中很少去直接使用 RS,而是使用 Deployment。Deployment 是比 RS 更高层的资源对象,它会去控制管理 RS,如下:

705510741755217e8112be06e247f2b0 MD5

从上图可以看到 Deployment、ReplicaSet、Pod 它们以层层控制关系,Deployment 可以拥有多个 ReplicaSet,一个 ReplicaSet 可以拥有多个 Pod。一个 Deployment 拥有多个 ReplicaSet 主要是为了支持回滚操作,每当操作 Deployment 的时候,就会生成一个新的 ReplicaSet,然后逐步更新新的 Pod,而老的 ReplicaSet 会逐步减少 Pod 直到新的 ReplicaSet 全部接管。这时候并不会删除老的 ReplicaSet,系统会将其保存下来,以备回滚使用。

ReplicaSet 还负责通过"控制器模式",保证系统的 Pod 数永远等于期望数,这也是 Deployment 只允许 restartPolicy=Always 的原因:只有在容器能保证自己始终处于 running 状态,通过 ReplicaSet 调整 Pod 的数量才有意义。

而在此基础上,Deployment 同样通过"控制器模式",来操作 ReplicaSet 的个数和属性,进而实现水平扩展/收缩和滚动更新这两个动作。其中水平扩展和收缩非常容易实现,Deployment Controller 只需要修改它的 ReplicaSet 的 Pod 副本数就可以了。

创建一个 Deployment 的清单如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

启动过后可以看到如下信息:

kubectl get deployments.apps
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           19s
kubectl get rs
NAME                         DESIRED   CURRENT   READY   AGE
nginx-deployment-8f458dc5b   3         3         3       21s
kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-8f458dc5b-8nn5c   1/1     Running   0          24s
nginx-deployment-8f458dc5b-hxc57   1/1     Running   0          24s
nginx-deployment-8f458dc5b-znrff   1/1     Running   0          24s
1
2
3
4
5
6
7
8
9
10
11

从上面信息可知,如果创建一个 Deployment 对象,会自动创建一个 RS 对象,然后通过 RS 对象创建对应的 Pod 数。

# 水平扩展/收缩

上面我们创建一个 3 副本的 Pod,如果现在需要对其进行扩展/收缩,则可以通过以下三种方式:

  • kubectl scale 命令
  • kubectl edit 运行中的 Deployment
  • 通过修改 YAML 清单,然后使用 kubectl apply 进行更新

具体采用哪种方式根据情况而定。

# 1、通过 kubectl scale 命令进行扩缩

扩展和收缩的命令是一样,扩展就是增加副本数,收缩就是减少副本数。

(1)扩展

我们现在有 3 个副本,如果想要 4 个副本,则使用以下命令:

kubectl scale deployment nginx-deployment --replicas 4
deployment.apps/nginx-deployment scaled
1
2

可以看到 Pod 数变成了 4 个。

kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-8f458dc5b-8nn5c   1/1     Running   0          8m3s
nginx-deployment-8f458dc5b-cv6mw   1/1     Running   0          29s
nginx-deployment-8f458dc5b-hxc57   1/1     Running   0          8m3s
nginx-deployment-8f458dc5b-znrff   1/1     Running   0          8m3s
1
2
3
4
5
6

(2)收缩

现在集群里有 4 个副本,如果只想要 2 个副本,则使用如下命令

kubectl scale deployment nginx-deployment --replicas 2
deployment.apps/nginx-deployment scaled
1
2

现在集群里就只有两个 Pod 了。

kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-8f458dc5b-8nn5c   1/1     Running   0          9m36s
nginx-deployment-8f458dc5b-hxc57   1/1     Running   0          9m36s
1
2
3
4

# 2、通过 kubectl edit 直接编辑 Deployment

我们也可以直接通过 kubectl edit 直接编辑运行中的 Deployment,修改其副本数,如下:

kubectl edit deployments.apps nginx-deployment -oyaml
1

编辑界面如下:

d03a405d9bc6ae1191c1a64e232890f8 MD5

修改过后使用:wq保存退出,可以看到副本数又变成 4 个了。

kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-8f458dc5b-8nn5c   1/1     Running   0          14m
nginx-deployment-8f458dc5b-hxc57   1/1     Running   0          14m
nginx-deployment-8f458dc5b-mq69h   1/1     Running   0          92s
nginx-deployment-8f458dc5b-xktq2   1/1     Running   0          92s
1
2
3
4
5
6

# 3、通过修改本地 YAML 文件,使用 kubectl apply 更新

我们还可以通过直接修改本地 YAML 的方式扩缩,比如直接在 YAML 文件中将副本数改成 2:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后直接使用kubectl apply -f nginx.yaml部署即可。

# 滚动更新/回滚

业务应用基本都是通过 Deployment 的方式部署在 Kubernetes 中的,应用的更新和回滚是常态的工作,特别是在互联网企业,快速迭代抓住用户的一个重要途径。

但是,并不是每一次的迭代都是 100%正常的,如果异常,如何快速恢复也是要考虑的事情。

为适应这种场景,Deployment 提供滚动更新和快速回滚的能力。

# 滚动更新

Deployment 默认的更新方式就是滚动更新,可以通过strategy.type来指定更新方式。

  • Recreate:先删除所有的 Pod,再创建
  • RollingUpdate:先启动新的 Pod,再替换老的 Pod

如果要更改更新方式,配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
---
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
1
2
3
4
5
6
7
8
9
10
11
12
13

说明:

(1)、maxSurge:定义除了 DESIRED 数量之外,在一次滚动更新过程中,Deployment 还可以创建多少 Pod;

(2)、maxUnavailable:定义在一次滚动更新过程中,Deployment 最多可以删除多少 Pod;

另外,这两个配置还可以通过设置百分值来表示。

一般情况下,我们就保持默认的更新方式即可,这也是在生产中用的比较多的。

现在,来看看滚动更新的效果。首先创建一个 nginx 应用,如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后使用kubectl apply -f deploy.yaml,然后使用kubectl get po -w观察升级效果。

另外开启一个 shell 窗口,使用以下命令更新应用:

kubectl patch deployment nginx-deployment --patch '{"spec": {"template": {"spec": {"containers": [{"name": "nginx","image":"nginx:1.9"}]}}}}'
1

然后可以从另一个窗口查看升级的过程,如下:

kubectl get po -w
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-6c74f576b9-h565l   1/1     Running   0          22s
nginx-deployment-6c74f576b9-k65q6   1/1     Running   0          22s
nginx-deployment-6c74f576b9-qr2xc   1/1     Running   0          22s
nginx-deployment-778d9f5866-n69qd   0/1     Pending   0          0s
nginx-deployment-778d9f5866-n69qd   0/1     Pending   0          0s
nginx-deployment-778d9f5866-n69qd   0/1     ContainerCreating   0          0s
nginx-deployment-778d9f5866-n69qd   0/1     ContainerCreating   0          0s
nginx-deployment-778d9f5866-n69qd   1/1     Running             0          41s
nginx-deployment-6c74f576b9-qr2xc   1/1     Terminating         0          3m23s
nginx-deployment-778d9f5866-42vhv   0/1     Pending             0          0s
nginx-deployment-778d9f5866-42vhv   0/1     Pending             0          0s
nginx-deployment-778d9f5866-42vhv   0/1     ContainerCreating   0          0s
nginx-deployment-778d9f5866-42vhv   0/1     ContainerCreating   0          1s
nginx-deployment-6c74f576b9-qr2xc   1/1     Terminating         0          3m24s
nginx-deployment-6c74f576b9-qr2xc   0/1     Terminating         0          3m24s
nginx-deployment-778d9f5866-42vhv   1/1     Running             0          1s
nginx-deployment-6c74f576b9-k65q6   1/1     Terminating         0          3m24s
nginx-deployment-778d9f5866-tndn8   0/1     Pending             0          0s
nginx-deployment-778d9f5866-tndn8   0/1     Pending             0          0s
nginx-deployment-778d9f5866-tndn8   0/1     ContainerCreating   0          0s
nginx-deployment-6c74f576b9-k65q6   1/1     Terminating         0          3m24s
nginx-deployment-6c74f576b9-qr2xc   0/1     Terminating         0          3m24s
nginx-deployment-6c74f576b9-qr2xc   0/1     Terminating         0          3m24s
nginx-deployment-778d9f5866-tndn8   0/1     ContainerCreating   0          0s
nginx-deployment-6c74f576b9-k65q6   0/1     Terminating         0          3m25s
nginx-deployment-6c74f576b9-k65q6   0/1     Terminating         0          3m25s
nginx-deployment-6c74f576b9-k65q6   0/1     Terminating         0          3m25s
nginx-deployment-778d9f5866-tndn8   1/1     Running             0          1s
nginx-deployment-6c74f576b9-h565l   1/1     Terminating         0          3m25s
nginx-deployment-6c74f576b9-h565l   1/1     Terminating         0          3m25s
nginx-deployment-6c74f576b9-h565l   0/1     Terminating         0          3m26s
nginx-deployment-6c74f576b9-h565l   0/1     Terminating         0          3m26s
nginx-deployment-6c74f576b9-h565l   0/1     Terminating         0          3m26s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

老的版本是nginx-deployment-6c74f576b9-*,新的版本是nginx-deployment-778d9f5866-*,会先创建一个新版本 Pod,再删除老版本 Pod,依次下去直到所有老的版本都被替换掉。

背后的实际逻辑是通过 Deployment 创建一个新的 ReplicaSet,然后通过新的 RS 来创建新的 Pod,可以通过kubectl get rs来查看:

kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6c74f576b9   0         0         0       9m49s
nginx-deployment-778d9f5866   3         3         3       7m7s
1
2
3
4

这种滚动更新的好处是:如果在更新过程中,新版本 Pod 有问题,那么滚动更新就会停止,这时候运维和开发就可以介入查看其原因,由于应用本身还有两个旧版本的 Pod 在线,所以并不会对服务造成太大的影响;当然,这时候应在 Pod 中加上 health check 检查应用的健康状态,而不是简单的依赖容器的 running 状态。为了进一步保证服务的延续性,Deployment Controller 还会确保在任何时间窗口内,只有指定比例的 Pod 处于离线状态,同时它也会确保在任何时间窗口内,只有指定比例的 Pod 被创建,这个比例默认是 DESIRED 的 25%。

当然可以通过修改 Deployment 对象的一个字段 RollingUpdateStrategy 来自定义,比如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
---
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 1
1
2
3
4
5
6
7
8
9
10
11
12
13

说明:

(1)、maxSurge:定义除了 DESIRED 数量之外,在一次滚动更新过程中,Deployment 还可以创建多少 Pod;

(2)、maxUnavailable:定义在一次滚动更新过程中,Deployment 最多可以删除多少 Pod;

另外,这两个配置还可以通过设置百分值来表示。

如此,我们可以得到如下关系图:

747e3868cd1f83edbf9b688588871dfd MD5

Deployment 实际控制的是 ReplicaSet 的数目以及每个 ReplicaSet 的属性。而一个应用版本,对应的就是一个 ReplicaSet,而这个版本应有的 Pod 数量,是通过 ReplicaSet 自己的控制器来管理。

# 回滚

有更新,就有回滚,它们是苦命鸳鸯。

在 Kubernetes 中,回滚使用kubectl rollout命令。在滚动更新的章节,我们更新了 Nginx 应用,现在新版本如果有问题,需要进行回滚操作。

(1)查看可以回滚的历史版本

kubectl rollout history deployment nginx-deployment
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
1
2
3
4
5

发现有两个版本,现在使用的就是 2 版本,我们需要回滚到 1 版本 。

(2)执行以下命令回滚到老版本

kubectl rollout undo deployment nginx-deployment --to-revision 1
deployment.apps/nginx-deployment rolled back
1
2

(3)通过查看 RS,查看是否回滚到老版本

kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6c74f576b9   3         3         3       27m
nginx-deployment-778d9f5866   0         0         0       25m
1
2
3
4

如果可以明确直接回滚到上一个版本,可以直接使用kubectl rollout undo deployment nginx-deployment。

回滚的操作比较简单,但是如果发布比较频繁,历史数据超过一定版本(默认 10 个版本)后,就无法回滚到更老的版本了。

当然,我们可以通过定义spec.revisionHistoryLimit来定义保留多少版本号,默认是 10 个,如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx-deployment
spec:
  progressDeadlineSeconds: 600
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
        - image: nginx:1.8
          imagePullPolicy: IfNotPresent
          name: nginx
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

以上就是 Deployment 的回滚操作,操作命令比较简单,主要是了解到 Kubernetes 为我们提供了这个功能,以备不时之需。

# 总结

从全文可知,Deployment 实际是一个两层控制器:

(1)、它通过 ReplicaSet 的个数来描述应用版本个数;

(2)、它通过 ReplicaSet 的属性来保证 Pod 的副本数;

而且 Deployment 的灵活控制,很方便水平扩展/收缩还有滚动更新以及回滚操作。

# DaemonSet

DaemonSet 保证在每个 Node 上都运行一个 Pod,如果新增一个 Node,这个 Pod 也会运行在新增的 Node 上,如果删除这个 DadmonSet,就会清除它所创建的 Pod。常用来部署一些集群日志收集,监控等全局应用。

常见的场景如下:

1、运行存储集群 daemon,比如 ceph,glusterd 等;

2、运行一个日志收集 daemon,比如 logstash,fluentd 等;

3、运行监控 daemon,比如 Prometheus Node Exporter,collectd,New Relic agent,Ganglia gmond 等;

比如运行一个 filebeat 的 DaemonSet,定义如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: filebeat-ds
  namespace: default
spec:
  selector:
    matchLabels:
      app: filebeat
      role: logstorage
  template:
    metadata:
      labels:
        app: filebeat
        role: logstorage
    spec:
      containers:
        - name: filebeat
          image: ikubernetes/filebeat:5.6.5-alpine
          env:
            - name: REDIS_HOST
              value: redis.default.svc.cluster.local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

执行过后,就可以看到在 kk-node01 节点上运行了 filebeat,如下:

kubectl get po -o wide
NAME                READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
filebeat-ds-kgqcq   1/1     Running   0          28s   172.16.51.212   kk-node01   <none>           <none>
1
2
3

可能有人好奇,集群本身有两个节点,为何只部署了一个 Pod?

那是因为我 master(控制节点)有污点,而我上面的 DaemonSet 没有容忍这个污点,所以就没有调度上去,具体的调度策略,我们留到kubernetes调度管理章节进行讲解。

DaemonSet 也是支持更新和回滚的,具体操作和 Deployment 类似,这里就不再赘述。

不过,这里要介绍一下 DaemonSet 的更新策略,目前支持两种更新策略:

  • OnDelete:先删后起,也就是先删除老的 Pod,再启动新的 Pod,这种策略会导致节点在更新的过程中出现断连的情况。
  • RollingUpdate:滚动更新,和 Deployment 滚动方式一样,默认的策略。

值得一提的是rollingUpdate的更新策略,在老的 Kubernetes 版本中只有 maxUnavailable 而没有 maxSurge,因为 DaemonSet 只允许在 Node 上运行一个。但是在新版本中有了 maxSurge 这个参数,由它来控制多少个节点可用,比如总共有 100 个节点,maxSurge 配置 30%,则表示至少保证 30 个节点可用。

# Job/CronJob

Kubernetes 的主要任务是保证 Pod 中的应用长久稳定的运行,但是我们有时候也需要一些只需要运行一次,执行完就退出了的"短时"任务,这时候使用 Deployment 等这类控制器就无法满足我们的需求,Kubernetes 就诞生了 Job Controller,专门用来处理这类需求。

# Job

Job 负责处理仅执行一次的任务,它保证批处理的任务的一个或多个成功结束,我们可以通过kubectl explain job来查看具体语法。

# 基本操作

Job 的定义语法和 Deployment、Pod 差不多,定义一个简单的 Job,如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
        - name: test-job
          image: busybox
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/sh"
            - "-c"
          args:
            - "for i in $(seq 10); do echo $i; done"
      restartPolicy: Never
  backoffLimit: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这个 Job 简单执行一个脚本,循环 10 次并输出,通过kubectl apply -f job-demo.yaml创建 Job,如下:

kubectl apply -f job-demo.yaml
job.batch/job-demo created
1
2

然后可以通过 kubectl logs 来查看日志输出,如下:

kubectl logs job-demo-wd67s
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11

一切都符合我们的预期,现在再来看看 Job 和 Pod 的状态,如下:

kubectl get jobs.batch
NAME       COMPLETIONS   DURATION   AGE
job-demo   1/1           23s        112s
kubectl get po
NAME             READY   STATUS      RESTARTS   AGE
job-demo-wd67s   0/1     Completed   0          114s
1
2
3
4
5
6

Job 的状态没有类似 Deployment 的 Ready 关键字,而是 COMPLETIONS(完成),1/1表示完成了这个 Job。而 Job 所控制的 Pod 的状态也是 Completed,如果是这种状态,就表示这个 Job 是成功的。

如果成功,Job 的 Pod 运行一次就结束了,如果失败呢?

现在将刚才的 Job 的 YAML 改成如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
        - name: test-job
          image: busybox
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/sh"
            - "-c"
          args:
            - "xxxxx"
      restartPolicy: Never
  backoffLimit: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

执行过后可以看到 Pod 在不断的重建,如下:

kubectl get po
NAME             READY   STATUS              RESTARTS   AGE
job-demo-kwsl8   0/1     Error               0          3s
job-demo-ltsvq   0/1     ContainerCreating   0          0s
job-demo-w54s4   0/1     Error               0          6s
1
2
3
4
5

为什么是重建而不是重启呢?

因为我们在上面的 YAML 里配置了restartPolicy: Never,如果 Job 失败就只会重建,如果要使用重启,可以配置restartPolicy: OnFaliure,表示只有在状态为 Failure 的时候才会重启,Job 没有 Always 参数。

把上面 YAML 中 restartPolicy 改成OnFaliure,效果如下:

kubectl get po
NAME             READY   STATUS             RESTARTS      AGE
job-demo-p9dkp   0/1     CrashLoopBackOff   3 (24s ago)   68s
1
2
3

可以看到该 Job 在不断的重启。

还有一种情况,如果这个 Job 一直不肯结束怎么办呢?比如我们将上面的 YAML 文件做如下修改:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
        - name: test-job
          image: busybox
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/sh"
            - "-c"
          args:
            - "sleep 3600"
      restartPolicy: OnFailure
  backoffLimit: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

为了避免这种情况,可以在 YAML 里加入activeDeadlineSeconds参数来指定 Pod 的存活时间,如下:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  template:
    metadata:
      name: job-demo
    spec:
      containers:
        - name: test-job
          image: busybox
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/sh"
            - "-c"
          args:
            - "sleep 3600"
      restartPolicy: OnFailure
  backoffLimit: 4
  activeDeadlineSeconds: 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

该值适用于 Job 的整个生命期,无论 Job 创建了多少个 Pod。 一旦 Job 运行时间达到 activeDeadlineSeconds 秒,其所有运行中的 Pod 都会被终止, 并且 Job 的状态更新为 type: Failed 及 reason: DeadlineExceeded。

Job 的 .spec.activeDeadlineSeconds 优先级高于其 .spec.backoffLimit 设置。 因此,如果一个 Job 正在重试一个或多个失效的 Pod,该 Job 一旦到达 activeDeadlineSeconds 所设的时限即不再部署额外的 Pod, 即使其重试次数还未达到 backoffLimit 所设的限制。

# 并行控制

在 Job 对象中,负责控制并行的参数为:

  • completions:定义 Job 至少要完成的 Pod 数目,既 Job 的最小完成数;
  • parallelism:定义一个 Job 在任意时间最多可以启动多少个 Pod;

我们定义下面一个 Job 的 YAML 文件:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-demo
  namespace: default
spec:
  parallelism: 2
  completions: 4
  template:
    metadata:
      name: job-demo
    spec:
      containers:
        - name: test-job
          image: busybox
          imagePullPolicy: IfNotPresent
          command:
            - "/bin/sh"
            - "-c"
          args:
            - "for i in $(seq 10); do echo $i; done"
      restartPolicy: OnFailure
  backoffLimit: 4
  activeDeadlineSeconds: 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

parallelism: 2 和  completions: 4 表示要完成 4 个 pod,每次可以同时运行两个 Pod,我们创建这个 Job,观察结果如下:

kubectl get po
NAME             READY   STATUS      RESTARTS   AGE
job-demo-5wlp8   0/1     Completed   0          2s
job-demo-6wfkw   0/1     Completed   0          2s
job-demo-d54vz   0/1     Completed   0          5s
job-demo-x5mpz   0/1     Completed   0          5s
1
2
3
4
5
6

从上面可以知道,Job Controller 实际控制的就是 Pod,它在创建的时候会在 Job 和 Pod 里自动生成随机字符串的 label,然后将它们进行绑定。

Job Controller 在实际的调谐操作是根据实际在 running 状态的 Pod 数,还有已经退出的 Pod 数以及 parallelism 和 completions 的参数值共同计算出在 Job 周期内应该创建或者删除多少 Pod,然后调用 kube-api 来执行这类操作。

所以 Job Controller 实际上是控制的 Pod 的并行度以及总共要完成的任务数这两个重要的参数。

# CronJob

CronJob 其实就在 Job 的基础上加了时间调度,类似于用 Deployment 管理 Pod 一样。它和我们 Linux 上的 Crontab 差不多。

比如定义简单的 CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busybox
              command:
                - "/bin/sh"
                - "-c"
              args:
                - "for i in $(seq 10); do echo $i; done"
          restartPolicy: OnFailure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

我们可以看到 spec 里其实就是一个 Job Template。另外其 schedule 就是一个便准的 Cron 格式,如下:

分钟   小时    日    月    星期
*      *      *     *      *
1
2

运行过后,查看状态如下:

kubectl get cronjobs.batch
NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        45s             69s
kubectl get po
NAME                   READY   STATUS      RESTARTS   AGE
hello-27628291-h8skg   0/1     Completed   0          50s
1
2
3
4
5
6

需要注意的是,由于 cron 的特殊性,有时候会存在由于上一个定时任务还没有执行完成,新的定时任务又开始了的情况,我们可以通过定义 spec.concurrencyPolicy 字段来定义规则,比如:

  • concurrencyPolicy=Allow:表示这些 Job 可以同时存在
  • concurrencyPolicy=Firbid:表示不会创建新的 Job,也就是这个定时任务被跳过
  • concurrencyPolicy=Replace:表示产生的新 Job 会替代旧的 Job

如果某一个 Job 创建失败,那么这次创建就会被标记为 miss,当在指定的时间窗口内,Miss 的数达到 100,那么 CronJob 就会停止再创建这个 Job。这个时间窗口可以通过 spec.startingDeadlineSeconds 来指定。

# 最后

上面介绍的是日常工作中常用的控制器,其中 Deployment 和 DaemonSet 的使用频率最高,熟练掌握这些控制器,并且学会在什么时候选择什么样的控制器,合理使用使工作效率最高。

上次更新: 2025/07/19, 21:23:02
Kubernetes权限管理
Kubernetes调度管理

← Kubernetes权限管理 Kubernetes调度管理→

最近更新
01
elastic 账户认证 401 问题
07-20
02
使用 helm 安装 es 和 kibana
07-20
03
elastic stack 搭建
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式