K8s 特殊容器、调度管理与优先级学习笔记

一、K8s 特殊容器

K8s 中除了常规业务容器(主容器),还有三类核心特殊容器,分别承担初始化、辅助、调试等特定功能,与主容器协同工作,适配不同场景需求,且各自遵循独特的生命周期规则。

1.1 核心特殊容器类型(3类)

(1)Init 容器(初始化容器)

Init 容器是在主容器启动前执行的特殊容器,用于完成主容器启动前的初始化任务,必须全部执行成功,主容器才会启动。若 Init 容器执行失败,kubelet 会根据 Pod 的 restartPolicy 不断重启,直至执行成功(restartPolicy 为 Never 时,失败会直接导致 Pod 状态为失败)。

核心特性:

  • 执行顺序:多个 Init 容器按配置顺序依次执行,前一个执行成功后,下一个才会启动。

  • 资源隔离:拥有独立的资源请求/限制,不共享主容器的资源配置。

  • 生命周期:执行完成后立即终止,不长期运行,不参与主容器的业务流程。

典型应用场景:

  • 等待依赖服务就绪(如数据库、API 服务),通过循环检测服务可达性实现。

  • 初始化配置:生成主容器所需的配置文件、密钥,或下载依赖资源。

  • 前置校验:检查端口、文件是否存在,确保主容器启动环境符合要求。

(2)Sidecar 容器(边车容器)

Sidecar 容器与主容器并行运行,不参与核心业务逻辑,仅为主体容器提供辅助功能,通过共享 Pod 的网络命名空间和存储卷,与主容器实现紧密协同,是微服务架构中解耦辅助逻辑的核心方案。

核心特性:

  • 生命周期同步:与主容器同时启动、同时终止,主容器异常时,Sidecar 容器也会随之终止。

  • 资源共享:与主容器共享网络(可通过 localhost 直接通信)和存储卷,无需额外配置网络连接。

  • 功能独立:辅助功能与主业务解耦,可单独升级、维护,不影响主容器。

典型应用场景:

  • 日志收集:如使用 Fluentd、Filebeat 收集主容器的日志,统一上报至日志系统。

  • 服务网格:如 Istio、Linkerd 的 Sidecar 容器,负责流量转发、监控、熔断等。

  • 代理转发:通过 Sidecar 容器实现主容器的网络代理,简化主容器的网络配置。

(3)Ephemeral 容器(临时容器)

Ephemeral 容器是动态注入到运行中 Pod 的临时容器,主要用于调试或诊断,不参与 Pod 的原生生命周期管理,无法通过常规方式创建,也不能长期运行。

核心特性:

  • 动态注入:需通过 kubectl 命令或 API 直接注入,无法在 Pod 初始 YAML 中定义,也不能通过 kubectl edit 修改。

  • 生命周期独立:注入后完成调试任务即可删除,不影响主容器和 Pod 的运行状态。

  • 启用条件:K8s 1.16+ 版本需启用 EphemeralContainers 特性门控(在 kube-apiserver 配置中添加 --feature-gates=EphemeralContainers=true)。

典型应用场景:

  • 调试运行中 Pod:主容器无法进入(如命令行工具缺失)时,注入临时容器(如 busybox)排查问题。

  • 临时工具注入:添加 curl、tcpdump 等调试工具,无需修改主容器镜像。

1.2 实操示例(YAML+命令)

示例1:Init 容器(等待依赖服务+初始化配置)

apiVersion: v1
kind: Pod
metadata:
  name: init-demo-pod
spec:
  containers:
  - name: main-app
    image: busybox
    command: ["sh", "-c", "echo 主容器运行中 && sleep 3600"]
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  # Init 容器(按顺序执行)
  initContainers:
  - name: wait-mysql
    image: busybox
    # 等待 mysql 服务就绪(循环检测域名解析)
    command: ["sh", "-c", "until nslookup mysql; do echo 等待 mysql 服务; sleep 2; done"]
  - name: init-config
    image: busybox
    command: ["sh", "-c", "echo 'app.config=demo' > /tmp/config/app.conf"]
    volumeMounts:
    - name: config-volume
      mountPath: /tmp/config
  volumes:
  - name: config-volume
    emptyDir: {}

查看 Init 容器执行状态:kubectl get pod init-demo-pod -w,状态流转为:Init:0/2 → Init:1/2 → PodInitializing → Running。

示例2:Sidecar 容器(日志收集)

apiVersion: v1
kind: Pod
metadata:
  name: sidecar-demo-pod
spec:
  containers:
  # 主容器:输出日志到文件
  - name: main-app
    image: busybox
    command: ["sh", "-c", "while true; do echo $(date) 业务日志 >> /var/log/app.log; sleep 1; done"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  # Sidecar 容器:收集日志并打印
  - name: log-collector
    image: busybox
    command: ["sh", "-c", "tail -f /var/log/app.log"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes:
  - name: log-volume
    emptyDir: {}

示例3:Ephemeral 临时容器(调试 Pod)

# 给运行中的 pod 注入临时容器(名为 debugger,镜像为 busybox)
kubectl debug -it sidecar-demo-pod --image=busybox --target=main-app --name=debugger

# 手动注入临时容器(YAML 方式)
kubectl patch pod sidecar-demo-pod -p '{
  "spec": {
    "ephemeralContainers": [
      {
        "name": "debugger",
        "image": "busybox",
        "command": ["sh"],
        "tty": true,
        "stdin": true
      }
    ]
  }
}'

1.3 常见问题与注意事项

  • Init 容器失败:检查 Init 容器的命令是否正确(如依赖服务地址错误),或 restartPolicy 配置是否合理,避免无限重启。

  • Sidecar 容器资源耗尽:Sidecar 需单独配置资源请求/限制,避免因资源不足导致主容器被驱逐。

  • 临时容器注入失败:确认 K8s 版本≥1.16,且已启用 EphemeralContainers 特性门控;临时容器无法修改 Pod 的原有配置。

  • 多 Init 容器顺序:严格按 YAML 中定义的顺序执行,前一个失败会阻塞后续所有容器启动。

二、K8s 调度管理

K8s 调度管理的核心是 kube-scheduler(调度器),其职责是根据集群节点资源、Pod 需求,将待调度的 Pod 合理分配到合适的节点上,实现资源高效利用和业务高可用。调度过程遵循“过滤→打分→绑定”的核心流程,支持多种调度策略适配不同场景。

2.1 核心调度原理

kube-scheduler 是集群级组件,运行在控制平面,核心流程分为3步:

  1. 过滤(Predicate):从所有节点中筛选出“满足 Pod 资源需求”的候选节点(如 CPU、内存充足,符合 Pod 的调度约束),排除不满足条件的节点(如节点污点未被容忍)。

  2. 打分(Priority):对候选节点进行优先级打分(0-10分),分数越高,节点越适合运行该 Pod(如节点资源剩余越多、负载越低,分数越高)。

  3. 绑定(Bind):将 Pod 绑定到打分最高的节点,完成调度;若多个节点分数相同,随机选择一个。

补充:可通过自定义调度器配置(K8s 1.25+ 稳定版),修改调度的各个阶段行为,支持多调度配置文件,通过 kube-scheduler --config 启用,需使用 kubeschedulerconfiguration v1 版本(v1beta3 已废弃)。

2.2 核心调度策略(4种常用)

(1)节点选择器(NodeSelector)

最基础的调度策略,通过“节点标签+Pod 选择器”,将 Pod 强制调度到带有指定标签的节点上,适用于简单的节点分组调度(如区分 CPU 型、GPU 型节点)。

# 1. 给节点打标签
kubectl label nodes node-1 node-type=cpu

# 2. Pod 配置 NodeSelector,强制调度到带有 node-type=cpu 标签的节点
apiVersion: v1
kind: Pod
metadata:
  name: nodeselector-demo
spec:
  containers:
  - name: nginx
    image: nginx:alpine
  nodeSelector:
    node-type: cpu  # 匹配节点标签

(2)节点亲和性(NodeAffinity)

NodeSelector 的增强版,支持更灵活的匹配规则(如模糊匹配、逻辑运算),分为两种类型,适配不同约束需求:

  • requiredDuringSchedulingIgnoredDuringExecution(强制亲和):必须满足匹配规则,否则 Pod 无法调度(等价于 NodeSelector,但支持更复杂表达式)。

  • preferredDuringSchedulingIgnoredDuringExecution(优先亲和):优先调度到符合规则的节点,若没有符合条件的节点,可调度到其他节点,支持 weight 权重配置(0-100)。

apiVersion: v1
kind: Pod
metadata:
  name: nodeaffinity-demo
spec:
  containers:
  - name: nginx
    image: nginx:alpine
  affinity:
    nodeAffinity:
      # 强制亲和:必须调度到带有 node-type=cpu 或 node-type=mix 的节点
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node-type
            operator: In  # 匹配规则:In(包含)、NotIn(不包含)、Exists(存在标签)等
            values: ["cpu", "mix"]
      # 优先亲和:优先调度到带有 zone=zone1 标签的节点,权重 80
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values: ["zone1"]

(3)Pod 亲和性/反亲和性(PodAffinity/PodAntiAffinity)

根据“已运行 Pod 的标签”调度当前 Pod,实现 Pod 间的协同或隔离,适用于分布式应用(如数据库主从、服务集群)。

  • PodAffinity(Pod 亲和性):将当前 Pod 调度到“运行了指定标签 Pod”的节点上(如将前端 Pod 调度到后端 Pod 所在节点,减少网络延迟)。

  • PodAntiAffinity(Pod 反亲和性):将当前 Pod 调度到“未运行指定标签 Pod”的节点上(如避免同类型 Pod 调度到同一节点,实现容灾)。

(4)污点(Taint)与容忍度(Toleration)

与亲和性相反,污点作用于节点,用于排斥 Pod;容忍度作用于 Pod,用于让 Pod 容忍节点的污点,实现“反调度”控制,核心用于节点隔离(如 Master 节点、故障节点)。

核心污点效果(3种):

  • NoSchedule:仅排斥未配置对应容忍度的新 Pod,已运行的 Pod 不受影响(最常用,如 Master 节点默认污点)。

  • NoExecute:排斥未配置对应容忍度的新 Pod,且会驱逐已运行的、无对应容忍度的 Pod(如节点故障时标记污点,驱逐 Pod)。

  • PreferNoSchedule:尽量排斥未配置对应容忍度的 Pod,若没有其他合适节点,仍可调度(优先级最低)。

# 1. 给节点添加污点(key=node-role.kubernetes.io/control-plane,效果 NoSchedule)
kubectl taint nodes master node-role.kubernetes.io/control-plane:NoSchedule

# 2. Pod 配置容忍度,允许调度到带有该污点的节点
apiVersion: v1
kind: Pod
metadata:
  name: toleration-demo
spec:
  containers:
  - name: nginx
    image: nginx:alpine
  tolerations:
  - key: "node-role.kubernetes.io/control-plane"
    operator: "Exists"  # 匹配规则:Exists(存在该key即可)、Equal(key和value都匹配)
    effect: "NoSchedule"

补充:Master 节点默认带有 node-role.kubernetes.io/control-plane:NoSchedule 污点,因此普通 Pod 不会被调度到 Master 节点,需配置容忍度才能调度。

2.3 实操示例(调度排查+自定义调度)

# 查看 Pod 调度情况(查看调度到哪个节点、调度原因)
kubectl describe pod nodeselector-demo | grep -i "Node Selected"

# 查看调度器日志(排查调度失败原因)
kubectl logs -n kube-system kube-scheduler-master

# 强制指定节点(绕过调度器,直接绑定节点,仅用于调试)
apiVersion: v1
kind: Pod
metadata:
  name: force-node-pod
spec:
  nodeName: node-1  # 直接指定节点名称,跳过调度流程
  containers:
  - name: nginx
    image: nginx:alpine

# 自定义调度器配置(极简示例)
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: /etc/srv/kubernetes/kube-scheduler/kubeconfig
profiles:
- schedulerName: my-scheduler  # 自定义调度器名称

2.4 常见调度问题与最佳实践

(1)常见问题

  • Pod 一直 Pending(调度失败):检查节点资源是否充足(CPU/内存不足)、节点污点是否未被容忍、亲和性规则是否过严(无符合条件的节点)。

  • Pod 调度到不合适的节点:检查亲和性/反亲和性配置是否错误,或节点标签是否正确。

  • 调度器异常:查看 kube-scheduler 日志,确认调度器是否正常运行,配置文件是否正确(如 v1beta3 版本已废弃)。

(2)最佳实践

  • 节点分组:给节点打标签(如 node-type、zone),结合 NodeSelector 或节点亲和性,实现 Pod 按类型调度。

  • 容灾隔离:使用 Pod 反亲和性,将核心服务的 Pod 调度到不同节点,避免单点故障;使用污点隔离 Master 节点和故障节点。

  • 资源预留:给节点预留部分资源(如给系统组件),避免 Pod 资源耗尽导致节点不可用。

  • 避免强制指定 nodeName:仅用于调试,生产环境优先使用调度策略,确保调度灵活性。

三、K8s Pod 优先级与抢占机制

K8s 中 Pod 优先级用于定义 Pod 的重要程度,优先级高的 Pod 会优先被调度;当集群资源不足时,高优先级 Pod 可“抢占”低优先级 Pod 的资源(驱逐低优先级 Pod),确保核心业务的可用性,该特性自 K8s 1.14 起稳定。

3.1 核心概念

(1)PriorityClass(优先级类)

集群级资源(不属于任何命名空间),用于定义优先级等级,映射“优先级名称”与“优先级数值”,数值越高,优先级越高。

核心属性:

  • value:优先级数值(32位整数,范围 -2147483648 ~ 1000000000,超过1000000000的数值预留用于系统核心组件)。

  • globalDefault:是否作为全局默认优先级(整个集群只能有一个 PriorityClass 设为 true),未指定优先级的 Pod 会使用该优先级(默认优先级为0)。

  • preemptionPolicy:抢占策略,可选 PreemptLowerPriority(默认,允许抢占低优先级 Pod)、Never(禁止抢占)。

  • description:优先级描述,用于说明该优先级的适用场景。

内置 PriorityClass(系统默认,不可修改):

  • system-node-critical(value=2000001000):节点关键组件(如 kubelet、容器运行时)。

  • system-cluster-critical(value=2000000000):集群核心组件(如 CoreDNS、etcd)。

(2)Pod 优先级配置

通过 Pod 的 priorityClassName 字段,引用已创建的 PriorityClass,实现 Pod 优先级的配置;也可直接设置 priority 字段(不推荐,优先级数值难以管理)。

(3)抢占机制(Preemption)

当高优先级 Pod 无法调度(资源不足)时,调度器会触发抢占逻辑,驱逐节点上优先级更低的 Pod,腾出资源供高优先级 Pod 运行,核心流程:

  1. 高优先级 Pod 创建,触发调度流程,发现无足够资源。

  2. 调度器评估候选节点,计算需要驱逐的低优先级 Pod(优先驱逐优先级最低、资源消耗最小的 Pod)。

  3. 驱逐低优先级 Pod,等待 Pod 终止后,将高优先级 Pod 绑定到该节点。

注意:抢占机制仅在资源不足时触发,且不会驱逐优先级相同或更高的 Pod;若低优先级 Pod 配置了 PodDisruptionBudget(PDB),可能会阻止抢占(避免驱逐过多 Pod 导致服务不可用)。

3.2 实操示例(PriorityClass+Pod 优先级+抢占)

示例1:创建 PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority  # 高优先级(核心业务)
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "用于核心生产服务(如支付、订单)"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: low-priority  # 低优先级(测试、批处理任务)
value: 100000
preemptionPolicy: Never  # 禁止抢占其他 Pod
description: "用于测试环境和批处理任务,不抢占资源"

示例2:创建带优先级的 Pod

# 高优先级 Pod(核心服务)
apiVersion: v1
kind: Pod
metadata:
  name: high-priority-pod
spec:
  priorityClassName: high-priority  # 引用高优先级类
  containers:
  - name: core-app
    image: nginx:alpine
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
---
# 低优先级 Pod(测试任务)
apiVersion: v1
kind: Pod
metadata:
  name: low-priority-pod
spec:
  priorityClassName: low-priority  # 引用低优先级类
  containers:
  - name: test-app
    image: busybox
    command: ["sh", "-c", "sleep 3600"]
    resources:
      requests:
        cpu: 100m
        memory: 128Mi

示例3:查看优先级与抢占情况

# 查看所有 PriorityClass
kubectl get priorityclasses.scheduling.k8s.io

# 查看 Pod 的优先级(priority 字段)
kubectl get pods -o custom-columns=NAME:.metadata.name,PRIORITY:.spec.priority,NODE:.spec.nodeName

# 查看抢占事件(高优先级 Pod 驱逐低优先级 Pod 的日志)
kubectl get events | grep -i preemption

3.3 常见问题与最佳实践

(1)常见问题

  • 高优先级 Pod 无法抢占低优先级 Pod:检查低优先级 Pod 是否配置了 PDB(Pod 中断预算),或高优先级 Pod 的 preemptionPolicy 设为 Never。

  • 恶意高优先级 Pod 挤占资源:未授权用户创建高优先级 Pod,导致核心服务被抢占,需通过资源配额限制用户创建高优先级 Pod 的权限。

  • 优先级配置错误:PriorityClass 的 value 超出范围,或多个 PriorityClass 设为 globalDefault,导致 Pod 优先级异常。

(2)最佳实践

  • 合理划分优先级:按业务重要性划分优先级(如核心服务→普通服务→测试任务→批处理任务),避免优先级混乱。

  • 限制高优先级使用:通过资源配额(ResourceQuota),限制不同用户/命名空间创建高优先级 Pod 的数量,防止恶意抢占。

  • 批处理任务配置:给批处理、日志分析等非核心任务设置低优先级,并设 preemptionPolicy: Never,避免抢占核心服务。

  • 不修改系统内置优先级:禁止修改 system-node-critical、system-cluster-critical 的优先级,确保集群核心组件正常运行。

四、总结

  1. 特殊容器:Init 容器负责主容器前置初始化,Sidecar 容器提供并行辅助功能,Ephemeral 容器用于临时调试,三者与主容器协同,覆盖初始化、辅助、排障等场景,需注意各自的生命周期规则。

  2. 调度管理:核心是 kube-scheduler,通过“过滤→打分→绑定”流程调度 Pod,常用调度策略包括 NodeSelector、节点亲和性、Pod 亲和性/反亲和性、污点与容忍度,用于实现 Pod 的精准调度和节点隔离。

  3. 优先级与抢占:通过 PriorityClass 定义 Pod 优先级,高优先级 Pod 优先调度,资源不足时可抢占低优先级 Pod,核心是保障核心业务的资源供给,需合理划分优先级并限制高优先级使用,避免资源滥用。

  4. 三者关联:特殊容器的运行依赖调度管理(如 Sidecar 与主容器需调度到同一节点),优先级决定 Pod 的调度顺序和资源抢占权限,结合使用可实现集群的高效、安全、稳定运行。

Logo

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

更多推荐