前言

在 Kubernetes(K8s)集群管理中,调度机制决定了 Pod 如何高效分配到节点,而存储管理则解决了容器数据持久化与共享的核心痛点。本文将结合理论原理与实战案例,详细拆解 K8s 集群调度流程、核心策略以及 PV/PVC 持久化存储机制,帮助开发者和运维人员快速掌握关键技能。

一、Kubernetes 集群调度:Pod 的"智能分配"之道

K8s 调度的核心目标是将未绑定节点的 Pod 合理分配到集群节点,实现资源均衡利用、高可用性与灵活性。其背后依赖 List-Watch 组件协作机制与分层调度策略。

1. 核心组件与协作流程

K8s 通过 List-Watch 机制实现组件解耦与数据实时同步,关键组件分工如下:

组件 核心职责
kubectl/API 客户端 发起资源创建/管理请求
APIServer 集群控制入口,负责权限校验、存储交互
etcd 存储集群所有状态信息
Controller Manager 维持副本数、执行自愈逻辑(扩容/重建)
Scheduler 核心调度器,为 Pod 选择合适节点
kubelet 节点代理,管理 Pod 生命周期与状态上报
Pod 创建完整流程(List-Watch 模型)
  1. 三大组件(Controller Manager、Scheduler、kubelet)启动后,通过 HTTPS 6443 端口监听 APIServer 资源事件;
  2. 用户通过 kubectl apply -f pod.yaml 发送创建请求,APIServer 校验后将 Pod 元数据写入 etcd;
  3. etcd 触发 Create 事件,Controller Manager 监听后通过 RC/ReplicaSet 保证副本数;
  4. Scheduler 发现处于 Pending 状态的 Pod,经调度算法选择节点并更新绑定信息;
  5. kubelet 监听分配给自己的 Pod,拉取镜像、创建并启动容器,最后上报状态至 APIServer;
  6. APIServer 更新 etcd 中 Pod 状态,集群同步完成,Pod 进入 Running 状态。

2. Scheduler 调度核心流程

Scheduler 的调度过程分为「过滤」和「优选」两个阶段,确保筛选出最优节点:

(1)过滤阶段(Predicate):排除不满足条件的节点

通过一系列算法筛选出符合 Pod 基础需求的节点,常见过滤规则:

  • PodFitsResources:节点剩余资源满足 Pod 需求;
  • PodFitsHost:匹配指定 NodeName;
  • PodFitsHostPorts:检查端口是否冲突;
  • PodSelectorMatches:节点标签与 Pod 选择器匹配;
  • NoDiskConflict:避免 Volume 挂载冲突。
    若无符合条件的节点,Pod 会持续处于 Pending 状态并重试。
(2)优选阶段(Priorities):对可行节点打分排序

对过滤后的节点按优先级权重排序,常见优选规则:

  • LeastRequestedPriority:资源使用率越低,权重越高;
  • BalancedResourceAllocation:CPU 与内存使用率越接近越好;
  • ImageLocalityPriority:优先选择已存在目标镜像的节点。
    最终选择权重最高的节点完成调度。

3. 常用调度策略:从简单绑定到智能亲和

(1)直接指定节点
  • nodeName:强制绑定节点,跳过 Scheduler 调度,适用于特殊场景:
spec:
  nodeName: node01  # 直接调度到 node01
  • nodeSelector:基于节点标签匹配,属于强制约束:
spec:
  nodeSelector:
    yjs: a  # 匹配标签 yjs=a 的节点

需先为节点设置标签:kubectl label nodes node01 yjs=a

(2)亲和性与反亲和性:更灵活的调度规则

亲和性分为「节点亲和性」和「Pod 亲和性/反亲和性」,支持「硬策略」(必须满足)和「软策略」(优先满足),操作符包括 In、NotIn、Exists、Gt 等。

  • 节点亲和性示例(软硬结合):
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略:排除 node02
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: NotIn
          values: ["node02"]
    preferredDuringSchedulingIgnoredDuringExecution:  # 软策略:优先选择 yjs=a 的节点
    - weight: 1
      preference:
        matchExpressions:
        - key: yjs
          operator: In
          values: ["a"]
  • Pod 亲和性:让新 Pod 与指定 Pod 处于同一拓扑域(如同一标签节点):
affinity:
  podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values: ["myapp01"]
      topologyKey: yjs  # 基于 yjs 标签划分拓扑域
  • Pod 反亲和性:避免新 Pod 与指定 Pod 处于同一拓扑域,常用于分散部署提高可用性。
(3)污点(Taint)与容忍(Toleration):节点的"排斥与例外"

污点让节点排斥特定 Pod,容忍则允许 Pod 突破排斥,两者配合实现节点隔离。

  • 污点格式:key=value:effect,effect 支持三种类型:

    • NoSchedule:不调度新 Pod 到该节点;
    • PreferNoSchedule:尽量避免调度;
    • NoExecute:不调度且驱逐已存在的 Pod。
  • 常用命令:

# 给 node01 设置污点:不允许未容忍的 Pod 调度
kubectl taint node node01 key1=value1:NoSchedule
# 去除污点
kubectl taint node node01 key1:NoSchedule-
# 给 Pod 设置容忍
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"

4. 节点维护与 Pod 驱逐

当需要升级节点或维护时,可通过以下命令安全迁移 Pod:

  • kubectl cordon <node>:标记节点为不可调度;
  • kubectl drain <node> --ignore-daemonsets --delete-local-data:驱逐节点上所有 Pod;
  • kubectl uncordon <node>:恢复节点可调度状态。

二、PV 与 PVC:K8s 持久化存储解决方案

容器文件系统是临时的,Pod 重启或删除会导致数据丢失。K8s 通过 Volume 抽象解决该问题,而 PV/PVC 则进一步实现了存储资源的"申请-分配"解耦。

1. 基础存储卷类型对比

存储类型 生命周期 共享性 持久性 典型场景
emptyDir Pod 生命周期 同 Pod 内容器 临时缓存、容器间共享
hostPath 节点生命周期 单节点 节点级持久化(如日志存储)
NFS 独立服务 多节点 集群共享存储
(1)emptyDir 示例:Pod 内容器共享数据
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/
  - name: busybox
    image: busybox:latest
    volumeMounts:
    - name: html
      mountPath: /data/
    command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
  volumes:
  - name: html
    emptyDir: {}
(2)NFS 示例:多节点共享存储
  1. 部署 NFS 服务端:
yum install -y nfs-utils rpcbind
mkdir /data/volumes && chmod 777 /data/volumes
echo "/data/volumes 192.168.10.0/24(rw,no_root_squash)" > /etc/exports
systemctl start rpcbind nfs
  1. Pod 挂载 NFS:
volumes:
- name: html
  nfs:
    path: /data/volumes
    server: stor01  # NFS 服务端地址

2. PV 与 PVC 核心概念

  • PV(Persistent Volume):持久化存储卷,由运维人员定义,描述底层存储资源(如 NFS 路径、容量、访问模式);
  • PVC(Persistent Volume Claim):存储资源请求,由开发者创建,指定所需存储的容量、访问模式等,K8s 自动匹配符合条件的 PV;
  • StorageClass:动态 PV 模板,可自动创建 PV,适用于大规模部署。
PV 与 PVC 生命周期

Provisioning(配置)→ Binding(绑定)→ Using(使用)→ Releasing(释放)→ Recycling(回收)

  • PV 状态:Available(可用)→ Bound(已绑定)→ Released(已释放)→ Failed(失败);
  • 回收策略:
    • Retain:保留数据,需手动清理;
    • Delete:自动删除存储资源(云存储如 AWS EBS 支持);
    • Recycle:清空数据后重新可用(仅 NFS/HostPath 支持)。

3. PV + PVC 实战(NFS 后端)

(1)定义 PV(5 个示例 PV)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: stor01
  accessModes: ["ReadWriteMany", "ReadWriteOnce"]  # 支持多路读写和单Pod读写
  capacity:
    storage: 2Gi  # 容量2Gi
  persistentVolumeReclaimPolicy: Retain  # 保留策略
(2)定义 PVC + Pod
# PVC 申请 2Gi 多路读写存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi

# Pod 挂载 PVC
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-pvc
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: mypvc  # 关联 PVC
(3)验证
kubectl apply -f pv-demo.yaml && kubectl apply -f pod-vol-pvc.yaml
# 在 NFS 服务端写入测试数据
echo "welcome to use pv3" > /data/volumes/v3/index.html
# 访问 Pod 验证
curl $(kubectl get pod pod-vol-pvc -o jsonpath='{.status.podIP}')

4. StorageClass 动态存储(进阶)

手动创建 PV 效率低下,通过 StorageClass 可实现 PV 自动创建,适用于大规模集群。

(1)部署 NFS Provisioner(存储分配器)

需先创建 RBAC 权限、配置 NFS 服务,再部署 Provisioner 组件(负责自动创建 PV 与 NFS 挂载点关联)。

(2)创建 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client-storageclass
provisioner: nfs-storage  # 与 Provisioner 名称一致
parameters:
  archiveOnDelete: "false"  # 删除 PVC 时自动删除数据
(3)测试动态存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-nfs-pvc
spec:
  accessModes: ["ReadWriteMany"]
  storageClassName: nfs-client-storageclass  # 关联 StorageClass
  resources:
    requests:
      storage: 1Gi

创建 PVC 后,K8s 会自动创建对应的 PV 并绑定,NFS 服务端也会生成对应的存储目录。

三、核心总结

  1. 调度机制:以 Scheduler 为核心,通过「过滤-优选」流程实现 Pod 智能分配,配合亲和性、污点/容忍策略满足复杂场景需求;
  2. 存储方案:从临时存储(emptyDir)到集群共享(NFS),再到生产级持久化(PV/PVC),StorageClass 进一步简化大规模部署;
  3. 关键原则:“调度是吸引,污点是排斥,亲和性是偏好,容忍是例外”;PV 是"存储资源池",PVC 是"资源申请单",解耦运维与开发职责。

通过本文的理论与实战,相信你已掌握 K8s 调度与存储的核心能力。在实际生产中,需根据业务场景选择合适的调度策略与存储方案,兼顾性能、可用性与可维护性。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐