当前位置: 首页 > news >正文

深入理解 EKS 节点自愈架构:NPD + npd-node-replace 的设计与实现

深入理解 EKS 节点自愈架构:NPD + npd-node-replace 的设计与实现

管 K8s 集群的人都绕不开一个问题——节点故障处理。内核崩溃、OOM、硬件坏了,节点变成 NotReady。手动 drain、手动替换,半夜来一次谁都受不了。这篇从架构层面拆解一下 npd-node-replace 这个方案,看看它是怎么把节点自愈做稳的。

背景:节点故障处理的演进

在亚马逊云科技 EKS 集群中,节点故障的处理经历了几个阶段:

阶段 1:纯人肉。告警来了,登录查看,kubectl drain,手动替换。耗时长,体验差。

阶段 2:NPD + 人工。Node Problem Detector 自动检测问题并上报 Event,但处理还是靠人。好处是问题发现变快了。

阶段 3:NPD + Karpenter。Karpenter 可以自动回收和替换问题节点。但只支持 Karpenter 自己管理的节点,托管节点组和自管理节点组不行。

阶段 4:NPD + npd-node-replace。这就是今天要聊的方案。覆盖托管节点组和自管理节点组,故障持久化记录,三重防误操作机制。

整体架构

npd-node-replace 由三个核心 Controller 组成,各司其职:

EventController

职责:从 API Server 接收 NPD 上报的节点问题 Event。

NPD 以 DaemonSet 方式部署在每个节点上。它通过检查系统日志、内核消息等方式发现问题,然后向 API Server 上报 Event。

EventController 监听这些 Event,解析出事件类型(OOMKilling、KernelOops、KernelHang 等),创建或更新对应节点的 NodeIssueReport 自定义资源。

NodeIssueReport(NIR)是这个方案的核心数据结构。它记录了每个节点的完整故障历史,包括事件类型、发生时间、处理状态。这比 K8s 原生 Event(默认只保留 1 小时)持久化得多。

NIRController

职责:分析 NodeIssueReport,决定是否触发自愈操作。

NIRController Watch NodeIssueReport 的变更。每当 NIR 被创建或更新时,它会:

  1. 统计指定时间窗口内特定事件类型的发生次数
  2. 与 Tolerance 配置的阈值对比
  3. 达到阈值则触发对应的 Action(Reboot 或 Replace)
  4. 检查节点是否在白名单中(有 npd-node-replace-enabled=true 标签)

NodeController

职责:监控节点 Ready Condition 的变化。

不是所有问题都能通过 NPD Event 捕获。比如 kubelet 进程挂了,节点直接变成 Unknown 状态,可能不会产生 Event。

NodeController 监控所有节点的状态。当发现某个节点从 Ready 变成 NotReady 或 Unknown 时:

  1. 记录到对应的 NodeIssueReport
  2. 启动 Double Check 计时
  3. 等待配置的时间后再次检查
  4. 如果仍然异常,触发 Replace 操作

防误操作机制详解

节点自动替换是个高风险操作。如果误触发,可能比节点故障本身造成的影响还大。npd-node-replace 在这方面做了很细致的设计。

时间窗口机制

Tolerance 配置支持 timewindowinminutes 参数:

{"tolerancecollection": {"OOMKilling": {"times": 2,"action": "reboot","timewindowinminutes": 30},"KernelOops": {"times": 3,"action": "replace","timewindowinminutes": 60}}
}

以 KernelOops 为例:只有在 60 分钟内累计出现 3 次 KernelOops,才会触发 Replace。

为什么不用总次数?因为节点运行时间长了,偶发的小问题可能累积很多次。用总次数的话,一台跑了半年的节点可能因为历史上偶发的几次小问题被误替换。

时间窗口让判断聚焦在「近期是否频繁出问题」,更合理。

Double Check 机制

- name: NODE_DOULBE_CHECK_GRACE_TIMEvalue: "15"

节点状态变成 NotReady 或 Unknown 后,不立即执行替换。等待 15 分钟(可配置)后再次检查状态。

为什么需要这个?几个场景:

  • 节点重启过程中会短暂出现 NotReady,这是正常的
  • 网络抖动可能导致 API Server 暂时收不到心跳
  • kubelet 重启也会出现短暂的状态异常

Double Check 过滤掉了这些瞬态问题。

注意:这个值必须大于新节点启动和节点重启所需的时间。否则节点正在恢复的过程中就被判定为异常了。

白名单机制

kubectl label nodes <node-name> npd-node-replace-enabled=true

默认情况下,npd-node-replace 只做问题收集和记录,不会对任何节点执行 Reboot 或 Replace。只有打了标签的节点才会被自动处理。

这个设计让你可以:

  1. 先在非关键节点上验证
  2. 逐步扩展到更多节点
  3. 对关键业务节点保持人工确认

Replace 流程深入

Replace 的流程比简单的删除重建复杂得多:

第一步:从 Auto Scaling 组分离

调用 EC2 Auto Scaling API,将问题节点从 ASG 中分离(Detach)。分离后,ASG 会自动拉起一台新实例来维持 Desired Count。

第二步:等待新节点就绪

不是分离了就完事。npd-node-replace 会等待新节点加入集群并且状态变成 Ready。这一步确保替换后集群的计算容量没有减少。

第三步:Drain 旧节点

新节点就绪后,执行 kubectl drain 操作,优雅地驱逐旧节点上的 Pod。

第四步:通知管理员

通过 Amazon SNS 发送通知邮件,包含节点问题详情和执行的自愈动作。

第五步:删除旧 Node 对象

从 K8s 集群中删除旧的 Node 对象。但关键的是——不终止 EC2 实例

为什么不终止?因为要保留故障现场。运维人员可以通过 EC2 控制台找到这个实例,SSH 登录上去收集日志、检查内核 dump,做根因分析。分析完了再手动终止。

Reboot 流程

Reboot 相对简单:

  1. 设置节点为不可调度(Cordon)
  2. Drain Pod
  3. 调用 EC2 RebootInstances API 重启实例
  4. 等待节点恢复 Ready
  5. 取消不可调度(Uncordon)
  6. SNS 通知管理员

部署实践

前置条件

  • EKS 集群 + IAM OIDC 提供商
  • Fargate Profile(重要:npd-node-replace 自身需要跑在 Fargate 上,避免处理自己所在的节点)
  • 已部署 Node Problem Detector
  • Amazon SNS 主题 + 订阅
  • Amazon ECR 仓库

IAM 策略

{"Version": "2012-10-17","Statement": [{"Sid": "Statement1","Effect": "Allow","Action": ["ec2:RebootInstances","ec2:DescribeInstances","autoscaling:DetachInstances","sns:Publish"],"Resource": ["*"]}]
}
aws iam create-policy \--policy-name NPDNodeReplacePolicy \--policy-document file://iam_policy.jsoneksctl create iamserviceaccount \--cluster=<cluster-name> \--namespace=<fargate-namespace> \--name=npd-node-replace-sa \--attach-policy-arn=arn:aws-cn:iam::<account-id>:policy/NPDNodeReplacePolicy \--override-existing-serviceaccounts \--region <region> \--approve

Helm 部署

docker pull zxxxxzz/npd-node-replace:v1.2
docker tag zxxxxzz/npd-node-replace:v1.2 \<account-id>.dkr.ecr.<region>.amazonaws.com.cn/<repo-name>:v1.2aws ecr get-login-password --region <region> | \docker login --username AWS --password-stdin \<account-id>.dkr.ecr.<region>.amazonaws.com.cn
docker push <account-id>.dkr.ecr.<region>.amazonaws.com.cn/<repo-name>:v1.2helm repo add npd-replace https://normalzzz.github.io/npd-node-replace/

values.yaml 关键配置:

kubernetesClusterDomain: cluster.local
npdNodeReplace:npdNodeReplace:env:snsTopicArn: <sns-topic-arn>nodeDoubleCheckGraceTime: 15image:repository: <account-id>.dkr.ecr.<region>.amazonaws.com.cn/<repo-name>tag: v1.2imagePullPolicy: Alwaysreplicas: 1
sa:serviceAccount:annotations:eks.amazonaws.com/role-arn: <irsa-iam-role-arn>
toleranceConfig:toleranceJson: |-{"tolerancecollection": {"OOMKilling": {"times": 2,"action": "reboot","timewindowinminutes": 30},"KernelOops": {"times": 3,"action": "replace","timewindowinminutes": 60}}}
helm install npd-replace npd-replace/npd-node-replace \--namespace <fargate-namespace> \--set serviceAccount.create=false \-f values.yaml# 验证
kubectl get deployment -n <fargate-namespace> | grep npd-node-replace

测试验证

模拟节点问题

# 模拟 OOM
echo "Killed process 1234 (myapp) total-vm:102400kB, anon-rss:51200kB, file-rss:2048kB" \| sudo tee /dev/kmsg# 模拟内核错误
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>

模拟节点状态异常

systemctl stop kubelet
# 节点变 Unknown → 15分钟 Double Check → Replace

上线建议

阶段 1:观察期。部署组件,不打标签。观察 NPD 检测和 SNS 通知是否正常。

阶段 2:验证期。选几台非关键节点打标签,验证 Reboot 和 Replace 流程。

阶段 3:推广期。逐步扩展到更多节点。

日志建议输出到 CloudWatch。

总结

npd-node-replace 的设计思路很清晰:NPD 管发现,它管处理。通过 NodeIssueReport 实现故障持久化,通过时间窗口 + Double Check + 白名单实现三重防护。

比 Karpenter 方案的优势在于支持更多节点形态。Replace 时保留 EC2 实例的设计也很实用——出了问题能回去查。

代码仓库:npd-node-replace


参考

  • 亚马逊云科技官博原文
  • Node Problem Detector
  • Amazon EKS
  • Fargate 日志记录
http://www.jsqmd.com/news/756768/

相关文章:

  • 别再问‘我的手机是arm几’了!用adb一条命令快速查清安卓设备CPU架构(附模拟器/多设备场景)
  • D3KeyHelper:5分钟配置你的暗黑3技能连点器,彻底解放双手!
  • 基于遗传算法的阵列天线方向图优化MATLAB实现
  • 河南物业软件怎么选靠谱?本土企业选型核心标准 - movno1
  • 网盘直链下载助手:告别客户端,3分钟掌握浏览器下载网盘的终极方法
  • 告别重复操作:用快马生成高效飞书cli工具,自动化你的团队管理流程
  • CPPM面授课值得去吗? - 众智商学院官方
  • 快速构建quartus ii安装引导器:快马原型设计助力环境搭建效率翻倍
  • 亨得利维修保养服务中心地址电话全攻略:为什么懂表的人只选这6城?400-901-0695正规渠道揭秘 - 时光修表匠
  • 底层模型一变,为什么下游工作流经常会一起抖动
  • 保姆级教程:用CANoe 16 Demo版从零搭建你的第一个汽车ECU仿真项目(附源码)
  • 基于VuePress构建私有化团队Wiki:静态站点生成器的实践指南
  • OpenClaw.NET .NET 原生插件开发完全指南:以 Mempalace 插件为范例
  • ThreeFingerDragOnWindows终极指南:在Windows上实现macOS式三指拖拽的完整教程
  • 2026职场必备:Gemini3.1Pro提效指南
  • 南京黄金上门回收天花板!2026 闭眼选 福正美黄金回收 - 福正美黄金回收
  • 2025届最火的五大AI辅助写作助手实测分析
  • 3分钟掌握WebSite-Downloader:Python网站离线下载终极指南
  • ChatGPT for Bot:构建多平台AI聊天机器人的开源框架部署与实战
  • AI增强安全运维:基于LLM的自动化渗透测试与安全评估实践
  • 2026 柳州黄金回收榜|福正美黄金回收位列榜一 - 福正美黄金回收
  • 山东一卡通回收攻略:长期在外工作怎么办 - 抖抖收
  • 如何用Windows Cleaner彻底解决C盘爆红问题:一份3步终极指南
  • 第6篇:数组和列表——存储多个数据 原生中文编程
  • 全栈项目模板:现代Web应用开发的瑞士军刀与最佳实践
  • 2026年知网AI检测更新:学生党高效降AI率收藏指南 - 降AI实验室
  • 别再手动算误差了!用ggplot2的stat_summary函数一键搞定柱状图误差线
  • 瑞祥商联卡回收:附近没有合作商户怎么办 - 抖抖收
  • ChatGPTPowerToys:模块化工具箱加速AI应用开发与集成
  • 5个简单步骤掌握MediaPipe TouchDesigner插件:免费AI视觉创作终极指南