HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • 概览

    • K8s 实战学习实验室
    • 服务访问清单
    • K8s-Lab 学习总纲、仓库评估与专家路线图
  • 课程正文

    • 环境验证与第一课:认识你的真实集群
    • 第二课:kubectl apply 之后,到底发生了什么
    • 第三课:调度器如何选节点,为什么 Pod 会 Pending
    • 第四课:Kubernetes 网络、协议分层、VXLAN/IPIP/WireGuard 原理与排障
    • 第五课:NetworkPolicy、零信任网络与流量边界
    • 第六课:身份、认证、授权、准入与 ServiceAccount / RBAC 原理
    • 第七课:ConfigMap 与 Secret 注入模型、更新机制与安全边界
    • 第八课:存储持久化、PV / PVC / StorageClass 与 NFS 原理
    • 第九课:StatefulSet、Headless Service、稳定身份与存储原理
    • 第十课:探针、滚动更新、优雅终止与 PDB 原理
    • 第十一课:requests / limits、QoS、OOM 与驱逐原理
    • 第十二课:HPA、自动扩缩容、指标链路与副本伸缩原理
    • 第十三课:Service、EndpointSlice、kube-proxy、CoreDNS 与服务发现原理
    • 第十四课:Ingress-nginx、反向代理、Host / Path、NodePort 与北南向流量原理
    • 第十五课:HTTPS、TLS、SNI、证书信任与 Ingress 终止原理
    • 第十六课:cert-manager、Ingress 自动签发、证书生命周期与 ACME 工作流原理
    • 第十七课:ACME、Let's Encrypt、HTTP-01 / DNS-01、Orders / Challenges 与生产限制原理
    • 第十八课:大模型全生态,从数据到训练到部署到治理原理
    • 第十九课:大模型数据集、清洗、标注、切分、版本管理与质量治理原理
    • 第二十课:大模型训练、SFT、LoRA、Checkpoint、Adapter 与模型产物原理
    • 第二十一课:大模型推理、量化、KV Cache、vLLM、吞吐/延迟与部署发布链路原理
  • 实验操作记录

    • 本次仓库审查操作记录与命令原理
    • 本轮操作记录:环境验证、集群基线盘点与故障样本采集
    • 本轮操作记录:kubectl apply 主链路实验
    • 本轮操作记录:调度实验与 Pending 排查
    • 本轮操作记录:Kubernetes 网络原理、协议对比与调试实验
    • 本轮操作记录:NetworkPolicy 与零信任网络实验
    • 本轮操作记录:身份、认证、授权、准入实验
    • 本轮操作记录:ConfigMap 与 Secret 注入、更新与安全边界实验
    • 本轮操作记录:存储持久化、PV / PVC / StorageClass 与 NFS 实验
    • 本轮操作记录:StatefulSet、Headless Service 与稳定身份实验
    • 本轮操作记录:探针、滚动更新、优雅终止与 PDB 实验
    • 本轮操作记录:资源模型、QoS、OOM 与 CPU 节流实验
    • 本轮操作记录:HPA 自动扩缩容实验
    • 本轮操作记录:Service、EndpointSlice、CoreDNS 与服务发现排障实验
    • 本轮操作记录:Ingress-nginx、NodePort 与北南向流量实验
    • 本轮操作记录:HTTPS、TLS、自签证书与 Ingress 实验
    • 本轮操作记录:cert-manager 安装、CA 签发与 Ingress 自动证书实验
    • 本轮操作记录:ACME staging、HTTP-01 失败样本与排障实验
    • 本轮操作记录:大模型全生态与基础原理科普文撰写
    • 本轮操作记录:大模型数据集样本与治理文档编写
    • 本轮操作记录:大模型训练与模型产物概念文撰写
    • 本轮操作记录:大模型推理与服务发布概念文撰写

StatefulSet、DaemonSet、Job — 不同场景的工作负载

工作负载选型指南

在动手之前先建立直觉——什么场景用什么控制器:

场景控制器原因
无状态 Web/API 服务Deployment副本可互换,随时扩缩
数据库、缓存、消息队列StatefulSet需要稳定身份、持久存储、有序启停
每节点运行一份(日志/监控/网络)DaemonSet自动跟随节点增减
一次性批处理任务Job跑完即退出
定时任务CronJob按 cron 表达式周期触发 Job

StatefulSet — 有状态应用

Deployment 解决不了什么问题?

假设你要部署 3 个 Redis 副本做主从复制:

需求Deployment 能做吗?
固定的 Pod 名称(redis-0 是主,redis-1/2 是从)不能,Pod 名称随机
有序启动(主先启动,从再启动)不能,并行启动
稳定的 DNS(从节点要通过固定地址找到主节点)不能,Pod IP 每次变
每个副本有独立的持久存储不能,共享同一个 PVC

StatefulSet 全部能做。

Headless Service — StatefulSet 的伴侣

apiVersion: v1
kind: Service
metadata:
  name: web-headless
  namespace: dev
spec:
  clusterIP: None          # ★ 关键:None 表示 Headless
  selector:
    app: web
  ports:
  - port: 80

普通 Service vs Headless Service:

特性ClusterIP ServiceHeadless Service
ClusterIP有(如 10.110.86.136)None
DNS 解析返回ClusterIP(一个 IP)所有 Pod IP(多个 A 记录)
负载均衡kube-proxy 做客户端自己选
用途无状态服务StatefulSet、需要直连 Pod

我们创建的 StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: dev
spec:
  serviceName: "web-headless"    # ★ 必须关联 Headless Service
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.25

实际验证结果

Pod 名称是有序的(不是随机哈希):
  web-0   → cp-3           (10.244.242.4)
  web-1   → wk-1           (10.244.147.69)
  web-2   → us590068728056  (10.244.119.197)

创建顺序:web-0 先 Ready → web-1 创建 → web-1 Ready → web-2 创建
删除顺序:反过来,web-2 先删

每个 Pod 有稳定的 DNS:
  web-0.web-headless.dev.svc.cluster.local
  web-1.web-headless.dev.svc.cluster.local
  web-2.web-headless.dev.svc.cluster.local

从 web-0 访问 web-1:
  $ curl http://web-1.web-headless
  Welcome to nginx!    ← 成功!

StatefulSet 的核心保证

  1. 稳定的网络身份 — Pod 名称和 DNS 不变(重建后 web-0 还叫 web-0)
  2. 有序部署和删除 — 按序号创建,逆序删除
  3. 稳定的持久存储 — 每个 Pod 绑定独立的 PVC,缩容后 PVC 保留(数据不丢)

面试高频题

Q: StatefulSet 的 Pod 被删除后,新 Pod 会分配到同一个节点吗?

不一定。Pod 名称和 PVC 绑定关系不变,但调度可能到不同节点。如果 PVC 使用的是 local PV(节点本地存储),那新 Pod 会被 nodeAffinity 约束到原节点。NFS 等网络存储则不受节点限制。

Q: StatefulSet 可以并行启动吗?

可以。设置 podManagementPolicy: Parallel(默认是 OrderedReady)。但主从复制等场景通常需要有序启动。


DaemonSet — 每节点一份

典型场景

  • 日志收集 — Fluentd/Promtail 在每个节点收集容器日志
  • 监控采集 — node-exporter 采集每个节点的系统指标
  • 网络插件 — calico-node、kube-proxy 本身就是 DaemonSet
  • 存储插件 — CSI node plugin

我们创建的 DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: log-collector
  namespace: dev
spec:
  selector:
    matchLabels:
      app: log-collector
  template:
    metadata:
      labels:
        app: log-collector
    spec:
      containers:
      - name: collector
        image: busybox:1.36
        command: ["sh", "-c", "while true; do echo \"collecting logs on $(hostname)\"; sleep 60; done"]
        resources:
          requests:
            cpu: 10m
            memory: 16Mi

实际验证结果

log-collector-crk6f   → cp-3             (LA Worker-2)
log-collector-k2vfp   → us590068728056   (LA Worker-1)
log-collector-zw5pf   → hk652699382121   (HK Worker-3)
log-collector-2c644   → wk-1             (HK Worker-4)

注意:Master (us480851516617a) 上没有!
原因:Master 有 Taint node-role.kubernetes.io/control-plane:NoSchedule
DaemonSet 默认也尊重 Taint。

如何让 DaemonSet 跑在 Master 上?

spec:
  template:
    spec:
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule

加了这个 toleration,Pod 就能"容忍" Master 的 taint,被调度上去。

DaemonSet vs Deployment with nodeAffinity?

两者都能在指定节点上跑 Pod,但:

  • DaemonSet 保证每个匹配节点恰好一个 Pod
  • Deployment 需要手动设 replicas = 节点数,且新增节点时不会自动扩
  • DaemonSet 在节点加入集群时自动部署,节点删除时自动清理

Job — 一次性任务

我们创建的 Job

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-calc
spec:
  completions: 5          # 总共要完成 5 个任务
  parallelism: 2          # 最多同时跑 2 个 Pod
  backoffLimit: 3         # 失败重试次数上限
  template:
    spec:
      containers:
      - name: pi
        image: busybox:1.36
        command: ["sh", "-c", "echo scale=1000; 4*a(1) | bc -l"]
      restartPolicy: Never   # Job Pod 不自动重启(与 Deployment 不同)

执行流程

T0: Pod-1 ●  Pod-2 ●         (2 个并行)
T1: Pod-1 ✓  Pod-2 ●  Pod-3 ●  (1完成,补1个)
T2: Pod-1 ✓  Pod-2 ✓  Pod-3 ●  Pod-4 ●  (2完成,补1个)
T3: Pod-1 ✓  Pod-2 ✓  Pod-3 ✓  Pod-4 ●  Pod-5 ●
T4: 全部 5 个完成 → Job 状态变为 Complete

CronJob — 定时 Job

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"          # 每天凌晨 2 点
  concurrencyPolicy: Forbid      # 上一次没跑完就不启新的
  startingDeadlineSeconds: 600   # 超过 10 分钟没启动就跳过
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: busybox
            command: ["sh", "-c", "echo backing up..."]
          restartPolicy: Never

concurrencyPolicy 三种模式:

模式含义
Allow允许并发(可能多个 Job 同时跑)
Forbid上一个没完成就跳过本次
Replace杀掉上一个,启动新的

面试题

Q: Job 的 restartPolicy 为什么不能是 Always?

因为 Job 的语义是"跑完就结束"。Always 意味着容器退出后永远重启——Job 永远完成不了。只能用 Never(失败就创建新 Pod 重试)或 OnFailure(失败在同一个 Pod 里重启)。


总结:工作负载控制器对比

特性DeploymentStatefulSetDaemonSetJob
Pod 命名随机哈希有序数字随机哈希随机哈希
扩缩容任意有序自动跟节点N/A
存储共享 PVC每个独立 PVC通常 hostPath通常无
网络身份无稳定 DNS无无
适用场景Web/APIDB/Cache/MQAgent/MonitorBatch/ETL

→ 04-rbac.md