🎯 学习目标
- 创建和管理 Pods
- 使用 Deployments 进行应用部署
- 理解 DaemonSets 和 StatefulSets
📌 核心知识点
工作负载
📋 本章概览
| 主题 | 考试权重 | 重要程度 |
|---|---|---|
| Deployments | ★★★★★ | 必考 |
| 滚动更新与回滚 | ★★★★★ | 高频考点 |
| ReplicaSets | ★★★★☆ | 重要 |
| DaemonSets | ★★★★☆ | 重要 |
| StatefulSets | ★★★☆☆ | 了解 |
| Jobs/CronJobs | ★★★☆☆ | 了解 |
💡 考试占比:工作负载和调度占 CKA 考试的 15%
🎯 学习目标
学完这一章,你应该能够:
- 理解为什么 Kubernetes 需要不同类型的工作负载 —— 不同的应用有不同的运行需求
- 熟练创建和管理 Deployment(CKA 最核心的考点之一)
- 独立完成 滚动更新和回滚 操作
- 区分 Deployment / DaemonSet / StatefulSet / Job / CronJob 的使用场景
- 理解 ReplicaSet 和 Deployment 的关系
📝 学习建议
通俗理解: "工作负载"就是你要在 Kubernetes 上运行的各种类型的应用程序。
想象你经营一家连锁餐厅集团,需要雇佣不同类型的员工:
- 服务员(Deployment) —— 你需要 5 个服务员,如果有人请假(Pod 挂了),自动补一个上来。换新工服(更新镜像)时,不能让所有人同时换,要一个一个轮流换(滚动更新),万一新工服有问题还能换回旧的(回滚)
- 保洁员(DaemonSet) —— 每家分店必须有一个保洁员,开新分店(新节点)就自动派一个去。不需要多,也不会少
- 大厨(StatefulSet) —— 每个大厨有自己专属的厨房和刀具(持久存储),换人时厨房不能给别人。大厨有固定编号(chef-0, chef-1, chef-2),招新的必须从小号开始,裁人从大号开始
- 外卖配送员(Job) —— 送完这单就完事,不需要一直待命
- 钟点工(CronJob) —— 每天下午 2 点来打扫一次,干完就走
CKA 考试中,很多题目是给你一个场景,让你选择正确的工作负载类型或者修复一个错误的配置。如果你不理解每种类型的本质区别,就会在选型上犯错。
本章知识地图:
Kubernetes 工作负载
│
├── Pod(最小部署单元)
│ └── 容器的"胶囊" —— 共享网络和存储
│
├── Deployment(无状态应用 —— 最常用)
│ ├── ReplicaSet(自动管理,通常不直接创建)
│ ├── 滚动更新 ── maxSurge / maxUnavailable
│ ├── 回滚 ────── rollout undo
│ └── HPA ─────── 自动水平扩缩
│
├── DaemonSet(每节点一份)
│ └── 日志收集 / 监控代理 / 网络插件
│
├── StatefulSet(有状态应用)
│ └── 稳定网络标识 + 独立持久存储 + 有序部署
│
├── Job(一次性任务)
│ └── completions / parallelism / backoffLimit
│
└── CronJob(定时任务)
└── Cron 表达式 + 并发策略
从容器到 Pod —— 先理解基础
为什么需要 Pod?
在学具体的工作负载类型之前,先理解一个基础问题:为什么 Kubernetes 不直接管理容器,而是管理 Pod?
图解说明:
- 这张图在讲什么:从传统部署 → 虚拟化部署 → 容器化部署的演进过程。
- 你应该先看哪里:先看三种部署方式的资源利用效率差异,再看容器化如何解决虚拟化的开销问题。
- 考试关联:理解容器化的优势(轻量、快速启动、资源隔离),才能理解为什么 Kubernetes 选择管理容器而不是虚拟机。
📖 图片来源:Kubernetes 官方文档 - CC BY 4.0
Pod 存在的理由: 有些应用需要多个容器紧密协作。比如一个 Web 应用容器 + 一个日志收集容器,它们需要共享网络(通过 localhost 通信)和共享存储(读同一个日志文件)。Pod 就是这样一个"容器组"的概念 —— 同一个 Pod 里的容器共享网络命名空间和存储卷。
图解说明:
- 这张图在讲什么:Pod 内部的容器如何共享网络和存储,以及 Pod 的生命周期。
- 你应该先看哪里:先看 Pod 的边界(同一个 Pod 内的容器共享 IP 地址),再看 Volume 是如何被多个容器挂载的。
- 考试怎么考:多容器 Pod 的场景题 —— sidecar、init container 等模式。
📖 图片来源:Kubernetes 官方文档 - CC BY 4.0
💡 一句话记忆:Pod = 一个或多个容器 + 共享网络 + 共享存储。它是 Kubernetes 调度的最小单位(Scheduler 调度 Pod,不调度单个容器)。
工作负载资源概览
图解说明:
- 这张图在讲什么:Pod 和 Volume 的关系,以及工作负载控制器如何管理 Pod。
- 你应该先看哪里:先看 Pod 如何引用 Volume,再看控制器(Deployment/DaemonSet 等)如何定义 Pod 模板。
- 最易混淆:Pod 不直接定义存储大小,存储是通过 Volume 和 PVC 定义的。
📖 图片来源:Kubernetes 官方文档 - CC BY 4.0
工作负载类型一览
┌─────────────────────────────────────────────────────────────────────────────┐
│ Kubernetes 工作负载类型 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Deployment │ │ DaemonSet │ │ StatefulSet │ │ Job/ │ │
│ │ │ │ │ │ │ │ CronJob │ │
│ │ 无状态应用 │ │ 每节点一份 │ │ 有状态应用 │ │ 批处理任务 │ │
│ │ │ │ │ │ │ │ │ │
│ │ • Web 服务 │ │ • 日志收集 │ │ • 数据库 │ │ • 数据处理 │ │
│ │ • API 服务 │ │ • 监控代理 │ │ • 消息队列 │ │ • 定时任务 │ │
│ │ • 微服务 │ │ • 网络插件 │ │ • 缓存集群 │ │ • 备份任务 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ ReplicaSet │ │ 直接管理 │ │ 直接管理 │ │ 直接管理 │ │
│ │ (自动创建) │ │ Pods │ │ Pods │ │ Pods │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
选型速查表
| 场景 | 推荐类型 | 原因 | 快速记忆 |
|---|---|---|---|
| 无状态 Web/API 应用 | Deployment | 支持滚动更新、扩缩容、回滚 | "标准选择" |
| 每个节点运行代理 | DaemonSet | 自动在每个节点部署一份 | "每人一份" |
| 数据库/有状态应用 | StatefulSet | 稳定网络标识和持久存储 | "有名有姓" |
| 一次性批处理 | Job | 运行完成后自动结束 | "干完就走" |
| 定时任务 | CronJob | 按计划周期执行 | "闹钟任务" |
⚠️ 考试快速判断:95% 的场景用 Deployment。只有在看到"每个节点"→ DaemonSet、"有状态/数据库"→ StatefulSet、"一次性/定时"→ Job/CronJob 这些关键词时才考虑其他类型。
Deployments(重点中的重点)
什么是 Deployment?
术语:Deployment
- 一句话解释:管理无状态应用的控制器,支持声明式更新、滚动发布、版本回滚。它是你在 Kubernetes 中用得最多的资源类型。
- 形象比喻:像一个智能排班系统。你告诉它"我需要 3 个 nginx 服务员值班",它会确保始终有 3 个在岗。换班时一个一个换(滚动更新),出问题可以一键回到上一班(回滚)。
- 考试怎么考:创建 Deployment、修改镜像版本、执行回滚、扩缩容 —— 这些是 CKA 的"基本功"。
- 最易混淆:Deployment 不直接管理 Pod,而是通过 ReplicaSet 间接管理。Deployment → ReplicaSet → Pod 是三层关系。
Deployment 的层级关系
直接管理 Pod 不好吗?想想滚动更新的场景:你有 3 个 v1 的 Pod,要更新到 v2。如果直接管理 Pod,更新过程很复杂。但有了 ReplicaSet:
- Deployment 创建一个新的 ReplicaSet(管理 v2 Pod)
- 新 RS 逐步增加 v2 Pod 数量
- 旧 RS 逐步减少 v1 Pod 数量
- 回滚时只要让旧 RS 重新扩容、新 RS 缩容就行
┌──────────────────────────────────────────────────────────────┐
│ Deployment │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ spec │ │
│ │ replicas: 3 │ │
│ │ strategy: │ │
│ │ type: RollingUpdate │ │
│ │ rollingUpdate: │ │
│ │ maxSurge: 1 ← 最多临时多创建几个 Pod │ │
│ │ maxUnavailable: 0 ← 更新时最多允许几个不可用 │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 自动管理 │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ ReplicaSet │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Pod 1 │ │ Pod 2 │ │ Pod 3 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
创建 Deployment
命令行方式(考试首选 —— 快!):
# 创建 Deployment
kubectl create deployment nginx --image=nginx:1.21 --replicas=3
# 带 dry-run 生成 YAML(先生成再修改,考试最常用的套路)
kubectl create deployment nginx --image=nginx:1.21 --replicas=3 \
--dry-run=client -o yaml > deployment.yaml
YAML 方式(需要复杂配置时用):
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx # 必须和 template.labels 完全匹配!
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 滚动更新时最多额外创建的 Pod 数
maxUnavailable: 0 # 滚动更新时最多不可用的 Pod 数
template:
metadata:
labels:
app: nginx # 必须和 selector.matchLabels 匹配!
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
livenessProbe: # 存活探针:Pod 是否还活着
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe: # 就绪探针:Pod 是否准备好接收流量
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
💡 selector 和 template labels 必须匹配,这是最常见的 YAML 配置错误。不匹配的话 Deployment 创建会直接报错。
🔥 滚动更新(高频考点)
通俗理解: 滚动更新就像餐厅轮流换工服 —— 不是所有服务员同时去换衣服(那就没人服务了),而是一个一个轮着来。换好一个,确认没问题,再换下一个。
两种更新策略:
| 策略 | 过程 | 使用场景 |
|---|---|---|
| RollingUpdate(默认) | 逐步替换旧 Pod,全程保持服务可用 | 需要零停机更新(99% 的场景) |
| Recreate | 先删除所有旧 Pod,再创建所有新 Pod | 应用不支持两个版本同时运行(比如数据库 schema 变更) |
maxSurge 和 maxUnavailable 是什么?
这两个参数控制滚动更新的"节奏":
- maxSurge:更新过程中最多可以临时多出几个 Pod。设为 1 表示 replicas=3 的 Deployment 更新时最多有 4 个 Pod
- maxUnavailable:更新过程中最多允许几个 Pod 不可用。设为 0 表示任何时候都必须有 3 个 Pod 在服务
滚动更新完整过程(maxSurge=1, maxUnavailable=0, replicas=3):
初始状态 (v1): [Pod-v1] [Pod-v1] [Pod-v1] ← 3个旧版本
│
▼ 触发更新 (v1 → v2)
│
Step 1 (多创建1个): [Pod-v1] [Pod-v1] [Pod-v1] [Pod-v2] ← 先加一个 v2
│
▼ v2 Pod 通过 Readiness 检查
│
Step 2 (删1个旧): [Pod-v1] [Pod-v1] [Pod-v2] ← 删一个 v1
│
▼ 再加一个 v2
│
Step 3: [Pod-v1] [Pod-v1] [Pod-v2] [Pod-v2]
│
▼
Step 4: [Pod-v1] [Pod-v2] [Pod-v2]
│
▼
Step 5: [Pod-v2] [Pod-v2] [Pod-v2] ← 全部更新完成
关键: 每一步都先确认新 Pod 健康了(通过 Readiness Probe),才删掉旧 Pod。所以整个过程对用户完全无感。
图解说明:
- 这张图在讲什么:Kubernetes 滚动更新的可视化过程 —— 新旧版本 Pod 如何交替。
- 你应该先看哪里:先看更新前的状态(所有 Pod 是旧版本),再看中间的过渡状态(新旧混合),最后看完成状态(所有 Pod 是新版本)。
- 考试常见问法:更新卡住了怎么办?答:
kubectl rollout status查看状态,kubectl describe deployment查看事件。
📖 图片来源:Kubernetes 官方文档 - CC BY 4.0
更新操作命令(考试必背):
# 方法 1:直接设置镜像(最快,考试首选)
kubectl set image deployment/nginx nginx=nginx:1.22
# 方法 2:编辑 Deployment(需要修改多个字段时)
kubectl edit deployment nginx
# 方法 3:使用 patch(脚本化场景)
kubectl patch deployment nginx -p '{"spec":{"template":{"spec":{"containers":[{"name":"nginx","image":"nginx:1.22"}]}}}}'
# 查看更新进度(会阻塞直到更新完成或超时)
kubectl rollout status deployment/nginx
# 查看更新历史(每次更新对应一个 revision)
kubectl rollout history deployment/nginx
# 查看特定版本详情
kubectl rollout history deployment/nginx --revision=2
🔥 回滚操作(高频考点)
通俗理解: 回滚就是"后悔药"。新版本有 bug?一条命令回到上一个正常版本。
为什么 Deployment 能回滚? 因为每次更新,Deployment 不会删除旧的 ReplicaSet,而是把旧 RS 的 replicas 设为 0("休眠")。回滚时只要把旧 RS 的 replicas 恢复,新 RS 的 replicas 设为 0 就行了。
# 回滚到上一个版本(最常用)
kubectl rollout undo deployment/nginx
# 回滚到指定版本(需要先 rollout history 查看可用版本)
kubectl rollout undo deployment/nginx --to-revision=2
# 暂停滚动更新(想中途暂停,比如做金丝雀发布)
kubectl rollout pause deployment/nginx
# 恢复滚动更新
kubectl rollout resume deployment/nginx
# 重启 Deployment(触发重新部署,但不改配置 —— 常用于拉取最新 latest 镜像)
kubectl rollout restart deployment/nginx
⚠️ 考试场景:题目说"更新镜像后新 Pod 一直 ImagePullBackOff" → 镜像名可能写错了 →
kubectl rollout undo deployment/nginx先回滚,再修正镜像名重新更新。
扩缩容
手动扩缩容:
# 手动调整副本数
kubectl scale deployment nginx --replicas=5
自动扩缩容(HPA — Horizontal Pod Autoscaler):
通俗理解: HPA 就像 Auto Scaling —— 根据 CPU 或其他指标自动增减 Pod 数量。
图解说明:
- 这张图在讲什么:HPA 如何从 Metrics Server 获取指标,根据设定的目标值自动调整 Deployment 的副本数。
- 你应该先看哪里:先看 Metrics Server(指标来源),再看 HPA 的决策逻辑(当前值 vs 目标值),最后看它如何修改 Deployment 的 replicas。
- 前提条件:HPA 需要 Metrics Server 才能工作。如果集群没装 Metrics Server,
kubectl top命令不可用,HPA 也不会工作。
📖 图片来源:Kubernetes 官方文档 - HPA - CC BY 4.0
# 使用 autoscale 快速创建 HPA(需要 Metrics Server)
kubectl autoscale deployment nginx --min=2 --max=10 --cpu-percent=80
# 查看 HPA
kubectl get hpa
HPA YAML 配置:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80 # CPU 超过 80% 就扩容
💡 HPA 生效的前提:Pod 必须在 containers.resources 中设置了
requests。没有 requests,HPA 不知道怎么算利用率百分比。
ReplicaSets
ReplicaSet 是什么?
术语:ReplicaSet
- 一句话解释:确保指定数量的 Pod 副本始终在运行。少了就创建,多了就删除。
- 形象比喻:像餐厅的"人数维持器" —— 你说要 3 个服务员,它就确保永远有 3 个。一个请假了就立刻补一个。
- 最易混淆:通常不直接创建 ReplicaSet。你创建 Deployment,Deployment 会自动创建 ReplicaSet 来管理 Pod。直接用 ReplicaSet 就没有滚动更新和回滚功能了。
⚠️ 考试提示:通常不直接创建 ReplicaSet,而是使用 Deployment。Deployment 会自动管理 ReplicaSet。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rs
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
# 也支持 matchExpressions(更灵活的选择器)
# matchExpressions:
# - key: app
# operator: In
# values:
# - nginx
# - nginx-v2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ReplicaSet vs Deployment
| 特性 | ReplicaSet | Deployment |
|---|---|---|
| 副本管理 | ✅ | ✅ (通过 RS) |
| 滚动更新 | ❌ | ✅ |
| 版本回滚 | ❌ | ✅ |
| 更新历史 | ❌ | ✅ |
| 推荐使用 | ❌ 不推荐直接用 | ✅ 强烈推荐 |
# 查看 Deployment 自动创建的 ReplicaSet
kubectl get rs -l app=nginx
# 查看 RS 详情(可以看到它被哪个 Deployment 管理)
kubectl describe rs nginx-rs
💡 底层关系:
kubectl get rs可以看到每次更新后保留的旧 ReplicaSet(replicas=0)。这就是 Deployment 能回滚的原因 —— 旧版本的 RS 还在。
DaemonSets
DaemonSet 是什么?
术语:DaemonSet
- 一句话解释:确保所有(或部分)节点上恰好运行一个 Pod 副本。新增节点自动部署,删除节点自动清理。
- 形象比喻:像连锁餐厅的必配员工制度 —— 每家分店(节点)都必须有一个保安(DaemonSet Pod)。开新分店(新节点加入集群)就自动派一个保安过去,关分店(节点离开)保安就跟着撤。
- 考试怎么考:题目说"在每个节点上运行 XXX" → DaemonSet。
- 最易混淆:DaemonSet 不指定 replicas(因为 Pod 数量由节点数量决定)。Deployment 指定 replicas(你说要几个就几个)。
┌─────────────────────────────────────────────────────────────────┐
│ DaemonSet │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │ Node 4 │ │
│ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │
│ │ │ DS Pod │ │ │ │ DS Pod │ │ │ │ DS Pod │ │ │ │ DS Pod │ │ │
│ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │
│ │
│ 每个节点自动且仅运行一个 Pod 副本 │
└─────────────────────────────────────────────────────────────────┘
典型使用场景
| 场景 | 示例 | 为什么用 DaemonSet |
|---|---|---|
| 日志收集 | Fluentd, Filebeat | 每个节点的日志都需要收集 |
| 监控代理 | Prometheus Node Exporter | 需要监控每台机器的指标 |
| 网络插件 | Calico, Flannel, Weave | 每个节点都需要网络功能 |
| 存储插件 | GlusterFS, Ceph | 每个节点都需要存储驱动 |
创建 DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
labels:
app: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
tolerations:
# 允许在 master 节点运行(master 默认有 taint 阻止普通 Pod)
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluentd:v1.14
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
⚠️ 关键点:DaemonSet 要在 Master 节点上也运行的话,必须加 tolerations 来容忍 Master 节点的 taint。否则 DaemonSet 的 Pod 不会被调度到 Master 上。
限制 DaemonSet 只在部分节点运行
用 nodeSelector 只在有特定标签的节点上运行:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: gpu-monitor
spec:
selector:
matchLabels:
name: gpu-monitor
template:
metadata:
labels:
name: gpu-monitor
spec:
nodeSelector:
gpu: "true" # 只在有 gpu=true 标签的节点运行
containers:
- name: monitor
image: gpu-monitor:v1
# 查看 DaemonSet
kubectl get ds -n kube-system
# 查看 DaemonSet 详情(可以看到在哪些节点上运行了 Pod)
kubectl describe ds fluentd -n kube-system
StatefulSets
StatefulSet 是什么?
术语:StatefulSet
- 一句话解释:管理有状态应用的控制器。每个 Pod 有固定编号、稳定的网络地址和独立的持久存储,Pod 被删除重建后身份不变。
- 形象比喻:像医院的专属诊室制度。张医生是"诊室-0",李医生是"诊室-1",王医生是"诊室-2"。张医生请假了,来了替班的也叫"诊室-0 的医生"(名字不变),用的还是张医生原来的诊室和病历柜(存储不变)。新招人从"诊室-3"开始,裁人从"诊室-2"开始裁。
- 考试怎么考:题目说"数据库集群"、"需要稳定主机名"、"每个 Pod 要独立存储" → StatefulSet。
- 最易混淆:Deployment 的 Pod 名字是随机的(nginx-abc123),StatefulSet 的 Pod 名字是固定编号的(mysql-0, mysql-1, mysql-2)。
StatefulSet 和 Deployment 最核心的区别是什么?
Deployment 的 Pod:
nginx-7b4f9c ← 随机名字
nginx-k8s2p ← 随机名字
nginx-mx9q1 ← 随机名字
Pod 挂了重建 → nginx-新随机名 ← 名字变了!
StatefulSet 的 Pod:
mysql-0 ← 固定编号
mysql-1 ← 固定编号
mysql-2 ← 固定编号
Pod 挂了重建 → mysql-0 (还是0) ← 名字不变!
为什么数据库需要 StatefulSet? 因为 MySQL 主从复制需要知道"谁是主节点"。如果 Pod 名字随机变化,其他节点就不知道该找谁同步数据了。StatefulSet 保证了 mysql-0 永远是 mysql-0。
┌──────────────────────────────────────────────────────────────────┐
│ StatefulSet │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 有序部署:mysql-0 → mysql-1 → mysql-2 │ │
│ │ 有序删除:mysql-2 → mysql-1 → mysql-0 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ mysql-0 │ │ mysql-1 │ │ mysql-2 │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Pod │ │ │ │ Pod │ │ │ │ Pod │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ PVC │ │ │ │ PVC │ │ │ │ PVC │ │ │
│ │ │ data-0 │ │ │ │ data-1 │ │ │ │ data-2 │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ mysql-0.mysql mysql-1.mysql mysql-2.mysql │
│ (稳定 DNS 名) (稳定 DNS 名) (稳定 DNS 名) │
└──────────────────────────────────────────────────────────────────┘
StatefulSet 五大特性
| 特性 | 描述 | 为什么重要 |
|---|---|---|
| 稳定网络标识 | Pod 名称固定({name}-0, -1, -2...) | 数据库主从知道"找谁" |
| 稳定存储 | 每个 Pod 绑定独立 PVC,Pod 重建后 PVC 不变 | 数据不丢失 |
| 有序部署 | 按顺序创建 Pod(0 → 1 → 2) | 先启动主节点,再启动从节点 |
| 有序删除 | 按逆序删除 Pod(2 → 1 → 0) | 先删从节点,最后删主节点 |
| 有序更新 | 按顺序滚动更新 | 逐个更新,确保一致性 |
创建 StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql # 必须关联一个 Headless Service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates: # 为每个 Pod 自动创建独立的 PVC
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
配套的 Headless Service(必须有):
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None # Headless Service 的标志:clusterIP 设为 None
selector:
app: mysql
ports:
- port: 3306
什么是 Headless Service?为什么 StatefulSet 需要它?
普通 Service 给你一个虚拟 IP(ClusterIP),流量随机分配到后端 Pod。但数据库场景下你要精确访问某个 Pod(比如只读请求要发到从节点 mysql-1)。Headless Service(clusterIP: None)不分配虚拟 IP,而是让你通过 DNS 直接访问每个 Pod:mysql-0.mysql.default.svc.cluster.local。
StatefulSet vs Deployment 对比
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机后缀(nginx-abc123) | 有序编号(mysql-0, mysql-1) |
| Pod 替换 | 新 Pod 新名称 | 保持原名称 |
| 存储 | 共享 PVC 或无 | 每个 Pod 独立 PVC |
| 网络标识 | 不稳定 | 稳定 DNS 名 |
| 扩缩容 | 并行 | 有序(先 0 再 1 再 2) |
| 使用场景 | 无状态应用 | 有状态应用(数据库、缓存集群) |
Jobs
Job 是什么?
术语:Job
- 一句话解释:运行一次性任务。Pod 成功完成后就结束,不会像 Deployment 那样持续运行。
- 形象比喻:像叫了一个外卖配送员 —— 送完这单就完事,不需要一直待命。
- 最易混淆:Job 的
restartPolicy必须是Never或OnFailure,不能是Always。因为 Always 意味着"永远重启",那 Job 永远不会完成。
apiVersion: batch/v1
kind: Job
metadata:
name: backup-job
spec:
completions: 3 # 需要成功完成的 Pod 总数
parallelism: 2 # 同时并行运行的 Pod 数量
backoffLimit: 4 # 允许失败重试的次数
activeDeadlineSeconds: 100 # 任务整体超时时间(秒)
ttlSecondsAfterFinished: 60 # 完成后 60 秒自动清理
template:
spec:
restartPolicy: Never # 必须是 Never 或 OnFailure!
containers:
- name: backup
image: backup-tool
command: ["/bin/sh", "-c"]
args: ["echo 'Backup started'; sleep 10; echo 'Backup completed'"]
completions 和 parallelism 的关系:
completions=3, parallelism=2(需要成功3次,同时最多跑2个)
时间线:
─────────────────────────────────────────────────►
t0: [Pod-1 运行中] [Pod-2 运行中] ← 先启动2个
t1: [Pod-1 完成 ✓] [Pod-2 运行中] [Pod-3 启动] ← Pod-1完成,立即启动Pod-3
t2: [Pod-2 完成 ✓] [Pod-3 运行中]
t3: [Pod-3 完成 ✓]
Job 完成 ✓ (3/3 completions)
# 快速创建 Job
kubectl create job hello --image=busybox -- echo "Hello World"
# 查看 Job 状态
kubectl get jobs
# 查看 Job 详情
kubectl describe job backup-job
# 查看 Job 的 Pod
kubectl get pods -l job-name=backup-job
# 删除 Job(会同时删除关联的 Pod)
kubectl delete job backup-job
CronJobs
CronJob 是什么?
术语:CronJob
- 一句话解释:按 Cron 表达式定义的时间计划,周期性地创建 Job。
- 形象比喻:像一个闹钟 —— 每天早上 6 点叫你起床(触发一个 Job),起完床(Job 完成)就安静了,明天早上 6 点再叫。
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
concurrencyPolicy: Forbid # 禁止并发:上一个还没完就不启动新的
successfulJobsHistoryLimit: 3 # 保留最近 3 个成功 Job 记录
failedJobsHistoryLimit: 1 # 保留最近 1 个失败 Job 记录
startingDeadlineSeconds: 200 # 超过截止时间未启动就跳过
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool
command: ["/bin/sh", "-c"]
args: ["backup.sh"]
Cron 表达式速查
┌───────────── 分钟 (0 - 59)
│ ┌───────────── 小时 (0 - 23)
│ │ ┌───────────── 日 (1 - 31)
│ │ │ ┌───────────── 月 (1 - 12)
│ │ │ │ ┌───────────── 星期 (0 - 6) (周日 = 0)
│ │ │ │ │
* * * * *
常用示例(考试可能让你写 Cron 表达式):
| 表达式 | 描述 |
|---|---|
0 * * * * | 每小时整点 |
*/5 * * * * | 每 5 分钟 |
0 2 * * * | 每天凌晨 2 点 |
0 0 * * 0 | 每周日午夜 |
0 0 1 * * | 每月 1 号午夜 |
0 9-17 * * 1-5 | 工作日 9-17 点每小时 |
并发策略
| 策略 | 描述 | 场景 |
|---|---|---|
| Allow | 允许并发运行(默认) | 任务之间互不影响 |
| Forbid | 禁止并发,跳过新任务 | 上一次没跑完就不启动新的 |
| Replace | 取消正在运行的,启动新的 | 只关心最新一次执行 |
# 快速创建 CronJob
kubectl create cronjob hello --image=busybox --schedule="*/1 * * * *" \
-- echo "Hello"
# 查看 CronJob
kubectl get cronjobs
# 手动触发一次执行(不等定时)
kubectl create job hello-manual --from=cronjob/hello
# 暂停 CronJob
kubectl patch cronjob hello -p '{"spec":{"suspend":true}}'
# 恢复 CronJob
kubectl patch cronjob hello -p '{"spec":{"suspend":false}}'
🎯 考试技巧
快速创建命令
# 考试开始时设置别名
alias k=kubectl
export do="--dry-run=client -o yaml"
# 快速创建 Deployment 并导出 YAML(先生成 → 再修改 → 再 apply)
k create deployment nginx --image=nginx --replicas=3 $do > dep.yaml
# 快速创建 Job
k create job backup --image=busybox -- echo "backup" $do > job.yaml
# 快速创建 CronJob
k create cronjob cj --image=busybox --schedule="*/5 * * * *" -- echo "hi"
常用操作速查
| 操作 | 命令 |
|---|---|
| 创建 Deployment | kubectl create deployment nginx --image=nginx |
| 扩缩容 | kubectl scale deployment nginx --replicas=5 |
| 更新镜像 | kubectl set image deployment/nginx nginx=nginx:1.22 |
| 查看更新状态 | kubectl rollout status deployment/nginx |
| 查看历史 | kubectl rollout history deployment/nginx |
| 回滚 | kubectl rollout undo deployment/nginx |
| 回滚到指定版本 | kubectl rollout undo deployment/nginx --to-revision=2 |
⚠️ 常见错误与误区
错误 1:selector 与 template labels 不匹配
这是最常见的 YAML 错误,创建时会直接报错。
# ❌ 错误:selector 和 template labels 不匹配
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: web # 和上面的 nginx 不一样!→ 报错
# ✅ 正确:必须完全匹配
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx # 和 selector 一致
错误 2:Job restartPolicy 设为 Always
# ❌ 错误:Job 的 restartPolicy 不能是 Always
spec:
template:
spec:
restartPolicy: Always # Job 不允许 Always!
# ✅ 正确:必须是 Never 或 OnFailure
spec:
template:
spec:
restartPolicy: Never
为什么? Always 意味着容器退出后永远重启 —— 但 Job 就是要"完成就结束",如果永远重启那永远不会完成。
错误 3:DaemonSet 不在 Master 节点运行
# ❌ 问题:DaemonSet 的 Pod 没有出现在 Master 节点
# 原因:Master 节点有 taint,DaemonSet 没有对应的 toleration
# ✅ 解决:添加 tolerations
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
# 或者用万能容忍(容忍所有 taint):
# - operator: Exists
错误 4:StatefulSet 忘了配 Headless Service
❌ 创建了 StatefulSet 但没创建 serviceName 对应的 Headless Service
→ Pod 无法获得稳定的 DNS 名称
✅ StatefulSet 的 serviceName 字段必须指向一个已存在的 Headless Service
(clusterIP: None)
📝 考试场景题
场景 1:Deployment 更新后 Pod 无法启动
问题:更新镜像后,新 Pod 一直处于 ImagePullBackOff。
排查思路:镜像名写错了,或者私有仓库没配认证。先回滚恢复服务,再修正配置。
# 1. 查看 Pod 状态,确认是镜像问题
kubectl get pods
kubectl describe pod <pod-name> # 看 Events 里的错误信息
# 2. 确认是镜像名写错了 → 先回滚恢复服务
kubectl rollout undo deployment/nginx
# 3. 确认回滚成功
kubectl rollout status deployment/nginx
# 4. 修正镜像名,重新更新
kubectl set image deployment/nginx nginx=nginx:1.22
场景 2:需要在所有节点运行监控代理
解决方案:DaemonSet + 万能 toleration
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
tolerations:
- operator: Exists # 容忍所有 taint,确保在所有节点运行
containers:
- name: exporter
image: prom/node-exporter
EOF
练习题
题目 1
创建一个名为 web-app 的 Deployment,使用 nginx:1.21 镜像,副本数为 3。
kubectl create deployment web-app --image=nginx:1.21 --replicas=3
或使用 YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: nginx
image: nginx:1.21
</details>
题目 2
将 web-app Deployment 的镜像更新为 nginx:1.22,然后回滚到上一个版本。
# 更新镜像
kubectl set image deployment/web-app nginx=nginx:1.22
# 查看更新状态
kubectl rollout status deployment/web-app
# 回滚
kubectl rollout undo deployment/web-app
# 确认回滚
kubectl rollout status deployment/web-app
</details>
题目 3
DaemonSet 的主要使用场景是什么?
- A. 无状态 Web 应用
- B. 每个节点运行监控代理
- C. 数据库集群
- D. 批处理任务
答案:B
解析:DaemonSet 确保在每个节点(或部分节点)上运行恰好一个 Pod 副本。典型场景:日志收集(Fluentd)、监控代理(Node Exporter)、网络插件(Calico)。A 用 Deployment,C 用 StatefulSet,D 用 Job。
</details>题目 4
StatefulSet 与 Deployment 的主要区别是什么?
- A. StatefulSet 支持滚动更新
- B. StatefulSet 提供稳定的网络标识和持久存储
- C. StatefulSet 可以扩缩容
- D. StatefulSet 只能运行一个副本
答案:B
解析:StatefulSet 的核心特点:
- 稳定的网络标识:Pod 名称固定编号(mysql-0, mysql-1)
- 稳定的持久存储:每个 Pod 独立 PVC,Pod 重建后存储不丢
- 有序的部署和扩缩容:先 0 再 1 再 2
A 和 C 两者都支持,D 是错误的(StatefulSet 支持多副本)。
</details>题目 5(实操题)
创建一个 CronJob,每 5 分钟运行一次,输出当前时间。
<details> <summary>查看答案</summary>kubectl create cronjob time-logger --image=busybox \
--schedule="*/5 * * * *" \
-- /bin/sh -c 'date'
或使用 YAML:
apiVersion: batch/v1
kind: CronJob
metadata:
name: time-logger
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: logger
image: busybox
command: ["/bin/sh", "-c", "date"]
</details>
本章小结
| 工作负载类型 | 使用场景 | 关键特性 | 比喻 |
|---|---|---|---|
| Deployment | 无状态应用 | 滚动更新、回滚、扩缩容 | 可替换的服务员 |
| ReplicaSet | 副本管理 | 维持 Pod 数量(通常由 Deployment 管理) | 人数维持器 |
| DaemonSet | 每节点代理 | 每节点一个 Pod | 每店必配保洁 |
| StatefulSet | 有状态应用 | 稳定标识、独立存储、有序部署 | 专属诊室的医生 |
| Job | 一次性任务 | completions + parallelism | 外卖配送员 |
| CronJob | 定时任务 | Cron 表达式 + 并发策略 | 闹钟 |
核心记忆:
- 95% 的场景用 Deployment
- "每个节点" → DaemonSet
- "有状态/数据库/固定名字" → StatefulSet
- "干完就走" → Job
- "定时执行" → CronJob
- 滚动更新:
set image→rollout status→ 出问题rollout undo - selector 和 template labels 必须匹配
- Job 的 restartPolicy 不能是 Always