考证匠/CKA/第 2 章
第 2 章

工作负载资源

⏱️ 120 分钟📚 Workloads & Scheduling难度: ⭐⭐⭐
📝 30 题练习
备考助手

🎯 学习目标

  • 创建和管理 Pods
  • 使用 Deployments 进行应用部署
  • 理解 DaemonSets 和 StatefulSets

📌 核心知识点

Pod 生命周期Deployments 与 ReplicaSetsDaemonSets 与 StatefulSetsJobs 和 CronJobs滚动更新与回滚

工作负载

📋 本章概览

主题考试权重重要程度
Deployments★★★★★必考
滚动更新与回滚★★★★★高频考点
ReplicaSets★★★★☆重要
DaemonSets★★★★☆重要
StatefulSets★★★☆☆了解
Jobs/CronJobs★★★☆☆了解

💡 考试占比:工作负载和调度占 CKA 考试的 15%


🎯 学习目标

学完这一章,你应该能够:

  1. 理解为什么 Kubernetes 需要不同类型的工作负载 —— 不同的应用有不同的运行需求
  2. 熟练创建和管理 Deployment(CKA 最核心的考点之一)
  3. 独立完成 滚动更新和回滚 操作
  4. 区分 Deployment / DaemonSet / StatefulSet / Job / CronJob 的使用场景
  5. 理解 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?

容器技术演进

图解说明:

  1. 这张图在讲什么:从传统部署 → 虚拟化部署 → 容器化部署的演进过程。
  2. 你应该先看哪里:先看三种部署方式的资源利用效率差异,再看容器化如何解决虚拟化的开销问题。
  3. 考试关联:理解容器化的优势(轻量、快速启动、资源隔离),才能理解为什么 Kubernetes 选择管理容器而不是虚拟机。

📖 图片来源:Kubernetes 官方文档 - CC BY 4.0

Pod 存在的理由: 有些应用需要多个容器紧密协作。比如一个 Web 应用容器 + 一个日志收集容器,它们需要共享网络(通过 localhost 通信)和共享存储(读同一个日志文件)。Pod 就是这样一个"容器组"的概念 —— 同一个 Pod 里的容器共享网络命名空间和存储卷。

Pod 内部结构

图解说明:

  1. 这张图在讲什么:Pod 内部的容器如何共享网络和存储,以及 Pod 的生命周期。
  2. 你应该先看哪里:先看 Pod 的边界(同一个 Pod 内的容器共享 IP 地址),再看 Volume 是如何被多个容器挂载的。
  3. 考试怎么考:多容器 Pod 的场景题 —— sidecar、init container 等模式。

📖 图片来源:Kubernetes 官方文档 - CC BY 4.0

💡 一句话记忆:Pod = 一个或多个容器 + 共享网络 + 共享存储。它是 Kubernetes 调度的最小单位(Scheduler 调度 Pod,不调度单个容器)。


工作负载资源概览

Pod 概览 - 工作负载与 Volume 关系

图解说明:

  1. 这张图在讲什么:Pod 和 Volume 的关系,以及工作负载控制器如何管理 Pod。
  2. 你应该先看哪里:先看 Pod 如何引用 Volume,再看控制器(Deployment/DaemonSet 等)如何定义 Pod 模板。
  3. 最易混淆: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 的层级关系

为什么需要 ReplicaSet 这个"中间人"?

直接管理 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 Rolling Update 流程

图解说明:

  1. 这张图在讲什么:Kubernetes 滚动更新的可视化过程 —— 新旧版本 Pod 如何交替。
  2. 你应该先看哪里:先看更新前的状态(所有 Pod 是旧版本),再看中间的过渡状态(新旧混合),最后看完成状态(所有 Pod 是新版本)。
  3. 考试常见问法:更新卡住了怎么办?答: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 数量。

Horizontal Pod Autoscaler 工作原理

图解说明:

  1. 这张图在讲什么:HPA 如何从 Metrics Server 获取指标,根据设定的目标值自动调整 Deployment 的副本数。
  2. 你应该先看哪里:先看 Metrics Server(指标来源),再看 HPA 的决策逻辑(当前值 vs 目标值),最后看它如何修改 Deployment 的 replicas。
  3. 前提条件: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

特性ReplicaSetDeployment
副本管理✅ (通过 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 对比

特性DeploymentStatefulSet
Pod 名称随机后缀(nginx-abc123)有序编号(mysql-0, mysql-1)
Pod 替换新 Pod 新名称保持原名称
存储共享 PVC 或无每个 Pod 独立 PVC
网络标识不稳定稳定 DNS 名
扩缩容并行有序(先 0 再 1 再 2)
使用场景无状态应用有状态应用(数据库、缓存集群)

Jobs

Job 是什么?

术语:Job

  • 一句话解释:运行一次性任务。Pod 成功完成后就结束,不会像 Deployment 那样持续运行。
  • 形象比喻:像叫了一个外卖配送员 —— 送完这单就完事,不需要一直待命。
  • 最易混淆:Job 的 restartPolicy 必须是 NeverOnFailure不能是 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"

常用操作速查

操作命令
创建 Deploymentkubectl 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。

<details> <summary>查看答案</summary>
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,然后回滚到上一个版本。

<details> <summary>查看答案</summary>
# 更新镜像
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. 批处理任务
<details> <summary>查看答案</summary>

答案: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 只能运行一个副本
<details> <summary>查看答案</summary>

答案: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 imagerollout status → 出问题 rollout undo
  • selector 和 template labels 必须匹配
  • Job 的 restartPolicy 不能是 Always
📝 章节练习 (30 题)
开始练习