---

title: 增强 EKS 节点自愈方案深度解析:npd-node-replace 架构原理、部署配置与实测验证
tags: AWS,EKS,Kubernetes,NPD,节点自愈,亚马逊云科技,K8s,Node Problem Detector,自动恢复,容器,运维
category: 云计算

问题背景

Amazon EKS 集群中节点运行过程中可能遭遇多种异常:内核崩溃、OOM、底层硬件故障。缺乏自动化处理机制时,异常节点长时间留在集群中,带来 Pod 调度失败和级联故障风险。

Kubernetes 官方的 Node Problem Detector(NPD)提供了节点问题检测能力,通过 DaemonSet 在每个节点上运行,检测问题并上报 Event 到 API Server。但 NPD 只做发现,不做处理。

之前的 NPD + Karpenter 方案依赖 Karpenter 的节点回收能力,无法覆盖托管节点组和自管理节点组场景。

npd-node-replace 组件

架构

Node Problem Detector(DaemonSet)
    ↓ Events
API Server
    ↓
npd-node-replace(Deployment on Fargate)
    ├── EventController:接收 NPD 事件 → NodeIssueReport CR
    ├── NIRController:Tolerance 匹配 → Reboot / Replace
    └── NodeController:节点状态监控 → NotReady/Unknown → Replace

npd-node-replace 运行在 Fargate 上,避免处理自身所在节点导致中断。

设计要点

维度 实现
节点形态 托管节点组 + 自管理节点组,不依赖 Karpenter
问题持久化 NodeIssueReport CRD,可查询历史,支持根因分析
容忍策略 按事件类型独立配置:时间窗口 + 次数 + 动作
现场保留 Replace 仅删 Node 对象,EC2 实例保留供排查
状态覆盖 NotReady + Unknown 状态自动处理
通知 Amazon SNS 集成
白名单 标签 npd-node-replace-enabled=true 控制生效范围

防误处理机制(详细分析)

时间窗口

Tolerance 配置中 timewindowinminutes 定义事件统计窗口。只计算窗口内的事件次数,窗口外的历史事件仅记录不计入。

{
  "KernelOops": {
    "times": 3,
    "action": "replace",
    "timewindowinminutes": 60
  }
}

含义:60 分钟内出现 3 次 KernelOops 才触发 Replace。一天内分散出现 3 次不触发。

Double Check

节点状态变为 NotReady 或 Unknown 后,等待 NODE_DOULBE_CHECK_GRACE_TIME(默认 15 分钟)后二次检查。必须大于节点重启所需时间,避免"正在重启"被误判为"持续异常"。

白名单

默认不对任何节点操作。手动 kubectl label nodes <node> npd-node-replace-enabled=true 启用。支持渐进式推广。

Replace 流程

  1. 从 EC2 Auto Scaling Group 分离问题节点
  2. ASG 自动拉起新节点,等待 Ready
  3. Drain 旧节点
  4. SNS 通知管理员
  5. 删除旧 Node 对象(EC2 实例保留)

Reboot 流程

  1. 节点设为不可调度
  2. Drain 节点
  3. 重启 EC2 实例
  4. 恢复可调度状态
  5. SNS 通知

NodeIssueReport 生命周期

  1. EventController 接收 NPD Event 或 NodeController 检测到状态异常 → 创建/更新 NodeIssueReport
  2. NIRController 统计窗口内事件次数 vs Tolerance 阈值 → 触发动作

部署

前提

  • EKS 集群 + IAM OIDC provider
  • Fargate Profile
  • NPD 已部署
  • SNS 主题 + 邮件订阅
  • ECR 镜像仓库

IAM

最小权限:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "ec2:RebootInstances",
      "ec2:DescribeInstances",
      "autoscaling:DetachInstances",
      "sns:Publish"
    ],
    "Resource": ["*"]
  }]
}

通过 IRSA 关联:

eksctl create iamserviceaccount \
  --cluster=<cluster> \
  --namespace=<fargate-ns> \
  --name=npd-node-replace-sa \
  --attach-policy-arn=<policy-arn> \
  --approve

Helm

docker pull zxxxxzz/npd-node-replace:v1.2
docker tag zxxxxzz/npd-node-replace:v1.2 <ecr-repo>:v1.2
docker push <ecr-repo>:v1.2

helm repo add npd-replace https://normalzzz.github.io/npd-node-replace/
helm install npd-replace npd-replace/npd-node-replace \
  --namespace <fargate-ns> \
  --set serviceAccount.create=false \
  -f values.yaml

values.yaml 关键字段:snsTopicArnnodeDoubleCheckGraceTimerepositorytoleranceJson

验证

kubectl get deployment -n <fargate-ns> | grep npd-node-replace

测试验证

模拟故障

参考 NPD 的 problem-maker 实现,向 /dev/kmsg 写入模拟日志:

# OOMKilling
echo "Killed process 1234 (myapp) total-vm:102400kB, anon-rss:51200kB, file-rss:2048kB" \
  | sudo tee /dev/kmsg

# KernelOops
echo "<1>BUG: unable to handle kernel NULL pointer dereference at 0x00000000" \
  | sudo tee /dev/kmsg
echo "<1>divide error: 0000 [#1] SMP" | sudo tee /dev/kmsg

验证记录

kubectl get nodeissuereport
kubectl describe nodeissuereport <name>

测试 Reboot

OOMKilling 达到 2 次阈值(30 分钟窗口内),白名单节点触发 Reboot。

测试 Replace

KernelOops 达到 3 次阈值(60 分钟窗口内),白名单节点触发 Replace。

测试状态异常

systemctl stop kubelet → 节点 Unknown → 等 15 分钟 Double Check → Replace。

最佳实践

阶段 操作 目标
1. 观察 部署但不打标签 确认 NPD 检测 + SNS 通知正常
2. 验证 非关键节点打标签 验证 Reboot/Replace 流程
3. 推广 全集群打标签 全面启用自愈

日志建议:配置 Fargate 日志输出到 CloudWatch,集中监控和排查。

适用场景

  • EKS 集群节点偶发故障需要自动修复
  • 托管/自管理节点组场景(非 Karpenter)
  • 需要节点故障历史持久化数据
  • 运维资源不足以 7×24 人工处理
  • 需要渐进式启用以降低风险

代码https://github.com/normalzzz/npd-node-replace

参考

posted @ 2026-04-17 18:06  亚马逊云开发者  阅读(3)  评论(0)    收藏  举报