ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从Inform到Operate:基于Mission调度的云成本巡检闭环实践

从Inform到Operate:基于Mission调度的云成本巡检闭环实践 大部分团队的云成本巡检停留在“Inform”这一层系统每天扫一遍资源发现几个闲置实例弹一条告警然后……就没有然后了。告警发出去之后真正去处理的可能不到三成剩下的七成要么没人认领要么下次巡检时又被筛出来周而复始。我今天想聊的这套机制目标是把巡检从“通知”推到“运营”让告警直接驱动资源回收、降配、退订这些动作而把人工留给真正需要判断的场景。整个闭环的编排由一个自研的 Mission 调度模块负责——它就是整个巡检机制的“发令枪”和“监工”。1. 巡检这件事为什么容易“只报告不解决”成本巡检在很多团队里是个“有也行没有也行”的活。为什么会这样因为巡检最原始的形态就是写几个脚本调用云厂商的查询接口把数据拉下来算一遍然后输出一张 Excel 或者推送几条消息。这种模式刚上线时效果很明显——第一次扫出几十台闲置机器老板觉得这系统真有价值。但运行几个月后大家就开始麻木了。1.1 成本失控的几个典型场景我先列举我在实际巡检中最常遇到的几类问题这些基本覆盖了大部分云上成本浪费。第一类是纯粹的闲置资源。开发环境一台 32C64G 的机器连续 15 天 CPU 使用率没超过 3%内存占用不到 10%。这种机器往往是人走了流程没走完项目下线了但机器没同步清理。更典型的是 NAT 网关、负载均衡这类附属资源它们不直接产生计算账单但每个月光服务费加流量费累计起来非常可观。第二类是低利用率资源。不能说完全没用但配置远高于实际需求。比如一个内网管理系统两台 8C16G 的实例做双机日常 QPS 不到 20CPU 长期在 5% 到 8% 之间徘徊。这类资源不能直接释放但降配甚至合并到一台机器是完全可行的。第三类是规格膨胀。业务初期买了 64G 内存的机器后来业务量反而下降了但没人去降配。还有一类常见的是给存储卷买了远超实际使用的容量比如挂载了一个 500G 的高效云盘实际只用了 30G。第四类是流量与带宽异常。带宽按固定规格购买但实际峰值一直用不满或者某个实例出方向流量突然翻倍导致流量费暴涨。前者是浪费后者是异常都需要巡检能分辨出来。这几类场景有一个共同点用一句话就能判断要做什么资源能不能释放、能不能降配、是不是异常。但就是没有一套机制把“判断”变为“执行”。1.2 Inform 阶段的三个死穴“查出来有什么用呢”——这是我经常听到的一句话。之前纯 Inform 的巡检机制也就是只管发告警、出报表的模式至少要回答三个问题数据准不准谁来处置处置结果有没有反馈第一个死穴是告警疲劳。巡检规则一多每天产生的告警条目动辄几十条。人的注意力是有限的连续看一周后看到一个“存在闲置资源”的卡片完全无感。最后的结果就是告警被群消息折叠出现的频率越高被处理的概率越低。第二个死穴是没有处置闭环。告警里写了“某实例 CPU 使用率低于 5%请检查是否需要释放”但没有任何后续动作的追踪。这个实例是已经被处理了还是仍然闲置报告里看不出来下次巡检还会再报一次。等于把一个判断题做成了一道永远重复的选择题。第三个死穴是策略无法沉淀。哪些规则精确、哪些规则误报多依赖人的记忆。巡检系统只负责算不负责“改进”。比如你根据几次误报调整了阈值但调完没有记录过几个月同事接手又调回去了。这三个死穴的根源在于巡检链路只覆盖到“消息通知”这一环后面从决策到执行都是断的。要解决它不能只靠加规则而是要把调度和执行也纳入这条链路。这就是我下文要说的 Mission 调度要干的事。2. 机制设计的核心思路从 Inform 到 Operate我理解很多团队把成本巡检做成了“报表工具”而一个好的巡检机制本质应该是一个“运营工具”。差别在哪报表工具的输出是信息运营工具的输出是动作。所以在设计这套机制时我给自己定了一条原则每一条巡检洞察都必须映射到一个明确的处置动作上。这条原则直接决定了后面所有的架构决策。2.1 一条巡检结果必须对应一个处置动作我按照这个思路重新梳理了巡检结果的分类。拿一台低利用率实例来说传统巡检输出的是“实例 i-xxxx 过去 7 天 CPU 平均 3%请关注。”新机制输出的应该是“实例 i-xxxx 过去 7 天 CPU 平均 3%满足‘低利用率实例’规则建议降配到 4C8G预计每月节省 420 元操作安全等级中可自动执行。”同样的数据源输出格式一改整个处置链路就完全不一样了。要让每条巡检结果都能映射到动作需要把“分析”和“决策”做两层拆分。分析层只判断事实这个实例利用率是多少是否符合某个规则的特征。决策层则负责判断怎么做能不能动、怎么动、谁来批准、何时执行。这里有个很容易踩的坑把决策逻辑和巡检逻辑写在一起。比如在巡检脚本里直接写上“如果 CPU 小于 5% 就调用释放接口”这看起来很高效但实际上非常危险。因为巡检脚本每天都在跑它没有处理各种边界情况比如这台机器是不是被某个临时任务占用着。我的建议始终是巡检只负责“标记”决策层负责“处置”两层用数据表或消息队列解耦。2.2 Mission 调度的定位与职责在明确了巡检和处置分开之后剩下的问题是谁来把“标记”变成“处置”谁来保证整个环节按时、按序、可重试这就是 Mission 调度的位置。Mission 在整套机制里承担三个职责第一定时触发巡检任务。它按照配置好的 cron 表达式到点把采集任务、分析任务、决策任务依次调度起来。这就是最基础的“任务编排”。第二管理任务间的依赖关系。采集成功之后才能做分析分析有结果之后决策层才能拿到数据。Mission 通过任务状态和依赖规则保证它们按正确的顺序执行而不是靠一个脚本从头跑到尾。第三提供执行能力和审计能力。巡检分析完如果命中可自动执行的规则Mission 负责调用对应的云 API 来执行操作并且把操作记录落库形成一条完整的审计链路。我会在第四节详细说 Mission 的实现细节这里先强调它的边界它不管业务逻辑只负责“调度”和“执行”业务逻辑全部放在巡检规则和处置动作的插件化配置里。2.3 闭环流程的完整链路从整体上看这套机制运行起来之后是这样的每天凌晨 2 点Mission 里的全量巡检任务被触发。先把云上的实例、存储卷、带宽、负载均衡、NAT 网关等资源清单拉一遍同时拉取前一天的账单明细。数据落库后分析任务开始跑把每条资源喂给预定义的规则集输出命中结果。那些命中“可自动执行”规则的资源被决策层放入执行队列按预设的执行策略进行操作。执行完成后验证任务再跑一次确认操作生效比如实例确实降配了、快照确实删除了。最后生成一份巡检日报里面不只写“发现了什么问题”还写“自动处理了什么”“节省了多少钱”“哪些需要人工确认”。日报推送到 IM 群的时候大家看到的是结果而不是一串等待处理的待办。这个设计上的转变直接改变了成本巡检在团队里的定位——它从“找问题的”变成了“解决问题的”。这个闭环的价值在于巡检不再是一次性动作而是一个可持续运转的机制。只要调度器在跑、规则在更新成本的浪费就会持续被识别、被消除、被验证。3. 巡检规则与成本分析的关键细节闭环设计好之后下一个问题就是规则怎么定才算准我自己写规则踩过很多坑这里把关键细节展开说说。3.1 数据源与采集口径的选择成本巡检的数据源一般有三类。第一类是资源清单接口返回当前账号下有哪些实例、存储卷、IP、负载均衡等以及它们的状态和规格。第二类是监控指标接口提供 CPU、内存、流量、IOPS 这些时序数据。第三类是账单明细包含每小时或每天的费用明细以及产品类型、地域、标签等维度信息。这三类数据必须交叉使用不能只看一类。比如只看资源清单你会发现一堆“看起来处于运行中”的实例但不知道它们实际负载情况只看监控指标又可能漏掉那些没有监控数据但仍在计费的附属资源。最典型的坑是判断一个实例是否闲置如果只凭 CPU 使用率很容易漏掉那些 CPU 本来就没压力的内存型应用所以需要同时看内存、IOPS、网络流量等多维指标。还有一个关键点是采集口径的时间对齐。云厂商的账单数据普遍存在 T1 延迟甚至个别产品 T2。如果巡检任务在凌晨 0 点跑拉到的可能是前天甚至更早的数据如果白天跑当天账又没出。我实际用的方案是凌晨 2 点跑一次全量中午 12 点跑一次增量增量主要用于覆盖前一天账单的修正数据。这样可以有效避免因为数据延迟导致的漏检。3.2 常用的巡检规则与阈值设计规则是巡检机制的“大脑”规则准不准直接决定整个系统可信不可信。下面是我目前在用的几套规则参数都是经过几轮调优后的结果规则判断条件建议动作安全等级闲置弹性 IP公网出流量 7 天为 0且无绑定解绑并释放低风险可自动执行低利用率实例CPU 均值 5% 且内存 10%持续 7 天降配或迁移中风险需谨慎自动执行过期快照创建时间超过 30 天且不是最近 3 份之一删除低风险可自动执行空负载均衡后端服务器数为 0持续 7 天释放低风险可自动执行带宽超配近 30 天出方向峰值 购买带宽 20%下调带宽档位低风险可自动执行跨地域流量激增单日流量费环比增长 100%告警并生成分析报告高风险仅通知关于阈值有几个经验可以分享。判断“低利用率”如果用 7 天 CPU 均值小于 5%在业务稳定的系统上很准但对深夜无流量的国内业务会误报所以要加“工作时间段窗口”的概念只统计 9 点到 18 点的均值。判断快照是否该删别只看年龄还要看它是不是某个自定义镜像或系统镜像的依赖最稳妥的做法是先标记“候选删除”保留 7 天冷静期再真正删除。另一点很重要规则的输出不能只给“是/否”还要带上支撑数据。命中“低利用率实例”规则时报告中要能直接看到近 7 天的 CPU 趋势、内存趋势、出方向流量趋势。这样无论是人工复核还是自动执行的审计都有据可查。3.3 误报治理与例外清单没有规则是一上来就准的误报是常态关键是建立误报治理的方法。我在实践中主要做了三件事。第一建立例外清单。某些业务季末月结、或者固定的数据批处理任务会在月末或每周固定时间打满 CPU这类资源要加入例外清单规则直接跳过。清单要支持按标签匹配、按实例 ID 匹配也要支持有效期。比如“该实例在营销活动期间不巡检”有效期 15 天到期自动失效。第二告警收敛。同一资源连续多天命中同一条规则时只在第 1 天和第 7 天告警中间的天数静默。避免“狼来了”效应。第三建立反馈通道。巡检报告里每条结果都支持标记“误报”并填写原因。这些反馈会回写到一个样本表定期用来调优规则阈值。我把这个表叫“规则评测样本集”每个季度跑一次看哪些规则精确率低于 80%就针对性调整或下线。误报治理这件事看起来不起眼实际上决定了巡检机制能不能长期运营下去。原因很简单用户对系统的信任是被一次次误报消耗掉的。一旦群里的告警被集体屏蔽再好的技术方案都是白搭。4. Mission 调度的实现细节现在讲回 Mission 调度本身。这是整套机制里技术含量最高的部分也是保证“可持续运行”的底座。4.1 任务模型设计我把一个完整巡检流程拆成三类子任务采集任务、分析任务、执行任务。这三类统称为一个 Mission。Mission 定义里面包含的信息有任务的唯一标识、执行计划cron 表达式、依赖关系、超时时间、重试策略、执行时使用的凭证标识。下面是一个 Mission 定义的简化示例我习惯用 YAML 来管理mission: name: daily-cost-inspect schedule: 0 2 * * * # 每天凌晨 2 点 timeout: 3600 retry: max_attempts: 3 backoff: 5m,30m,2h # 指数退避 steps: - name: collect-resources type: collect depends_on: [] - name: analyze-cost type: analyze depends_on: [collect-resources] - name: decide-operations type: decide depends_on: [analyze-cost] - name: execute-operations type: operate depends_on: [decide-operations]这里每个 step 之间通过 depends_on 建立依赖Mission 控制器只会把“依赖已满足”的任务投递到执行队列。从这套模型里可以看出来Mission 没有把逻辑写死在代码里而是通过配置组装流程这也是它能够复用的原因。4.2 调度策略与依赖管理调度策略核心是三件事定时触发、依赖满足与超时处理。定时触发使用 cron 表达式。实际配置中我把任务分为两类全量巡检每天跑一次增量巡检每 4 小时跑一次。全量巡检负责把资源清单和账单重新梳理一遍增量巡检只关注是否有新产生的资源以及有没有突发的异常指标。全量跑太久会挤占增量任务所以我在调度器里加了任务互斥锁同一类型的 Mission 同时只能有一个实例在执行避免执行重叠。依赖管理上Mission 用“前置依赖成功数”来判断是否放行。比如 analyze-cost 依赖 collect-resources那么只有 collect-resources 标记为 succeeded 后analyze-cost 才会被投递。如果前置任务失败后置任务不会启动整个 Mission 会进入 Retrying 状态按配置的重试策略重跑。这里有个容易被忽略的细节任务超时。云厂商接口偶尔会卡住如果采集任务没有超时控制整个 Mission 会一直卡在 running 状态。我在设计里给每个任务强制加了 timeout默认 30 分钟超时后任务被标记为 failedMission 进入重试流程。实测下来这个机制能挡住绝大多数“莫名卡死”的情况。4.3 幂等控制与状态回溯调度系统做自动执行最怕的是执行动作重复。比如同一个闲置 IP第一次巡检把它解绑了但释放动作因为网络超时报了失败重试时发现这个 IP 已经不存在了再调一次释放接口可能就报错甚至影响其他资源。所以幂等是必须的。我的做法是给每个 Mission 实例生成全局唯一的 run_id这个 run_id 贯穿整个流程。在操作执行前先查审计表如果发现该 run_id 已经执行过同一操作就直接跳过。同时所有操作在真正调用云 API 之前都要先做一次前置确认比如“实例还存在吗”“实例规格是否已经变化”前置确认不通过就不执行这样即使状态信息有延迟也不会做无效操作。状态回溯方面我保留了每个 Mission run 的完整事件日志。从任务启动、采集完成、分析命中、操作执行到执行结果的每一步都写入一张 event_log 表。之后排查问题或者复盘时直接根据 run_id 查整条链路的日志效率非常高。这张表也是我后面做成本节省量统计的基础数据源。4.4 失败重试与告警升级最后说处理失败。调度系统不可能永远成功关键是失败后怎么处理。Mission 里每个任务都有独立的失败处理策略默认重试 3 次使用指数退避5 分钟、30 分钟、2 小时。如果 3 次重试后仍然失败Mission 会被标记为 failed并触发告警升级流程。告警升级的逻辑是这样的先把失败信息推送给 Mission 的负责人如果 2 小时内没有得到确认再推送给上层值班组。这里的关键是“升级”而不是“群发”。刚开始我用的方案是一失败就把告警发到所有相关群结果往往是很多人看到但没人认领。改成单点负责人 超时升级之后处理效率反而高了很多。另外还有一个实操经验重试时一定要区分错误类型。如果是因为凭证过期、权限不足这类配置错误重试再多也没用应该第一时间告警让人介入。如果是因为网络超时、接口限流这类瞬时错误重试才有意义。我在代码里把错误分成了可重试和不可重试两类不可重试的错误直接跳过重试并告警。5. 实操落地部署一套成本巡检与 Mission 调度前面讲的都是设计这一节直接讲怎么落地。我把这套机制跑在一个独立的运维区域里使用容器部署下面按步骤说明。5.1 环境准备与凭证配置我用的部署环境是一台 4C8G 的云服务器组件包括MySQL存储规则、任务状态、审计日志、Redis任务队列、Mission Controller 和 Mission Worker。Controller 负责解析 Mission 定义并管理调度状态Worker 负责真正执行具体的采集、分析、操作任务。两者都可以通过容器镜像部署用 docker compose 起起来比较省事。关键点是云厂商凭证的配置。我强烈建议使用子账号凭证而不是主账号密钥。这个子账号只需要授予巡检所需的只读权限以及指定资源操作的权限。比如要支持自动删除快照那就只授予“删除快照”和“查询快照列表”这几个 Action要支持降配实例只授予“查询实例”和“修改实例规格”。权限边界越窄自动执行的风险越低。还有一个容易忽略的点凭证要加密存储并通过环境变量或密管系统注入到容器。别把凭证明文写在配置文件里更别提交到代码仓库。这块出过不少事故我是深有体会。5.2 巡检规则文件的配置示例规则配置我放在一个 rules.yaml 文件里加载进系统时校验格式。下面是两个规则的示例rules: - name: idle_eip_release description: 闲置弹性 IP 释放 resource_type: eip condition: outbound_bytes_7d: { operator: sum, max: 1024 } binding: { operator: eq, value: false } action: type: release approval: auto cooldown_days: 7 - name: low_utilization_instance description: 低利用率实例降配 resource_type: instance condition: cpu_avg_7d: { operator: lt, value: 5, window: 9-18 } mem_avg_7d: { operator: lt, value: 10, window: 9-18 } running_days: { operator: gte, value: 30 } action: type: resize approval: manual target_spec: auto看上面第二个规则我加了 running_days 大于等于 30 天的条件这是为了过滤掉刚创建不久、还在压测或预热阶段的实例。target_spec 设置为 auto表示让系统根据资源近 30 天的峰值自动推荐一个规格而不是写死某个目标规格。5.3 注册 Mission 并调试调度规则配好后通过管理接口注册 Mission。系统会校验 Mission 定义的格式、规则文件是否存在、子任务依赖是否成环。校验通过后Mission 会出现在调度列表里可以手动触发一次也可以等待 cron 到点触发。初次调试时我一般先手动触发一次并把 execution_mode 设为 dry_run。dry_run 模式下分析照常跑命中照常记录但不会真正执行操作。查看报告确认命中结果符合预期后再把模式切换为 real_execute。这个开关是整套机制的“保险丝”建议一直保留紧急情况下可以一键把所有操作类任务下掉。5.4 自动执行与后端验证当 Mission 运行到 operate 阶段时Worker 会调用云 API 执行操作。每个操作在真正执行前都走一遍前置确认流程查资源状态、查操作是否已在审计表中存在、查规则是否仍然命中。都通过之后才执行。执行完成后验证任务会再次查询资源状态确认操作是否真正生效。比如删除快照操作验证任务会再查一次快照列表确认目标快照已不在列表中。验证结果、操作耗时、失败原因都会写入 event_log。如果验证失败会按照 4.4 节的策略进入重试或告警升级流程。整个系统跑起来之后我每天只需要做一件事花几分钟看巡检日报确认哪些资源被自动处理了哪些需要人工审批。大部分时间日报上的“自动处理”一栏就是当天的全部工作。6. 常见问题与排查实录这部分记录我在实际运行中遇到的最典型问题按问题现象、排查过程、解决方案的方式写。6.1 采集数据延迟导致漏检现象某天巡检报告里前一天新开通的一批实例完全没有出现。排查过程我先查事件日志发现采集任务正常执行返回的资源清单数量也对。再对比云控制台上的实际资源发现新实例是前一天下午创建的而 Mission 在凌晨 2 点拉到的账单和资源数据里部分产品的状态信息还停留在前天的缓存上。解决方案把全量巡检时间从凌晨 0 点调整到凌晨 2 点给数据同步留出足够时间。同时增加 12 点的增量巡检专门覆盖前一天的修正数据。另外在采集任务内部加了一个“数据新鲜度检查”如果拉到的账单数据最终更新时间早于当前时间 12 小时就标记该批次数据为 stale分析任务会绕开这些数据避免基于旧数据做判断。6.2 误报太多导致告警被屏蔽现象上线第一周群里告警非常多很多根本不值得处理。第二周开始群里基本没人讨论告警了。排查过程我逐条看了告警发现大头来自两条规则。一条是低利用率实例把周末没人访问的测试环境实例都标成了低利用率另一条是闲置弹性 IP把那些虽然没绑定但在走“预释放流程”的 IP 都标成了闲置。解决方案低利用率规则增加了工作时间窗口只统计 9 点到 18 点的指标闲置 IP 规则增加了一个 exclude_status 参数跳过处于“预释放”状态的 IP。同期把告警收敛策略打开同一资源同一规则 7 天内只报一次。改完一周后告警量下降了约 70%且每个告警都有人响应。6.3 Mission 重复执行导致操作重复现象某天手动触发了一个维护任务同时又撞上 cron 定时触发结果同一批快照的删除操作被执行了两次。还好删除接口是幂等的第二次调用报了个“目标不存在”没有造成实际损失但审计日志里出现了两条执行记录。排查过程查调度日志发现手动触发时没有检查当前是否有同类型 Mission 实例在运行导致两个实例并发执行了。解决方案在 Mission 控制器里加了互斥锁同一 Mission 名称下同时只允许一个运行中的实例。同时把操作前置确认做实任何操作在执行前都要查一次目标资源是否存在如果资源已经不存在就不再调用云 API只记录一次 skip 事件。从那以后这个坑再也没踩到。6.4 权限不足导致执行失败现象自动降配操作在执行阶段大面积失败错误信息显示“无权限”。排查过程核查后发现凭证本身有权限但 Worker 调用的是另一个项目的接口而该接口要求必须在那个项目下额外绑定权限。也就是说权限不是加在用户上的还要加在项目维度上。解决方案重新梳理了权限模型在云厂商的项目维度上补齐了 Worker 需要的权限策略并为不同项目分别建立了一组凭证。配置落实后我又补了一条规范权限变更时必须执行一遍 Mission 的 dry_run 加单条执行测试验证通过后才能恢复自动执行。最后聊点个人体会。做这套机制最让我意外的不是技术难度而是运营节奏的变化。以前每天的工作是“看告警、转派、催人处理”现在变成了“看报告、审异常、调规则”。省下来的时间可以用来做更重要的预算规划和架构优化。如果你也正被一堆成本告警淹没我的建议是先别急着加告警规则而是想一想这些告警背后的动作是什么谁能执行怎么验证想清楚了把从 Inform 到 Operate 这条链路补上你会很快看到不同。
返回列表