Azure 成本优化实战:基于 autoskills azure-cost 技能的工作流完整指南
Azure 成本优化实战:基于 autoskills azure-cost 技能的工作流完整指南
本文围绕 autoskills 仓库中 azure-cost 技能的 成本优化工作流 展开,系统讲解在 Azure 订阅中"找浪费、清孤儿资源、做缩容(rightsizing)"的标准化操作路径:从前置条件校验、专项分析(Redis/AKS)、azqr 扫描、资源发现、真实成本查询、定价验证、利用率指标采集,到最终生成可审计的优化报告。读完本文,你将掌握一套可复制的端到端成本优化方案,并理解每一步背后的 API 调用细节与限流等约束。
工作流定位与核心原则
成本优化工作流属于 azure-cost 技能(见 SKILL.md)的三大工作流之一。该技能在用户表达"optimize costs / reduce spending / find cost savings / orphaned resources / rightsize VMs / reduce waste"等意图时被激活,其路由表将"降低成本、找浪费"这类请求明确指向本文所讲的工作流。
执行本工作流必须遵守以下核心原则(来自 SKILL.md 的 Best Practices):
- 先查真实成本,绝不估算:所有结论必须基于 Cost Management API 返回的实际数据(ACTUAL DATA),禁止凭空假设;
- 账单与建议必须同时呈现:任何优化建议都要搭配总账单和成本分解(来自 Cost Query Workflow)一起展示,否则读者无法判断优化的优先级和收益;
- 定价必须来自官方来源:并充分考虑免费额度(free tier);
- 成本查询用 REST API:
az rest比az costmanagement query更可靠; - 留存审计轨迹:保存所有查询与响应;
- 不执行破坏性操作:删除资源前必须获得明确批准,提供 dry-run 命令与回滚方案。
Step 0:校验前置条件
开始优化前,先确认工具链与权限就绪。
必需工具:
- Azure CLI 已安装并完成认证(
az login); - Azure CLI 扩展:
costmanagement、resource-graph; - Azure Quick Review(azqr)已安装,安装与用法详见 azure-quick-review.md。
必需权限:
- Cost Management Reader 角色;
- Monitoring Reader 角色;
- 订阅/资源组上的 Reader 角色。
验证命令:
az --version
az account show
az extension show --name costmanagement
azqr version
az account show 同时用于确认当前登录的订阅上下文,后续所有 scope 拼接都依赖它。
Step 1:加载成本优化最佳实践
在动手扫描之前,先通过 MCP 工具加载官方最佳实践,为查询设计和建议生成提供依据:
azure__get_azure_bestpractices({
intent: "Get cost optimization best practices",
command: "get_bestpractices",
parameters: { resource: "cost-optimization", action: "all" }
})
该工具在 SKILL.md 的 MCP 工具表中被定义为 azure__get_azure_bestpractices,参数为 intent(优化上下文),作用是"用成本管理最佳实践指导查询设计"。技能整体推荐 优先使用 MCP 工具而非直接 CLI,仅当 MCP 无法覆盖特定操作时才退回 az rest、az monitor metrics list 等 CLI 命令。
Step 1.5 / 1.6:Redis 专项成本优化(条件分支)
如果用户明确提到 "Redis"、"Azure Cache for Redis" 或 "Azure Managed Redis",且关注点是 Redis 资源本身的优化而非整个订阅分析,则切换到专项参考文档:
参考:Azure Redis Cost Optimization
随后需要与用户确认分析范围,等待用户回复后再继续:
- 特定订阅 ID(Specific Subscription ID)
- 订阅名称(Subscription Name)
- 订阅前缀(Subscription Prefix,例如 "CacheTeam")
- 我的所有订阅(All My Subscriptions)
- 租户级(Tenant-wide)
注意:若是一般性的订阅级优化,跳过此分支直接进入 Step 2;只有 Redis 专属分析才走该参考文档。
Step 1.7 / 1.8:AKS 专项成本优化(条件分支)
当用户提到 "AKS"、"Kubernetes"、"cluster"、"node pool"、"pod"、"kubectl",或希望启用 AKS 成本分析插件、按命名空间查看成本、报告成本突增/集群利用率异常/预算告警时,进入 AKS 专项分析。
工具选择:
- 优先使用 MCP:
azure__aks用于 AKS 操作(列集群、获取节点池、检查配置),元数据更丰富,且与本仓库 AKS 技能惯例一致; - 退回 CLI:仅当 MCP 无法覆盖特定操作时,使用
az aks和kubectl。
按需加载的参考文件(只加载当前请求需要的部分):
- Cost Analysis Add-on —— 启用命名空间级成本可见性;
- Anomaly Investigation —— 处理成本突增、扩缩容事件、预算告警。
AKS 专项分析同样需要先与用户确认分析范围并等待回复:
- 特定集群名称 —— 分析单个 AKS 集群
- 资源组 —— 分析资源组内所有集群
- 订阅 ID —— 分析订阅内所有集群
- 我的所有集群 —— 扫描所有可访问订阅下的集群
注意:一般性订阅级优化(包括 AKS 所在资源组)直接进入 Step 2;AKS 聚焦分析按上述参考文档执行。
AKS 成本分析插件启用要点(来自 azure-aks-cost-addon.md)
启用前先检查状态与集群层级:
# 检查插件是否已启用
az aks show \
--name "<cluster-name>" --resource-group "<resource-group>" \
--query "addonProfiles.costAnalysis" -o json
# 检查集群层级(插件要求 Standard 或 Premium)
az aks show \
--name "<cluster-name>" --resource-group "<resource-group>" \
--query "{tier:sku.tier, name:name}" -o table
Standard/Premium 集群直接启用:
az aks update \
--name "<cluster-name>" --resource-group "<resource-group>" \
--enable-cost-analysis
若集群处于 Free 层级,必须先警告用户:从 Free 升级到 Standard 会引入持续的集群管理费用,需通过官方 AKS 定价页或本技能的定价验证步骤确认当前费用,获得用户明确批准后再执行:
az aks update \
--name "<cluster-name>" --resource-group "<resource-group>" \
--tier standard
az aks update \
--name "<cluster-name>" --resource-group "<resource-group>" \
--enable-cost-analysis
启用后,命名空间级成本数据可从 Azure Portal 的 AKS 集群 → Cost Analysis 面板查看,也可在 Azure Cost Management 中按集群资源 ID + kubernetes namespace 维度过滤。插件本身启用风险低,但 Free → Standard 升级有成本,务必先确认。
Step 2:运行 Azure Quick Review(azqr)扫描
azqr 用于发现孤儿资源,是立即可兑现的成本节约来源。执行方式见 azure-quick-review.md:
azure__extension_azqr
subscription: "<SUBSCRIPTION_ID>"
resource-group: "<RESOURCE_GROUP>" # optional
重点排查对象:
- 孤儿资源:未挂载的磁盘(unattached disks)、未使用的 NIC、空闲的 NAT 网关;
- 过度配置:过长的保留周期、过大的 SKU;
- 缺失成本标签(missing cost tags)。
用 filters.yaml 聚焦成本扫描
为保证扫描聚焦于成本主题,可创建 filters.yaml:
includeSections:
- Costs
- Advisor
- Inventory
- Orphaned
excludeSections:
- Recommendations
- AzurePolicy
- DefenderRecommendations
也可以直接走 CLI(MCP 不可用时):
azqr scan --subscription "<SUBSCRIPTION_ID>" --resource-group "<RESOURCE_GROUP>" --filters ./filters.yaml --output json
输出管理约定:
- 创建
output/文件夹(mkdir output,若不存在); - 将报告保存为
output/azqr_report_<YYYYMMDD_HHMMSS>.json; - 扫描完成后删除临时
filters.yaml。
扫描产生的 JSON 报告按影响级别(High/Medium/Low)分类给出建议,包含:孤儿资源(NIC、磁盘、IP)、Azure Advisor 成本建议、资源清单、按资源的成本分解。需要强调的是:azqr 提供的是定性治理建议,任何发现都必须先用真实成本数据验证后再做变更,且工具要求订阅/资源组上的 Reader 角色。
Step 3:发现资源
使用 Azure Resource Graph(ARG)实现跨订阅的高效资源发现,孤儿资源检测模式见 azure-resource-graph.md。
az account show
az resource list --subscription "<SUBSCRIPTION_ID>" --resource-group "<RESOURCE_GROUP>"
ARG 常用查询模式
ARG 通过 az graph query 以 KQL 执行(前置条件:az extension add --name resource-graph)。也可用 MCP 工具 azure__extension_cli_generate 生成命令:
azure__extension_cli_generate
intent: "query Azure Resource Graph to <describe what you want to find>"
cli-type: "az"
核心数据表:Resources(所有 ARM 资源)、ResourceContainers(订阅/资源组/管理组)、AdvisorResources(成本与性能建议)。
查找孤儿(未挂载)托管磁盘:
Resources
| where type =~ 'microsoft.compute/disks'
| where isempty(managedBy)
| project name, resourceGroup, location, diskSizeGb=properties.diskSizeGB, sku=sku.name
查找未关联公网 IP:
Resources
| where type =~ 'microsoft.network/publicipaddresses'
| where isempty(properties.ipConfiguration)
| project name, resourceGroup, location, sku=sku.name
查找孤儿网络接口:
Resources
| where type =~ 'microsoft.network/networkinterfaces'
| where isempty(properties.virtualMachine)
| project name, resourceGroup, location
按 SKU/层级统计资源数(发现过大 SKU):
Resources
| where isnotempty(sku.name)
| summarize count() by type, tostring(sku.name)
| order by count_ desc
成本分配标签覆盖率:
Resources
| extend hasCostCenter = isnotnull(tags['CostCenter'])
| summarize total=count(), tagged=countif(hasCostCenter) by type
| extend coverage=round(100.0 * tagged / total, 1)
| order by total desc
空闲负载均衡器(无后端池):
Resources
| where type =~ 'microsoft.network/loadbalancers'
| where array_length(properties.backendAddressPools) == 0
| project name, resourceGroup, location, sku=sku.name
获取 Advisor 成本建议:
AdvisorResources
| where properties.category == 'Cost'
| project name, impact=properties.impact, description=properties.shortDescription.solution
实用提示:资源类型默认小写,用 =~ 做大小写不敏感匹配;属性用 properties.fieldName 导航;--first N 限制结果数;--subscriptions 限定订阅范围;孤儿资源的判定结果要与 Cost Management API 的真实成本数据交叉验证后再下结论。
Step 4:查询真实成本
从 Azure Cost Management API 获取最近 30 天的真实成本数据。按照 Cost Query Workflow 的配置创建请求体。
创建 temp/cost-query.json:
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<START_DATE>",
"to": "<END_DATE>"
},
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "Cost",
"function": "Sum"
}
},
"grouping": [
{
"type": "Dimension",
"name": "ResourceId"
}
]
}
}
必做计算:
<START_DATE>= 30 天前的日期,<END_DATE>= 今天,均采用 ISO 8601 格式。
执行并保存结果到 output/cost-query-result<timestamp>.json。所有 Cost Management API 请求必须携带 --headers "ClientType=GitHubCopilotForAzure" 头。
建议:同时运行一个按
ServiceName分组的 cost-by-service 查询,以便在给出优化建议的同时呈现总账单分解。可参考 examples.md。
警告(限流):同一 scope 的顺序查询共享按 scope 的限流(4 次请求/分钟)。收到 429 时,检查所有
x-ms-ratelimit-microsoft.costmanagement-*-retry-after响应头,在最长 retry-after 时间完全过去之前不要继续发送请求。
成本查询请求体解析(来自 request-body-schema.md)
Cost Management Query API 端点为 POST {scope}/providers/Microsoft.CostManagement/query?api-version=2023-11-01。请求体核心字段:
| 字段 | 必填 | 说明 |
|---|---|---|
type |
是 | ActualCost(实际账单含采购)、AmortizedCost(预留/节省计划摊分到使用期)、Usage |
timeframe |
是 | 预定义窗口或 Custom:WeekToDate / MonthToDate / BillingMonthToDate / YearToDate / TheLastWeek / TheLastMonth / TheLastBillingMonth / TheLastYear / TheLast7Days / TheLast3Months / Custom |
timePeriod |
条件 | 仅当 timeframe 为 Custom 时必填,含 from/to ISO 8601 日期 |
dataset.granularity |
是 | None(最长 12 个月)、Daily(最长 31 天)、Monthly(最长 12 个月) |
dataset.aggregation |
是 | 仅支持 Sum 函数,来源列 Cost / PreTaxCost / UsageQuantity,键为输出别名(如 totalCost) |
dataset.grouping |
是 | 最多 2 个维度,type 为 Dimension(内置)或 TagKey(资源标签),不允许重复 |
dataset.filter |
否 | 支持 and/or(至少 2 个子表达式)与 not(恰好 1 个);比较操作符 In/Equal/Contains/LessThan/GreaterThan/NotEqual;目标类型 dimensions 或 tags |
dataset.sorting |
否 | direction(Ascending/Descending)+ name(必须是聚合或分组中的列) |
执行成本查询(REST 方式,比 az costmanagement query 更可靠):
# 创建 temp 文件夹
New-Item -ItemType Directory -Path "temp" -Force
# 调用 REST API
az rest --method post `
--url "<scope>/providers/Microsoft.CostManagement/query?api-version=2023-11-01" `
--headers "ClientType=GitHubCopilotForAzure" `
--body '@temp/cost-query.json'
scope 路径可参考 SKILL.md 中的 Scope Reference 表:订阅 /subscriptions/<subscription-id>、资源组 /subscriptions/<subscription-id>/resourceGroups/<resource-group-name>、管理组、账单账户、账单配置文件等。
关键护栏(来自 guardrails.md)
| 规则 | 约束 |
|---|---|
| Daily 粒度最大范围 | 31 天 |
| Monthly/None 粒度最大范围 | 12 个月 |
| API 绝对最大范围 | 37 个月(超出返回 HTTP 400) |
| 最大 GroupBy 维度数 | 2 |
| ResourceId 分组作用域 | 仅订阅与资源组支持,更高 scope 不支持 |
| 每页最大行数 | 5,000(默认 1,000) |
| Custom 时间窗 | 必须提供 timePeriod 的 from/to |
and/or 过滤器 |
必须至少 2 个表达式 |
其他值得注意的行为:未指定时间窗时默认"本月第一天 → 今天";from 晚于 to 时系统会静默交换;两个日期都在未来时整个区间平移至去年同期,仅 to 在未来时 to 调整为今天——这些静默调整务必警惕;最早允许的 from 日期为 2014-05-01。EA 账号在管理组作用域按 SubscriptionName 过滤必须同时附带 SubscriptionId 过滤器,否则报错。按 ResourceId 分组在高 scope(计费账户、管理组)不可用,此时应改用 ServiceName、SubscriptionName 等维度,或先收窄到具体订阅。
分页:响应中若出现 nextLink,需继续请求后续页面直到其为 null。
错误处理速查(来自 workflow.md)
| HTTP 状态 | 错误 | 处理建议 |
|---|---|---|
| 400 | 无效请求体 | 检查 schema、日期范围与维度兼容性 |
| 401 | 未授权 | 重新认证(az login) |
| 403 | 禁止访问 | 确认 scope 上有 Cost Management Reader 角色 |
| 404 | 未找到 scope | 核对 scope URL 与资源 ID |
| 429 | 请求过多 | 检查所有 x-ms-ratelimit-microsoft.costmanagement-*-retry-after 头(qpu/entity/tenant),等待最长值,最多重试 3 次 |
| 503 | 服务不可用 | 查看 Azure 状态页 |
Step 5:验证定价
使用 fetch_webpage 从官方 Azure 定价页面获取当前定价:
重点验证的服务:
- Container Apps:
https://azure.microsoft.com/pricing/details/container-apps/ - 虚拟机:
https://azure.microsoft.com/pricing/details/virtual-machines/ - App Service:
https://azure.microsoft.com/pricing/details/app-service/ - Log Analytics:
https://azure.microsoft.com/pricing/details/monitor/
重要:检查免费额度(free tier)——许多 Azure 服务有慷慨的免费限制,忽略它们会导致建议失真。
在 SKILL.md 的数据分类中,来自官方定价页的数据被标记为 VALIDATED PRICING,与 ACTUAL DATA(Cost Management API)、ACTUAL METRICS(Azure Monitor)一起构成计算 ESTIMATED SAVINGS 的三大依据——三者缺一不可。
Step 6:采集利用率指标
查询 Azure Monitor 获取最近 14 天的利用率数据,用于支撑缩容(rightsizing)建议:
$startTime = (Get-Date).AddDays(-14).ToString("yyyy-MM-ddTHH:mm:ssZ")
$endTime = Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ"
# VM CPU 利用率
az monitor metrics list `
--resource "<RESOURCE_ID>" `
--metric "Percentage CPU" `
--interval PT1H `
--aggregation Average `
--start-time $startTime `
--end-time $endTime
典型的缩容决策路径:先拿到 30 天真实成本(Step 4),再验证官方定价(Step 5),最后用 14 天利用率指标判断资源是否长期低水位运行。若资源成本低于 10 美元/月(SKILL.md 建议),应把重点放在运维改进而非财务节约上。
AKS 异常调查的指标配合(来自 azure-aks-anomalies.md)
当用户报告成本/利用率突增时,按时间窗关联三类证据:
- 成本数据:Daily 粒度、按
ResourceId分组的ActualCost查询; - 节点数与扩缩容事件:先
az monitor metrics list-definitions确认可用指标,再拉取节点数指标;用kubectl get events --all-namespaces --field-selector reason=SuccessfulRescale --sort-by='.lastTimestamp'查看 HPA 扩缩容事件; - Top 消费者:
kubectl top nodes与kubectl top pods --all-namespaces --sort-by=cpu。
常见诱因对照:非高峰节点数激增 → HPA/VPA 配置问题(检查 HPA min replicas);单 Pod 占满 CPU → 内存泄漏或失控进程(查日志、加资源限制);某天成本突增 → 批处理任务意外运行(检查 CronJob 调度);节点数居高不下 → CAS 缩容受阻(检查 PodDisruptionBudgets、系统 Pod);某命名空间成本突增 → 新部署未设资源限制(补 requests/limits)。
调查完成后可创建月度预算告警(80% 阈值):
az consumption budget create \
--budget-name "aks-monthly-budget" \
--amount <budget-amount> \
--time-grain Monthly \
--start-date "<YYYY-MM-01>" \
--end-date "<YYYY-MM-01>" \
--resource-group "<resource-group>" \
--threshold 80 \
--contact-emails "<contact-email>"
Step 7:生成优化报告
生成报告到 output/costoptimizereport<YYYYMMDD_HHMMSS>.md,完整模板见 report-template.md。报告必须包含:执行摘要(executive summary)、按服务的成本分解、免费额度分析、孤儿资源清单、按优先级排序的优化建议、实施命令;同时将成本查询结果保存为 output/cost-query-result<YYYYMMDD_HHMMSS>.json 作为审计轨迹,最后清理临时文件。
报告结构模板:
# Azure Cost Optimization Report
**Generated**: <timestamp>
## Executive Summary
- Total Monthly Cost: $X (ACTUAL DATA from Cost Management API)
- Top Cost Drivers: [List top 3 services with costs]
- Potential Savings: $Y/month
## Cost Breakdown by Service
| Service | Cost (USD) | % of Total |
|---------|-----------|------------|
| ... | ... | ... |
| **Total** | **$X** | **100%** |
## Free Tier Analysis
[Resources operating within free tiers]
## Orphaned Resources (Immediate Savings)
[From azqr — resources that can be deleted immediately]
## Optimization Recommendations
### Priority 1: High Impact, Low Risk
- ACTUAL cost: $X/month
- ESTIMATED savings: $Y/month
- Commands to execute
### Priority 2: Medium Impact, Medium Risk
- ACTUAL baseline, ACTUAL metrics, VALIDATED pricing
- ESTIMATED savings with commands
### Priority 3: Long-term Optimization
[Reserved Instances, Storage tiering]
## Total Estimated Savings
- Monthly: $X | Annual: $Y
## Implementation Commands
[Safe commands with approval warnings]
## Validation Appendix
- Cost Query Results: `output/cost-query-result<timestamp>.json`
- Pricing Sources: [Links]
报告要点:
- 优先级划分:Priority 1(高影响、低风险,如删除孤儿资源)必须给出真实成本与估算节约额;Priority 2 必须同时具备 ACTUAL baseline、ACTUAL metrics、VALIDATED pricing 三项证据;Priority 3 面向长期优化(预留实例、存储分层);
- Portal 链接格式:所有资源附带 Azure Portal 链接,统一使用:
https://portal.azure.com/#@<TENANT_ID>/resource/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/<RESOURCE_PROVIDER>/<RESOURCE_TYPE>/<RESOURCE_NAME>/overview - 审计轨迹:成本查询结果 JSON 必须归档以便复现;
- 清理:报告生成后删除临时文件:
Remove-Item -Path "temp" -Recurse -Force -ErrorAction SilentlyContinue
常见坑与安全要求
来自 SKILL.md 的实践约束,同样适用于本工作流:
常见坑:
- 凭空假设成本:必须查询 Cost Management API 的真实数据;
- 忽略免费额度:许多服务有可观免费额度;
- 日期范围错误:成本用 30 天、利用率用 14 天;
- 不展示账单:优化建议必须搭配成本分解呈现;
- 成本查询失败:用
az rest+ JSON 请求体,不要用az costmanagement query。
安全要求:
- 删除资源前必须获得批准;
- 变更先在非生产环境测试;
- 提供 dry-run 命令用于验证;
- 附上回滚流程。
总结:一条完整的优化执行链
将上述步骤串起来,一次完整的 Azure 成本优化会话遵循:校验环境(Step 0)→ 加载最佳实践(Step 1)→ 判断是否走 Redis/AKS 专项(Step 1.5–1.8)→ azqr 扫描找孤儿资源(Step 2)→ ARG 跨订阅发现资源(Step 3)→ 拉取 30 天真实成本(Step 4)→ 验证官方定价与免费额度(Step 5)→ 采集 14 天利用率指标(Step 6)→ 生成带审计轨迹的优化报告(Step 7)。每一步的证据边界都清晰可查:真实成本来自 Cost Management API、利用率来自 Azure Monitor、定价来自官方页面,三者交叉验证后才给出可执行的节约建议——这正是 azure-cost 技能在 SKILL.md 中反复强调的数据分类原则在实战中的落地。