第一章:Go开源系统故障自愈能力构建概述
现代云原生系统对高可用性提出严苛要求,而Go语言凭借其轻量协程、静态编译、低GC延迟与强类型安全等特性,成为构建高韧性自愈系统的理想载体。故障自愈并非简单重启服务,而是指系统在检测到异常(如进程崩溃、HTTP健康检查失败、goroutine泄漏、内存超限)后,能自动执行诊断、隔离、恢复与反馈闭环的一整套能力。
自愈能力的核心构成
- 可观测性前置:集成
prometheus/client_golang暴露指标,结合net/http/pprof启用运行时剖析端点; - 多维度健康探测:除标准HTTP
/healthz外,需扩展对数据库连接池、gRPC后端、本地磁盘空间的主动探活; - 策略化恢复机制:支持分级响应——轻度异常触发goroutine熔断与重试,严重异常启动进程级热替换或容器重建;
- 可审计的决策日志:所有自愈动作必须记录
level=info action=restart reason="heap_usage > 95%" trace_id=xxx格式结构化日志。
Go中实现基础自愈循环的示例
以下代码片段展示一个嵌入式健康检查与自动重启的守护逻辑(适用于单体Go服务):
// 启动健康检查goroutine,每10秒探测一次
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
if !isHealthy() { // 自定义健康判断函数
log.Warn("Service unhealthy, triggering graceful restart...")
// 发送SIGUSR2信号触发平滑重启(需配合第三方库如 "github.com/alexcesaro/signal")
if err := syscall.Kill(syscall.Getpid(), syscall.SIGUSR2); err != nil {
log.Error("Failed to send restart signal:", err)
os.Exit(1) // 紧急兜底退出
}
return
}
}
}()
关键依赖与实践约束
| 组件 | 推荐方案 | 注意事项 |
|---|---|---|
| 进程管理 | supervisord 或 systemd |
避免直接使用nohup,需支持信号透传与退出码捕获 |
| 指标采集 | Prometheus + Grafana | /metrics端点须暴露go_goroutines, process_resident_memory_bytes等关键指标 |
| 日志规范 | zap + lumberjack轮转 |
所有自愈事件必须包含event=auto_heal字段便于ELK聚合 |
自愈系统的设计哲学是“防御性编码”与“渐进式恢复”的结合——不追求零故障,而确保每次故障都能被精确感知、可控收敛,并留下可追溯的行为证据。
第二章:eBPF内核观测与事件驱动机制设计
2.1 eBPF程序生命周期管理与Go agent协同模型
eBPF程序在内核中并非长期驻留,其加载、运行、卸载需与用户态Go agent紧密协同,形成闭环生命周期。
生命周期关键阶段
- 加载(Load):Go agent调用
bpf.NewProgram()加载验证后的字节码 - 挂载(Attach):绑定至特定钩子(如
kprobe,tracepoint) - 运行(Run):事件触发执行,通过
perf_event_array或ringbuf输出数据 - 卸载(Detach/Close):agent显式调用
prog.Close(),内核自动回收资源
数据同步机制
Go agent通过 ringbuf.NewReader() 实时消费eBPF程序写入的结构化事件:
rb, _ := ringbuf.NewReader(objs.RingbufMaps.Events)
for {
record, err := rb.Read()
if err != nil { break }
event := (*Event)(unsafe.Pointer(&record.Data[0]))
log.Printf("PID=%d, Comm=%s", event.Pid, event.Comm)
}
此代码创建ringbuf读取器,持续轮询事件;
record.Data是固定布局的二进制缓冲区,需按eBPF端定义的struct Event偏移解析字段。Read()阻塞等待新数据,内核保证零拷贝交付。
协同状态映射
| Agent状态 | eBPF程序行为 | 同步保障机制 |
|---|---|---|
| 初始化完成 | 允许挂载 | prog.Load() 返回非nil指针 |
| Ringbuf已就绪 | 开始写入事件 | rb.Read() 可立即调用 |
| Agent退出前 | 自动 detach & close | defer prog.Close() |
graph TD
A[Go agent启动] --> B[加载eBPF字节码]
B --> C[验证并创建Program]
C --> D[Attach到tracepoint]
D --> E[启动ringbuf监听]
E --> F[事件触发→内核执行→ringbuf写入]
F --> G[Go agent Read→解析→上报]
2.2 基于BPF_MAP_TYPE_PERF_EVENT_ARRAY的实时指标采集实践
BPF_MAP_TYPE_PERF_EVENT_ARRAY 是 eBPF 中专为高效内核事件采样设计的映射类型,常用于将 tracepoint/kprobe 采集的指标批量推送至用户态。
核心使用模式
- 用户态需预先为每个 CPU 分配一个 perf ring buffer;
- 内核态通过
bpf_perf_event_output()将结构化数据写入对应 CPU 的 buffer; - 用户态轮询
perf_event_mmap_page并调用read()或mmap()+ ring buffer 消费逻辑读取。
示例:采集进程调度延迟(ns)
// BPF 程序片段(C)
struct sched_latency {
u64 pid;
u64 delta_ns;
};
// 在 tracepoint: sched:sched_stat_runtime 中触发
struct sched_latency lat = {};
lat.pid = bpf_get_current_pid_tgid() >> 32;
lat.delta_ns = ... // 计算逻辑省略
bpf_perf_event_output(ctx, &perf_map, BPF_F_CURRENT_CPU, &lat, sizeof(lat));
逻辑分析:
&perf_map必须是BPF_MAP_TYPE_PERF_EVENT_ARRAY类型;BPF_F_CURRENT_CPU确保数据写入当前 CPU 对应的 buffer,避免跨 CPU 锁竞争;sizeof(lat)需严格匹配结构体实际大小,否则 ring buffer 解析错位。
用户态消费关键参数
| 参数 | 说明 |
|---|---|
PERF_FLAG_FD_CLOEXEC |
防止 fork 后子进程继承 fd |
mmap_size = page_size * (1 + nr_pages) |
首页为元数据页(perf_event_mmap_page),后续为数据页 |
sample_type = PERF_SAMPLE_RAW |
匹配 bpf_perf_event_output 的 raw 数据格式 |
graph TD
A[内核触发 tracepoint] --> B[bpf_perf_event_output]
B --> C[写入 per-CPU ring buffer]
C --> D[用户态 mmap + poll]
D --> E[解析 raw data → JSON/metrics]
2.3 故障特征提取:从tracepoint到可观测性信号建模
在内核态与用户态协同观测中,tracepoint 是轻量级、静态定义的探针锚点,为故障信号注入提供确定性入口。
核心数据流建模
// 示例:ext4_write_begin tracepoint 提取延迟与上下文
TRACE_EVENT(ext4_write_begin,
TP_PROTO(struct inode *inode, loff_t pos, unsigned len),
TP_ARGS(inode, pos, len),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(loff_t, pos)
__field(unsigned, len)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->pos = pos;
__entry->len = len;
)
);
逻辑分析:该 tracepoint 捕获文件写入起始时刻的设备号、inode、偏移与长度——四维元数据构成可关联的故障指纹;TP_fast_assign 确保零拷贝赋值,避免 probe 本身引入可观测性噪声。
特征升维映射表
| 原始 tracepoint 字段 | 可观测性信号维度 | 语义解释 |
|---|---|---|
dev, ino |
实体拓扑 | 定位存储卷与文件实例 |
pos, len |
负载模式 | 识别随机/顺序、小/大块 |
| 时间戳(隐式) | 时序异常 | 结合 eBPF 高精度采样 |
信号聚合路径
graph TD
A[Kernel tracepoint] --> B[eBPF map 缓存]
B --> C{滑动窗口聚合}
C --> D[延迟分布直方图]
C --> E[IO 模式熵值]
D & E --> F[多维特征向量]
2.4 Go agent中libbpf-go封装与安全加载策略
libbpf-go 是 Go 生态中对接 eBPF 的核心绑定库,Go agent 通过其抽象层实现 BPF 程序的编译、验证与加载。
安全加载的核心约束
- 必须启用
WithUnprivileged(false)强制 root 权限校验 - 加载前调用
obj.LoadAndAssign()触发内核 verifier 全路径检查 - 禁用
BPF_PROG_TYPE_SOCKET_FILTER等高危类型(除非显式白名单)
eBPF 程序加载流程
prog, err := obj.LoadAndAssign(
&ebpf.ProgramOptions{
LogLevel: 1, // 启用 verifier 日志输出
LogSize: 65536,
},
)
该调用触发内核 verifier 对指令流、内存访问、辅助函数调用链进行静态分析;LogLevel=1 输出关键校验点(如 map key/value 类型匹配),LogSize 需足够容纳完整验证日志,否则截断导致误判。
| 验证阶段 | 检查项 | 失败后果 |
|---|---|---|
| 指令解析 | 跳转偏移越界 | EINVAL |
| 辅助函数 | 未授权 bpf_map_lookup_elem | EACCES |
| 内存模型 | 栈溢出或未初始化读 | EACCES |
graph TD
A[LoadAndAssign] --> B[Verifer 静态分析]
B --> C{是否通过?}
C -->|是| D[注入内核 BPF 程序区]
C -->|否| E[返回详细 LogBuffer 错误]
2.5 生产级eBPF验证框架:单元测试+内核版本兼容性矩阵
构建可落地的eBPF程序,需同时解决行为正确性与内核适配确定性两大挑战。
单元测试:基于libbpf-testenv的轻量验证
// test_tc_ingress.c —— 模拟TC ingress钩子输入
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "test_skel.h"
void test_drop_by_port(void) {
struct test_bpf *skel = test_bpf__open_and_load();
assert(skel); // 加载时触发verifier校验
bpf_program__set_autoload(skel->progs.tc_ingress, false);
// 注入伪造skb:port=22 → 应drop
assert(bpf_prog_test_run_opts(skel->progs.tc_ingress, &opts) == 0);
}
逻辑分析:bpf_prog_test_run_opts 在用户态模拟内核执行路径,opts.data_in 注入原始包数据,opts.retval 返回程序返回码(如 TC_ACT_SHOT),全程不依赖真实网卡或内核模块加载。
内核兼容性矩阵
| 内核版本 | BTF 支持 | map_in_map | bpf_probe_read_kernel 可用性 |
验证状态 |
|---|---|---|---|---|
| 5.4 | ❌ | ✅ | ✅(需CONFIG_BPF_KPROBE_OVERRIDE) |
✅ |
| 5.10 | ✅(基础) | ✅ | ✅ | ✅ |
| 6.1 | ✅(完整) | ✅ | ✅(无条件) | ✅ |
验证流水线协同
graph TD
A[CI触发] --> B[libbpf-testenv单元测试]
B --> C{全内核版本矩阵遍历?}
C -->|yes| D[QEMU+kernelci启动对应vmlinux]
D --> E[运行eBPF object + bpftool load]
E --> F[注入预置tracepoint事件流]
F --> G[断言perf ringbuf输出符合预期]
第三章:自愈策略引擎核心实现
3.1 基于状态机的故障分类与SLA匹配决策逻辑
系统将故障生命周期建模为五态机:Idle → Detected → Classified → SLAMatched → Resolved,各状态迁移受事件与SLA约束双重驱动。
状态迁移核心逻辑
def transition(state, event, severity, sli_target):
# severity: 'CRITICAL', 'HIGH', 'MEDIUM'; sli_target: float (e.g., 0.999)
if state == "Detected" and severity == "CRITICAL":
return "Classified" if sli_target >= 0.99 else "SLAMatched" # SLA阈值触发分流
elif state == "Classified" and sli_target > 0.999:
return "SLAMatched" # 超高可用性要求强制升级响应等级
return state
该函数实现动态SLA感知的状态跃迁:sli_target直接决定是否跳过人工分类环节,缩短MTTR。
SLA-故障类型映射表
| 故障类别 | SLI阈值要求 | 响应窗口(min) | 自动处置动作 |
|---|---|---|---|
| 数据一致性中断 | ≥0.9999 | 2 | 触发强一致回滚 + 告警 |
| API超时率超标 | ≥0.99 | 5 | 流量降级 + 熔断 |
决策流程可视化
graph TD
A[Detected] -->|CRITICAL & SLI≥0.999| B[SLAMatched]
A -->|MEDIUM| C[Classified]
B --> D[Auto-Remediate]
C -->|SLI<0.99| D
C -->|SLI≥0.999| B
3.2 可插拔式修复动作注册机制(Restart/Reload/Throttle/Scale)
系统通过统一接口 RegisterAction(name string, handler ActionHandler) 实现修复策略的动态注入:
// 注册重启动作,支持优雅超时与失败回退
RegisterAction("restart", NewRestartHandler(
WithGracePeriod(30*time.Second),
WithMaxRetry(3),
WithFallback("throttle"),
))
该代码将 restart 动作绑定至具备优雅终止、重试控制及降级兜底能力的处理器;WithGracePeriod 确保服务在 SIGTERM 后等待关键任务完成;WithMaxRetry 防止雪崩重试;WithFallback 在连续失败后自动切换至更保守的 throttle 策略。
支持的内置动作语义对比
| 动作 | 触发条件 | 影响范围 | 可逆性 |
|---|---|---|---|
| Restart | 进程僵死、健康检查失联 | 单实例 | 是 |
| Reload | 配置热更新 | 当前进程 | 是 |
| Throttle | 流量突增、错误率超标 | 请求链路 | 是 |
| Scale | 持续CPU/延迟阈值越界 | 实例集群 | 否(需协调) |
扩展流程示意
graph TD
A[故障检测] --> B{策略匹配}
B -->|重启可行| C[Restart]
B -->|配置变更| D[Reload]
B -->|资源承压| E[Throttle]
B -->|长期过载| F[Scale]
C & D & E & F --> G[注册中心同步状态]
3.3 自愈策略灰度发布与回滚控制面设计
控制面需在灰度流量切换与异常自愈间建立毫秒级协同闭环。
核心状态机驱动
# 控制面状态定义(CRD片段)
spec:
strategy: canary
steps:
- setWeight: 5 # 初始灰度比例
probeTimeout: 30 # 健康探测超时(秒)
- setWeight: 20
autoRollbackOnFailure: true # 触发条件:连续3次probe失败
该配置声明式定义灰度节奏与回滚阈值,autoRollbackOnFailure 启用后,控制面监听指标服务上报的 canary_failure_rate > 5% 事件,自动触发反向权重迁移。
回滚决策矩阵
| 条件类型 | 触发阈值 | 动作 |
|---|---|---|
| 延迟突增 | P95 > 2s × 2 | 暂停灰度,保留当前权重 |
| 错误率超标 | 5xx > 3% × 60s | 立即回滚至基线版本 |
| 资源饱和 | CPU > 90% × 5m | 降权50%,异步扩容节点 |
流量调度流程
graph TD
A[灰度启动] --> B{健康检查通过?}
B -- 是 --> C[权重递增]
B -- 否 --> D[触发回滚]
D --> E[恢复基线Service Endpoints]
E --> F[下发Envoy xDS v3配置]
第四章:5分钟自动恢复POC工程落地
4.1 POC-1:HTTP服务OOM后进程级自动重启(含cgroup v2内存压测模拟)
内存隔离与压测准备
启用 cgroup v2 并创建受限内存环境:
# 创建 memory controller 目录并限制为128MB
sudo mkdir -p /sys/fs/cgroup/oom-test
echo "128M" | sudo tee /sys/fs/cgroup/oom-test/memory.max
echo "10M" | sudo tee /sys/fs/cgroup/oom-test/memory.low
memory.max是硬性上限,超限触发 OOM Killer;memory.low为内核回收优先级提示,不强制限制。
自动重启机制
使用 systemd 的 Restart= 策略配合 OOMScoreAdjust=:
# /etc/systemd/system/oom-http.service
[Service]
ExecStart=/usr/bin/python3 -m http.server 8000
MemoryMax=128M
OOMScoreAdjust=-1000 # 降低被误杀概率,确保由cgroup触发OOM
Restart=on-failure
RestartSec=3
压测验证流程
graph TD
A[启动服务] --> B[cgroup v2内存限制生效]
B --> C[注入内存压力:stress-ng --vm 1 --vm-bytes 200M]
C --> D[内核触发OOM Killer]
D --> E[systemd捕获exit code -9]
E --> F[3秒后自动拉起新进程]
| 指标 | 值 | 说明 |
|---|---|---|
memory.max |
128M |
进程组硬性内存上限 |
ExitCode |
-9 |
OOM 杀死的确定性信号 |
RestartSec |
3 |
避免雪崩式重启 |
4.2 POC-2:gRPC连接雪崩下的连接池动态熔断与恢复(含netlink socket监控)
当后端服务响应延迟突增,gRPC客户端连接池在重试机制下迅速耗尽,触发连接雪崩。我们引入基于 netlink socket 的实时网络状态感知能力,结合连接池健康度指标实现毫秒级动态熔断。
熔断决策信号源
NETLINK_ROUTE监听RTM_NEWROUTE/RTM_DELROUTE事件/proc/net/snmp中TcpExt: TCPTimeouts指标上升超阈值(>50/s)- 连接池活跃连接数 > 90% 且平均 RTT > 800ms
netlink 监控核心逻辑
// 初始化 netlink socket 监听路由变更
conn, _ := netlink.Dial(netlink.NETLINK_ROUTE, &netlink.Config{NetNS: -1})
defer conn.Close()
for {
msgs, _ := conn.Receive()
for _, m := range msgs {
if m.Header.Type == unix.RTM_NEWROUTE || m.Header.Type == unix.RTM_DELROUTE {
triggerHealthCheck() // 触发熔断评估
}
}
}
该代码通过 netlink.Dial 建立内核路由事件监听通道;RTM_NEWROUTE 表示新路由插入(如网关切换),RTM_DELROUTE 表示路由失效——二者均为网络拓扑异常强信号,需立即校验连接池可用性。
动态恢复策略
| 指标 | 熔断阈值 | 恢复条件 |
|---|---|---|
| 连接失败率 | ≥35% | 连续30s |
| netlink 路由抖动频次 | ≥3次/分钟 | 连续2分钟无事件 |
| 池内空闲连接数 | ≤2 | ≥8 且 RTT |
graph TD
A[netlink事件或指标越限] --> B{熔断器状态?}
B -- OPEN --> C[拒绝新建连接,降级为本地缓存]
B -- HALF_OPEN --> D[放行5%探测请求]
D -- 成功率≥90% --> E[切换为CLOSED]
D -- 失败率>20% --> B
4.3 POC-3:etcd leader异常时的Raft状态自检与peer重发现(含bpf_ktime_get_ns时序对齐)
当etcd集群 leader 因网络分区或进程崩溃失联,follower 节点需在超时后触发 Campaign,但传统心跳检测易受系统负载抖动干扰。本POC引入 eBPF 辅助时序锚定:
// bpf_prog.c:在raft.tick()入口处注入高精度时间戳
u64 now = bpf_ktime_get_ns(); // 纳秒级单调时钟,规避gettimeofday漂移
bpf_map_update_elem(&raft_tick_ts, &pid, &now, BPF_ANY);
该调用确保 Raft tick 事件的时间戳与内核调度上下文严格对齐,为后续 peer 心跳偏差分析提供可信基线。
核心改进点
- 使用
bpf_ktime_get_ns()替代用户态clock_gettime(CLOCK_MONOTONIC),消除 syscall 开销与时钟域切换误差(典型偏差 - 自检逻辑基于
raft.electionTimeoutMs × 2窗口内未收到有效 AppendEntries 的 peer 列表,触发主动 DNS SRV 查询与 gRPC 连接探测。
时序对齐效果对比
| 检测方式 | 时间误差范围 | 是否受CFS调度影响 | 适用场景 |
|---|---|---|---|
| 用户态 clock_gettime | ±1.2μs | 是 | 常规健康检查 |
bpf_ktime_get_ns() |
±86ns | 否 | Leader故障瞬态判定 |
graph TD
A[Leader心跳中断] --> B{follower检测tick缺失}
B -->|≥2×electionTimeout| C[读取bpf_map中各peer最近AppendEntries时间]
C --> D[按bpf_ktime_get_ns对齐后排序]
D --> E[发起gRPC Probe+SRV重解析]
4.4 生产就绪增强:指标上报OpenTelemetry、告警抑制与恢复确认闭环
OpenTelemetry 指标采集配置
通过 otelcol-contrib 部署轻量采集器,对接应用暴露的 /metrics 端点:
receivers:
prometheus:
config:
scrape_configs:
- job_name: 'app'
static_configs:
- targets: ['localhost:8080']
该配置启用 Prometheus 兼容抓取,自动注入 service.name 标签并关联资源属性(如 host.name, k8s.pod.name),为后续维度下钻提供基础。
告警抑制与恢复闭环机制
- 抑制规则基于语义标签(
alertgroup=database,severity=critical)动态匹配; - 恢复确认通过 Webhook 回调
/api/v1/ack?alert_id=xxx&operator=devops触发状态归档与工单关闭。
| 阶段 | 触发条件 | 动作 |
|---|---|---|
| 抑制生效 | 同组连续3次失败 | 暂停邮件/短信通道 |
| 恢复确认 | HTTP 200 + status=resolved |
更新 CMDB 状态并标记 SLA |
自动化闭环流程
graph TD
A[指标异常] --> B{是否满足抑制策略?}
B -->|是| C[静默告警]
B -->|否| D[推送至 Alertmanager]
D --> E[Webhook 调用确认接口]
E --> F[更新事件状态+记录 operator]
第五章:总结与展望
技术栈演进的实际影响
在某大型电商平台的微服务重构项目中,团队将原有单体架构迁移至基于 Kubernetes 的云原生体系。迁移后,平均部署耗时从 47 分钟缩短至 92 秒,CI/CD 流水线失败率下降 63%。关键变化在于:
- 使用 Argo CD 实现 GitOps 自动同步,配置变更通过 PR 审批后 12 秒内生效;
- Prometheus + Grafana 告警响应时间从平均 18 分钟压缩至 47 秒;
- Istio 服务网格使跨语言调用错误率降低 41%,尤其在 Java 与 Go 混合服务链路中表现显著。
生产环境可观测性落地细节
以下为某金融风控系统在生产集群中采集的真实指标对比(单位:毫秒):
| 组件 | 迁移前 P95 延迟 | 迁移后 P95 延迟 | 下降幅度 |
|---|---|---|---|
| 用户认证服务 | 382 | 116 | 69.6% |
| 实时评分引擎 | 1,240 | 298 | 76.0% |
| 黑名单同步任务 | 8,910 | 1,420 | 84.1% |
该成果依赖于 OpenTelemetry SDK 的深度埋点改造——在 Spring Boot 应用中注入自定义 SpanProcessor,捕获数据库连接池等待、Redis Pipeline 超时等 17 类业务感知延迟节点。
工程效能提升的量化证据
某政务云平台采用 Terraform 模块化管理 23 个地市集群,通过统一模块版本控制与 CI 验证机制,基础设施即代码(IaC)变更平均审核时长从 3.2 小时降至 11 分钟。关键实践包括:
- 每次 PR 触发
terraform validate+tflint+ 自定义 Python 脚本校验合规策略(如禁止明文密钥、强制启用加密); - 使用
terratest编写 86 个端到端测试用例,覆盖 VPC 网络连通性、安全组规则冲突检测等场景; - 所有模块发布均生成 SBOM(软件物料清单),集成到 Nexus IQ 实现许可证风险自动拦截。
# 生产环境灰度发布检查脚本核心逻辑(已上线运行 14 个月)
if ! kubectl wait --for=condition=available deploy/${APP} --timeout=120s; then
echo "Deployment failed: ${APP}" | slack-alert --channel "#prod-alerts"
kubectl rollout undo deployment/${APP}
exit 1
fi
多云协同的实战挑战
某跨国制造企业同时使用 AWS(亚太)、Azure(欧洲)、阿里云(中国)三套基础设施,通过 Crossplane 构建统一资源编排层。实际运行中发现:
- Azure 的 LoadBalancer 类型需额外配置
service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: "/healthz"才能与 Istio IngressGateway 正常联动; - 阿里云 ACK 集群的 NodePort 范围默认为 30000–32767,而 AWS EKS 允许扩展至 30000–65535,导致跨云 Helm Chart 需动态注入
--set service.nodePort=32000参数; - 三云日志格式不一致问题通过 Fluent Bit 的
lua插件统一转换,共维护 23 个字段映射规则。
graph LR
A[Git 仓库] --> B(Terraform Cloud)
B --> C{多云目标}
C --> D[AWS EKS]
C --> E[Azure AKS]
C --> F[Aliyun ACK]
D --> G[Argo Rollouts]
E --> G
F --> G
G --> H[(Prometheus Alertmanager)] 