Kubernetes 实战笔记(二):Pod 控制器详解(ReplicaSet / Deployment / DaemonSet / Job / CronJob)

环境说明

项目 版本/参数
Kubernetes v1.35.7(kubeadm 部署)
集群拓扑 1 Master(172.25.254.100)+ 多 Node
镜像仓库 reg.timinglee.org(Harbor,library 项目)
测试镜像 myapp:v1 / myapp:v2、perl:5.34.0、busybox

Pod 自身没有自愈能力,删除就没了。控制器负责维持"期望状态":副本数不够就补,节点挂了就在别处重建,任务跑完才收工。


一、ReplicaSet 控制器

1. 建立控制器

# 用 deployment 的模板生成 RS 的 yaml(改 kind 即可)
[root@master controler]# kubectl create deployment webcluster --image myapp:v1 --dry-run=client -o yaml > repset.yml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  labels:
    app: webcluster
  name: webcluster
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webcluster
  template:
    metadata:
      labels:
        app: webcluster
    spec:
      containers:
      - image: myapp:v1
        name: myapp
[root@master controler]# kubectl apply -f repset.yml

# 开一个新 shell 持续监控 pod 变化
[root@master ~]# watch -n 1 kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
webcluster-tbxq4   1/1     Running   0          6m49s   app=webcluster
webcluster-jna23   1/1     Running   0          6m49s   app=webcluster

# 命令行扩缩容
kubectl scale replicaset webcluster --replicas 4		# 扩到 4
kubectl scale replicaset webcluster --replicas 2		# 缩回 2
kubectl scale replicaset webcluster --replicas 0		# 清零

2. 测试功能:删掉一个 pod 会怎样?

修改 yml 中 replicas: 4 后 apply:

NAME               READY   STATUS    RESTARTS   AGE     LABELS
webcluster-4sxz4   1/1     Running   0          13s     app=webcluster
webcluster-lvtsn   1/1     Running   0          13s     app=webcluster
webcluster-tbxq4   1/1     Running   0          9m23s   app=webcluster
webcluster-tr5qk   1/1     Running   0          13s     app=webcluster

此时手动删除任意一个 pod,RS 立刻拉起新 pod 补齐到 4 个——这就是"期望副本数"的含义。

Replicaset控制器


二、Deployment 控制器

Deployment 是生产中最常用的控制器,在 ReplicaSet 之上封装了版本管理能力:每次改 Pod 模板都会生成新的 ReplicaSet,旧 RS 保留用于回滚。

1. 监控命令

同时观察 pod 和 rs 两层的变化:

[root@master ~]# watch -n 1 "kubectl get pods --show-labels;echo ====;kubectl get replicasets.apps"

2. 建立 Deployment 并发布服务

[root@master controler]# kubectl create deployment webcluster --image myapp:v1 --dry-run=client -o yaml > dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: webcluster
  name: webcluster
spec:
  minReadySeconds: 5	# 新 pod 就绪后需稳定运行 5 秒才算可用,避免抖动
  replicas: 2
  selector:
    matchLabels:
      app: webcluster
  template:
    metadata:
      labels:
        app: webcluster
    spec:
      containers:
      - image: myapp:v1
        name: myapp

[root@master controler]# kubectl apply -f dep.yml

# Deployment 创建的 pod 带 pod-template-hash 标签,且底下挂着一个 RS
Every 1.0s:  kubectl get pods --show-labels;echo ====;kubectl get replicase...  master: Sat Apr 11 10:43:24 2026

NAME                          READY   STATUS    RESTARTS   AGE   LABELS
webcluster-77c87d9946-49kh5   1/1     Running   0          45s   app=webcluster,pod-template-hash=77c87d9946
webcluster-77c87d9946-m2x2d   1/1     Running   0          45s   app=webcluster,pod-template-hash=77c87d9946
====
NAME                    DESIRED   CURRENT   READY   AGE
webcluster-77c87d9946   2         2         2       45s

发布 ClusterIP 服务并验证负载均衡:

[root@master controler]# kubectl expose deployment webcluster --port 80 --target-port 80
service/webcluster exposed

[root@master controler]# kubectl describe services webcluster
Name:              webcluster
Namespace:         default
Selector:          app=webcluster
Type:              ClusterIP
IP:                10.107.142.60
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.2.6:80,10.244.1.9:80

[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

3. 升级和回滚

升级即修改 Pod 模板中的镜像版本:

# 升级 v1 → v2
      containers:
      - image: myapp:v2			# 改这里
        name: myapp

[root@master controler]# kubectl apply -f dep.yml
deployment.apps/webcluster configured

[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>

# 回滚 v2 → v1(把镜像改回去再 apply)
      containers:
      - image: myapp:v1			# 改回 v1
        name: myapp

[root@master controler]# kubectl apply -f dep.yml
deployment.apps/webcluster configured

[root@master controler]# curl 10.107.142.60
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

整个过程中 Service 不变,流量无感知切换。

deployment 版本升级和回滚


三、版本更新策略及优化

1. 查看默认更新策略

先把副本调到 6 个方便观察滚动过程:

[root@master controler]# kubectl describe deployments.apps webcluster
RollingUpdateStrategy:  25% max unavailable, 25% max surge

默认策略含义(replicas=6 时):更新期间最多允许 1~2 个 pod 不可用(25% 向下取整)、最多比期望值多创建 1~2 个 pod(25% 向上取整)。

2. 设定自定义更新策略

spec:
  minReadySeconds: 5
  replicas: 6
  selector:
    matchLabels:
      app: webcluster
  strategy:
    rollingUpdate:
      maxSurge: 1				# 更新时 pod 数量最多比期望值多 1 个
      maxUnavailable: 0			# 不可用 pod 数量允许超出期望值 0 个(全程保住服务容量)

这组配置的效果:先建新 pod → 新 pod Ready 且稳定 5 秒 → 再杀旧 pod,任何时刻可用容量都不低于 6 个,适合不允许容量下跌的业务。

3. 更新暂停和恢复

金丝雀发布的底层机制——暂停时已提交的变更不执行,恢复时一次性生效:

# 查看历史版本
[root@master controler]# kubectl rollout history deployment webcluster
REVISION  CHANGE-CAUSE
7         <none>
8         <none>

# 暂停更新
[root@master controler]# kubectl rollout pause deployment webcluster
deployment.apps/webcluster paused

# 提交新版本(apply 成功,但监控中看不到任何变化)
      containers:
      - image: myapp:v2
        name: myapp

[root@master controler]# kubectl apply -f dep.yml
deployment.apps/webcluster configured

# 版本号没变,确认更新被挂起
[root@master controler]# kubectl rollout history deployment webcluster
REVISION  CHANGE-CAUSE
7         <none>
8         <none>

# 恢复更新,一次性完成滚动
[root@master controler]# kubectl rollout resume deployment webcluster
deployment.apps/webcluster resumed

[root@master controler]# kubectl rollout history deployment webcluster
REVISION  CHANGE-CAUSE
8         <none>
9         <none>

四、DaemonSet 控制器

DaemonSet 保证每个节点上有且只有一个指定 pod,新增节点自动部署、节点退出自动回收。典型场景:日志采集、监控 agent、网络插件本身。

[root@master controler]# kubectl create deployment daemonset --image myapp:v1 --dry-run=client -o yaml > daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: daemonset
  name: daemonset
spec:
  selector:
    matchLabels:
      app: daemonset
  template:
    metadata:
      labels:
        app: daemonset
    spec:
      containers:
      - image: myapp:v1
        name: myapp

[root@master controler]# kubectl apply -f daemonset.yml

验证"新节点加入即自动部署":

# 准备一台新机器 node3,完成初始化集群时的所有设定并确保服务开启

# master 上重新生成注册 token
[root@master controler]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token lqcz14.xxxxxxxx --discovery-token-ca-cert-hash sha256:6b59...

# node3 加入集群
[root@node3 ~]# kubeadm join 172.25.254.100:6443 --token lqcz14.xxxxxxxx \
  --discovery-token-ca-cert-hash sha256:6b59... \
  --cri-socket unix:///var/run/cri-dockerd.sock

# node3 一加入集群,DaemonSet 立即在它上面拉起 pod —— 无需任何手动操作

DaemonSet


五、Job 控制器

前面的控制器都追求"一直活着",Job 相反:把任务跑完就结束。适用于一次性批处理任务。restartPolicy 必须设为 OnFailureNever

# 准备 perl 镜像推入仓库
[root@master ~]# docker load -i perl-5.34.tar.gz
[root@master ~]# docker tag perl:5.34.0 reg.timinglee.org/library/perl:5.34.0
[root@master ~]# docker login reg.timinglee.org -u admin
Password:
Login Succeeded
[root@master ~]# docker push reg.timinglee.org/library/perl:5.34.0

[root@master controler]# kubectl create job job --image perl:5.34.0 --dry-run=client -o yaml > job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: testjob
spec:
  completions: 6			# 总共要成功跑完 6 次
  parallelism: 2			# 同时最多 2 个 pod 并发
  backoffLimit: 4			# 失败重试上限 4 次,超过则标记任务失败
  template:
    spec:
      containers:
      - image: busybox
        name: testjob
        command: ["/bin/sh", "-c"]
        args:
        - |
          echo this is testjob message
          sleep 10
      restartPolicy: Never	# Job 场景必须显式声明

[root@master controler]# kubectl apply -f job.yml

# 查看某个已完成 pod 的输出
[root@master ~]# kubectl logs pods/testjob-4cdlr
this is testjob message

三个参数的协作逻辑:并发度 2 × 轮次 = 依次跑满 6 次 completions;单个 pod 失败计入 backoffLimit 重试。

Job控制器


六、Cronjob 控制器

CronJob 在 Job 之上加了定时调度,schedule 字段与 Linux crontab 语法完全一致:分 时 日 月 周

[root@master controler]# kubectl create cronjob cronjob --image busybox --schedule "* * * * *" --dry-run=client -o yaml > cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: cronjob
spec:
  jobTemplate:				# 内嵌的就是一个 Job 模板
    metadata:
      name: cronjob
    spec:
      template:
        spec:
          containers:
          - image: busybox
            name: cronjob
            command:
            - /bin/sh
            - -c
            - echo "hello timinglee"
          restartPolicy: OnFailure
  schedule: '* * * * *'		# 每分钟执行一次(整分触发)

[root@master controler]# kubectl apply -f cronjob.yml

[root@master controler]# kubectl get cronjobs.batch
NAME      SCHEDULE    TIMEZONE   SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob   * * * * *   <none>     False     0        17s             70s

# 到点生成的 pod 里能看到任务输出
[root@master controler]# kubectl logs cronjob-29598241-pxggh
hello timinglee

Cronjob控制器


总结:控制器选型速查表

控制器 核心职责 典型场景
ReplicaSet 维持固定副本数 一般不直接使用,被 Deployment 包含
Deployment 副本管理 + 滚动更新 + 版本回滚 无状态应用首选
DaemonSet 每节点一个 pod 日志/监控 agent、网络存储插件
Job 跑完 N 次就结束 数据迁移、批量计算
CronJob 定时创建 Job 定时备份、周期报表

核心记忆点:

  1. 层级关系:Deployment → ReplicaSet → Pod;CronJob → Job → Pod;
  2. pod-template-hash 是 Deployment 区分新旧版本的依据,也是回滚能工作的基础;
  3. maxSurge + maxUnavailable + minReadySeconds 三件套控制滚动更新的平滑程度;
  4. rollout pause/resume 实现金丝雀:一部分节点先验证新版本,确认无误再全量;
  5. Job/CronJob 的容器必须配 restartPolicy: Never/OnFailure,用 Always 会直接被 API 拒绝。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐