K8S控制器全解:从RS到DaemonSet,掌握五大控制器的核心原理与实战
导读 :在 Kubernetes 中,你几乎不会直接创建 Pod------而是通过控制器(Controller) 来管理 Pod 的生命周期。控制器是 K8S 实现声明式 API 和自愈能力的核心机制。本文将从 ReplicaSet 到 DaemonSet,逐一拆解五大控制器的工作原理、标签选择器机制、升级策略,并通过完整实战帮你建立系统性的控制器知识体系。最后附上面试高频题:"如何通过 Pod 反向查找控制器"。
一、为什么需要控制器?
在 K8S 中,直接创建的 Pod 是"裸奔"的------节点故障 Pod 不会自动迁移,进程崩溃不会自动恢复,应用更新无法平滑过渡。控制器就是来解决这些问题的:
┌──────────────────────────────────────────────────────────┐
│ K8S 控制器核心能力 │
├──────────────┬───────────────────────────────────────────┤
│ 自愈能力 │ Pod 挂了自动重建,节点宕机自动迁移 │
│ 弹性伸缩 │ 自动维持期望的 Pod 副本数量 │
│ 滚动更新 │ 零停机升级应用版本 │
│ 版本回滚 │ 更新出问题可以快速回退到上一个版本 │
│ 声明式管理 │ 只需声明期望状态,控制器自动调和差异 │
└──────────────┴───────────────────────────────────────────┘
控制器的核心工作原理:控制循环(Reconciliation Loop)
期望状态(Desired State) 实际状态(Current State)
┌──────────────────┐ ┌──────────────────┐
│ replicas: 3 │ │ running pods: 2 │
│ image: nginx:v2 │ │ image: nginx:v1 │
└────────┬─────────┘ └────────┬─────────┘
│ │
└───────────┬───────────────┘
▼
┌─────────────────┐
│ 控制循环比较差异 │
│ (Reconcile) │
└────────┬────────┘
▼
┌─────────────────┐
│ 执行调和动作 │
│ 创建/删除/更新Pod│
└─────────────────┘
二、K8S 控制器全景图
┌─────────────────────────────────────────────────────────┐
│ K8S 工作负载控制器 │
├──────────────────┬──────────────────────────────────────┤
│ Deployment │ 无状态应用(Web服务、API) │
│ ├─ ReplicaSet │ 副本管理(底层控制器) │
│ └─ Pod │ │
├──────────────────┼──────────────────────────────────────┤
│ DaemonSet │ 每个节点一个Pod(监控、日志采集) │
├──────────────────┼──────────────────────────────────────┤
│ Job │ 一次性任务(数据库备份、数据迁移) │
│ └─ CronJob │ 定时任务(增量备份、日志清理) │
├──────────────────┼──────────────────────────────────────┤
│ StatefulSet │ 有状态应用(数据库、消息队列) │
├──────────────────┼──────────────────────────────────────┤
│ HPA/VPA │ 自动伸缩(动态调整副本/资源) │
└──────────────────┴──────────────────────────────────────┘
控制器选型决策表:
| 场景 | 推荐控制器 | 原因 |
|---|---|---|
| Web 服务、API | Deployment | 支持滚动更新、回滚、弹性伸缩 |
| 监控 Agent、日志采集 | DaemonSet | 每个节点一个 Pod,自动跟随节点 |
| 数据库全量备份 | Job | 一次性任务,执行完成即终止 |
| 定时清理日志 | CronJob | 周期性执行,类似 Linux crontab |
| MySQL、Redis 集群 | StatefulSet | 需要稳定的网络标识和存储 |
| 批处理任务 | Job + parallelism | 并行处理多个任务 |
三、ReplicaSet:副本管理的基石
3.1 RS 与 RC 的关系
ReplicaSet(RS)是 ReplicationController(RC)的升级版,官方已废弃 RC。两者的核心区别在于标签选择器的能力:
| 特性 | RC | RS |
|---|---|---|
| 等值选择 | 支持 | 支持(matchLabels) |
| 集合选择 | 不支持 | 支持 (matchExpressions) |
| 当前状态 | 已废弃 | 推荐使用(但通常被 Deployment 管理) |
生产提示 :虽然 RS 功能更强大,但在实际工作中几乎不会直接创建 RS------而是通过 Deployment 间接管理。Deployment 是对 RS 的上层封装,增加了声明式更新和版本管理能力。
3.2 RS 基础实战:matchLabels
yaml
# rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs
spec:
replicas: 3 # 期望副本数
selector:
matchLabels: # 等值匹配:标签必须完全一致
apps: lingxi
template: # Pod 模板
metadata:
labels:
apps: lingxi
version: v1
spec:
containers:
- name: c1
image: nginx:1.20
bash
kubectl apply -f rs.yaml
# 查看 RS 和 Pod(Pod 名称自动带上 RS 名称前缀)
kubectl get rs,pods -o wide
# NAME DESIRED CURRENT READY
# replicaset.apps/rs 3 3 3
#
# NAME READY STATUS IP NODE
# pod/rs-xxx 1/1 Running 10.100.2.103 worker233
# pod/rs-xxx 1/1 Running 10.100.1.124 worker232
# pod/rs-xxx 1/1 Running 10.100.2.104 worker233
验证自愈能力: 删除所有 Pod,RS 会自动重建:
bash
kubectl delete pods -l apps=lingxi
# pod "rs-xxx" deleted (x3)
kubectl get pods -o wide
# NAME READY STATUS AGE IP NODE
# pod/rs-new 1/1 Running 4s 10.100.1.125 worker232
# pod/rs-new 1/1 Running 4s 10.100.2.105 worker233
# pod/rs-new 1/1 Running 4s 10.100.1.126 worker232
3.3 RS 进阶:matchExpressions 集合选择
matchExpressions 支持四种操作符,能力远超 RC 的等值匹配:
yaml
# rs-matchExpressions.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-match-expressions
spec:
replicas: 5
selector:
matchExpressions: # 集合选择器
- key: version
values:
- v1
- v2
- v3
operator: In # version 的值在 [v1,v2,v3] 中
template:
metadata:
labels:
apps: lingxi
version: v1
spec:
containers:
- name: c1
image: nginx:1.20
四种操作符:
| 操作符 | 说明 | 示例 |
|---|---|---|
| In | key 的值在 values 列表中 | version In [v1, v2, v3] |
| NotIn | key 的值不在 values 列表中 | env NotIn [prod] |
| Exists | key 存在即可(忽略 values) | version Exists |
| DoesNotExist | key 不存在 | debug DoesNotExist |
关键行为: RS 的 matchExpressions 会同时管理已存在的匹配 Pod 。例如集群中已有标签 version=v1 的独立 Pod,创建 RS 后这些 Pod 也会被纳入管理。删除 RS 后,被纳入管理的 Pod 也会被一并删除。
四、Deployment:无状态应用的"最佳拍档"
4.1 Deployment 与 RS 的关系
Deployment 是对 RS 的上层封装,核心价值在于声明式更新 和版本管理:
┌──────────────┐ 管理 ┌──────────────────┐ 管理 ┌─────┐
│ Deployment │ ──────────→ │ ReplicaSet (v1) │ ──────────→ │ Pod │
│ │ │ ReplicaSet (v2) │ ──────────→ │ Pod │
│ │ │ ReplicaSet (v3) │ ──────────→ │ Pod │
└──────────────┘ └──────────────────┘ └─────┘
版本管理 历史版本保留 实例
滚动更新 每次更新创建新RS 运行中
回滚能力 旧RS保留用于回滚
核心机制:每次更新 Deployment 的 Pod 模板(如修改镜像版本),会创建一个新的 RS,逐步将流量从旧 RS 的 Pod 切换到新 RS 的 Pod。旧 RS 默认保留,支持快速回滚。
4.2 Deployment 基础实战
yaml
# deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
replicas: 3
selector:
matchLabels:
apps: lingxi
template:
metadata:
labels:
apps: lingxi
version: v1
spec:
containers:
- name: c1
image: registry.cn-hangzhou.aliyuncs.com/eci_open/nginx:latest
bash
kubectl apply -f deploy.yaml
# 查看 Deployment、RS、Pod 的层级关系
kubectl get deploy,rs,po --show-labels
# deployment.apps/deploy 3/3 3 3 13s
#
# replicaset.apps/deploy-568cf47956 3 3 3
# 标签: apps=lingxi,pod-template-hash=568cf47956,version=v1
#
# pod/deploy-568cf47956-hwglc 1/1 Running
# 标签: apps=lingxi,pod-template-hash=568cf47956,version=v1
关键标签 :
pod-template-hash是 K8S 自动添加的标签,值由 Pod 模板内容哈希生成。相同模板的 Pod 拥有相同的 hash,用于 RS 精确匹配自己的 Pod。
4.3 滚动更新(RollingUpdate)
更新 Deployment 的镜像版本,观察滚动更新过程:
yaml
# 修改 image: apps:v2 → apps:v3
spec:
containers:
- name: c1
image: registry.openanolis.cn/openanolis/nginx:1.14.1-8.6
bash
kubectl apply -f deploy-update.yaml
# 观察更新过程:新RS创建,旧RS缩容
kubectl get deploy,rs,po -o wide
# deployment.apps/deploy 5/5 5 5
#
# replicaset.apps/deploy-5f77fddbd7 0/0/0 ← 旧RS(v2),Pod已清空
# replicaset.apps/deploy-78ddbbdbfb 5/5/5 ← 新RS(v3),Pod已就绪
4.4 升级策略详解
Deployment 支持两种升级策略:
yaml
spec:
strategy:
type: RollingUpdate # 默认策略
rollingUpdate:
maxSurge: 2 # 滚动更新时最多允许超出期望副本数2个
maxUnavailable: 1 # 滚动更新时最多允许1个Pod不可用
| 参数 | Recreate | RollingUpdate |
|---|---|---|
| 更新方式 | 先删全部旧Pod,再创建新Pod | 逐步替换,新旧Pod共存 |
| 停机时间 | 有(删除到创建的间隔) | 无(始终有Pod在运行) |
| 资源开销 | 低(同一时刻只有一套Pod) | 高(新旧Pod共存,需要maxSurge资源) |
| 回滚速度 | 慢(需要重新创建全部Pod) | 快(旧RS还在,直接扩容) |
| 适用场景 | 不兼容的多版本更新 | 绝大多数场景(推荐) |
maxSurge 和 maxUnavailable 的计算规则:
| 场景 | replicas=5, maxSurge=25%(默认) | replicas=5, maxSurge=2 |
|---|---|---|
| 更新时最多Pod数 | ceil(5 * 1.25) = 7 | 5 + 2 = 7 |
| 场景 | replicas=1, maxUnavailable=25%(默认) | replicas=1, maxUnavailable=1 |
| 更新时最少可用Pod | floor(1 * 0.75) = 0 | 1 - 1 = 0 |
生产建议 :设置
maxUnavailable: 0(零停机),配合maxSurge: 25%,确保更新过程中始终有足够的 Pod 处理流量。
4.5 版本回滚
bash
# 查看更新历史
kubectl rollout history deployment deploy
# 回滚到上一个版本
kubectl rollout undo deployment deploy
# 回滚到指定版本
kubectl rollout undo deployment deploy --to-revision=2
# 查看回滚状态
kubectl rollout status deployment deploy
五、Job:一次性任务的执行器
5.1 Job 控制器概述
Job 用于运行一次性任务,任务执行完成后 Pod 自动终止。典型的应用场景:
- 数据库全量备份
- 数据迁移
- 批量邮件发送
- CI/CD 流水线中的构建任务
- 大数据处理任务
5.2 Job 实战:Python 脚本执行
yaml
# job-python.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: jobs-py
spec:
template:
spec:
containers:
- name: py
image: python:3.9.16-alpine3.16
command:
- python3
- -c
- "print('https://www.baidu.com/')"
restartPolicy: Never # Job 只能是 Never 或 OnFailure
backoffLimit: 4 # 失败重试次数(创建新Pod重试)
bash
kubectl apply -f job-python.yaml
# 查看执行状态
kubectl get jobs,pods -o wide
# NAME COMPLETIONS DURATION AGE
# job.batch/jobs-py 1/1 4s 6s
#
# NAME READY STATUS RESTARTS AGE
# pod/jobs-py-xxx 0/1 Completed 0 6s
# 查看执行日志
kubectl logs jobs-py-965q5
5.3 Job 关键参数
| 参数 | 说明 | 默认值 |
|---|---|---|
backoffLimit |
失败重试次数(创建新 Pod 重试) | 6 |
completions |
需要成功完成的 Pod 数 | 1 |
parallelism |
并行运行的 Pod 数 | 1 |
activeDeadlineSeconds |
Job 超时时间,超时自动标记失败 | 无限制 |
ttlSecondsAfterFinished |
完成后自动清理的延迟时间 | 永不清理 |
restartPolicy |
重启策略,只能是 Never 或 OnFailure | Never |
5.4 Job 的 restartPolicy 详解
| 策略 | 行为 | 适用场景 |
|---|---|---|
| Never | 失败后创建新 Pod 重试(Pod 数量会增加) | 大多数场景(推荐) |
| OnFailure | 失败后在同一个 Pod 内重启容器 | 需要保留容器状态 |
关键区别 :
Never会创建新的 Pod 来重试,每次重试都是"干净的"环境;OnFailure在同一 Pod 内重启容器,可能残留上次的状态。
六、CronJob:定时任务的调度器
6.1 CronJob 概述
CronJob 基于 Job 控制器,按 Cron 表达式 定期创建 Job。它不直接管理 Pod,而是周期性地调用 Job 控制器。
CronJob ──(按Cron触发)──→ Job ──(创建)──→ Pod
定时调度 任务管理 执行
典型应用场景:
- 数据库增量备份(每天凌晨2点)
- 证书续期检查(每周一)
- 日志清理(每天)
- 数据报表生成(每月1号)
6.2 CronJob 实战
yaml
# cronjob-demo.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cj-hello
spec:
schedule: "* * * * *" # Cron 表达式:每分钟执行
jobTemplate:
spec:
template:
spec:
volumes:
- name: dt
hostPath:
path: /etc/localtime # 同步宿主机时区
containers:
- name: hello
image: busybox:1.28
volumeMounts:
- name: dt
mountPath: /etc/localtime
command:
- /bin/sh
- -c
- date -R; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
bash
kubectl apply -f cronjob-demo.yaml
# 等待几分钟后查看(每分钟创建一个Job,每个Job创建一个Pod)
kubectl get cj,jobs,pods -o wide
# NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE
# cronjob.batch/cj-hello * * * * * False 0 22s
#
# NAME COMPLETIONS DURATION AGE
# job.batch/cj-hello-29413519 1/1 3s 2m22s
# job.batch/cj-hello-29413520 1/1 3s 82s
# job.batch/cj-hello-29413521 1/1 3s 22s
#
# pod/cj-hello-29413519-xxx Completed
# pod/cj-hello-29413520-xxx Completed
# pod/cj-hello-29413521-xxx Completed
6.3 Cron 表达式格式
┌─── 分钟 (0 - 59)
│ ┌─── 小时 (0 - 23)
│ │ ┌─── 日 (1 - 31)
│ │ │ ┌─── 月 (1 - 12)
│ │ │ │ ┌─── 星期 (0 - 6, 0=周日)
│ │ │ │ │
* * * * *
常用表达式速查:
| 表达式 | 含义 |
|---|---|
*/5 * * * * |
每 5 分钟 |
0 2 * * * |
每天凌晨 2 点 |
0 2 * * 1 |
每周一凌晨 2 点 |
0 0 1 * * |
每月 1 号零点 |
0 9-18 * * 1-5 |
工作日 9~18 点每小时 |
30 2 1,15 * * |
每月 1 号和 15 号凌晨 2:30 |
6.4 CronJob 历史记录管理
yaml
spec:
successfulJobsHistoryLimit: 3 # 保留最近3个成功的Job
failedJobsHistoryLimit: 1 # 保留最近1个失败的Job
concurrencyPolicy: Forbid # 禁止并发:上一个未完成时不创建新的
# concurrencyPolicy: Allow # 允许并发(默认)
# concurrencyPolicy: Replace # 替换:取消上一个,创建新的
startingDeadlineSeconds: 200 # 错过调度后的宽容时间(秒)
suspend: false # 暂停调度(true=暂停)
七、DaemonSet:每个节点的守护进程
7.1 DaemonSet 概述
DaemonSet(DS)确保每个(或特定)节点上运行一个 Pod 副本。当节点加入集群时自动创建 Pod,节点移除时自动回收 Pod。
┌─────────────────────────────────────────┐
│ DaemonSet: node-exporter │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐│
│ │ Worker-1 │ │ Worker-2 │ │ Worker-3││
│ │ [Pod-1] │ │ [Pod-2] │ │ [Pod-3] ││
│ └──────────┘ └──────────┘ └────────┘│
│ │
│ 每个节点有且仅有一个 Pod │
│ 新节点加入 → 自动创建 Pod │
│ 节点移除 → 自动回收 Pod │
└─────────────────────────────────────────┘
典型应用场景:
- 监控采集:Prometheus Node Exporter、Zabbix Agent
- 日志采集:Fluentd、Filebeat、Flume
- 网络插件:Calico、Flannel(CNI 组件本身就是 DaemonSet)
- 存储插件:Ceph OSD(Rook 部署的 Ceph 使用 DaemonSet)
7.2 DaemonSet 实战
yaml
# ds.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ds
spec:
selector:
matchLabels:
apps: lingxi
template:
metadata:
labels:
apps: lingxi
spec:
volumes:
- name: dt
hostPath:
path: /etc/localtime
containers:
- name: c1
image: nginx:1.20
volumeMounts:
- name: dt
mountPath: /etc/localtime
bash
kubectl apply -f ds.yaml
# 每个 Worker 节点上创建一个 Pod
kubectl get pods -o wide
# NAME READY STATUS IP NODE
# ds-hgqt4 1/1 Running 10.100.2.203 worker233
# ds-lrf5x 1/1 Running 10.100.1.213 worker232
# 注意:Master 节点默认不调度(因为有污点)
kubectl get nodes
# NAME STATUS ROLES ...
# master231 Ready control-plane,master ← 有污点,DS Pod 不会调度到此
# worker232 Ready <none>
# worker233 Ready <none>
污点与 DaemonSet :如果需要在有污点的节点(如 Master)上也运行 DaemonSet Pod,需要在 Pod 模板中配置
tolerations。这是 DaemonSet 与 Deployment 的一个重要区别------DaemonSet 的调度需求更强,经常需要容忍污点。
7.3 DaemonSet 更新策略
yaml
spec:
updateStrategy:
type: RollingUpdate # 默认:逐个节点滚动更新
rollingUpdate:
maxUnavailable: 1 # 最多1个Pod不可用
# type: OnDelete # 手动模式:删除旧Pod后才创建新Pod
| 策略 | 行为 | 适用场景 |
|---|---|---|
| RollingUpdate(默认) | 自动逐个替换 Pod | 常规更新 |
| OnDelete | 只有手动删除旧 Pod 后才创建新的 | 精确控制更新节奏 |
八、面试实战:通过 Pod 反向查找控制器
这是一个经典面试题:"给你一台服务器,Pod 处于 Pending 状态,如何排查并修复?"
8.1 核心思路
Pod 的配置问题不能直接修改 Pod 本身(控制器会自动回滚),必须找到管理该 Pod 的控制器并修改控制器。
8.2 操作步骤
bash
# 第1步:使用 describe 查看 Pod 的事件
kubectl describe pod <pod-name>
# 第2步:查看 "Controlled By" 字段,找到控制器
kubectl describe pod ds-qxww6
# ...
# Controlled By: DaemonSet/ds ← 被DaemonSet管理
8.3 三层控制器链路
Pod (被 RS 管理)
└── Controlled By: ReplicaSet/deploy-568cf47956
└── Controlled By: Deployment/deploy ← 最终需要修改这个
不同控制器的 Controlled By:
| 控制器 | Pod 的 Controlled By | 下一步操作 |
|---|---|---|
| Deployment | ReplicaSet/xxx |
再 describe rs 找到 Deployment |
| DaemonSet | DaemonSet/xxx |
直接 edit ds |
| Job | Job/xxx |
直接 edit job |
| CronJob | Job/xxx |
找到 Job 后编辑 CronJob |
| StatefulSet | StatefulSet/xxx |
直接 edit sts |
bash
# Deployment 需要两层查找
kubectl describe pod deploy-568cf47956-92chs
# Controlled By: ReplicaSet/deploy-568cf47956
kubectl describe rs deploy-568cf47956
# Controlled By: Deployment/deploy
# 最终修改 Deployment
kubectl edit deploy deploy
8.4 Pod Pending 的六种常见原因
| 原因 | 排查方法 | 修复方式 |
|---|---|---|
| 镜像拉取失败 | Events: Failed to pull image |
修改控制器的 image |
| 节点资源不足 | Events: Insufficient cpu/memory |
添加节点或降低 requests |
| 亲和性不满足 | Events: didn't match Pod's node affinity |
修改 nodeAffinity 配置 |
| 存储卷未就绪 | Events: persistentvolumeclaim not found |
创建 PVC 或绑定 PV |
| 污点不容忍 | Events: had taint that pod didn't tolerate |
配置 tolerations |
| nodeName 不存在 | Events: node not found |
修改 nodeName 或确认节点存在 |
九、控制器对比总结
| 维度 | Deployment | DaemonSet | Job | CronJob |
|---|---|---|---|---|
| Pod 数量 | 可指定 replicas | 每个节点 1 个 | 指定 completions | 由 Job 决定 |
| 生命周期 | 持续运行 | 持续运行 | 完成后终止 | 周期性完成 |
| 更新方式 | 滚动更新/重建 | 滚动更新/OnDelete | N/A | N/A |
| 回滚 | 支持 | 不支持 | 不支持 | 不支持 |
| 自愈 | Pod 挂了重建 | Pod 挂了重建 | 失败重试 | 失败重试 |
| restartPolicy | Always | Always | Never/OnFailure | Never/OnFailure |
| 典型场景 | Web 服务 | 监控/日志/网络 | 备份/迁移 | 定时任务 |