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 失败样本与排障实验
    • 本轮操作记录:大模型全生态与基础原理科普文撰写
    • 本轮操作记录:大模型数据集样本与治理文档编写
    • 本轮操作记录:大模型训练与模型产物概念文撰写
    • 本轮操作记录:大模型推理与服务发布概念文撰写

高级调度 — 控制 Pod 去哪里

调度器做决策的完整流程

Pod 创建 → Scheduler Watch 到
         → 过滤(哪些节点不行)
         → 打分(剩下的哪个最好)
         → 绑定(Pod → Node)
         → kubelet 拉起容器

四种调度控制手段

1. nodeSelector — 最简单的约束

spec:
  nodeSelector:
    topology.kubernetes.io/region: la    # 只去 LA 节点

实验结果:

  • la-only Pod → 调度到 us590068728056 (LA)
  • hk-only Pod → 调度到 wk-1 (HK)

原理: 硬约束。不匹配的节点直接排除,无商量余地。

2. nodeAffinity — 更灵活的节点选择

spec:
  affinity:
    nodeAffinity:
      # 硬约束:必须不是 Master
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node-role.kubernetes.io/control-plane
            operator: DoesNotExist
      # 软约束:优先选 LA(但 HK 也行)
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: topology.kubernetes.io/region
            operator: In
            values: ["la"]

required vs preferred:

类型含义不满足时
required硬约束,必须满足Pod 永远 Pending
preferred软约束,尽量满足调度到其他节点

operator 支持: In, NotIn, Exists, DoesNotExist, Gt, Lt

3. TopologySpreadConstraints — 跨拓扑域均匀分布

spec:
  topologySpreadConstraints:
  - maxSkew: 1                              # 最大偏差
    topologyKey: topology.kubernetes.io/region  # 按什么维度分散
    whenUnsatisfiable: DoNotSchedule         # 不满足时:拒绝调度
    labelSelector:
      matchLabels:
        app: spread-demo

实验结果(6 副本,maxSkew=1,按 region 分散):

LA: 3 个 Pod(cp-3 × 1, us590068728056 × 2)
HK: 3 个 Pod(wk-1 × 3)
偏差: |3-3| = 0 ≤ 1  ✓

maxSkew 的含义: 任意两个拓扑域的 Pod 数量差不能超过 maxSkew。如果设为 2,则允许 LA:4 HK:2 这样的不均匀分布。

whenUnsatisfiable 两种策略:

  • DoNotSchedule — 不满足就不调度(硬约束)
  • ScheduleAnyway — 尽量满足但不强制(软约束)

面试深度题: "TopologySpreadConstraints 和 podAntiAffinity 的区别?"

  • podAntiAffinity: "我不要和同类 Pod 在同一个节点/zone"(二元:同/不同)
  • TopologySpread: "我要均匀分散,偏差不超过 N"(量化:允许多大偏差)

4. Taint/Toleration — "节点赶人"

Taint 是节点上的标记:"我有特殊情况,普通 Pod 别来"。 Toleration 是 Pod 上的声明:"我能容忍你的特殊情况"。

# 给节点加 Taint
kubectl taint nodes hk652699382121 region=hk:NoSchedule

# Pod 需要对应的 Toleration 才能调度上去
spec:
  tolerations:
  - key: "region"
    operator: "Equal"
    value: "hk"
    effect: "NoSchedule"

三种 effect:

Effect含义
NoSchedule新 Pod 不调度上来,已有 Pod 不影响
PreferNoSchedule尽量不调度,但没其他选择时可以
NoExecute新 Pod 不调度,已有 Pod 被驱逐

Master 为什么默认不跑业务 Pod? 因为 kubeadm 自动给 Master 加了 Taint:

node-role.kubernetes.io/control-plane:NoSchedule

模拟 GPU 调度

原理:Extended Resources

K8s 的 GPU 调度不是什么黑魔法——它使用 Extended Resources 机制:

  1. 设备插件(Device Plugin)向 kubelet 注册资源(如 nvidia.com/gpu: 2)
  2. kubelet 上报给 API Server,写入 Node 的 status.capacity
  3. Pod 声明 resources.limits: nvidia.com/gpu: 1
  4. Scheduler 像分配 CPU/内存一样分配 GPU

实验:手动注册 fake GPU

# 通过 kubectl proxy + PATCH API 给节点注册 GPU
curl --request PATCH \
  --data '[{"op":"add","path":"/status/capacity/nvidia.com~1gpu","value":"2"}]' \
  http://localhost:8099/api/v1/nodes/wk-1/status

实验结果:

Pod请求 GPU调度到原因
gpu-job-11hk652699382121有 1 GPU
gpu-job-2gpu2wk-1唯一有 2 GPU 的节点
gpu-job-impossible5(Pending)无节点有 5 GPU

面试重点:GPU 不能超配。 CPU 有 requests/limits 之分(limits 可以大于 requests,允许 burst),但 GPU 只有 limits,且 limits = requests,不允许超配。原因:GPU 不像 CPU 可以时间分片,一个进程占了 GPU 就独占了。

→ 02-storage-nfs.md