2.4. 工作负载
Pod 是最小单元,但你不应该直接管理 Pod。工作负载控制器负责副本数、发布策略、稳定身份和任务生命周期——选错控制器,后面的存储、网络、升级都会别扭。
- 层级:
L2 · 核心
- 难度:
基础到进阶
- 预计:
55 分钟
- 前置:
Pod 与容器
2.4.1. 你将学到
五种工作负载的适用边界,以及如何选
Deployment 的滚动更新参数与回滚机制
ReplicaSet 的所有权与蓝绿发布切换
StatefulSet 的稳定身份、有序启停与卷模板
DaemonSet 的节点覆盖与更新策略
Job / CronJob 的完成语义与并发控制
PDB、HPA 与工作负载的配合
2.4.2. 怎么选
工作负载 |
适合 |
身份 |
存储 |
典型例子 |
|---|---|---|---|---|
Deployment |
无状态、可水平扩展 |
随机 |
共享或无 |
Web、API |
StatefulSet |
有状态、需要稳定身份 |
固定序号 |
每副本独立 PVC |
数据库、消息队列 |
DaemonSet |
每个节点一个 |
跟随节点 |
节点本地 |
日志、CNI、监控代理 |
Job |
运行到完成 |
随机 |
通常无 |
迁移、批处理 |
CronJob |
定时任务 |
随机 |
通常无 |
备份、报表 |
图:工作负载选型的最简决策树。
2.4.3. Deployment
Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。这种三层结构让「滚动发布」和「回滚」成为可能。
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-demo
spec:
replicas: 4
revisionHistoryLimit: 5
progressDeadlineSeconds: 300
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: demoapp
release: stable
template:
metadata:
labels:
app: demoapp
release: stable
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
name: http
readinessProbe:
httpGet:
path: /readyz
port: http
initialDelaySeconds: 5
2.4.3.1. 发布策略
策略 |
行为 |
适用 |
|---|---|---|
|
逐步替换旧 Pod,服务不中断 |
大多数无状态服务 |
|
先删光旧 Pod,再创建新 Pod |
不能并存两个版本的应用 |
maxSurge 和 maxUnavailable 可以是绝对值或百分比:
maxSurge: 1:最多多出 1 个 Pod;maxUnavailable: 1:最多有 1 个 Pod 不可用;两者不能同时为 0。
发布过程:
kubectl apply -f deployment-demo.yaml
kubectl rollout status deploy/deployment-demo
kubectl rollout history deploy/deployment-demo
kubectl set image deploy/deployment-demo demoapp=ikubernetes/demoapp:v1.1
kubectl rollout status deploy/deployment-demo
kubectl rollout undo deploy/deployment-demo
小技巧
发布失败自动停住
progressDeadlineSeconds 控制「多久没有进展就标记失败」。配合 kubectl rollout status --timeout=120s,可以让 CI 在发布卡住时及时失败,而不是一直等。
2.4.3.2. 常见的「发布卡住」
新 Pod readiness 一直失败;
资源不足,新 Pod
Pending;镜像拉取失败;
maxUnavailable太小,旧 Pod 不退,新 Pod 起不来。
排查:
kubectl rollout status deploy/deployment-demo
kubectl get rs -l app=demoapp
kubectl describe pod <新 Pod>
kubectl logs <新 Pod>
2.4.4. ReplicaSet 与蓝绿发布
Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。日常不需要直接操作 ReplicaSet,但理解它可以解释两件事:为什么改镜像会出现新的 ReplicaSet,以及如何用两个 ReplicaSet 做蓝绿发布。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: demoapp-stable
spec:
replicas: 3
selector:
matchLabels:
app: demoapp
track: stable
template:
metadata:
labels:
app: demoapp
track: stable
version: v1.0
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: demoapp-canary
spec:
replicas: 3
selector:
matchLabels:
app: demoapp
track: canary
template:
metadata:
labels:
app: demoapp
track: canary
version: v2.0
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v2.0
ports:
- containerPort: 80
蓝绿的关键在 Service 的 selector:先让 Service 只选 track: stable,验证新版本 ReplicaSet 就绪后,再 patch selector 切到 track: canary。
kubectl apply -f replicaset-blue-green.yaml
kubectl expose rs demoapp-stable --name=demoapp-svc --port=80
kubectl get pods -l app=demoapp --show-labels
kubectl patch svc demoapp-svc -p '{"spec":{"selector":{"app":"demoapp","track":"canary"}}}'
切流前确认:
新 ReplicaSet 的
READY等于期望副本数;新旧版本使用兼容的数据库 schema 与 API;
观察错误率与延迟,再决定保留或回滚;
回滚只需把 selector 切回
track: stable。
备注
ReplicaSet 的 selector 不可变
spec.selector 创建后不可修改;标签不匹配时,ReplicaSet 可能“收养”已有 Pod。直接使用 ReplicaSet 时务必确保 selector 足够精确,生产环境优先用 Deployment。
对应原书第 8 章的 replicaset-blue-green.yaml、service-blue-green.yaml、replicaset-demo.yaml。
2.4.5. StatefulSet
StatefulSet 为每个副本提供:
稳定网络标识:
<pod-name>.<service-name>.<namespace>.svc.cluster.local;稳定存储:
volumeClaimTemplates为每个序号创建独立 PVC;有序生命周期:默认按 0 → N-1 创建,按 N-1 → 0 删除。
apiVersion: v1
kind: Service
metadata:
name: demoapp-sts
spec:
clusterIP: None
selector:
app: demoapp
controller: sts-demo
ports:
- name: http
port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sts-demo
spec:
serviceName: demoapp-sts
replicas: 2
podManagementPolicy: OrderedReady
selector:
matchLabels:
app: demoapp
controller: sts-demo
template:
metadata:
labels:
app: demoapp
controller: sts-demo
spec:
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
name: web
volumeMounts:
- name: appdata
mountPath: /app/data
volumeClaimTemplates:
- metadata:
name: appdata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs-csi"
resources:
requests:
storage: 2Gi
创建后:
kubectl apply -f statefulset-demo.yaml
kubectl get pod -l controller=sts-demo -w
kubectl get pvc -l app=demoapp
预期 Pod 名为 sts-demo-0、sts-demo-1,PVC 为 appdata-sts-demo-0、appdata-sts-demo-1。
2.4.5.1. 稳定身份验证
kubectl run -it --rm dns-test --image=busybox:1.36 --restart=Never -- \
nslookup sts-demo-0.demoapp-sts
预期解析到 sts-demo-0 的 Pod IP。删除 sts-demo-0 后重建,名字和 PVC 都不变。
警告
Headless Service 不是可选的
serviceName 必须指向一个 clusterIP: None 的 Service。没有它,StatefulSet 的稳定 DNS 不成立,Pod 之间无法通过序号互相发现。
2.4.5.2. 扩缩容与更新
kubectl scale sts sts-demo --replicas=3
kubectl rollout status sts/sts-demo
kubectl rollout history sts/sts-demo
StatefulSet 默认从最后一个序号开始缩容,并保留 PVC(persistentVolumeClaimRetentionPolicy 可配置删除策略)。这意味着「缩容再扩容」通常能拿回原数据。
2.4.6. DaemonSet
DaemonSet 保证每个(符合条件的)节点运行一个 Pod。典型用途:日志采集、监控代理、CNI、kube-proxy。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-agent
spec:
selector:
matchLabels:
app: node-agent
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
template:
metadata:
labels:
app: node-agent
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: agent
image: busybox:1.36
command: ["sh", "-c", "while true; do date; sleep 30; done"]
resources:
requests:
cpu: 10m
memory: 16Mi
kubectl apply -f daemonset-demo.yaml
kubectl get ds node-agent
kubectl get pod -l app=node-agent -o wide
DESIRED 应该等于可调度节点数。如果某个节点没有 Pod,检查污点与 tolerations。
2.4.7. Job 与 CronJob
2.4.7.1. Job
Job 保证指定数量的 Pod 成功完成。
apiVersion: batch/v1
kind: Job
metadata:
name: job-demo
spec:
completions: 3
parallelism: 2
backoffLimit: 3
activeDeadlineSeconds: 120
ttlSecondsAfterFinished: 300
template:
spec:
restartPolicy: Never
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "echo processing $HOSTNAME; sleep 5"]
字段 |
含义 |
|---|---|
|
需要成功完成多少个 Pod |
|
最多并行多少个 |
|
失败重试次数 |
|
整个 Job 的超时 |
|
完成后保留多久自动删除 |
2.4.7.2. CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob-demo
spec:
schedule: "*/2 * * * *"
timeZone: "Asia/Shanghai"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 60
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
ttlSecondsAfterFinished: 600
template:
spec:
restartPolicy: OnFailure
containers:
- name: task
image: busybox:1.36
command: ["sh", "-c", "date; echo hello from cronjob"]
警告
仓库中的旧 API
原仓库的 examples/jobs_and_cronjobs/cronjob-demo.yaml 使用 batch/v1beta1,该版本在 Kubernetes 1.25 已移除。请改用 batch/v1,字段结构基本一致。
concurrencyPolicy:
Allow:允许并发(默认);Forbid:上一次没跑完就跳过本次;Replace:取消上一次,启动本次。
2.4.8. PDB 与 HPA
2.4.8.1. PodDisruptionBudget
PDB 限制「自愿中断」时同时不可用的 Pod 数,例如节点维护、kubectl drain:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: demoapp-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: demoapp
注意:PDB 不阻止节点故障、OOM、删除 Pod 等非自愿中断。
2.4.8.2. HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: demoapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deployment-demo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
HPA 需要 metrics-server 或自定义指标适配器,详见 调度与弹性。
2.4.9. 实验:一次失败的滚动发布
部署
deployment-demo,确认 4 个副本 Ready;执行:
kubectl set image deploy/deployment-demo demoapp=ikubernetes/demoapp:v9.9
kubectl rollout status deploy/deployment-demo --timeout=60s
观察
kubectl get pods:新 Pod 处于ImagePullBackOff,旧 Pod 仍然可用(取决于maxUnavailable)。回滚:
kubectl rollout undo deploy/deployment-demo
kubectl rollout status deploy/deployment-demo
记录:发布过程中
kubectl get rs出现了几个 ReplicaSet?各自副本数是多少?
2.4.10. 排错表
现象 |
原因 |
处理 |
|---|---|---|
Deployment 不创建 Pod |
selector 与 template labels 不匹配 |
|
滚动更新卡住 |
新 Pod 不 Ready / 资源不足 |
|
回滚后仍是新版本 |
回滚到了错误的 revision |
|
StatefulSet Pod 一直 Pending |
PVC 未绑定 / StorageClass 不存在 |
|
StatefulSet 只起了 0 号 |
0 号未 Ready,后续不创建 |
先修 0 号 |
DaemonSet 少了某节点 |
污点未容忍 |
|
Job 一直不完成 |
|
|
CronJob 不触发 |
|
|
PDB 阻止 drain |
|
调整 PDB 或先扩容 |
2.4.11. 进阶挑战
把 Deployment 的
maxSurge设为 0、maxUnavailable设为 1,观察发布期间可用副本数。给 StatefulSet 配置
podManagementPolicy: Parallel,对比启动顺序。用
kubectl rollout restart触发一次滚动重启,解释它与改镜像的区别。创建一个失败的 Job,观察
backoffLimit与activeDeadlineSeconds谁先生效。用 PDB +
kubectl drain验证「自愿中断」被阻止的场景。
2.4.12. 小结
无状态用 Deployment,有状态用 StatefulSet,节点级用 DaemonSet,任务用 Job/CronJob。
Deployment 的核心是 ReplicaSet 历史与滚动策略;StatefulSet 的核心是稳定身份与独立存储。
发布不是
apply完就结束,必须用rollout status验证,并准备好回滚。蓝绿发布用两个 ReplicaSet + Service selector 切换;回滚是切回,不是重建。