k8s服务被杀或优雅终止详细拆解

Kubernetes 中服务(Pod)被终止是一个系统性的决策过程,其背后有多重原因和一套清晰的 escalation 机制。


第一部分:服务被杀或优雅终止的原因

Pod 被终止的两大根本原因是:主动决策被动驱逐

类型原因触发者行为
主动决策 (主动终止)1. 用户/控制器操作:手动删除 (kubectl delete)、滚动更新、应用缩容 (HPA/VPA)、Job完成。用户、Deployment、StatefulSet、CronJob、HPA优雅终止。发送 SIGTERM,等待 terminationGracePeriodSeconds
2. 健康检查失败livenessProbe 探测失败。kubelet强制重启。先优雅终止,再重启容器(根据 restartPolicy)。
被动驱逐 (被动杀死)3. 资源不足:节点上的资源(CPU、内存等)耗尽。kubelet驱逐 (Eviction)。优雅终止整个 Pod,并在其他节点重建(如果由控制器管理)。
4. 节点问题:节点宕机、网络分区、非优雅关闭。node-controllerAPI 层面删除。直接删除 Pod 对象,由控制器在其他节点重建。

第二部分:资源不够用涉及哪些类型的资源?

Kubernetes 监控两大类资源不足的情况:

  1. 可压缩资源 (Compressible Resources)
    • CPU:当 CPU 不足时,Pod 的运行会变慢( throttled ),但不会被杀死。因为CPU是可以被“限制”和“压缩”的。
  2. 不可压缩资源 (Incompressible Resources)
    • 内存 (Memory):是最常见的驱逐原因。内存一旦耗尽,无法回收,必须通过杀死 Pod 来释放。
    • 本地存储 (Local Storage)
      • ephemeral-storage(容器日志、可写层、emptyDir)。
      • nodefs:守护进程(kubelet、容器运行时)所需的存储(根分区)。
    • PID (Process IDs):节点上的进程数达到上限。
    • 磁盘压力:与存储相关,但更关注 inode 或整体磁盘空间。

资源不足的层级

  • Pod 级别:Pod 请求的资源 (requests) 无法满足。
  • 节点级别:节点整体资源耗尽。

第三部分:资源如何影响服务运行及导致终止的步骤

这个过程是一个逐步升级的响应链,核心执行者是 kubelet

第一步:监控与压力检测

kubelet 持续监控节点上的资源使用情况(通过 cAdvisor)。当资源消耗达到一定阈值时,节点会进入压力状态

  • MemoryPressure:内存不足
  • DiskPressure:磁盘空间不足
  • PIDPressure:进程数过多

第二步:保护机制启动 – Node OOM (Out Of Memory) 行为

这是最直接、最暴力的方式,发生在 kubelet 驱逐之前,由 Linux 内核触发。

  1. 节点上的某个或多个进程(可能是某个容器)疯狂消耗内存,导致系统可用内存极低。
  2. Linux 内核的 OOM Killer (Out-of-Memory Killer) 被激活。
  3. OOM Killer 根据每个进程的 oom_score(评分)来选择要杀死的进程。通常,内存消耗越多、优先级越低的进程得分越高。
  4. Kubernetes 通过设置 oom_score_adj 来影响这个评分,确保重要的系统进程(如 kubelet)得分极低(不容易被杀),而 Pod 中的容器进程得分较高(更容易被杀)。
  5. 内核 OOM Killer 直接杀死选中的容器进程,没有优雅终止。这可能导致数据不一致。
  6. kubelet 检测到容器非正常退出,然后根据 Pod 的 restartPolicy 决定是否重启它。

OOM 杀死是系统最后的自我保护,不是 Kubernetes 的首选方案。

第三步:主动管理 – kubelet 驱逐 (Eviction)

Kubernetes 更倾向于一种更优雅、更可控的资源释放机制,这就是 驱逐

kubelet 在资源达到驱逐阈值时(比 OOM 发生的阈值更早)就会主动开始驱逐 Pod。这个过程是一步一步的:

  1. 决策阶段 (Who to kill?)
    kubelet 会按照以下顺序和策略对 Pod 进行排序,选择最优的驱逐候选者:
    • 1. 资源使用量:优先驱逐实际使用量超过请求量 (requests) 最大的 Pod。这确保了最“超支”的 Pod 先被处理。
    • 2. 优先级 (Priority):驱逐低优先级 (priorityClass) 的 Pod,保留高优先级的 Pod。
    • 3. 资源使用比例:比较 Pod 的 (内存请求量) / (节点内存总量),优先驱逐占用比例最小的 Pod,以最小化影响。
  2. 执行阶段 (How to kill?)
    一旦选中目标 Pod,kubelet 会启动优雅终止流程:
    • 1. 发送 SIGTERM:kubelet 向 Pod 内所有容器的主进程发送 SIGTERM 信号。
    • 2. 执行 PreStop Hook:如果 Pod 配置了 preStop 钩子,kubelet 会执行它(例如,通知上游解除注册、完成剩余任务)。这是一个关键的优雅处理窗口。
    • 3. 等待宽限期:kubelet 会等待 terminationGracePeriodSeconds(默认30秒)的时间,让容器自行退出。
    • 4. 强制终止:如果宽限期结束后容器仍未停止,kubelet 会发送 SIGKILL 信号强制杀死进程。
    • 5. 清理与通知:清理 Pod 占用的资源(网络、存储卷),并向 API Server 报告 Pod 状态为 Failed
  3. 后续阶段 (What’s next?)
    • 如果被驱逐的 Pod 是由 DeploymentStatefulSet 等控制器管理的,控制器会发现 Pod 数量不足,并立即尝试在其他资源充足的节点上调度一个新的 Pod 副本。
    • 对于 DaemonSet Pod,它会在当前节点上被重新创建。

第四步:节点级故障 – 控制平面驱逐

如果整个节点宕机或失联(NotReady/Unreachable),kubelet 本身已无法工作。

  1. node-controllernode-monitor-grace-period(默认40s)后将该节点标记为 NotReady
  2. 再经过一段时间(由 Pod 上的 tolerationSeconds 决定),控制平面会直接从 API Server 中删除该节点上的所有 Pod 对象。
  3. 这些 Pod 的控制器(Deployment等)检测到 Pod 被删除,会在其他健康节点上重建新的 Pod。

总结流程图:从资源不足到服务终止

graph TD
    A[节点资源消耗] --> B{资源是否达到驱逐阈值?}

    B -- No --> C[正常服务]
    B -- Yes --> D[kubelet 主动驱逐]

    A --> E{资源是否极度匮乏?}
    E -- Yes --> F[Linux 内核 OOM Killer 被触发]
    F --> G[根据 oom_score 强制杀死进程<br>无优雅终止]

    D --> H[kubelet 按策略选择驱逐目标Pod]
    H --> I[启动优雅终止流程]
    I --> J[1.发送 SIGTERM]
    J --> K[2.执行 preStop Hook]
    K --> L[3.等待 terminationGracePeriodSeconds]
    L --> M{容器是否退出?}
    M -- Yes --> N[驱逐成功]
    M -- No --> O[4.发送 SIGKILL 强制杀死]
    O --> N

    subgraph "后续操作"
        N --> P[控制器在其他节点重建Pod]
        G --> Q[kubelet 根据 restartPolicy 可能重启容器]
    end

核心结论

  • Kubernetes 优先通过 kubelet 驱逐 来优雅地处理资源不足,这是设计上的首选方案。
  • 内核 OOM 是底层系统的暴力保护机制,应通过合理设置 Pod 的 requestslimits 来尽量避免。
  • 理解这个过程,可以更好地配置资源请求/限制、设置优先级和探针,从而保证服务的稳定性和可预测性。
发布日期:
分类:k8s 标签: