跳转至

工作负载

Pod 是最小单元,但你不应该直接管理 Pod。工作负载控制器负责副本数、发布策略、稳定身份和任务生命周期——选错控制器,后面的存储、网络、升级都会别扭。

难度 · 基础到进阶 预计 55 分钟 前置: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。这种三层结构让「滚动发布」和「回滚」成为可能。

deployment-demo.yaml
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 在发布卡住时及时失败,而不是一直等。

常见的「发布卡住」

  1. 新 Pod readiness 一直失败;
  2. 资源不足,新 Pod Pending;
  3. 镜像拉取失败;
  4. 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 删除。
statefulset-demo.yaml
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。

daemonset-demo.yaml
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 成功完成。

job-demo.yaml
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

cronjob-demo.yaml
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:

pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: demoapp-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: demoapp

注意:PDB 不阻止节点故障、OOM、删除 Pod 等非自愿中断。

HorizontalPodAutoscaler

hpa.yaml
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 或自定义指标适配器,详见 调度与弹性。

实验:一次失败的滚动发布

  1. 部署 deployment-demo,确认 4 个副本 Ready;
  2. 执行:
kubectl set image deploy/deployment-demo demoapp=ikubernetes/demoapp:v9.9
kubectl rollout status deploy/deployment-demo --timeout=60s
  1. 观察 kubectl get pods:新 Pod 处于 ImagePullBackOff,旧 Pod 仍然可用(取决于 maxUnavailable)。

  2. 回滚:

kubectl rollout undo deploy/deployment-demo
kubectl rollout status deploy/deployment-demo
  1. 记录:发布过程中 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 或先扩容

进阶挑战

  1. 把 Deployment 的 maxSurge 设为 0、maxUnavailable 设为 1,观察发布期间可用副本数。
  2. 给 StatefulSet 配置 podManagementPolicy: Parallel,对比启动顺序。
  3. 用 kubectl rollout restart 触发一次滚动重启,解释它与改镜像的区别。
  4. 创建一个失败的 Job,观察 backoffLimit 与 activeDeadlineSeconds 谁先生效。
  5. 用 PDB + kubectl drain 验证「自愿中断」被阻止的场景。

小结

  • 无状态用 Deployment,有状态用 StatefulSet,节点级用 DaemonSet,任务用 Job/CronJob。
  • Deployment 的核心是 ReplicaSet 历史与滚动策略;StatefulSet 的核心是稳定身份与独立存储。
  • 发布不是 apply 完就结束,必须用 rollout status 验证,并准备好回滚。

延伸阅读