005-Kubernetes控制器

  1. 一、控制器概述
    1. 1. 概述
    2. 2. 控制器分类
  2. 二、ReplicationController 和 ReplicaSet
    1. 1. 案例:ReplicationController
    2. 2. 案例:ReplicaSet
      1. 2.1 RS yaml 文件
      2. 2.2 启动RS
      3. 2.3 查看RS启动详情
      4. 2.4 查看 RS 启动的 Pod
      5. 2.5 查看Pod的日志
      6. 2.6 验证Pod设置的环境变量是否有效
      7. 2.7 验证RS对于Pod副本数量的自动恢复
      8. 2.8 验证 yaml 文件中定义的Pod的标签与RS标签选择器不一致时,是否可以启动
    3. 3. 标签选择器
      1. 3.1 matchLabels 的匹配模式
      2. 3.2 matchExpressions 的匹配模式
      3. 3.3 matchLabels 和 matchExpressions 的关系
      4. 3.4 注意事项
      5. 3.5 实际案例
      6. 3.6 总结
  3. 三、Deployment 控制器
    1. 1. Deployment - 基本概念
    2. 2. Deployment - 与 RS 的关联
    3. 3. Deployment - 常用命令
    4. 4. apply 、create、replace 区别
      1. 4.1 命令式命令
      2. 4.2 声明式命令
      3. 4.3 关键区别总结
      4. 4.4 kubectl replace -f
    5. 5. Deployment - 案例实操
      1. 5.1 编写资源清单
      2. 5.2 创建 Deployment
      3. 5.3 查看 Deployment 和 Pod
      4. 5.4 Pod 副本扩缩容
      5. 5.5 动态更新 Deployment 的 Pod 容器镜像
    6. 6. Deployment 滚动升级
      1. 6.1 滚动升级的核心机制
      2. (1)更新流程
      3. (2)关键配置参数
      4. 6.2 触发滚动升级的方式
      5. (1)直接修改 YAML 并应用
      6. (2)命令式更新镜像版本
      7. (3)其他可触发升级的操作
      8. 6.3 查看升级状态
      9. (1)检查升级进度
      10. (2)查看历史版本
      11. (3)查看具体版本的配置
      12. 6.4 回滚升级
      13. (1)回滚到上一个版本
      14. (2)回滚到指定版本
      15. 6.5 高级控制技巧
      16. (1)暂停/恢复升级
      17. (2)强制重建 Pod(非滚动更新)
      18. (3)修改默认滚动策略
      19. 6.6 故障排查
      20. (1)升级卡住怎么办?
      21. (2)常见原因
    7. 7. 滚动升级案例实操
      1. 7.1 资源清单
      2. 7.2 创建 Service
      3. 7.3 创建Deloyment
      4. 7.4 Pod副本扩容
      5. 7.5 查看当前Pod的镜像版本
      6. 7.6 升级 Pod 镜像版本
    8. 8. Deloyment 回滚案例实操
      1. 8.1 资源清单
      2. 8.2 创建Deployment
      3. 8.3 升级镜像
      4. 8.4 回滚镜像版本
        1. 6.4.1 回滚到上一个版本
        2. 8.4.2 回滚到指定版本
    9. 9. Deployment 清理策略
  4. 四、DaemonSet 控制器
    1. 1. 基本概念
    2. 2. DaemonSet 的核心特性
    3. 3. 案例演示
      1. 3.1 资源清单
      2. 3.1 创建 DaemonSet
      3. 3.2 查看 DeamonSet
      4. 3.3 查看 Pod
      5. 3.4 为什么 DeamonSet 没有运行在主节点上
        1. 3.4.1 主节点的污点(Taints)
      6. 3.4.2 DaemonSet Pod 的容忍(Tolerations)
        1. 3.4.3 为什么主节点需要污点?
        2. 3.4.4 例外情况:系统 DaemonSet
        3. 3.4.5 如何让 DaemonSet Pod 运行在主节点?
  5. 五、Job 控制器
    1. 1. Job 控制器特性
    2. 2. 案例演示
      1. 2.1 Python代码
      2. 2.2 Dockerfile
      3. 2.3 构建镜像
      4. 2.4 资源清单
      5. 2.5 运行Pod,查看日志
    3. 3. Job - 正常退出完成
      1. 3.1 案例一:让容器返回码为1
      2. 3.2 案例二:随机生成返回码
  6. 六、CronJob 控制器
    1. 1. CronJob 控制器特性
    2. 2. CroneJob 资源清单
    3. 3. 案例演示
      1. 3.1 资源清单
      2. 启动 CronJob
    4. 4. CronJob - 限制
  7. 七、StatefulSet 控制器

一、控制器概述

1. 概述

在 Kubernetes 中运行了一系列控制器来确保集群的当前状态与期望状态保持一致,它们就是 Kubernetes 集群内部的管理控制中心或者说是”中心大脑”。例如,ReplicaSet 控制器负责维护集群中运行的 Pod 数量;Node 控制器负责监控节点的状态,并在节点出现故障时,执行自动化修复流程,确保集群始终处于预期的工作状态。

控制器-控制循环

控制器-控制循环

2. 控制器分类

Pod控制器有如下几类:

  • ReplicationController 和 ReplicaSet
    • 功能:确保指定数量的 Pod 副本始终运行。
      • ReplicationController(旧版):通过标签选择器管理 Pod 副本数,但功能较基础。
      • ReplicaSet(推荐):新一代控制器,支持更灵活的集合型标签选择器(如 matchLabelsmatchExpressions),通常被 Deployment 间接使用。
    • 典型场景:无状态应用的副本维护(如 Web 服务)。
  • Deployment
    • 功能:管理 ReplicaSet 并提供声明式更新(如滚动升级、回滚)。
      • 通过控制 ReplicaSet 实现 Pod 的副本管理。
      • 支持版本控制和更新策略(如 RollingUpdateRecreate)。
    • 典型场景:需要滚动更新或回滚的无状态应用(如微服务)。
  • DaemonSet
    • 功能:确保每个节点(或指定节点)运行一个指定的 Pod。
      • Pod 通常与节点绑定(如日志收集、网络插件)。
      • 节点加入集群时自动创建 Pod,节点移除时删除。
    • 典型场景:集群级守护进程(如 kube-proxyfluentd)。
  • StateFulSet
    • 功能:管理有状态应用的 Pod,提供稳定的标识和持久化存储。
      • 每个 Pod 有唯一名称(如 web-0web-1)和持久化存储(PVC)。
      • 支持有序部署/扩展(按序号顺序操作)。
    • 典型场景:数据库、分布式存储(如 MySQL、ZooKeeper)。
  • Job/CronJob
    • Job
      • 创建一次性 Pod 并确保其成功完成。
      • 支持并行执行和重试机制。
      • 场景:批处理任务(如数据处理)。
    • CronJob
      • 基于时间表(Cron 表达式)周期性运行 Job。
      • 场景:定时任务(如每日备份)。
  • Horizontal Pod Autoscaling
    • 功能:根据 CPU 使用率或其他自定义指标自动调整 Pod 副本数。
      • 与 ReplicaSet/Deployment 配合使用。
      • 支持动态扩缩容(如流量高峰时扩容)。
    • 典型场景:应对负载波动的服务(如电商大促)。

二、ReplicationController 和 ReplicaSet

ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收;

在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector;

1. 案例:ReplicationController

001-replication-controller.yaml

apiVersion: v1
kind: ReplicationController # 资源类型为 ReplicationController(旧版副本控制器)
metadata:
  name: rc-demo # RC 的名称,在命名空间中唯一
  namespace: default
spec:
  replicas: 3 # 确保始终有 3 个 Pod 副本在运行
  selector:
    app: rc-demo  # 选择标签为 `app: rc-demo` 的 Pod 进行管理
  template: # Pod 模板
    metadata:
      labels:
        app: rc-demo # 给 Pod 打上标签,与 selector 对应
    spec:
      containers:
        - name: rc-demo-container # 容器名称
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent
          env: # 定义环境变量
            - name: GET_HOSTS_FROM # 环境变量1 key
              value: dns
            - name: zhangsan # 环境变量2 key
              value: "123"
          ports:
            - containerPort: 80 # 容器暴露的端口

运行 ReplicationController

$ kubectl apply -f 001-replication-controller.yaml

查看Pod的运行情况

$ kubectl get pod -o wide -w
NAME            READY   STATUS    RESTARTS         AGE   IP              NODE         NOMINATED NODE   READINESS GATES
rc-demo-47nqt   1/1     Running   0                36s   172.16.85.199   k8s-node01   <none>           <none>
rc-demo-74hbx   1/1     Running   0                36s   172.16.58.198   k8s-node02   <none>           <none>
rc-demo-zp8ck   1/1     Running   0                36s   172.16.85.200   k8s-node01   <none>           <none>

在两个node子节点上,运行了三个 Pod

测试Pod自动恢复

# 新起一个shell终端,杀死一个pod
$ kubectl delete pod rc-demo-74hbx

# 观察Pod运行情况
$ kubectl get pod -o wide -w
NAME            READY   STATUS    RESTARTS         AGE   IP              NODE         NOMINATED NODE   READINESS GATES
rc-demo-47nqt   1/1     Running   0                36s   172.16.85.199   k8s-node01   <none>           <none>
rc-demo-74hbx   1/1     Running   0                36s   172.16.58.198   k8s-node02   <none>           <none>
rc-demo-zp8ck   1/1     Running   0                36s   172.16.85.200   k8s-node01   <none>           <none>
rc-demo-74hbx   1/1     Terminating   0                63s   172.16.58.198   k8s-node02   <none>           <none>
rc-demo-fgfmr   0/1     Pending       0                1s    <none>          <none>       <none>           <none>
rc-demo-fgfmr   0/1     Pending       0                1s    <none>          k8s-node02   <none>           <none>
rc-demo-fgfmr   0/1     ContainerCreating   0                2s    <none>          k8s-node02   <none>           <none>
rc-demo-74hbx   1/1     Terminating         0                65s   172.16.58.198   k8s-node02   <none>           <none>
rc-demo-74hbx   0/1     Terminating         0                71s   172.16.58.198   k8s-node02   <none>           <none>
rc-demo-74hbx   0/1     Terminating         0                75s   <none>          k8s-node02   <none>           <none>
rc-demo-74hbx   0/1     Terminating         0                75s   <none>          k8s-node02   <none>           <none>
rc-demo-74hbx   0/1     Terminating         0                75s   <none>          k8s-node02   <none>           <none>
rc-demo-fgfmr   0/1     ContainerCreating   0                13s   <none>          k8s-node02   <none>           <none>
rc-demo-fgfmr   1/1     Running             0                20s   172.16.58.199   k8s-node02   <none>           <none>

杀死了 Pod rc-demo-74hbx, k8s又自动创建了Pod rc-demo-fgfmr

2. 案例:ReplicaSet

2.1 RS yaml 文件

002-ReplicaSet.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica-set-demo # rs的名字,全局唯一,生成的Pod的名字:replica-set-demo-xxxx
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels: # 基于标签匹配
      app: rs-ml-demo # ReplicaSet 将管理带有标签 app=rs-ml-demo 的 Pod
  template:
    metadata:
      namespace: default
      labels: # 为 Pod 设置标签
        app: rs-ml-demo # 给 Pod 打上标签 app=rs-ml-demo,与 selector 中的 matchLabels 对应
    spec:
      containers:
        - name: rs-ml-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent
          env: # 定义容器的环境变量
            - name: GET_HOSTS_FROM
              value: dns
            - name: George
              value: "30"
          ports:
            - containerPort: 80

2.2 启动RS

$ kubectl apply -f 002-ReplicaSet.yaml

2.3 查看RS启动详情

$ kubectl describe replicaset replica-set-demo
Name:         replica-set-demo
Namespace:    default
Selector:     app=rs-ml-demo
Labels:       <none>
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=rs-ml-demo
  Containers:
   rs-ml-container:
    Image:      wangyanglinux/myapp:v1.0
    Port:       80/TCP
    Host Port:  0/TCP
    Environment:
      GET_HOSTS_FROM:  dns
      George:          30
    Mounts:            <none>
  Volumes:             <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  67s   replicaset-controller  Created pod: replica-set-demo-p4tpb
  Normal  SuccessfulCreate  67s   replicaset-controller  Created pod: replica-set-demo-hnddq
  Normal  SuccessfulCreate  67s   replicaset-controller  Created pod: replica-set-demo-89pz7

RS 创建了3个 Pod 副本,均已启动成功。

2.4 查看 RS 启动的 Pod

根据 Pod 的标签过滤

$ kubectl get pods -l app=rs-ml-demo -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
replica-set-demo-89pz7   1/1     Running   0          2m58s   172.16.58.203   k8s-node02   <none>           <none>
replica-set-demo-hnddq   1/1     Running   0          2m58s   172.16.85.202   k8s-node01   <none>           <none>
replica-set-demo-p4tpb   1/1     Running   0          2m58s   172.16.58.202   k8s-node02   <none>           <none>

2.5 查看Pod的日志

# 查看最近1000行日志
$ kubectl logs -f --tail=1000 replica-set-demo-89pz7

# 查看最近1小时的日志
$ kubectl logs -f --tail=1000 replica-set-demo-89pz7 --since=1h

2.6 验证Pod设置的环境变量是否有效

# 进入Pod容器
$ kubectl exec -it replica-set-demo-89pz7 /bin/bash

# 打印环境变量
replica-set-demo-89pz7:/# echo $GET_HOSTS_FROM
dns
replica-set-demo-89pz7:/# echo $George
30

2.7 验证RS对于Pod副本数量的自动恢复

# 在一个shell中监控RS
$ kubectl get replicaset replica-set-demo -o wide -w

# 在另一个shell中,杀死这个RS管理的其中一个Pod
$ kubectl delete pod replica-set-demo-89pz7

# 可以看到RS管理的Pod从最开始3个,变成2个,又迅速恢复成3个
$ kubectl get replicaset replica-set-demo -o wide -w
NAME               DESIRED   CURRENT   READY   AGE   CONTAINERS        IMAGES                     SELECTOR
replica-set-demo   3         3         3       10m   rs-ml-container   wangyanglinux/myapp:v1.0   app=rs-ml-demo
replica-set-demo   3         2         2       11m   rs-ml-container   wangyanglinux/myapp:v1.0   app=rs-ml-demo
replica-set-demo   3         3         2       11m   rs-ml-container   wangyanglinux/myapp:v1.0   app=rs-ml-demo
replica-set-demo   3         3         3       11m   rs-ml-container   wangyanglinux/myapp:v1.0   app=rs-ml-demo

2.8 验证 yaml 文件中定义的Pod的标签与RS标签选择器不一致时,是否可以启动

yaml文件如下:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replica-set-demo # rs命名,全局唯一
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels: # 基于标签匹配
      app: rs-ml-demo # ReplicaSet 将管理带有标签 app=rs-ml-demo 的 Pod
  template:
    metadata:
      namespace: default
      labels: # 为 Pod 设置标签
        app: rs-ml-demo-123 # 给 Pod 打上标签 app=rs-ml-demo,与 selector 中的 matchLabels 对应
    spec:
      containers:
        - name: rs-ml-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent
          env:
            - name: GET_HOSTS_FROM
              value: dns
            - name: George
              value: "30"
          ports:
            - containerPort: 80

RS 标签选择器管理的标签是 rs-ml-demo , 而 Pod模板定义的标签是 rs-ml-demo-123 ,这样 RS 将无法管理此 Pod。

启动RS

$ kubectl apply -f 002-ReplicaSet.yaml 
The ReplicaSet "replica-set-demo" is invalid: spec.template.metadata.labels: Invalid value: map[string]string{"app":"rs-ml-demo-123"}: `selector` does not match template `labels`

启动报错,K8S不允许定义定Pod标签与RS标签选择器无法匹配的情况。

3. 标签选择器

在 Kubernetes 的 ReplicaSet(或其他资源如 Deployment、Service 等)中,matchLabels 是 selector 的一部分,用于指定如何选择要管理的 Pod。它基于标签(labels)进行匹配。matchLabels 本身只支持一种匹配模式:精确匹配(exact match),即键值对必须完全相等。但 Kubernetes 提供了更灵活的标签选择器机制,比如 matchExpressions,可以实现更复杂的匹配模式。

3.1 matchLabels 的匹配模式

  • 定义:matchLabels 是一个键值对映射,要求被选中的 Pod 的标签与 matchLabels 中定义的键值对完全一致。

  • 模式:仅支持精确匹配

  • 语法

    selector:
      matchLabels:
        key1: value1
        key2: value2
  • 行为:Pod 必须同时具备所有指定的标签键值对才能被选中。(Pod定的标签可以比 matchLabels 定义的标签多,但是不能比 matchLabels 定义的标签少,否则无法被 RS 管理)

  • 示例: 在你的 ReplicaSet 配置中:

    selector:
      matchLabels:
        app: rs-ml-demo
        key2: value2
    • 这表示 ReplicaSet 只管理带有标签 app=rs-ml-demokey2=value2 的 Pod。
    • Pod 的标签必须完全匹配 app: rs-ml-demokey2: value2 ,多余的标签不会影响匹配,但缺少这个标签的 Pod 不会被选中。

3.2 matchExpressions 的匹配模式

Kubernetes 的 selector 还支持 matchExpressions,它提供了更丰富的匹配模式。matchExpressions 是一个可选字段,与 matchLabels 可以一起使用,允许基于逻辑运算进行标签选择。

它提供了更丰富的匹配模式。matchExpressions 是一个可选字段,与 matchLabels 可以一起使用,允许基于逻辑运算进行标签选择。

  • 语法

    selector:
      matchLabels:
        app: rs-ml-demo
      matchExpressions:
        - {key: environment, operator: In, values: [prod, staging]}
  • 支持的运算符(Operator)

    1. In
      • 标签的值必须在指定值列表中。
      • 示例:environment 的值必须是 prod 或 staging。
    2. NotIn
      • 标签的值不能在指定值列表中。
      • 示例:environment 的值不能是 dev 或 test。
    3. Exists
      • 标签键必须存在,值无要求。
      • 示例:Pod 必须有 environment 标签,具体值不限。
    4. DoesNotExist
      • 标签键不能存在。
      • 示例:Pod 不能有 environment 标签。
  • 示例

    selector:
      matchExpressions:
        - {key: tier, operator: In, values: [frontend, backend]}
        - {key: deprecated, operator: DoesNotExist}
    • 匹配条件:Pod 必须有 tier=frontend 或 tier=backend,且不能有 deprecated 标签。

3.3 matchLabels 和 matchExpressions 的关系

  • matchLabels 是简化的写法:它会被 Kubernetes 内部转换为等价的 matchExpressions,每个键值对对应一个 In 运算符。

    • 例如:

      matchLabels:
        app: rs-ml-demo

      等价于:

      matchExpressions:
        - {key: app, operator: In, values: [rs-ml-demo]}
  • 组合使用:如果同时指定 matchLabels 和 matchExpressions,Pod 必须满足两者的条件(逻辑“与”关系)。

3.4 注意事项

  • 一致性:selector.matchLabels(或 matchExpressions)必须与 template.metadata.labels 中的标签匹配,否则 ReplicaSet 无法正确管理 Pod。
  • 大小写敏感:标签键和值是大小写敏感的,例如 app: Rs-ml-demo 和 app: rs-ml-demo 不匹配。
  • Service 的选择器:虽然问题聚焦于 ReplicaSet,但值得一提的是 Service 的 selector 只支持 matchLabels 风格的精确匹配,不支持 matchExpressions。

3.5 实际案例

003-matchExpressions.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: rs-me-exists-demo
spec:
  replicas: 3
  selector:
    matchExpressions:
      - key: app # Pod 必须有 app 标签
        operator: Exists
      - key: version # Pod 必须有 version=v1 或 version=v2
        operator: In
        values:
          - v1
          - v2
      - key: souce # Pod不能有 souce 标签
        operator: DoesNotExist
  template:
    metadata:
      namespace: default
      labels: # 为 Pod 设置标签
        app: rs-ml-demo # 给 Pod 打上标签 app=rs-ml-demo,与 selector 中的 matchLabels 对应
        version: v1
    spec:
      containers:
        - name: rs-ml-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent
          env: # 定义容器的环境变量
            - name: GET_HOSTS_FROM
              value: dns
            - name: George
              value: "30"
          ports:
            - containerPort: 80

3.6 总结

  • matchLabels:只支持精确匹配,键值对必须完全相等。
  • matchExpressions(扩展功能):支持 In、NotIn、Exists、DoesNotExist 四种模式,提供更灵活的匹配。

三、Deployment 控制器

1. Deployment - 基本概念

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 ( declarative ) 方法,用来替代以前的 ReplicationController 来方便的管理应用。典型的应用场景包括:

  • 定义 Deployment 来创建 Pod 和 ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续 Deployment

2. Deployment - 与 RS 的关联

Deployment - 与 RS 的关联

Deployment 是通过创建 ReplicaSet 来间接的管理 Pod。

3. Deployment - 常用命令

# 根据资源清单创建 deployment 
# --record 参数可以记录命令,我们可以很方便的查看每次 revision 的变化
$ kubectl create -f deployment.yaml --record

# 修改 Deployment 类型的 Pod 的数量
$ kubectl scale deployment nginx-deployment --replicas 10

# 设置 Pod 自动扩缩容,最大15个,最小 10个,扩缩依据:CPU利用率是否超过 80%
$ kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80

# 更新 deployment 类型的Pod nginx-deployment 下的容器 nginx-deployment-container 的镜像为:wangyanglinux/myapp:v2.0
$ kubectl set image deployment/nginx-deployment nginx-deployment-container=wangyanglinux/myapp:v2.0

# 回滚Pod变更 (只能回滚到前一次变更操作,如:1 -> 2 -> 3, 第一次回滚回到2,第二次回滚则回到3,无法回滚到)
$ kubectl rollout undo deployment/nginx-deployment

4. apply 、create、replace 区别

kubectl create -f deployment.yamlkubectl apply -f deployment.yaml 都是用于创建或更新 Kubernetes 资源的命令,但它们在行为上有关键区别,主要涉及 声明式管理命令式管理 的不同逻辑。

4.1 命令式命令

kubectl create -f deployment.yaml
  • 作用严格创建新资源
  • 行为
    • 如果资源(如 Deployment)已存在,会报错并拒绝执行(报 AlreadyExists 错误)。
    • 仅适用于首次创建,不能用于更新。
  • 适用场景
    • 你明确知道资源不存在,且只需要一次性创建。
    • 脚本中需要严格避免覆盖现有配置时。

示例输出(资源已存在时):

Error from server (AlreadyExists): deployments.apps "deployment-demo-1" already exists

4.2 声明式命令

  • 作用创建或更新资源,智能合并变更。
  • 行为
    • 如果资源不存在,则创建它(等同于 create)。
    • 如果资源已存在,则对比当前配置和 YAML 文件的差异,增量更新(保留未修改的字段)。
    • 依赖 metadata.annotations 中的 kubectl.kubernetes.io/last-applied-configuration 记录上次配置,用于计算变更。
  • 适用场景
    • 日常维护(例如更新镜像版本、调整副本数)。
    • GitOps 或 CI/CD 流程中(推荐使用 apply 而非 create)。

示例输出(更新时)

deployment.apps/deployment-demo-1 configured

4.3 关键区别总结

特性 kubectl create kubectl apply
资源已存在 报错,拒绝执行 合并更新
资源不存在 创建资源 创建资源
管理方式 命令式(直接执行) 声明式(对比差异后更新)
记录变更 记录到 last-applied-configuration
适用场景 一次性创建 持续维护(创建 + 更新)

4.4 kubectl replace -f

  • 类似 create,但会强制替换现有资源(需资源已存在)。
  • apply 不同:不合并字段,直接覆盖整个配置(可能丢失未指定的字段)。
  • 慎用,通常仅在需要完全重置配置时使用。

建议

  • **优先使用 apply**(声明式操作更符合 Kubernetes 设计理念)。
  • 仅在需要严格控制创建时使用 create
  • 自动化流程:推荐始终使用 apply(避免因资源存在导致失败)。

5. Deployment - 案例实操

5.1 编写资源清单

004-deployment-demo.yaml

apiVersion: apps/v1 # 版本号
kind: Deployment # 控制器类型为:Deployment
metadata:
  labels:
    app: deployment-demo # 控制器标签
  name: deployment-demo-1 # 控制器名称
spec:
  replicas: 1 # 初始Pod副本数
  selector:
    matchLabels:
      app: deployment-demo # Pod标签选择器,管理 标签为 app: deployment-demo 的Pod
  template:
    metadata:
      labels:
        app: deployment-demo # Pod的标签,与 selector 标签选择器对应
    spec:
      containers:
        - image: wangyanglinux/myapp:v1.0 # 容器使用的镜像
          imagePullPolicy: IfNotPresent # 镜像拉取策略
          name: deployment-demo-container # 镜像

5.2 创建 Deployment

# 根据资源清单创建 Deployment 和对应的 Pod
$ kubectl apply -f 004-deployment-demo.yaml

5.3 查看 Deployment 和 Pod

查看 Deployment 信息

$ kubectl get deployment -o wide 
NAME                READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                  IMAGES                     SELECTOR
deployment-demo-1   1/1     1            1           2m21s   deployment-demo-container   wangyanglinux/myapp:v1.0   app=deployment-demo

可以看到创建一个 Deployment 类型的控制器,名称为:deployment-demo-1, 标签为:app=deployment-demo,控制器内 Pod的副本数为 1。

查看 Deployment 详细信息

$ kubectl get deployment deployment-demo-1 -o yaml

内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1" # 当前修订版本号(用于回滚)
    kubectl.kubernetes.io/last-applied-configuration: | # 上次 kubectl apply 使用的完整配置(用于对比变更)
      {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"deployment-demo"},"name":"deployment-demo-1","namespace":"default"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"deployment-demo"}},"template":{"metadata":{"labels":{"app":"deployment-demo"}},"spec":{"containers":[{"image":"wangyanglinux/myapp:v1.0","imagePullPolicy":"IfNotPresent","name":"deployment-demo-container"}]}}}}
  creationTimestamp: "2025-04-09T01:59:05Z"
  generation: 1
  labels:
    app: deployment-demo
  name: deployment-demo-1
  namespace: default
  resourceVersion: "1484447"
  uid: 8cec6671-c1e9-4292-921c-dbe8bae8c357
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10 # 保留最近 10 个历史版本(用于回滚),超出部分会被自动清理
  selector:
    matchLabels:
      app: deployment-demo
  strategy:
    rollingUpdate: # 滚动更新策略 (strategy)
      maxSurge: 25% # 允许临时超出期望副本数的比例(例如 1.25 个 Pod)
      maxUnavailable: 25% # 更新时允许不可用的 Pod 比例(例如最多 0.75 个 Pod 不可用)
    type: RollingUpdate # 更新策略类型: 滚动更新,逐步替换旧 Pod,确保服务不中断。
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: deployment-demo
    spec:
      containers:
      - image: wangyanglinux/myapp:v1.0
        imagePullPolicy: IfNotPresent
        name: deployment-demo-container
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30 # 终止前等待 30 秒(优雅退出)
status:
  availableReplicas: 1 # 当前可用的 Pod 数量
  conditions:
  - lastTransitionTime: "2025-04-09T01:59:07Z"
    lastUpdateTime: "2025-04-09T01:59:07Z"
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: "2025-04-09T01:59:05Z"
    lastUpdateTime: "2025-04-09T01:59:07Z"
    message: ReplicaSet "deployment-demo-1-6995c75668" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
  observedGeneration: 1
  readyReplicas: 1 # 已就绪的 Pod 数量
  replicas: 1 # 实际运行的 Pod 数量
  updatedReplicas: 1 # 已更新到最新版本的 Pod 数量

查看Pod

$ kubectl get pod -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
deployment-demo-1-6995c75668-klh7h   1/1     Running   0          33m   172.16.58.208   k8s-node02   <none>           <none>

注意看 Pod 的名字是 deployment-demo-1-6995c75668-klh7h ,这是由 Deployment 创建时,自动创建了 对应的 ReplicaSet 的名字(deployment-demo-1-6995c75668) 再拼接上 随机字符串:klh7h 生成的,可以查看是否有 ReplicaSet 验证:

$ kubectl get rs
NAME                           DESIRED   CURRENT   READY   AGE
deployment-demo-1-6995c75668   1         1         1       33m

5.4 Pod 副本扩缩容

# 查看当前Pod副本数
$ kubectl get pods
NAME                                 READY   STATUS    RESTARTS   AGE
deployment-demo-1-6995c75668-klh7h   1/1     Running   0          29h

# 扩展 Pod 数量
$ kubectl scale deployment deployment-demo-1 --replicas 10

# 再次查看 Pod 数量
$ kubectl get pod 
NAME                                 READY   STATUS    RESTARTS   AGE
deployment-demo-1-6995c75668-44fnz   1/1     Running   0          10s
deployment-demo-1-6995c75668-64zdm   1/1     Running   0          10s
deployment-demo-1-6995c75668-9xxbc   1/1     Running   0          10s
deployment-demo-1-6995c75668-klh7h   1/1     Running   0          29h
deployment-demo-1-6995c75668-kqqhj   1/1     Running   0          10s
deployment-demo-1-6995c75668-mmdrx   1/1     Running   0          10s
deployment-demo-1-6995c75668-twr24   1/1     Running   0          10s
deployment-demo-1-6995c75668-vq67h   1/1     Running   0          10s
deployment-demo-1-6995c75668-wpq6x   1/1     Running   0          10s
deployment-demo-1-6995c75668-zxvwc   1/1     Running   0          10s

# 缩小 Pod 副本数
$ kubectl scale deployment deployment-demo-1 --replicas 1

# 再次查看Pod
$ kubectl get pod 
NAME                                 READY   STATUS    RESTARTS   AGE
deployment-demo-1-6995c75668-klh7h   1/1     Running   0          29h

5.5 动态更新 Deployment 的 Pod 容器镜像

当前资源清单中使用的镜像是:wangyanglinux/myapp:v1.0 ,现在测试在不停止Pod运行的情况下,将镜像升级到 wangyanglinux/myapp:v2.0

查看当前运行中的Pod

# 创建 Pod
$ kubectl create -f 004-deployment-demo.yaml --record

# 查看Pod
$ kubectl get pods -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
deployment-demo-1-6995c75668-4rljk   1/1     Running   0          28m   172.16.58.213   k8s-node02   <none>           <none>

# 访问Pod,确认当前版本是 1.0
$ curl 172.16.58.213
www.xinxianghf.com | hello MyAPP | version v1.0

命令后面添加 --record 目的是为了让 kubectl 记住Pod操作历史,以便于后期回滚操作。

升级容器镜像版本

$ kubectl set image deployment/deployment-demo-1 deployment-demo-container=wangyanglinux/myapp:v2.0 --record


# 查看Pod
$ kubectl get pods -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
deployment-demo-1-6465d4c5c9-b5dsn   1/1     Running   0          89s   172.16.58.214   k8s-node02   <none>           <none>

# 访问Pod,镜像版本已更新到 2.0
$ curl 172.16.58.214
www.xinxianghf.com | hello MyAPP | version v2.0

查看Pod更新的历史记录

$ kubectl rollout history deployment/deployment-demo-1
deployment.apps/deployment-demo-1 
REVISION  CHANGE-CAUSE
1         kubectl apply --filename=004-deployment-demo.yaml --record=true
2         kubectl set image deployment/deployment-demo-1 deployment-demo-container=wangyanglinux/myapp:v2.0 --record=true

6. Deployment 滚动升级

在 Kubernetes 中,Deployment 是管理 Pod 副本和实现无缝升级的核心资源。滚动升级(Rolling Update) 是默认的更新策略,它允许逐步替换旧版本的 Pod,确保应用在升级过程中不中断服务

6.1 滚动升级的核心机制

(1)更新流程

  1. 创建新版本的 ReplicaSet
    • 当修改 Deployment 的 Pod 模板(如镜像版本)时,Kubernetes 会创建一个新的 ReplicaSet。
    • 新 ReplicaSet 逐步启动新 Pod,旧 ReplicaSet 逐步缩减旧 Pod。
  2. 逐步替换 Pod
    • 通过 maxSurgemaxUnavailable 控制替换速度:
      • **maxSurge**:允许临时超出 replicas 的 Pod 数量(默认 25%)。
      • **maxUnavailable**:升级过程中允许不可用的 Pod 比例(默认 25%)。
  3. 完成升级
    • 所有旧 Pod 被替换后,旧 ReplicaSet 保留(便于回滚),但 Pod 数为 0。

(2)关键配置参数

在 Deployment 的 spec.strategy 中定义:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%     # 允许临时多启动 25% 的 Pod(例如 replicas=4 时,最多 5 个 Pod)
      maxUnavailable: 25%  # 允许最多 25% 的 Pod 不可用(例如 replicas=4 时,至少 3 个 Pod 可用)

6.2 触发滚动升级的方式

(1)直接修改 YAML 并应用

kubectl apply -f deployment.yaml

适用于通过版本控制的 YAML 文件管理部署。

(2)命令式更新镜像版本

kubectl set image deployment/<deployment-name> <container-name>=<new-image>:<tag>

例如:

kubectl set image deployment/deployment-demo-1 deployment-demo-container=wangyanglinux/myapp:v2.0

(3)其他可触发升级的操作

  • 修改环境变量、资源限制(CPU/内存)、标签等 Pod 模板内容。
  • 调整副本数(kubectl scale)不会触发滚动升级。

6.3 查看升级状态

(1)检查升级进度

kubectl rollout status deployment/<deployment-name>

输出示例:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...

(2)查看历史版本

kubectl rollout history deployment/<deployment-name>

输出示例:

REVISION  CHANGE-CAUSE
1         kubectl apply --filename=deployment.yaml
2         kubectl set image deployment/deployment-demo-1 deployment-demo-container=myapp:v2.0

(3)查看具体版本的配置

kubectl rollout history deployment/<deployment-name> --revision=<revision-number>

例如:

kubectl rollout history deployment/deployment-demo-1 --revision=2

6.4 回滚升级

如果新版本出现问题,可快速回滚到之前的版本:

(1)回滚到上一个版本

kubectl rollout undo deployment/<deployment-name>

(2)回滚到指定版本

kubectl rollout undo deployment/<deployment-name> --to-revision=<revision-number>

例如:

kubectl rollout undo deployment/deployment-demo-1 --to-revision=1

6.5 高级控制技巧

(1)暂停/恢复升级

  • 暂停升级(手动分阶段发布):

    kubectl rollout pause deployment/<deployment-name>
  • 恢复升级

    kubectl rollout resume deployment/<deployment-name>

(2)强制重建 Pod(非滚动更新)

删除所有 Pod 触发重建(慎用):

kubectl delete pods -l app=<deployment-label>

(3)修改默认滚动策略

默认滚动策略如下:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%     # 允许临时多启动 25% 的 Pod(例如 replicas=4 时,最多 5 个 Pod)
      maxUnavailable: 25%  # 允许最多 25% 的 Pod 不可用(例如 replicas=4 时,至少 3 个 Pod 可用)

查看当前 Deployment 滚动策略:

$kubectl get deployment my-deployment-demo -o jsonpath='{.spec.strategy}'

{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"}

修改滚动策略,

$kubectl edit deployment my-deployment-demo --record

修改完成后保存,会自动生效。

6.6 故障排查

(1)升级卡住怎么办?

  • 检查事件:

    kubectl describe deployment/<deployment-name>
  • 查看 Pod 状态:

    kubectl get pods

(2)常见原因

  • 镜像拉取失败:检查镜像名称或权限。
  • 资源不足:节点 CPU/内存不足。
  • 就绪探针失败:新版本 Pod 未通过健康检查。

7. 滚动升级案例实操

演示一个 Deployment 下有多个 Pod 副本(10个),升级镜像版本,以及回退版本相关操作。

7.1 资源清单

005-deployment-demo.yaml

apiVersion: apps/v1 # 版本号
kind: Deployment # 控制器类型为:Deployment
metadata:
  labels:
    app: deployment-demo # 控制器标签
  name: my-deployment-demo # 控制器名称
spec:
  replicas: 1 # 初始Pod副本数
  selector:
    matchLabels:
      app: deployment-demo # Pod标签选择器,管理 标签为 app: deployment-demo 的Pod
  template:
    metadata:
      labels:
        app: deployment-demo # Pod的标签,与 selector 标签选择器对应
    spec:
      containers:
        - name: deployment-demo-container # 容器名称
          image: wangyanglinux/myapp:v1.0 # 容器使用的镜像
          imagePullPolicy: IfNotPresent # 镜像拉取策略

7.2 创建 Service

创建Service的目的是为了通过访问 service,查看 Pod 滚动升级的过程。

# 创建 Service
$ kubectl create svc clusterip deployment-demo --tcp=80:80

# 查看 Service 
$ kubectl get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
deployment-demo   ClusterIP   10.103.97.136   <none>        80/TCP    5m53s
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP   13d

# while 命令循环访问 service
$ while true; do curl 10.103.97.136; done

7.3 创建Deloyment

# 创建 Deployment ,添加了 --record 会记录操作的命令详情
$ kubectl create -f 005-deployment-demo.yaml --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/my-deployment-demo created

# 查看 Deployment
$ kubectl get deployment -o wide 
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS                  IMAGES                     SELECTOR
my-deployment-demo   1/1     1            1           3s    deployment-demo-container   wangyanglinux/myapp:v1.0   app=deployment-demo

7.4 Pod副本扩容

# 将当前Pod数量从1个,扩容到10个
$ kubectl scale deployment my-deployment-demo --replicas 10
deployment.apps/my-deployment-demo scaled

# 查看Deployment
$ kubectl get deployment -o wide 
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                  IMAGES                     SELECTOR
my-deployment-demo   10/10   10           10          2m49s   deployment-demo-container   wangyanglinux/myapp:v1.0   app=deployment-demo

# 查看Pod
$ kubectl get pod -l app=deployment-demo -o wide
NAME                                  READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
my-deployment-demo-6995c75668-5fxrj   1/1     Running   0          2m31s   172.16.85.211   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-977kq   1/1     Running   0          2m31s   172.16.58.223   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-lq4kj   1/1     Running   0          2m31s   172.16.58.221   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-mvdsm   1/1     Running   0          2m31s   172.16.58.220   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-rt2wp   1/1     Running   0          2m31s   172.16.85.208   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-ssx86   1/1     Running   0          2m31s   172.16.58.222   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-v69wv   1/1     Running   0          2m31s   172.16.85.210   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-vvjg2   1/1     Running   0          2m31s   172.16.85.212   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-whc7z   1/1     Running   0          5m3s    172.16.58.219   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-zsgg9   1/1     Running   0          2m31s   172.16.85.209   k8s-node01   <none>           <none>

7.5 查看当前Pod的镜像版本

$ while true; do curl 10.103.97.136; done
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0

当前访问的所有Pod 镜像版本都是 1.0

7.6 升级 Pod 镜像版本

新开一个shell终端,执行命令升级镜像版本,前一个shell终端循环访问pod,打印Pod镜像版本信息

# 升级镜像版本,并打印当前pod详细信息
$ kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v2.0 --record && kubectl get pod -l app=deployment-demo -o wide
deployment.apps/my-deployment-demo image updated
NAME                                  READY   STATUS              RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
my-deployment-demo-6465d4c5c9-crrcz   0/1     Pending             0          0s    <none>          k8s-node02   <none>           <none>
my-deployment-demo-6465d4c5c9-fckht   0/1     ContainerCreating   0          0s    <none>          k8s-node02   <none>           <none>
my-deployment-demo-6465d4c5c9-tkjst   0/1     ContainerCreating   0          0s    <none>          k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-2fg6c   1/1     Running             0          12m   172.16.58.229   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-bhs4q   1/1     Running             0          11m   172.16.85.221   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-fj7hf   1/1     Running             0          11m   172.16.85.220   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-p7bhr   1/1     Running             0          11m   172.16.85.218   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-qbdmg   1/1     Running             0          11m   172.16.58.230   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-qprl6   1/1     Running             0          11m   172.16.85.222   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-qtdbb   1/1     Terminating         0          11m   172.16.58.232   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-r2xvv   1/1     Running             0          11m   172.16.85.219   k8s-node01   <none>           <none>
my-deployment-demo-6995c75668-sg7vt   1/1     Terminating         0          11m   172.16.58.233   k8s-node02   <none>           <none>
my-deployment-demo-6995c75668-wbqn2   1/1     Running             0          11m   172.16.58.231   k8s-node02   <none>           <none>

此时Pod正在升级滚动升级中,当前看到共有13个Pod,这是因为Pod在滚动升级的过程中,默认允许新增不超过原设定副本数 25% 副本数量(maxSurge: 25%)。(上面的打印结果有2个Pod的状态是 Terminating,运行中和加载中的Pod数没有超过原设备Pod数的 125%)

查看前一个shell终端的打印结果如下:

w.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v2.0
www.xinxianghf.com | hello MyAPP | version v2.0
www.xinxianghf.com | hello MyAPP | version v2.0
www.xinxianghf.com | hello MyAPP | version v1.0
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
www.xinxianghf.com | hello MyAPP | version v1.0
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v2.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v2.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
www.xinxianghf.com | hello MyAPP | version v2.0
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
www.xinxianghf.com | hello MyAPP | version v1.0
curl: (7) Failed to connect to 10.103.97.136 port 80: Connection refused
www.xinxianghf.com | hello MyAPP | version v1.0
www.xinxianghf.com | hello MyAPP | version v1.0

可以得出如下结论:

  • 通过访问 Service 会轮询访问 lable 名称与 Service 名称一样的 Pod
  • Pod 正处于滚动升级的过程中,打印的镜像版本有的是 1.0,有的是 2.0,有的处于 Terminating 状态,不可访问

8. Deloyment 回滚案例实操

8.1 资源清单

006-deployment-demo.yaml

apiVersion: apps/v1 # 版本号
kind: Deployment # 控制器类型为:Deployment
metadata:
  labels:
    app: deployment-demo # 控制器标签
  name: my-deployment-demo # 控制器名称
spec:
  replicas: 1 # 初始Pod副本数
  selector:
    matchLabels:
      app: deployment-demo # Pod标签选择器,管理 标签为 app: deployment-demo 的Pod
  template:
    metadata:
      labels:
        app: deployment-demo # Pod的标签,与 selector 标签选择器对应
    spec:
      containers:
        - name: deployment-demo-container # 容器名称
          image: wangyanglinux/myapp:v1.0 # 容器使用的镜像
          imagePullPolicy: IfNotPresent # 镜像拉取策略

8.2 创建Deployment

# 1.创建 Service
$ kubectl create svc clusterip deployment-demo --tcp=80:80
# 查看 Service 
$ kubectl get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
deployment-demo   ClusterIP   10.103.97.136   <none>        80/TCP    5m53s
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP   13d


# 2.创建 Deployment ,添加了 --record 会记录操作的命令详情
$ kubectl create -f 006-deployment-demo.yaml --record


# 3. 将当前Pod数量从1个,扩容到10个
$ kubectl scale deployment my-deployment-demo --replicas 10

# 查看Deployment
$ kubectl get deployment -o wide 
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                  IMAGES                     SELECTOR
my-deployment-demo   10/10   10           10          2m49s   deployment-demo-container   wangyanglinux/myapp:v1.0   app=deployment-demo

# 查看当前 Pod 镜像版本
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v1.0

8.3 升级镜像

# 1. 将镜像版本升级到 2.0
$ kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v2.0 --record
# 测试访问镜像
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v2.0


# 2. 将镜像版本升级到 3.0
$ kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v3.0 --record
# 测试访问镜像
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v3.0


# 3. 查看 Deployment 历史版本(创建Deployment、升级镜像命令都添加了 --record 参数,所以CHANGE-CAUSE才有操作记录)
$ kubectl rollout history deployment/my-deployment-demo
deployment.apps/my-deployment-demo 
REVISION  CHANGE-CAUSE
1         kubectl apply --filename=006-deployment-demo.yaml --record=true
2         kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v2.0 --record=true
3         kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v3.0 --record=true

8.4 回滚镜像版本

6.4.1 回滚到上一个版本

# 1.回滚到上一个版本(从3.0 -> 2.0)
$ kubectl rollout undo deployment/my-deployment-demo

# 测试访问镜像(此时已回滚到 2.0版本)
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v2.0


# 2.再次执行版本回滚
$ kubectl rollout undo deployment/my-deployment-demo

# 测试访问镜像(此时又回滚到 3.0版本)
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v3.0

kubectl rollout undo deployment/<deployment-demo> 命令只能回滚到前一次操作,但无法回滚到前前上操作,例如升级操作如下:
v1 -> v2 -> v3
升级到v3版本后开始回滚:v1 -> v2 -> v3 -> v2 -> v3 -> v2 -> v3 ,
这样无法从 v3 -> v1

8.4.2 回滚到指定版本

前面的每次升级操作命令,都携带了参数 --record ,此参数会让 kubectl 记录操作命令,通过查看 deployment 历史版本,方便版本回滚。

# 1.查看 Deployment 历史版本(REVISION 就是每次操作后的版本号)
$ kubectl rollout history deployment/my-deployment-demo
deployment.apps/my-deployment-demo 
REVISION  CHANGE-CAUSE
1         kubectl apply --filename=006-deployment-demo.yaml --record=true
4         kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v2.0 --record=true
5         kubectl set image deployment/my-deployment-demo deployment-demo-container=wangyanglinux/myapp:v3.0 --record=true

# 2.回滚到 v1.0 版本(通过指定版本号)
$ kubectl rollout undo deployment my-deployment-demo --to-revision=1

# 测试访问镜像(此时回滚到 1.0版本)
$ curl 10.103.97.136
www.xinxianghf.com | hello MyAPP | version v1.0

9. Deployment 清理策略

过设置 .spec.revisionHistoryLimit 项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了

四、DaemonSet 控制器

1. 基本概念

在 Kubernetes 中,DaemonSet 控制器用于确保集群中的每个节点(或指定节点子集)都运行一个 Pod 副本。通常用于部署系统级别的服务,例如日志收集代理(如 Fluentd、Logstash)、监控代理(如 Prometheus Node Exporter)或网络代理(如 kube-proxy)。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterdceph
  • 在每个 Node 上运行日志收集 daemon,例如fluentdlogstash
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond

2. DaemonSet 的核心特性

  • 自动调度:DaemonSet 确保每个符合条件的节点上运行一个 Pod。如果节点被添加或移除,DaemonSet 会自动调整。
  • Pod 一致性:所有 Pod 通常使用相同的 Pod 模板运行。
  • 节点选择:可以通过 nodeSelector 或 taints/tolerations 控制 DaemonSet 在哪些节点上运行。
  • 更新策略:支持 RollingUpdate 和 OnDelete 两种更新策略,默认是 RollingUpdate。

3. 案例演示

3.1 资源清单

007-DaemonSet-demo.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: my-daemonset-demo # DaemonSet 控制器的名字
  namespace: default # 命名空间
  labels:
    app: daemonset-demo # DaemonSet 控制的标签
spec:
  selector:
    matchLabels:
      name: daemonset-demo # 匹配标签为 name: daemonset-demo 的Pod
  template:
    metadata:
      labels: # Pod模板标签,与 spec.selector.matchLabels 对应
        name: daemonset-demo
    spec:
      containers: # 主容器
        - name: daemonset-demo-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent

3.1 创建 DaemonSet

# 创建 DaemonSet 控制器
$ kubectl apply -f 007-DaemonSet-demo.yaml

3.2 查看 DeamonSet

# 查看 DaemonSet (指定了 名称空间、标签)
$ kubectl get ds -n default -l app=daemonset-demo -o wide
NAME                DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE    CONTAINERS                 IMAGES                     SELECTOR
my-daemonset-demo   2         2         2       2            2           <none>          4m5s   daemonset-demo-container   wangyanglinux/myapp:v1.0   name=daemonset-demo

3.3 查看 Pod

$ kubectl get pod -n default -o wide
NAME                      READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
my-daemonset-demo-kltsf   1/1     Running   0          36s   172.16.58.215   k8s-node02   <none>           <none>
my-daemonset-demo-tn554   1/1     Running   0          36s   172.16.85.211   k8s-node01   <none>           <none>

DanmonSet 控制器自动创建了两个 Pod,分别运行到两个子节点上(k8s-node01、k8s-node02),而没有运行到 master 节点,这是为什么呢?

3.4 为什么 DeamonSet 没有运行在主节点上

在 Kubernetes 中,DaemonSet 创建的 Pod 默认不会运行到主节点(master 或 control-plane 节点),主要是因为主节点通常被配置了污点(Taints),而 DaemonSet 创建的 Pod 没有默认的容忍(Tolerations)来匹配这些污点。以下是详细原因及机制:

3.4.1 主节点的污点(Taints)

Kubernetes 主节点通常被配置了一个或多个污点,以防止普通的工作负载(包括 DaemonSet 的 Pod)调度到这些节点上。常见的污点是:

  • node-role.kubernetes.io/master:NoSchedule(Kubernetes 1.24 及更早版本)
  • node-role.kubernetes.io/control-plane:NoSchedule(Kubernetes 1.25 及更新版本)

这些污点的效果是 NoSchedule,表示除非 Pod 明确声明可以容忍该污点,否则不会被调度到主节点。

查看主节点污点:

kubectl describe node <master-node-name> | grep -i taint

示例输出:

Taints:             node-role.kubernetes.io/control-plane:NoSchedule

3.4.2 DaemonSet Pod 的容忍(Tolerations)

DaemonSet 的 Pod 模板(spec.template.spec)默认不包含任何 tolerations。这意味着它们无法绕过主节点的 NoSchedule 污点,因此不会被调度到主节点。

例如资源清单 007-DaemonSet-demo.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: my-daemonset-demo # DaemonSet 控制器的名字
  namespace: default # 命名空间
  labels:
    app: daemonset-demo # DaemonSet 控制的标签
spec:
  selector:
    matchLabels:
      name: daemonset-demo # 匹配标签为 name: daemonset-demo 的Pod
  template:
    metadata:
      labels: # Pod模板标签,与 spec.selector.matchLabels 对应
        name: daemonset-demo
    spec:
      containers: # 主容器
        - name: daemonset-demo-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent

在这个配置中,spec.template.spec 没有定义 tolerations,因此 Pod 不会调度到带有 NoSchedule 污点的主节点。

3.4.3 为什么主节点需要污点?

主节点运行控制平面组件(如 kube-apiserver、kube-scheduler、kube-controller-manager),这些组件对集群的稳定性至关重要。允许普通工作负载(如 DaemonSet 的 Pod)运行在主节点可能会导致以下问题:

  • 资源竞争:DaemonSet Pod 可能消耗 CPU、内存等资源,影响控制平面组件的性能。
  • 稳定性风险:某些 DaemonSet Pod(例如日志收集或监控代理)可能出现异常,干扰主节点的运行。
  • 安全隔离:主节点通常需要更高的隔离级别,避免不受信任的工作负载运行。

因此,Kubernetes 默认通过污点机制保护主节点,只允许特定的、必要的 Pod(如控制平面组件或特定的系统 DaemonSet)运行。

3.4.4 例外情况:系统 DaemonSet

某些系统级的 DaemonSet(如 kube-proxy、CNI 插件)确实需要运行在所有节点上,包括主节点。这些 DaemonSet 的 YAML 通常会显式配置 tolerations 来容忍主节点的污点。例如:

spec:
  template:
    spec:
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule

这种配置允许 Pod 忽略主节点的污点,从而调度到主节点。

3.4.5 如何让 DaemonSet Pod 运行在主节点?

如果你希望 DaemonSet 的 Pod 运行在主节点上,可以在 DaemonSet 的 Pod 模板中添加相应的 tolerations。

查看节点列表

$ kubectl get nodes --show-labels
NAME         STATUS   ROLES           AGE   VERSION    LABELS
k8s-node01   Ready    <none>          14d   v1.29.15   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node01,kubernetes.io/os=linux
k8s-node02   Ready    <none>          14d   v1.29.15   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node02,kubernetes.io/os=linux
node         Ready    control-plane   14d   v1.29.15   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=

可以看到有三个节点,其中 name为 node 的就是 master 节点,因为该节点的标签中包含:node-role.kubernetes.io/control-plane

查看 master 节点详情

$ kubectl describe nodes node
Name:               node
Roles:              control-plane
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=node
                    kubernetes.io/os=linux
                    node-role.kubernetes.io/control-plane=
                    node.kubernetes.io/exclude-from-external-load-balancers=
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: unix:///var/run/cri-dockerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    projectcalico.org/IPv4Address: 192.168.6.139/24
                    projectcalico.org/IPv4IPIPTunnelAddr: 172.16.167.128
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Fri, 28 Mar 2025 17:24:27 +0800
# Node自身设置的污点,key为node-role.kubernetes.io/control-plane,影响效果::NoSchedule(不是key的值)
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  node
  AcquireTime:     <unset>
  RenewTime:       Sat, 12 Apr 2025 14:07:56 +0800
...

资源清单内容如下:
008-DaemonSet-demo.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: my-daemonset-demo # DaemonSet 控制器的名字
  namespace: default # 命名空间
  labels:
    app: daemonset-demo # DaemonSet 控制的标签
spec:
  selector:
    matchLabels:
      name: daemonset-demo # 匹配标签为 name: daemonset-demo 的Pod
  template:
    metadata:
      labels: # Pod模板标签,与 spec.selector.matchLabels 对应
        name: daemonset-demo
    spec:
      tolerations: # 定义了 Pod 的容忍度,允许 Pod 被调度到带有特定污点的节点上
        - key: node-role.kubernetes.io/control-plane # 这里允许 Pod 被调度到控制平面节点(通常有 node-role.kubernetes.io/control-plane:NoSchedule 污点)
          operator: Exists # 表示只要存在指定的 key 就容忍(Exists:只要节点上有这个键的污点就匹配(不需要检查值), 如果是Equal:要求键和值都匹配(此时需要指定 value 字段))
          effect: NoSchedule # 指定要容忍污点的影响效果
      containers: # 主容器
        - name: daemonset-demo-container
          image: wangyanglinux/myapp:v1.0
          imagePullPolicy: IfNotPresent

创建 DaemonSet

# 创建DS
$ kubectl apply -f 008-DaemonSet-demo.yaml --record

# 查看DS,可以看到DS运行了三个 Pod
$ kubectl get ds -o wide
NAME                DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE     CONTAINERS                 IMAGES                     SELECTOR
my-daemonset-demo   3         3         3       3            3           <none>          2m33s   daemonset-demo-container   wangyanglinux/myapp:v1.0   name=daemonset-demo

# 查看Pod(master节点也运行了一个Pod)
$ kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE   IP                NODE         NOMINATED NODE   READINESS GATES
my-daemonset-demo-gsjlk   1/1     Running   0          11s   192.168.167.141   node         <none>           <none>
my-daemonset-demo-j5nls   1/1     Running   0          11s   192.168.58.200    k8s-node02   <none>           <none>
my-daemonset-demo-vv9cf   1/1     Running   0          11s   192.168.85.204    k8s-node01   <none>           <none>

查看Pod

# 查看 DaemonSet,此时 DeamonSet 控制器有了三个Pod
$ kubectl get ds -o wide
NAME                DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE    CONTAINERS                 IMAGES                     SELECTOR
my-daemonset-demo   3         3         3       3            3           <none>          135m   daemonset-demo-container   wangyanglinux/myapp:v1.0   name=daemonset-demo

# 查看 Pod
$ kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS       AGE    IP                NODE         NOMINATED NODE   READINESS GATES
my-daemonset-demo-gsjlk   1/1     Running   1 (129m ago)   137m   192.168.167.142   node         <none>           <none>
my-daemonset-demo-j5nls   1/1     Running   1 (129m ago)   137m   192.168.58.201    k8s-node02   <none>           <none>
my-daemonset-demo-vv9cf   1/1     Running   1 (129m ago)   137m   192.168.85.205    k8s-node01   <none>           <none>

master 节点上也运行了一个 Pod,资源清单配置生效。

五、Job 控制器

1. Job 控制器特性

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

特殊说明

  • spec.template 格式同 Pod
  • RestartPolicy 仅支持 Never 或 OnFailure
  • 单个 Pod 时,默认 Pod 成功运行后 Job 即结束
  • .spec.completions 标志 Job 结束需要成功运行的 Pod 个数,默认为 1
  • .spec.parallelism 标志并行运行的 Pod 的个数,默认为 1
  • spec.activeDeadlineSeconds 设置了 Job 的最长活动时间(单位:秒)。这个计时器从 Job 被创建时开始计时:
    • 超时强制终止:如果 Job 运行时间超过 10 秒(包括 Pod 创建、执行、重试等所有时间),整个 Job 会被 Kubernetes 强制终止。
    • 状态标记:超时后 Job 的状态会变为 Failed,并显示原因 Reason: DeadlineExceeded

2. 案例演示

求 π 值,算法:马青公式

马青公式

这个公式由英国天文学教授 约翰·马青 于 1706 年发现。他利用这个公式计算到了 100 位的圆周率。马青公式每计算一项可以得到 1.4 位的 十进制精度。因为它的计算过程中被乘数和被除数都不大于长整数,所以可以很容易地在计算机上编程实现

2.1 Python代码

main.py

# -*- coding: utf-8 -*-
from __future__ import division
# 导入时间模块
import time
# 计算当前时间
time1=time.time()
# 算法根据马青公式计算圆周率 #
number = 1000
# 多计算10位,防止尾数取舍的影响
number1 = number+10
# 算到小数点后number1位
b = 10**number1
# 求含4/5的首项
x1 = b*4//5
# 求含1/239的首项
x2 = b // -239
# 求第一大项
he = x1+x2
#设置下面循环的终点,即共计算n项
number *= 2
#循环初值=3,末值2n,步长=2
for i in xrange(3,number,2):
  # 求每个含1/5的项及符号
  x1 //= -25
  # 求每个含1/239的项及符号
  x2 //= -57121
  # 求两项之和
  x = (x1+x2) // i
  # 求总和
  he += x
# 求出π
pai = he*4
#舍掉后十位
pai //= 10**10
# 输出圆周率π的值
paistring=str(pai)
result=paistring[0]+str('.')+paistring[1:len(paistring)]
print result
time2=time.time()
print u'Total time:' + str(time2 - time1) + 's'

2.2 Dockerfile

FROM python:2.7
ADD ./main.py /root
CMD /usr/bin/python /root/main.py

2.3 构建镜像

# 将 main.py 放到 Dockerfile 同级目录下,并在此目录执行下面的命令构建镜像
docker build -t tools:maqingpythonv1 .

2.4 资源清单

009-job-demo.yaml

apiVersion: batch/v1
kind: Job # Job控制器资源类型
metadata:
  name: job-demo
  namespace: default
  labels:
    app: my-job
spec:
  template:
    metadata:
      name: my-job-pod
      labels:
        app: my-job
    spec:
      containers:
        - name: job-demo-container
          image: wangyanglinux/tools:maqingpythonv1
      restartPolicy: Never # 对于 Job 控制器,容器重启策略仅支持 Never 和 OnFailure

查看日志,可以显示出打印的 2000 位 π 值

2.5 运行Pod,查看日志

运行Pod

# 运行job控制器
$ kubectl apply -f 009-job-demo.yaml

查看Job运行状态

# 查看job列表
$ kubectl get job
NAME       COMPLETIONS   DURATION   AGE
job-demo   1/1           3s         6m48s

# 查看Job详情
$ kubectl describe job job-demo
Name:             job-demo
Namespace:        default
Selector:         batch.kubernetes.io/controller-uid=ea1dec0d-b8d4-4942-8b72-2d3ac0abde91
Labels:           app=my-job
Annotations:      <none>
Parallelism:      1
Completions:      1
Completion Mode:  NonIndexed
Start Time:       Thu, 17 Apr 2025 21:32:13 +0800
Completed At:     Thu, 17 Apr 2025 21:32:16 +0800
Duration:         3s
Pods Statuses:    0 Active (0 Ready) / 1 Succeeded / 0 Failed
Pod Template:
  Labels:  app=my-job
           batch.kubernetes.io/controller-uid=ea1dec0d-b8d4-4942-8b72-2d3ac0abde91
           batch.kubernetes.io/job-name=job-demo
           controller-uid=ea1dec0d-b8d4-4942-8b72-2d3ac0abde91
           job-name=job-demo
  Containers:
   job-demo-container:
    Image:        wangyanglinux/tools:maqingpythonv1
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age    From            Message
  ----    ------            ----   ----            -------
  Normal  SuccessfulCreate  6m25s  job-controller  Created pod: job-demo-6q4sh
  Normal  Completed         6m22s  job-controller  Job completed

通过查看Job详情,观察到 Job 已运行完成

查看Pod列表

$ kubectl get pods -o wide
NAME             READY   STATUS      RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
job-demo-6q4sh   0/1     Completed   0          7m58s   172.16.58.217   k8s-node02   <none>           <none>

查看Pod运行日志

$ kubectl logs job-demo-6q4sh -f --tail=1000
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198

打印出了圆周率!!!

3. Job - 正常退出完成

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

3.1 案例一:让容器返回码为1

目的:指定容器退出的返回码为1,验证当返回码不为0时,Job会异常退出。

010-job-rand-demo.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: rand
  namespace: default
spec:
  completions: 1 # 标志Job结束需要成功运行的 Pod 个数,默认为1
  parallelism: 5 # 标志并行运行的Pod的个数,默认为1
  # activeDeadlineSeconds: 10 # 标志失败Pod 的重试最大时间,由于Pod重启策略为Never,因此超过 10秒,整个Pod强制中止
  template:
    metadata:
      name: rand
    spec:
      containers:
        - name: rand
          image: wangyanglinux/tools:randexitv1
          imagePullPolicy: IfNotPresent
          # 指定退出码
          args: ["--exitcode=1"]
      restartPolicy: Never

运行Pod,查看结果

$ kubectl apply -f 010-job-rand-demo.yaml

监控Pod运行结果

$ kubectl get pod -o wide -w
NAME         READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
rand-4ftcj   0/1     Error     0          27s   172.16.58.225   k8s-node02   <none>           <none>
rand-76fkv   1/1     Running   0          2s    172.16.58.224   k8s-node02   <none>           <none>
rand-zd9bp   0/1     Error     0          42s   172.16.58.228   k8s-node02   <none>           <none>
rand-76fkv   0/1     Error     0          6s    172.16.58.224   k8s-node02   <none>           <none>
rand-76fkv   0/1     Error     0          7s    172.16.58.224   k8s-node02   <none>           <none>

上面的Pod监控列表,一个Pod处于运行中,两个返回都是失败(运行中的Pod也会返回失败,因为在资源清单中制定了返回码为 1)。

由于在资源清单中指定了容器退出返回码为1,所以此清单创建的所有Pod都会返回失败。

查看Pod日志

$ kubectl logs rand-4ftcj -f --tail=100
休眠 4 秒,返回码为 1!

$ kubectl logs rand-76fkv -f --tail=100
休眠 4 秒,返回码为 1!

结论:只有当Pod容器返回码不为0为,Pod异常退出。

3.2 案例二:随机生成返回码

目的:当Pod容器返回码为1是,Pod成功退出。

011-job-rand-demo.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: rand
  namespace: default
spec:
  completions: 3 # 标志Job结束需要成功运行的 Pod 个数,默认为1
  parallelism: 5 # 标志并行运行的Pod的个数,默认为1
  template:
    metadata:
      name: rand
    spec:
      containers:
        - name: rand
          image: wangyanglinux/tools:randexitv1
          imagePullPolicy: IfNotPresent
      restartPolicy: Never

运行Pod,查看结果

# 运行Pod
$ kubectl apply -f 011-job-rand-demo.yaml

查看Pod运行列表

$ kubectl get pods
NAME         READY   STATUS    RESTARTS   AGE
rand-4jcmh   1/1     Running   0          5s
rand-dfnf9   1/1     Running   0          5s
rand-sh9vp   1/1     Running   0          5s

有3个Pod 并行运行,符合资源清单设置内容 spec.parallelism: 5

等待Job运行结束后,再次查看Pod列表

$ kubectl get pod -o wide
NAME         READY   STATUS      RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
rand-4jcmh   0/1     Error       0          4m43s   172.16.58.238   k8s-node02   <none>           <none>
rand-8kwhc   0/1     Completed   0          4m25s   172.16.58.242   k8s-node02   <none>           <none>
rand-dfnf9   0/1     Completed   0          4m43s   172.16.58.229   k8s-node02   <none>           <none>
rand-hk9lw   0/1     Completed   0          4m25s   172.16.58.239   k8s-node02   <none>           <none>
rand-sh9vp   0/1     Error       0          4m43s   172.16.85.215   k8s-node01   <none>           <none>

一共运行了5个Pod,其中3个运行完成,2个返回失败

查看Job

$ kubectl get job -o wide
NAME   COMPLETIONS   DURATION   AGE   CONTAINERS   IMAGES                           SELECTOR
rand   3/3           26s        48s   rand         wangyanglinux/tools:randexitv1   batch.kubernetes.io/controller-uid=76957688-7f47-4694-a451-9adfb4f9c9cd

Job 有3个Pod 运行完成,成功退出,满足资源清单设置要求:spec.completions: 3

六、CronJob 控制器

1. CronJob 控制器特性

Cron Job 管理基于时间的 Job,即:

  • 在给定时间点只运行一次

  • 周期性地在给定时间点运行

使用条件:当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)

典型的用法如下所示:

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

2. CroneJob 资源清单

  • .spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron

  • .spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job

  • .spec.startingDeadlineSeconds :启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限

  • .spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:

    • Allow(默认):允许并发运行 Job
    • Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个
    • Replace:取消当前正在运行的 Job,用一个新的来替换
    • 注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job, 它们创建的 Job 之间总是允许并发运行。
  • .spec.suspend :挂起,该字段也是可选的。如果设置为 true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false

  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit :历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 31。设置限制的值为 0,相关类型的 Job 完成后将不会被保留

3. 案例演示

3.1 资源清单

012-cronjob-demo.yaml

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cronjob-demo
  namespace: default
spec:
  # 调度,必需字段,指定任务运行周期,格式同 Cron
  schedule: "*/1 * * * *" # 每分钟执行一次
  startingDeadlineSeconds: 30 # 启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
  concurrencyPolicy: Allow # 并发策略,该字段也是可选的。(默认)允许并发运行 Job
  successfulJobsHistoryLimit: 10 # 保留运行成功的job数,默认:3
  failedJobsHistoryLimit: 3 #保留云心告失败的Job数,默认:1
  # Job 模板,必需字段,指定需要运行的任务,格式同 Job
  jobTemplate:
    spec:
      completions: 3 # 标志Job结束需要成功运行的 Pod 个数,默认为1
      parallelism: 3 # 标志并行运行的Pod的个数,默认为1
      template:
        spec:
          containers:
            - name: cronjob-demo-container
              image: busybox
              imagePullPolicy: IfNotPresent
              args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure #  重启策略:失败重启

启动 CronJob

$ kubectl apply -f 012-cronjob-demo.yaml

查看结果

# 查看Pod运行列表,完成了3个Pod
$ kubectl get pods -o wide
NAME                          READY   STATUS      RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
cronjob-demo-29082370-8zl5f   0/1     Completed   0          15s   172.16.58.244   k8s-node02   <none>           <none>
cronjob-demo-29082370-ht2mk   0/1     Completed   0          15s   172.16.85.217   k8s-node01   <none>           <none>
cronjob-demo-29082370-mm7kc   0/1     Completed   0          15s   172.16.58.247   k8s-node02   <none>           <none>

# 查看 cronjob 控制器(cronjob 每分钟都会执行一次)
$ kubectl get cronjob -o wide
NAME           SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE     CONTAINERS               IMAGES    SELECTOR
cronjob-demo   */1 * * * *   False     0        52s             2m14s   cronjob-demo-container   busybox   <none>

# 过一会再次查看Pod列表,又多了三个Pod,因为 cronjob 设置每次完成需要 3个pod,spec.completions: 3
$ kubectl get pods -o wide
NAME                          READY   STATUS      RESTARTS   AGE    IP              NODE         NOMINATED NODE   READINESS GATES
cronjob-demo-29082370-8zl5f   0/1     Completed   0          114s   172.16.58.244   k8s-node02   <none>           <none>
cronjob-demo-29082370-ht2mk   0/1     Completed   0          114s   172.16.85.217   k8s-node01   <none>           <none>
cronjob-demo-29082370-mm7kc   0/1     Completed   0          114s   172.16.58.247   k8s-node02   <none>           <none>
cronjob-demo-29082371-68kdz   0/1     Completed   0          54s    172.16.58.243   k8s-node02   <none>           <none>
cronjob-demo-29082371-9b5j6   0/1     Completed   0          54s    172.16.58.251   k8s-node02   <none>           <none>
cronjob-demo-29082371-cf2dg   0/1     Completed   0          54s    172.16.85.216   k8s-node01   <none>           <none>

4. CronJob - 限制

创建 Job 操作应该是幂等的

七、StatefulSet 控制器


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 george_95@126.com