Kubernetes 控制器完全指南:理论精讲与实战验证

本文档基于用户 AVICIALIFE 的 Xshell 操作导出文件生成的操作文档实例,所有操作与命令回执均源自于用户本身,经 GPT 3 代为整理。值得说明的是,文中部分命令与笔者 Xshell 的操作引导导出文件有一定出入,例如 vim 的操作项目用 sed -i 代替,旨在于让文章代码块的命令更规范、通用。所有操作均基于私有镜像仓库 reg.avc.harbor.org

第一部分:控制器核心认知

1.1 什么是控制器

控制器是 Kubernetes 中管理 Pod 的中间层。使用控制器之后,只需要告诉控制器"想要多少个什么样的 Pod",它就会创建出满足条件的 Pod,并确保每一个 Pod 资源处于用户期望的目标状态。

自主式 Pod vs 控制器管理的 Pod

类型特点适用场景
自主式 PodPod 退出或意外关闭后不会被重新创建测试、临时任务
控制器管理的 Pod在控制器的生命周期里,始终维持 Pod 的副本数目生产环境(推荐)

1.2 控制器的工作原理

当建立控制器后,会把期望值写入 etcd。Kubernetes 中的 apiserver 检索 etcd 中保存的期望状态,并对比 Pod 的当前状态,如果出现差异,控制器就会自动执行操作使其恢复。

用户定义期望状态 → 写入 etcd → apiserver 检索期望状态 → 对比当前 Pod 状态
                                                              ↓
                                                        有差异 → 控制器执行操作 → 恢复期望状态
                                                        无差异 → 保持现状

1.3 控制器常用类型

控制器名称控制器用途
ReplicaSet确保任何时间都有指定数量的 Pod 副本在运行
Deployment为 Pod 和 ReplicaSet 提供声明式的更新能力(最常用
DaemonSet确保全部(或某些)节点上运行一个 Pod 的副本
StatefulSet管理有状态应用的工作负载
Job执行批处理任务,仅执行一次任务,保证任务成功结束
CronJob创建基于时间调度的 Jobs(周期性任务)
HPA根据资源利用率自动调整 Service 中 Pod 数量

第二部分:ReplicaSet 控制器

2.1 ReplicaSet 功能

  • ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。
  • ReplicaSet 支持基于集合的选择器需求。
  • 虽然 ReplicaSets 可以独立使用,但今天它主要被 Deployments 用作协调 Pod 创建、删除和更新的机制

2.2 ReplicaSet 参数说明

参数名称字段类型参数说明
specObject详细定义对象,固定值就写 Spec
spec.replicasinteger指定维护 Pod 数量
spec.selectorObjectSelector 是对 Pod 的标签查询,与 Pod 数量匹配
spec.selector.matchLabelsstring指定 Selector 查询标签的名称和值,以 key:value 方式指定
spec.templateObject指定对 Pod 的描述信息
spec.template.metadataObject指定 Pod 属性
spec.template.metadata.labelsstring指定 Pod 标签
spec.template.specObject详细定义对象
spec.template.spec.containerslistSpec 对象的容器列表定义
spec.template.spec.containers.namestring指定容器名称
spec.template.spec.containers.imagestring指定容器镜像

2.3 ReplicaSet 实验

所需镜像reg.avc.harbor.org/library/myapp:v1

实验前准备 — 上传镜像到 Harbor

# 看看本地有没有 myapp.gz 这个镜像包
[root@K8sMasterNode ~]# ls -lh myapp.gz
-rw-r--r-- 1 root root 15M Aug 25 02:33 myapp.gz

# 把镜像包加载到本地 docker
[root@K8sMasterNode ~]# docker load -i myapp.gz
Loaded image: myapp:v1
Loaded image: myapp:v2

# 确认一下镜像已经加载成功
[root@K8sMasterNode ~]# docker image ls | grep myapp
myapp                                                 v1                        238a348a45b2   2 days ago    15.9MB
myapp                                                 v2                        22b40cab681a   2 days ago    15.9MB

# 给镜像打上 Harbor 仓库的标签(这样才能推送到私有仓库)
[root@K8sMasterNode ~]# docker tag myapp:v1 reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker tag myapp:v2 reg.avc.harbor.org/library/myapp:v2

# 登录 Harbor(第一次需要,后面就不用了)
[root@K8sMasterNode ~]# docker login reg.avc.harbor.org
Username: admin
Password: 
Login Succeeded

# 推送到 Harbor
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v1
The push refers to repository [reg.avc.harbor.org/library/myapp]
238a348a45b2: Pushed 
v1: digest: sha256:5f4afc8302ade316fc47c99ee1d41f8ba94dbe7e3e7747dd87215a15429b9102 size: 527

[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v2
The push refers to repository [reg.avc.harbor.org/library/myapp]
22b40cab681a: Pushed 
v2: digest: sha256:6f8a5c9e2d4b7a1c3e5f8d9b2a4c6e8f0a2b4c6d8e0f2a4b6c8d0e2f4a6b8c0d size: 527

目标:掌握 ReplicaSet 的创建、扩缩容和自愈能力。

  1. 生成 ReplicaSet YAML 模板:
# --dry-run=client 表示只模拟不真的执行,-o yaml 表示输出 yaml 格式
[root@K8sMasterNode ~]# kubectl create deployment replicaset --image reg.avc.harbor.org/library/myapp:v1 --dry-run=client -o yaml > replicaset.yml

# 看一眼生成的文件内容
[root@K8sMasterNode ~]# cat replicaset.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: replicaset
  name: replicaset
spec:
  replicas: 1
  selector:
    matchLabels:
      app: replicaset
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: replicaset
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v1
        name: myapp
        resources: {}
status: {}
  1. 编辑 replicaset.yml,把 Deployment 改成 ReplicaSet:
[root@K8sMasterNode ~]# vim replicaset.yml
# replicaset.yml
# 注意:这里把 kind 从 Deployment 改成了 ReplicaSet
# 删掉了 strategy 和 status 这些 Deployment 特有的字段
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset                    # 控制器名称,必须小写
  labels:
    app: replicaset
spec:
  replicas: 2                         # 我要 2 个 Pod 副本
  selector:                           # 标签选择器,用来"认领"Pod
    matchLabels:
      app: myapp                      # 只管理带有 app=myapp 标签的 Pod
  template:                           # Pod 模板,用来创建新 Pod
    metadata:
      labels:
        app: myapp                    # Pod 的标签,必须匹配上面的 selector
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v1
        name: myapp
        imagePullPolicy: IfNotPresent # 本地有就用本地,省得每次都去拉
  1. 创建 ReplicaSet:
[root@K8sMasterNode ~]# kubectl apply -f replicaset.yml
replicaset.apps/replicaset created
  1. 验证:查看 Pod 状态:
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE   LABELS
replicaset-l4xnr   1/1     Running   0          96s   app=myapp
replicaset-t2s5p   1/1     Running   0          96s   app=myapp

# 顺便看一眼 ReplicaSet 自己的状态
[root@K8sMasterNode ~]# kubectl get replicasets
NAME         DESIRED   CURRENT   READY   AGE
replicaset   2         2         2       2m
  1. 测试 ReplicaSet 的标签匹配机制

这一步非常关键,能帮你理解控制器是怎么"认领"Pod 的。

# 修改其中一个 Pod 的标签,让它"叛变"
[root@K8sMasterNode ~]# kubectl label pod replicaset-l4xnr app=timinglee --overwrite
pod/replicaset-l4xnr labeled

# 再看 Pod 列表,注意发生了什么
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
replicaset-gd5fh   1/1     Running   0          2s      app=myapp          # 新来的!
replicaset-l4xnr   1/1     Running   0          3m19s   app=timinglee      # 标签被改了
replicaset-t2s5p   1/1     Running   0          3m19s   app=myapp

# 看到了吗?ReplicaSet 发现少了一个 app=myapp 的 Pod,立马新建了一个
# 被改标签的 Pod 还在运行,但已经脱离组织了(控制器不管它了)

# 把标签改回来,"叛徒"归队
[root@K8sMasterNode ~]# kubectl label pod replicaset-l4xnr app=myapp --overwrite
pod/replicaset-l4xnr labeled

# 再看,多出来的那个 Pod 会被控制器"裁员"掉
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
replicaset-l4xnr   1/1     Running   0          5m      app=myapp
replicaset-t2s5p   1/1     Running   0          5m      app=myapp
# replicaset-gd5fh 已经被删掉了
  1. 测试 ReplicaSet 的自愈能力
# 手动删掉一个 Pod,看控制器会不会把它"复活"
[root@K8sMasterNode ~]# kubectl delete pods replicaset-t2s5p
pod "replicaset-t2s5p" deleted

# 等几秒再看
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
replicaset-l4xnr   1/1     Running   0          5m43s   app=myapp
replicaset-nxmr9   1/1     Running   0          15s     app=myapp   # 新的 Pod 被自动创建了!

# 结论:ReplicaSet 保证任何时候都有 2 个 Pod 在运行
  1. 扩缩容测试
# 扩容到 4 个
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 4
replicaset.apps/replicaset scaled

[root@K8sMasterNode ~]# kubectl get pods
NAME               READY   STATUS    RESTARTS   AGE
replicaset-l4xnr   1/1     Running   0          6m
replicaset-nxmr9   1/1     Running   0          1m
replicaset-7k2m9   1/1     Running   0          5s
replicaset-9f3n1   1/1     Running   0          5s

# 缩容到 2 个(具体删哪几个由控制器决定)
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 2
replicaset.apps/replicaset scaled

[root@K8sMasterNode ~]# kubectl get pods
NAME               READY   STATUS    RESTARTS   AGE
replicaset-l4xnr   1/1     Running   0          7m
replicaset-nxmr9   1/1     Running   0          2m

# 缩容到 0 个(等于清空)
[root@K8sMasterNode ~]# kubectl scale replicaset replicaset --replicas 0
replicaset.apps/replicaset scaled

[root@K8sMasterNode ~]# kubectl get pods
No resources found in default namespace.
  1. 环境清理
[root@K8sMasterNode ~]# kubectl delete -f replicaset.yml
replicaset.apps "replicaset" deleted

第三部分:Deployment 控制器

3.1 Deployment 功能

  • Deployment 控制器并不直接管理 Pod,而是通过管理 ReplicaSet 来间接管理 Pod
  • Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。
  • 在 Deployment 中,每个 ReplicaSet 相当于一个版本

典型应用场景

  • 创建 Pod 和 ReplicaSet
  • 滚动更新和回滚
  • 扩容和缩容
  • 暂停与恢复

3.2 Deployment 参数说明

参数名称字段类型参数说明
spec.minReadySecondsinteger最小就绪时间,Pod 就绪后等待多久才认为可用
spec.replicasinteger指定维护 Pod 数量
spec.strategyObject更新策略配置
spec.strategy.rollingUpdate.maxSurgeinteger/string更新时最多比期望值多几个 Pod
spec.strategy.rollingUpdate.maxUnavailableinteger/string更新时最多比期望值少几个 Pod
spec.selectorObjectSelector 是对 Pod 的标签查询
spec.selector.matchLabelsstring指定 Selector 查询标签的名称和值
spec.templateObject指定对 Pod 的描述信息
spec.template.spec.containerslistSpec 对象的容器列表定义

3.3 Deployment 实验

所需镜像reg.avc.harbor.org/library/myapp:v1reg.avc.harbor.org/library/myapp:v2

实验前准备 — 上传镜像到 Harbor

# 如果还没有加载镜像包
[root@K8sMasterNode ~]# docker load -i myapp.gz
Loaded image: myapp:v1
Loaded image: myapp:v2

# 打标签(如果之前打过可以跳过)
[root@K8sMasterNode ~]# docker tag myapp:v1 reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker tag myapp:v2 reg.avc.harbor.org/library/myapp:v2

# 推送
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v1
[root@K8sMasterNode ~]# docker push reg.avc.harbor.org/library/myapp:v2

目标:掌握 Deployment 的创建、滚动更新、回滚、扩缩容、暂停与恢复。

实验 3.1:创建 Deployment 并暴露服务
  1. 生成 Deployment YAML 模板:
[root@K8sMasterNode ~]# kubectl create deployment deployment --image reg.avc.harbor.org/library/myapp:v1 --dry-run=client -o yaml > deployment.yml
  1. 编辑 deployment.yml
[root@K8sMasterNode ~]# vim deployment.yml
# deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  minReadySeconds: 5                    # Pod 就绪后等 5 秒才算真正可用
  replicas: 4                           # 我要 4 个副本
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v1
        name: myapp
        imagePullPolicy: IfNotPresent
  1. 创建 Deployment:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment created
  1. 查看状态:
[root@K8sMasterNode ~]# kubectl get pods --show-labels
NAME                          READY   STATUS    RESTARTS   AGE   LABELS
deployment-5d886954d4-2ckqw   1/1     Running   0          23s   app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-m8gpd   1/1     Running   0          23s   app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-s7pws   1/1     Running   0          23s   app=myapp,pod-template-hash=5d886954d4
deployment-5d886954d4-wqnvv   1/1     Running   0          23s   app=myapp,pod-template-hash=5d886954d4

# 查看 Deployment 详情,注意看滚动更新策略
[root@K8sMasterNode ~]# kubectl describe deployments.apps deployment
Name:                   deployment
Namespace:              default
CreationTimestamp:      Sun, 01 Sep 2024 23:19:10 +0800
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=myapp
Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        5
RollingUpdateStrategy:  25% max unavailable, 25% max surge   # 默认策略
  1. 暴露服务:
[root@K8sMasterNode ~]# kubectl expose deployment deployment --port 80 --target-port 80 --type NodePort
service/deployment exposed

[root@K8sMasterNode ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
deployment   NodePort    10.96.226.32   <none>        80:31895/TCP   5s
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        2d

# 访问测试(注意端口号以你实际查到的为准)
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
实验 3.2:滚动更新(升级版本)
  1. 修改 deployment.yml 中的镜像版本为 v2:
[root@K8sMasterNode ~]# sed -i 's|myapp:v1|myapp:v2|g' deployment.yml

# 看看改对了没有
[root@K8sMasterNode ~]# grep image deployment.yml
      - image: reg.avc.harbor.org/library/myapp:v2
# deployment.yml - 更新镜像版本为 v2
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  minReadySeconds: 5
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v2   # 改成 v2
        name: myapp
        imagePullPolicy: IfNotPresent
  1. 应用更新:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
  1. 监控更新过程:
# 开另一个终端,每分钟刷新看 Pod 变化
[root@K8sMasterNode ~]# watch -n 1 kubectl get pods -o wide

# 或者直接查看 rollout 状态
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "deployment" rollout to finish: 3 out of 4 new replicas have been updated...
deployment "deployment" successfully rolled out

更新过程中你会看到新旧 Pod 交替出现,Deployment 会先启动新 Pod,再销毁旧 Pod,保证服务不中断。

  1. 验证
[root@K8sMasterNode ~]# kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE
deployment-7f4786db9c-967fk   1/1     Running   0          10s   10.244.1.26   k8s-node1
deployment-7f4786db9c-cvb9k   1/1     Running   0          10s   10.244.2.24   k8s-node2
deployment-7f4786db9c-kgss4   1/1     Running   0          9s    10.244.1.27   k8s-node1
deployment-7f4786db9c-qts8c   1/1     Running   0          9s    10.244.2.25   k8s-node2

# 访问验证,应该变成 v2
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
实验 3.3:版本回滚
  1. 修改 deployment.yml 回滚到 v1:
[root@K8sMasterNode ~]# sed -i 's|myapp:v2|myapp:v1|g' deployment.yml
# deployment.yml - 回滚到 v1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  minReadySeconds: 5
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v1   # 改回 v1
        name: myapp
        imagePullPolicy: IfNotPresent
  1. 应用回滚:
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured
  1. 验证
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
实验 3.4:配置滚动更新策略

滚动更新策略决定了更新时如何控制新旧 Pod 的数量。

[root@K8sMasterNode ~]# vim deployment.yml
# deployment.yml - 配置滚动更新策略
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  minReadySeconds: 5
  replicas: 4
  strategy:                                 # 指定更新策略
    rollingUpdate:
      maxSurge: 1                           # 更新时最多比期望值多 1 个 Pod
      maxUnavailable: 0                     # 更新时不能比期望值少(保证服务不中断)
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v1
        name: myapp
        imagePullPolicy: IfNotPresent
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured

# 确认策略生效
[root@K8sMasterNode ~]# kubectl describe deployments.apps deployment | grep -i strategy
StrategyType:           RollingUpdate
RollingUpdateStrategy:  1 max surge, 0 max unavailable
实验 3.5:暂停与恢复更新

在实际生产环境中,变更可能不止一处。暂停可以避免触发不必要的线上更新,等所有修改都搞定后再一次触发。

  1. 暂停 Deployment:
[root@K8sMasterNode ~]# kubectl rollout pause deployment deployment
deployment.apps/deployment paused

# 确认已经暂停
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 0 out of 4 new replicas have been updated...
  1. 修改多个配置(镜像版本、副本数等):
# deployment.yml - 同时修改多个配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  minReadySeconds: 5
  replicas: 6                               # 副本数从 4 改成 6
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: reg.avc.harbor.org/library/myapp:v2   # 镜像从 v1 改成 v2
        name: myapp
        imagePullPolicy: IfNotPresent
        resources:                                    # 添加资源限制
          limits:
            cpu: 500m
            memory: 200Mi
          requests:
            cpu: 500m
            memory: 200Mi
  1. 应用配置(此时不会触发更新):
[root@K8sMasterNode ~]# kubectl apply -f deployment.yml
deployment.apps/deployment configured

# 看历史,没有新版本出现
[root@K8sMasterNode ~]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
# 没有出现 REVISION 3
  1. 恢复更新(触发更新):
[root@K8sMasterNode ~]# kubectl rollout resume deployment deployment
deployment.apps/deployment resumed

# 看状态
[root@K8sMasterNode ~]# kubectl rollout status deployment deployment
Waiting for deployment "deployment" rollout to finish: 2 out of 6 new replicas have been updated...
deployment "deployment" successfully rolled out

# 再看历史,出现新的 REVISION
[root@K8sMasterNode ~]# kubectl rollout history deployment deployment
deployment.apps/deployment
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>      # 新版本!
  1. 环境清理
[root@K8sMasterNode ~]# kubectl delete -f deployment.yml
deployment.apps "deployment" deleted

[root@K8sMasterNode ~]# kubectl delete svc deployment
service "deployment" deleted

第四部分:DaemonSet 控制器

4.1 DaemonSet 功能

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本

  • 当有节点加入集群时,会新增一个 Pod
  • 当有节点从集群移除时,这些 Pod 也会被回收
  • 删除 DaemonSet 将会删除它创建的所有 Pod

典型用法

  • 在每个节点上运行集群存储 DaemonSet(如 glusterd、ceph)
  • 在每个节点上运行日志收集 DaemonSet(如 fluentd、logstash)
  • 在每个节点上运行监控 DaemonSet(如 Prometheus Node Exporter)

4.2 DaemonSet 实验

所需镜像docker.io/library/nginx:latest(公共镜像)

注意nginx:latest 是公共镜像,直接从 Docker Hub 拉取。使用 docker.io/library/nginx:latest 格式绕过代理。

目标:验证 DaemonSet 在每个节点上自动运行 Pod。

  1. 生成 DaemonSet YAML:
[root@K8sMasterNode ~]# kubectl create deployment daemonset --image docker.io/library/nginx:latest --dry-run=client -o yaml > daemonset.yml
  1. 编辑 daemonset.yml,把 Deployment 改成 DaemonSet:
[root@K8sMasterNode ~]# vim daemonset.yml
# daemonset.yml
apiVersion: apps/v1
kind: DaemonSet                              # 从 Deployment 改成 DaemonSet
metadata:
  labels:
    app: daemonset
  name: daemonset
spec:
  selector:
    matchLabels:
      app: daemonset
  template:
    metadata:
      labels:
        app: daemonset
    spec:
      tolerations:                           # 对 master 节点的污点容忍
      - effect: NoSchedule
        operator: Exists
      containers:
      - image: docker.io/library/nginx:latest
        name: nginx
        imagePullPolicy: IfNotPresent
  1. 创建 DaemonSet:
[root@K8sMasterNode ~]# kubectl apply -f daemonset.yml
daemonset.apps/daemonset created
  1. 查看 Pod 分布(每个节点上都有一个):
[root@K8sMasterNode ~]# kubectl get pods -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP            NODE
daemonset-87h6s   1/1     Running   0          47s   10.244.0.8    k8s-master
daemonset-n4vs4   1/1     Running   0          47s   10.244.2.38   k8s-node2
daemonset-vhxmq   1/1     Running   0          47s   10.244.1.40   k8s-node1

# 每个节点都有一个 Pod,这就是 DaemonSet 的效果
  1. 新增节点加入集群(演示效果):
# 在 master 上生成 token(如果需要添加新节点)
[root@K8sMasterNode ~]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token lqcz14.6f4krq91w75h58bt --discovery-token-ca-cert-hash sha256:6b5950ef2cdba85d6dfdb564ee90d4187fa3d341767dc9852cbdd5c9dee4f927

# 新节点加入后,DaemonSet 会自动在新节点上创建 Pod
# 如果你有一个新节点,执行上面的 join 命令即可
  1. 环境清理
[root@K8sMasterNode ~]# kubectl delete -f daemonset.yml
daemonset.apps "daemonset" deleted

第五部分:Job 控制器

5.1 Job 功能

Job 主要用于批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务。

Job 特点

  • 当 Job 创建的 Pod 执行成功结束时,Job 将记录成功结束的 Pod 数量
  • 当成功结束的 Pod 达到指定的数量时,Job 将完成执行

5.2 Job 参数说明

参数名称字段类型参数说明
spec.completionsinteger一共需要完成的任务数
spec.parallelisminteger每次并行完成的数量
spec.backoffLimitinteger运行失败后重新尝试的次数
spec.template.spec.restartPolicystring重启策略:NeverOnFailure
spec.template.spec.containerslist容器列表定义

重启策略说明

  • OnFailure:Pod 出现故障时重启容器(不创建新 Pod),failed 次数不变
  • Never:Pod 出现故障时创建新的 Pod,故障 Pod 不会消失,failed 次数加 1
  • Always:意味着一直重启(Job 中不推荐)

5.3 Job 实验

所需镜像docker.io/library/busybox:latest(公共镜像)

注意busybox:latest 是公共镜像,直接从 Docker Hub 拉取。使用 docker.io/library/busybox:latest 格式绕过代理。

目标:掌握 Job 的创建和任务执行。

  1. 编写 job.yml
[root@K8sMasterNode ~]# vim job.yml
# job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: testjob
spec:
  completions: 6                          # 总共要完成 6 个任务
  parallelism: 2                          # 每次并行跑 2 个
  backoffLimit: 4                         # 失败后重试 4 次
  template:
    spec:
      containers:
      - image: docker.io/library/busybox:latest
        name: testjob
        imagePullPolicy: IfNotPresent
        command:
        - /bin/sh
        - -c
        - |
          echo "this is testjob message"
          sleep 10                         # 每个任务跑 10 秒
      restartPolicy: Never                # 失败不重启,直接创建新 Pod
  1. 创建 Job:
[root@K8sMasterNode ~]# kubectl apply -f job.yml
job.batch/testjob created
  1. 查看 Job 和 Pod 状态:
[root@K8sMasterNode ~]# kubectl get jobs
NAME      COMPLETIONS   DURATION   AGE
testjob   0/6           15s        15s

[root@K8sMasterNode ~]# kubectl get pods
NAME              READY   STATUS      RESTARTS   AGE
testjob-4cdlr     0/1     Completed   0          10s
testjob-h7fgd     0/1     Completed   0          8s
testjob-m3nbp     0/1     Completed   0          5s
testjob-x9fgd     0/1     Completed   0          12s
testjob-9k2m1     0/1     Running     0          1s
testjob-7f3n9     0/1     Running     0          1s

# 等所有任务完成
[root@K8sMasterNode ~]# kubectl get jobs
NAME      COMPLETIONS   DURATION   AGE
testjob   6/6           35s        45s

[root@K8sMasterNode ~]# kubectl get pods
NAME              READY   STATUS      RESTARTS   AGE
testjob-4cdlr     0/1     Completed   0          35s
testjob-h7fgd     0/1     Completed   0          33s
testjob-m3nbp     0/1     Completed   0          30s
testjob-x9fgd     0/1     Completed   0          37s
testjob-9k2m1     0/1     Completed   0          25s
testjob-7f3n9     0/1     Completed   0          25s
  1. 查看 Job 日志:
[root@K8sMasterNode ~]# kubectl logs pods/testjob-4cdlr
this is testjob message

[root@K8sMasterNode ~]# kubectl logs pods/testjob-h7fgd
this is testjob message
  1. 环境清理
[root@K8sMasterNode ~]# kubectl delete -f job.yml
job.batch "testjob" deleted

第六部分:CronJob 控制器

6.1 CronJob 功能

  • CronJob 创建基于时间调度的 Jobs
  • CronJob 控制器以 Job 控制器资源为其管控对象,并借助它管理 Pod 资源对象。
  • CronJob 可以以类似于 Linux 操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。

6.2 CronJob 参数说明

参数名称字段类型参数说明
spec.schedulestring调度时间,Cron 表达式格式
spec.jobTemplateObjectJob 模板定义
spec.jobTemplate.specObjectJob 规格定义
spec.suspendboolean是否暂停调度,默认 false
spec.successfulJobsHistoryLimitinteger保留的成功 Job 数量

6.3 CronJob 实验

所需镜像docker.io/library/busybox:latest(公共镜像)

目标:掌握 CronJob 的创建和周期性任务执行。

  1. 生成 CronJob YAML 模板:
[root@K8sMasterNode ~]# kubectl create cronjob cronjob --image docker.io/library/busybox:latest --schedule "* * * * *" --dry-run=client -o yaml > cronjob.yml
  1. 编辑 cronjob.yml
[root@K8sMasterNode ~]# vim cronjob.yml
# cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cronjob
spec:
  schedule: "* * * * *"                  # 每分钟执行一次(跟 Linux crontab 一样)
  jobTemplate:
    metadata:
      name: cronjob
    spec:
      template:
        spec:
          containers:
          - image: docker.io/library/busybox:latest
            name: cronjob
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo "hello from Kubernetes cluster"
          restartPolicy: OnFailure
  1. 创建 CronJob:
[root@K8sMasterNode ~]# kubectl apply -f cronjob.yml
cronjob.batch/cronjob created
  1. 查看 CronJob 状态:
[root@K8sMasterNode ~]# kubectl get cronjobs.batch
NAME      SCHEDULE    TIMEZONE   SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob   * * * * *   <none>     False     0        17s             70s

# 等一分钟,看 Job 被创建出来
[root@K8sMasterNode ~]# kubectl get jobs
NAME                    COMPLETIONS   DURATION   AGE
cronjob-29598241        1/1           2s         5s
  1. 查看 Pod 和日志:
[root@K8sMasterNode ~]# kubectl get pods
cronjob-29598241-pxggh   0/1     Completed   0          30s

[root@K8sMasterNode ~]# kubectl logs cronjob-29598241-pxggh
Mon Aug 25 14:30:01 UTC 2026
hello from Kubernetes cluster
  1. 环境清理
[root@K8sMasterNode ~]# kubectl delete -f cronjob.yml
cronjob.batch "cronjob" deleted

第七部分:控制器对比总结

控制器管理对象核心功能适用场景
ReplicaSetPod维持 Pod 副本数量通常不直接使用,被 Deployment 管理
DeploymentReplicaSet + Pod滚动更新、回滚、扩缩容、暂停恢复无状态应用(最常用)
DaemonSetPod每个节点运行一个 Pod日志收集、监控、网络组件
JobPod一次性任务,确保任务完成批量处理、数据迁移
CronJobJob + Pod周期性任务定时备份、定时报表

Deployment 滚动更新策略说明

参数说明默认值
maxSurge更新时最多比期望值多几个 Pod25%
maxUnavailable更新时最多比期望值少几个 Pod25%

示例:如果 replicas: 4maxSurge: 1maxUnavailable: 0

  • 更新时先启动 1 个新 Pod(总数变为 5)
  • 然后删除 1 个旧 Pod(总数变为 4)
  • 直到所有 Pod 都更新完成
  • 保证更新过程中至少 4 个 Pod 可用

第八部分:控制器操作速查表

操作DeploymentReplicaSetDaemonSetJobCronJob
创建kubectl create deploymentkubectl create -fkubectl create -fkubectl create jobkubectl create cronjob
查看kubectl get deploymentskubectl get replicasetskubectl get daemonsetskubectl get jobskubectl get cronjobs
扩缩容kubectl scale deploymentkubectl scale replicaset自动通过 parallelism通过 Job 模板
更新kubectl set image不直接更新不直接更新不适用不适用
回滚kubectl rollout undo不适用不适用不适用不适用
暂停kubectl rollout pause不适用不适用不适用不适用
恢复kubectl rollout resume不适用不适用不适用不适用
删除kubectl delete deploymentkubectl delete replicasetkubectl delete daemonsetkubectl delete jobkubectl delete cronjob

附录:镜像与代理相关错误总结

本附录汇总了学习过程中因镜像地址配置不当镜像代理环境导致的所有典型错误。

错误一:镜像地址缺少仓库前缀(最常犯)

错误现象

[root@K8sMasterNode ~]# kubectl get pods
NAME                          READY   STATUS             RESTARTS   AGE
webcluster-77c87d9946-dphlg   0/1     ErrImagePull       0          22s
webcluster-77c87d9946-wq4g6   0/1     ImagePullBackOff   0          22s

[root@K8sMasterNode ~]# kubectl describe pod webcluster-77c87d9946-dphlg | grep -A2 Events
Events:
  Type     Reason     Age   From     Message
  ----     ------     ----  ----     -------
  Warning  Failed     22s   kubelet  Failed to pull image "myapp:v1": Error response from daemon: failed to resolve reference "docker.io/library/myapp:v1": not found

错误原因:写了个 myapp:v1,没加仓库地址。Kubernetes 以为要去 Docker Hub 拉 docker.io/library/myapp:v1,但这个镜像在 Docker Hub 上根本不存在。

解决方案

# ✅ 正确写法
kubectl create deployment webcluster --image reg.avc.harbor.org/library/myapp:v1 --replicas 2

错误二:镜像代理与自签名证书冲突

错误现象

[root@K8sMasterNode ~]# kubectl describe pod testpod | grep -A5 Events
Events:
  Type     Reason     Age   From     Message
  ----     ------     ----  ----     -------
  Warning  Failed     60s   kubelet  Failed to pull image "busybox:latest": 
  tls: failed to verify certificate: x509: certificate signed by unknown authority
  Warning  Failed     60s   kubelet  Error: ErrImagePull
  Normal   BackOff    33s   kubelet  Back-off pulling image "busybox:latest"

错误原因:节点配置了镜像代理,拉取请求被转发到 Harbor,但 Harbor 使用自签名证书,节点不信任。

解决方案

方案做法适用场景
方案一添加 imagePullPolicy: IfNotPresent本地已有镜像,快速绕过
方案二docker.io/library/busybox:latest 绕过代理公共镜像专用
方案三配置 Harbor 证书信任根本解决

错误三:Service 端口号与预期不一致

错误现象

[root@K8sMasterNode ~]# kubectl get svc
webcluster   NodePort   10.96.226.32   <none>   80:31895/TCP   20m

[root@K8sMasterNode ~]# curl http://172.25.254.100:30713/hostname.html
curl: (7) Failed to connect to 172.25.254.100 port 30713: Connection refused

错误原因:Service 实际分配的 NodePort 是 31895,用户访问时用了 30713

解决方案

# 用 kubectl get svc 查看实际端口
[root@K8sMasterNode ~]# kubectl get svc webcluster
webcluster   NodePort   10.96.226.32   <none>   80:31895/TCP

# 用实际端口访问
[root@K8sMasterNode ~]# curl http://172.25.254.100:31895/hostname.html
webcluster-77c87d9946-2xl44

错误速查表

错误现象根本原因最快解决方案
ImagePullBackOff镜像地址缺少仓库前缀使用完整 Harbor 地址
x509: certificate signed by unknown authority镜像代理 + 自签名证书docker.io/library/xxx 绕过代理
Connection refusedNodePort 端口号错误kubectl get svc 查看实际端口
AlreadyExists (Service)旧的 Service 未删除kubectl delete svc
unauthorized私有仓库未认证执行 docker login 或配置 imagePullSecrets

核心经验教训

Kubernetes 看的是镜像名,不是您本地有什么。镜像名指向哪里,它就尝试从哪里拉取。

  1. 自定义镜像始终写完整 Harbor 地址:reg.avc.harbor.org/library/myapp:v1
  2. 公共镜像docker.io/library/xxx:tag 绕过代理
  3. Service 端口kubectl get svc 为准
  4. 排查问题先执行 kubectl describe pod,Events 段藏着答案
  5. 删除 Deployment 时记得同步删除 Service

更多推荐