工作负载¶
Pod 是最小单元,但你不应该直接管理 Pod。工作负载控制器负责副本数、发布策略、稳定身份和任务生命周期——选错控制器,后面的存储、网络、升级都会别扭。
你将学到¶
- 五种工作负载的适用边界,以及如何选
- Deployment 的滚动更新参数与回滚机制
- StatefulSet 的稳定身份、有序启停与卷模板
- DaemonSet 的节点覆盖与更新策略
- Job / CronJob 的完成语义与并发控制
- PDB、HPA 与工作负载的配合
怎么选¶
| 工作负载 | 适合 | 身份 | 存储 | 典型例子 |
|---|---|---|---|---|
| Deployment | 无状态、可水平扩展 | 随机 | 共享或无 | Web、API |
| StatefulSet | 有状态、需要稳定身份 | 固定序号 | 每副本独立 PVC | 数据库、消息队列 |
| DaemonSet | 每个节点一个 | 跟随节点 | 节点本地 | 日志、CNI、监控代理 |
| Job | 运行到完成 | 随机 | 通常无 | 迁移、批处理 |
| CronJob | 定时任务 | 随机 | 通常无 | 备份、报表 |
flowchart TD
A[需要长期运行?] -->|否| B[一次性任务?]
B -->|是| C[Job]
B -->|定时| D[CronJob]
A -->|是| E[每个节点都要?]
E -->|是| F[DaemonSet]
E -->|否| G[需要稳定身份/独立存储?]
G -->|是| H[StatefulSet]
G -->|否| I[Deployment]
图:工作负载选型的最简决策树。
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
发布策略¶
| 策略 | 行为 | 适用 |
|---|---|---|
RollingUpdate |
逐步替换旧 Pod,服务不中断 | 大多数无状态服务 |
Recreate |
先删光旧 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 在发布卡住时及时失败,而不是一直等。
常见的「发布卡住」¶
- 新 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>
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。
稳定身份验证¶
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 之间无法通过序号互相发现。
扩缩容与更新¶
kubectl scale sts sts-demo --replicas=3
kubectl rollout status sts/sts-demo
kubectl rollout history sts/sts-demo
StatefulSet 默认从最后一个序号开始缩容,并保留 PVC(persistentVolumeClaimRetentionPolicy 可配置删除策略)。这意味着「缩容再扩容」通常能拿回原数据。
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。
Job 与 CronJob¶
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"]
| 字段 | 含义 |
|---|---|
completions |
需要成功完成多少个 Pod |
parallelism |
最多并行多少个 |
backoffLimit |
失败重试次数 |
activeDeadlineSeconds |
整个 Job 的超时 |
ttlSecondsAfterFinished |
完成后保留多久自动删除 |
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:取消上一次,启动本次。
PDB 与 HPA¶
PodDisruptionBudget¶
PDB 限制「自愿中断」时同时不可用的 Pod 数,例如节点维护、kubectl drain:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: demoapp-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: demoapp
注意:PDB 不阻止节点故障、OOM、删除 Pod 等非自愿中断。
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 或自定义指标适配器,详见 调度与弹性。
实验:一次失败的滚动发布¶
- 部署
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 get rs出现了几个 ReplicaSet?各自副本数是多少?
排错表¶
| 现象 | 原因 | 处理 |
|---|---|---|
| Deployment 不创建 Pod | selector 与 template labels 不匹配 | kubectl describe deploy |
| 滚动更新卡住 | 新 Pod 不 Ready / 资源不足 | rollout status、describe pod |
| 回滚后仍是新版本 | 回滚到了错误的 revision | rollout history 对照 |
| StatefulSet Pod 一直 Pending | PVC 未绑定 / StorageClass 不存在 | describe pvc |
| StatefulSet 只起了 0 号 | 0 号未 Ready,后续不创建 | 先修 0 号 |
| DaemonSet 少了某节点 | 污点未容忍 | describe node 的 Taints |
| Job 一直不完成 | completions 大于可并行数 / 任务失败 |
describe job、Pod 日志 |
| CronJob 不触发 | schedule 时区 / startingDeadlineSeconds |
kubectl get cj -o yaml 看 lastScheduleTime |
| PDB 阻止 drain | minAvailable 太高 |
调整 PDB 或先扩容 |
进阶挑战¶
- 把 Deployment 的
maxSurge设为 0、maxUnavailable设为 1,观察发布期间可用副本数。 - 给 StatefulSet 配置
podManagementPolicy: Parallel,对比启动顺序。 - 用
kubectl rollout restart触发一次滚动重启,解释它与改镜像的区别。 - 创建一个失败的 Job,观察
backoffLimit与activeDeadlineSeconds谁先生效。 - 用 PDB +
kubectl drain验证「自愿中断」被阻止的场景。
小结¶
- 无状态用 Deployment,有状态用 StatefulSet,节点级用 DaemonSet,任务用 Job/CronJob。
- Deployment 的核心是 ReplicaSet 历史与滚动策略;StatefulSet 的核心是稳定身份与独立存储。
- 发布不是
apply完就结束,必须用rollout status验证,并准备好回滚。