Silas2323/fabric-chaos-sentry

GitHub: Silas2323/fabric-chaos-sentry

面向 AI 网络 fabric 的混沌工程项目,通过在 EVPN-VXLAN/Cumulus 环境中主动注入故障来验证监控系统的检测能力和收敛时间。

Stars: 0 | Forks: 0

# fabric-chaos-sentry 一个手动搭建的 EVPN-VXLAN fabric,已运行至接近生产环境的状态,目前正 在为其注入蓄意的故障——以此来验证监控系统能否捕获关键问题。这是 一本运维人员的实战笔记,而不是一个演示仓库:以下每一个数据都是 在本仓库的 `configs/` 所在的实验室中实测的,每一项“故障记录”都是真实的 事件,任何尚未执行的测试都会明确标出。 **核心主张:** AI 训练流量依赖于无损、低延迟的 fabric。 最具破坏性的故障并非完全中断的链路——而是那些让一切看起来都“正常”, 却让集合通信变得极其缓慢的静默配置错误和灰色故障。你无法确信监控系统 能够捕获这些问题,除非你故意注入它们。你同样无法确信你的 fabric 真正 有效,除非你曾无意中将其搞崩溃过。 ## 构建内容 拓扑:一个完整的 CLOS 网络——2 个 spine,2 个 leaf,每个 leaf 连接到 每个 spine——基于 Cumulus Linux(使用 NVUE CLI,底层为 FRR 10.x) 在 NVIDIA Air 上构建。四台 Ubuntu 主机连接在 leaf 下方(每个 leaf 两台), 作为 EVPN-VXLAN 租户。 | 节点 | Loopback / Router-ID | ASN | Fabric 端口 | |---------|-----------------------|----------------|----------------------------------| | SPINE-1 | 10.0.0.1/32 | 65100 (共享) | swp1→LEAF-1, swp2→LEAF-2 | | SPINE-2 | 10.0.0.2/32 | 65100 (共享) | swp1→LEAF-1, swp2→LEAF-2 | | LEAF-1 | 10.0.0.11/32 | 65101 | swp1/2 上行链路; swp10/11 服务器 | | LEAF-2 | 10.0.0.12/32 | 65102 | swp1/2 上行链路; swp10/11 服务器 | 完整的各节点配置转储位于 [configs/](configs/)。 **BGP unnumbered 底层网络 (RFC 5549)。** 每条面向 fabric 的链路都运行 eBGP,且完全不使用 IPv4 寻址——邻居通过接口命名,会话通过 IPv6 链路本地地址(`fe80::`)下一跳建立,而 IPv4 前缀(即 loopback)则 *通过*该 IPv6 下一跳进行传递。Loopback 通过 `redistribute connected` 进行通告——这也是为什么它们在 BGP 表中携带来源 `?`(不完整)的原因; `network` 语句是更干净的生产环境模式,而本实验室是有意采取了 这种捷径。ZeroTouch 邻接:在两台 Cumulus 设备之间插入一条链路,会话 就会建立,完全不需要逐链路规划 IP。 **EVPN-VXLAN 叠加网络。** 所有四个节点(包括两个 spine)都启用了 L2VPN EVPN 地址族——spine 永远不会发起 VTEP,但必须 在 leaf 之间中继 EVPN 路由;跳过它们是典型的初级 错误。这两个 leaf 运行实际的 VTEP,源于其 loopback。 VLAN 10 → VNI 10010 和 VLAN 20 → VNI 10020 作为单个广播域 跨越两个 leaf 进行延伸。 **具有 Anycast 网关 + L3VNI 的对称 IRB。** 两个 leaf 为每个 VLAN 运行一个 SVI 作为本地默认网关,共享一个 anycast 网关 MAC (`44:38:39:ff:00:01`),因此无论主机连接到哪个 leaf, 它都会获得相同的网关身份。跨 VLAN 流量(VLAN 10 ↔ VLAN 20,租户 VRF `TENANT1`)通过 L3VNI `104001` 进行对称路由, 而不是通过一个中央路由器进行发夹式转发——每个 leaf 在本地 路由进入 VXLAN fabric,而远端 leaf 在本地路由出去。 ## 测量结果 - **在 FIB 和 BGP 中均已验证的 ECMP。** `10.0.0.12/32`(LEAF-2 的 loopback,从 LEAF-1 观察)解析为两个 `fe80::` 下一跳——每个 分别经由一个 spine——两者都安装在 FIB 中,并且 BGP 在 multipath 下 显示 2 条路径。由于两个 spine 共享 ASN 65100,两个 AS 路径 完全相同,并且无需额外配置即可满足标准 ECMP 条件; BGP 仍会将一条路径标记为 "best (Older Path)"——那只是 通告的决胜规则,两条路径都会进行转发。设计权衡: 为每个 spine 分配唯一的 ASN(另一种常见的 CLOS 模式)会使得 AS 路径不同,从而需要配置 `bestpath as-path multipath-relax` 来保持 两条路径。 - **约 3 秒的链路故障收敛时间。** 断开一条 leaf↔spine 链路并 测量重新收敛的时间,结果约为 3 秒:unnumbered BGP 会话会随着 链路本身一起断开(没有 IP 邻居可以维持,因此无需等待 hold-timer), 剩下的延迟则是 VM 载波检测延迟。BFD 是推动其达到亚秒级的 已知下一步——参见路线图。 - **具有跳数证据的底层网络连通性。** 双向的 Loopback 到 Loopback Ping 测试均已验证:相邻节点之间 `ttl=64`,穿过一个 spine 的 leaf 到 leaf 之间为 `ttl=63`——TTL 的差值证实了 实际路径长度,而不仅仅是“Ping 通了”。 - **62 处的对称 IRB 的 TTL 证据。** 连接到不同 leaf 的 VLAN 10 和 VLAN 20 主机之间的 Ping 落在 TTL 62 处 (64 − 2):在入口 leaf 的 SVI 处 减一,在出口 leaf 的 SVI 处减一。这个 两跳的特征就是流量确实通过对称路由穿过了两个 leaf 的本地网关, 而不是通过单台设备进行发夹式转发的证明。 支持这些数字的原始抓包数据位于 [evidence/](evidence/) 中。 ## 发生过的故障及其教训 - **以配置作为唯一事实来源,这是惨痛的教训。** NVIDIA Air 的恢复按钮 会在会话出现故障时重置节点的运行状态——包括从未保存的 配置。这曾经导致一次正在进行的构建前功尽弃。解决方法并不是 “小心谨慎”,而是采用与生产环境 network-as-code 相同的策略: 存放在 git 中的配置才是唯一事实来源,而不是设备上当前运行的 内容。`configs/` 中的每个文件都是因为这个而存在的。 - **密码掩码恢复陷阱。** `nv config show -o commands` 会为本地账户打印 `hashed-password '*'`——它掩码了真实的 哈希值而不是显示出来。逐字重播该输出(例如,从保存的转储中恢复) 会将账户密码设置为一个任何输入都无法匹配的值,从而将自己锁在门外。 解决方法:在准备重播配置之前剔除 `aaa`/`user` 行,并且在没有 开启另一个已通过身份验证的会话作为救命稻草的情况下,绝不 触碰身份验证配置。生产环境的类比是 vault 或 TACACS+——凭证完全 存在于配置工件之外,这也是为什么这里的 `configs/` 也将它们清除了。 - **SVI 已创建但未配置地址。** 在 LEAF-1 上,`vlan20` 最终被创建 并绑定了 VRF,但从未分配地址——`ipv4 address` 行在一次混乱的 apply 过程中丢失了。该 SVI 存在,在配置中也显示了出来,但 默默地在罢工:没有可用于路由的地址,没有可用的网关, 也没有报错。它是通过将健康的 `vlan10` 输出与 `vlan20` 进行并排对比发现的——那个正常工作的孪生兄弟让缺失的行变得 一目了然,而仅仅盯着坏掉的那个是永远看不出来的。两个教训: “对象存在”和“对象可用”是不同的检查点,当你有已知良好的 同胞配置时,对比诊断胜过孤立地分析故障件。 - **重复 VNI 校验捕获。** 使用较旧的方法手动映射了 VLAN 30 → VNI 104001 ——而此时 `TENANT1` VRF 声明已经在自动推导相同 VNI 的 L3VNI 管道。`nv config apply` 直接拒绝了重复的声明:手动输入的 配置和推导出的配置都在声明对 VNI 104001 的所有权。教训在于 基于意图的配置模型:当系统从你的声明中推导出对象时,用旧方法手动 放置相同的对象不仅是多余的——它是一种冲突,而在 apply 阶段就能 捕获到它的验证控制平面,正是被拒绝的命令和微妙的崩溃状态之间的 区别。 ## 混乱与检测层 上述 fabric 是基础底座。在此之上,`chaos/` 注入 此类 fabric 在实际现场环境中产生的故障,而 `sentry/` (Prometheus/Grafana/Alertmanager)必须证明它能捕获这些故障—— 带有可测量的检测时间,而不是猜测。 ``` ┌─────────────────────────────┐ ┌──────────────────────────────┐ │ NVIDIA Air lab │ │ Kubernetes cluster │ │ EVPN-VXLAN fabric (BUILT) │ │ (PLANNED — see Roadmap) │ │ SPINE-1/2 ── LEAF-1/2 │ │ Proxmox VMs, kubespray │ │ ▲ │ │ ▲ │ │ │ chaos/fabric/ │ │ │ chaos/k8s/ │ │ │ (Ansible + NVUE) │ │ │ (Chaos Mesh) │ └────────┼────────────────────┘ └────────┼─────────────────────┘ │ node_exporter │ node_exporter │ (ethtool collector) │ (+ kube-state-metrics) ▼ ▼ ┌──────────────────────────────────────────┐ │ sentry/ (Docker Compose) │ │ Prometheus ──► Alertmanager ──► webhook │ │ │ │ │ └──► Grafana (Fabric Overview) │ └──────────────────────────────────────────┘ ``` ## 仓库布局 ``` configs/ # Per-node NVUE config dumps (nv config show -o commands), scrubbed of secrets docs/ # Cheat sheet, troubleshooting guide, one runbook per chaos scenario evidence/ # Raw command output backing the "what was measured" claims above chaos/ fabric/ # Cumulus failure injection (Ansible/NVUE) — 6 scenarios k8s/ # Chaos Mesh experiments — 4 scenarios sentry/ # Prometheus + Grafana + Alertmanager (Docker Compose) ``` 每个混乱场景文件夹都包含一个注入脚本,一个 `expected-signals.md`(因果链 + 必须触发的确切指标/警报), 以及一个回滚脚本。 ## 场景表 | # | 注入的故障 | 预期信号 | 检测时间 | 补救措施 | |---|------------------|-----------------|----------------|-------------| | F01 | **PFC 配置错误** — 无损优先级与一个 leaf 端口未绑定 | 邻居上 Pause-frame 速率激增,TC3 上的丢包;`PFCPauseFrameStorm`, `PFCLosslessClassDrops` | 目标 < 60 秒 (实测:TODO) | [Runbook](docs/runbooks/01-pfc-misconfig.md) → [回滚](chaos/fabric/01-pfc-misconfig/rollback.yml) | | F02 | 链路抖动 (leaf↔spine) | Carrier 变化,BGP 循环,ECMP 路径丢失 | TODO | TODO | | F03 | BGP 会话重置 | 对端离开 Established 状态,EVPN 路由下降 | TODO | TODO | | F04 | ECN 阈值失准 | 队列深度高 **且缺少 ECN 标记**,Pause 风暴 | TODO | TODO | | F05 | MTU 不匹配 | 某一跳丢包,大负载 RDMA 失败但 BGP 正常 | TODO | TODO | | F06 | 灰色故障(链路性能下降) | 缓慢的 CRC/错误增加,每流不对称 | TODO | TODO | | K01 | Pod kill | 重启次数,恢复到 Ready 的时间 | TODO | TODO | | K02 | 节点间的网络延迟 | RTT 偏移,TCP 重传 | TODO | TODO | | K03 | 网络分区 | 跨节点调用失败,endpoint 频繁变动 | TODO | TODO | | K04 | 资源匮乏 | CPU 限流,OOMKill,节点压力 | TODO | TODO | **状态:** F01 已完成脚手架搭建——注入/回滚剧本、警报规则和 Runbook 均已编写——但**从未被运行过**:没有实测的 检测时间,也尚未在实时的 fabric 上进行过演练。它还 针对尚未在基础 fabric 上绑定的 PFC/QoS 配置文件进行注入 (参见路线图),因此首次真正运行需要先完成该层。所有其他 场景都是带有 TODO 文档的存根。K01–K04 还依赖于目前 尚不存在的 Kubernetes 集群。 ## 预期工作流(目标状态) ``` # 1. Start the detection stack cd sentry docker compose up -d # Prometheus :9090 │ Grafana :3000 (admin/admin) │ Alertmanager :9093 # 2. Point Prometheus at your lab # Edit sentry/prometheus/prometheus.yml with your NVIDIA Air mgmt IPs # and K8s node IPs, then: curl -X POST localhost:9090/-/reload # 3. Run scenario F01 cd ../chaos/fabric ansible-playbook -i inventory/hosts.yml 01-pfc-misconfig/inject.yml \ -e target_switch=leaf01 -e target_interface=swp1 # 4. Generate RoCE load, watch Grafana, record time-to-alert, then roll back ansible-playbook -i inventory/hosts.yml 01-pfc-misconfig/rollback.yml \ -e target_switch=leaf01 -e target_interface=swp1 ``` ## 设计决策 ### 补救措施是 Runbook,而不是自动修复(目前) 每个警报都映射到已记录的 Runbook,而不是自动化的补救措施。这是 有意为之的,并且涉及到影响范围: 1. **错误的自动修复比缓慢的人工修复更糟糕。** 一个自动化执行者 如果为了响应拥塞信号而重写 QoS 配置或翻转链路,一旦发生误报, 将会*制造*出它旨在防止的中断。 在 fabric 中,补救操作(排空一条链路、重置一次会话、重写 PFC/ECN 配置)会降低容量或导致状态动荡——这正是你在由其他原因 (例如,真正的 incast)引起的真正拥塞期间不希望发生的。 2. **必须首先证明检测精度。** 存在混乱场景是为了 测量这种精度:每个的故障都有一个已知的真实基准,因此 误报率和漏报率是可测量的,而不是猜测的。 3. **晋升路径。** 一项补救措施只有在经历了多次混乱测试并幸存下来后, 才能获得自动化资格:Runbook → 由人工触发的脚本化修复(回滚剧本 已经是这样了)→ 带有范围限制(一个端口、一台交换机)和 自动停止条件的受控自动修复。 回滚剧本被有意编写为未来的自动化程序: 它们在执行后会验证状态,并拒绝在多于一台的设备上运行。 ### 其他选择 - **1:1 场景→警报映射。** 每个混乱场景都有一个命名警报。 如果某个场景未触发警报,那就是检测差距——这是该项目能产出的 最有价值的发现。 - **15 秒的 scrape 间隔**,以便测量的检测时间反映的是规则延迟, 而不是抓取延迟。 - **注入的配置从不保存到 startup**——重启始终是 有效的最后手段回滚。 - **Recording rules 抽象了平台指标名称**(Pause 计数器因 NIC/驱动程序而异),因此仪表板和警报共享同一个定义。 - **配置存放在 git 中,机密信息已被清除。** `configs/` 保存了每个 节点精确的 `nv config show -o commands` 输出,并移除了 `aaa`/`user` 行——关于为什么这些行很危险(甚至不应提交), 请参阅上文的“密码掩码恢复陷阱”。 ## 实验室环境 - **Fabric:** NVIDIA Air —— 2 个 spine / 2 个 leaf Cumulus Linux (NVUE), EVPN-VXLAN,具有 anycast 网关的对称 IRB。RoCEv2 PFC/ECN QoS 已规划范围,但尚未在基础 fabric 上绑定(路线图)。 - **检测:** Prometheus / Grafana / Alertmanager 通过 Docker Compose 一个 Kubernetes 集群(通过 kubespray 部署的 Proxmox 虚拟机, 使用 Chaos Mesh 进行注入)已计划但尚未构建——参见路线图。 `chaos/k8s/` 场景文档和 `sentry/` 中的 k8s 抓取任务是 提前编写的脚手架。 ## 路线图 - [ ] 在 fabric 链路上启用 BFD;重新进行收敛演练,并针对上述约 3 秒的基线 记录前后的数据。 - [*] 在基础 fabric 上绑定实际的 RoCEv2 QoS 层(switch-priority 3 上的 PFC + ECN/DCQCN 阈值)——目前只有 F01 混乱场景假定它存在; 而已知良好的配置文件本身尚未构建。 - [ ] 针对真实的 RDMA 负载首次运行 F01 并记录实测的 检测时间;实现 F02–F06。 - [ ] 构建 Kubernetes 集群:通过 kubespray 部署 Proxmox 虚拟机, Chaos Mesh,kube-state-metrics——然后针对其实现 K01–K04。 - [ ] 来自注入脚本的 Grafana 注释(混乱时间轴叠加层)。 - [ ] 架构图。 - [ ] 平行构建:在 EVE-NG 上使用 Arista + Ansible,作为相同底层/叠加 设计的第二供应商实现——进行多供应商重复验证, 而不是重新验证核心主张。 - [ ] 一旦检测精度得到证实,首次为 F01 引入受控的自动补救措施。
标签:子域名突变, 版权保护, 系统提示词, 自定义请求头