「成本估算与优化」

把成本治理前移到 IaC 阶段:用 Infracost 在 plan 阶段预估费用、在实例规格与存储类型之间做权衡、清理闲置资源、用预算告警与策略护栏兜底,并建立变更的成本影响评审机制。

1. 为什么要在 IaC 阶段管成本

一句话总结: 云账单是滞后的,等到月结才发现超支已经无法挽回,而 IaC 是唯一能在「资源创建之前」看到成本影响的位置。

传统成本治理是「事后对账」:月底看账单、发现超支、再去翻谁开了大规格实例。问题在于资源一旦创建就开始计费,追溯成本高、止损慢。

# 账单只能告诉你「花了多少」,不能告诉你「为什么」
aws ce get-cost-and-usage \
  --time-period Start=2026-09-01,End=2026-10-01 \
  --granularity MONTHLY \
  --metrics UnblendedCost

把成本前移到 plan 阶段,逻辑就变成「这个 PR 会让月账单增加多少」。这是 FinOps 里投入产出比最高的一环:

阶段成本可见性干预成本
写配置无最低
plan可预估低
apply 后有真实数据中
月结账单滞后高

一句话:成本治理的最佳时机不是「账单出来之后」,而是「代码评审的时候」——那时候改一行配置的成本最低。

2. Infracost 与 plan 成本预估

一句话总结: Infracost 读取 plan JSON,结合云厂商价格 API 计算出「本次变更的月度成本增量」,直接贴到 PR 评论里。

Infracost 的输入是 terraform plan 的 JSON 输出,输出是人类可读的成本差异:

# 生成 plan 并交给 Infracost 分析,再只看变更增量
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
infracost breakdown --path plan.json
infracost diff --path plan.json
# 典型输出:逐资源列出增量,最后给出月度总增量
# + aws_instance.web      +$146.00/mo
# + aws_db_instance.main  +$312.40/mo
# Monthly cost will increase by $458.40

在 CI 里自动评论到 PR:

# .github/workflows/cost.yml
name: cost-estimate

on:
  pull_request:
    paths: ["**.tf"]

jobs:
  infracost:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}
      - run: |
          terraform init
          terraform plan -out=plan.tfplan
          terraform show -json plan.tfplan > plan.json
      - run: infracost comment github --path plan.json \
          --repo $GITHUB_REPOSITORY \
          --pull-request ${{ github.event.pull_request.number }} \
          --github-token ${{ secrets.GITHUB_TOKEN }} \
          --behavior update

2.1 用策略文件固化成本基线

Infracost 支持策略文件,表达「这个资源每月不得超过 X 美元」这类约束,超标即 CI 失败:

# infracost-policy.yml
version: 0.2

policies:
  - name: 单实例月成本上限
    resource_type: aws_instance
    checks:
      - type: monthly_cost
        max: 200
infracost breakdown --path plan.json --config-file infracost-policy.yml

一句话:Infracost 的价值不在「算得准不准」,而在「让成本在评审时可见」——可见的成本才有机会被讨论。

3. 实例规格与存储权衡

一句话总结: 成本优化的最大杠杆是「选对规格」而非「砍掉资源」,多数团队的浪费来自长期低负载的大规格实例与过高冗余的存储类型。

3.1 实例规格的取舍

场景常见浪费优化方向
Web 前端长期 CPU 低于 10%降规格或改用 Graviton
批处理按峰值选规格改为按需伸缩 + Spot
数据库按峰值选实例读写分离 + 只读副本
缓存内存按峰值预留降一档并观察命中率
# 用变量表达规格,便于统一调整与成本对比
variable "web_instance_type" {
  description = "Web 层实例规格"
  type        = string
  default     = "t3.medium"
}

resource "aws_instance" "web" {
  count         = var.web_instance_count
  instance_type = var.web_instance_type
  ami           = data.aws_ami.ubuntu.id
}
# 用 ARM 架构降本(同规格约便宜 20%)
variable "use_graviton" {
  type    = bool
  default = true
}

locals {
  instance_type = var.use_graviton ? "t4g.medium" : "t3.medium"
}

3.2 存储类型的权衡

# gp3 比 gp2 便宜且性能可调,新项目默认选 gp3
resource "aws_ebs_volume" "data" {
  availability_zone = "us-east-1a"
  size              = 100
  type              = "gp3"    # 而非 gp2

  # 按实际需要配置 IOPS,避免为默认值付溢价
  iops       = 3000
  throughput = 125
}

# 冷数据用 infrequent access 或归档层
resource "aws_s3_bucket_lifecycle_configuration" "logs" {
  bucket = aws_s3_bucket.logs.id

  rule {
    id     = "archive-old-logs"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }
  }
}

一句话:规格优化的顺序是「先看清用量,再动配置」——没有监控数据支撑的降配,只是在赌运气。

4. 闲置资源清理

一句话总结: 云账号里常年躺着未挂载的磁盘、未关联的弹性 IP、闲置的负载均衡与快照,它们不产生价值却持续计费,需要定期巡检与自动清理。

# 未挂载的 EBS 卷
aws ec2 describe-volumes \
  --filters Name=status,Values=available \
  --query 'Volumes[].{ID:VolumeId,Size:Size,Created:CreateTime}' \
  --output table

# 未关联的弹性 IP(闲置也要计费)
aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId==null].PublicIp'

# 停止超过 30 天的实例
aws ec2 describe-instances \
  --filters Name=instance-state-name,Values=stopped \
  --query 'Reservations[].Instances[].{ID:InstanceId,Launch:LaunchTime}'

用 Terraform 管理「该被清理的资源」本身就是一种治理:把巡检脚本的输出与 state 对比,找出不在 IaC 管理范围内、又长期闲置的资源。

# 用 data source 找出未使用的卷,纳入告警
data "aws_ebs_volumes" "all" {
  filter {
    name   = "status"
    values = ["available"]
  }
}

output "orphan_volumes" {
  value = [for v in data.aws_ebs_volumes.all.ids : v]
}
闲置类型计费清理方式
未挂载 EBS是快照后删除
未关联 EIP是直接释放
空负载均衡是确认后删除
老快照是生命周期策略
保留的日志组是设置保留天数
# 日志组设置保留天数,避免无限累积
resource "aws_cloudwatch_log_group" "app" {
  name              = "/app/service"
  retention_in_days = 30
}

一句话:闲置资源的可怕之处是「没人认领」——把它写进 IaC 并打上 Owner 标签,它就从「无主账单」变成「有人负责的资产」。

5. 成本护栏与预算告警

一句话总结: 护栏在资源创建前拦截超规格配置,预算告警在成本超阈值时通知,两者一前一后构成成本治理的自动防线。

# AWS Budgets 预算告警
resource "aws_budgets_budget" "monthly" {
  name         = "monthly-total"
  budget_type  = "COST"
  limit_amount = "5000"
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 80
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = ["finops@example.com"]
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = ["finops@example.com"]
  }
}
# 在代码层面限制规格:用 validation 拒绝超大实例
variable "instance_type" {
  type = string

  validation {
    condition = contains(
      ["t3.micro", "t3.small", "t3.medium", "t4g.medium"],
      var.instance_type
    )
    error_message = "仅允许使用审批过的实例规格。"
  }
}

5.1 用标签做成本分摊

locals {
  cost_tags = {
    CostCenter  = var.cost_center
    Environment = var.environment
    Owner       = var.team
    ManagedBy   = "terraform"
  }
}
# 激活成本分配标签后可按团队/环境查询
aws ce get-cost-and-usage \
  --time-period Start=2026-09-01,End=2026-10-01 \
  --granularity MONTHLY \
  --metrics UnblendedCost \
  --group-by Type=TAG,Key=CostCenter

一句话:护栏的意义是「让超支配置根本进不来」——事后告警只能止损,事前拦截才能省钱。

6. 变更的成本影响评审

一句话总结: 把成本纳入 PR 评审流程:Infracost 评论给出增量、策略文件给出阈值、评审人基于数据判断这笔钱该不该花。

一个完整的成本评审闭环:

# PR 模板中的成本检查项
# ## 成本影响
# - [ ] Infracost 评论已查看
# - [ ] 增量超过 $100/月 已获得审批
# - [ ] 已确认无替代的更省方案
# 对比优化前后的成本
infracost breakdown --path plan-before.json --format json > before.json
infracost breakdown --path plan-after.json  --format json > after.json
infracost diff --path after.json --compare-to before.json
变更类型成本影响评审重点
新增资源线性增长规格是否最小可用
扩容倍数增长是否有自动伸缩替代
存储层调整长期累积生命周期策略是否配置
架构重构可能双向长期 TCO 而非月费

一句话:成本评审不是「审批花钱」,而是「让花钱这件事有据可依」——有数据的讨论远比拍脑袋的争论高效。

7. 成本可见性与持续优化

一句话总结: 一次性优化会随规模增长而失效,需要把成本指标纳入日常看板、定期复查规格与利用率,形成持续优化的节奏。

# 用 Cost Explorer 找出 Top 成本项
aws ce get-cost-and-usage \
  --time-period Start=2026-09-01,End=2026-10-01 \
  --granularity MONTHLY \
  --metrics UnblendedCost \
  --group-by Type=DIMENSION,Key=SERVICE \
  --query 'ResultsByTime[0].Groups | sort_by(@, &Metrics.UnblendedCost.Amount) | reverse(@)[:10]'
# 利用率与成本对照:低利用率 + 高成本 = 优先优化对象
aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-0abc123 \
  --start-time 2026-09-01T00:00:00Z \
  --end-time 2026-10-01T00:00:00Z \
  --period 86400 \
  --statistics Average

持续优化的三个节奏:

节奏动作负责方
每次 PRInfracost 增量评审开发者
每周闲置资源巡检平台团队
每季度规格与保留实例复查FinOps

一句话:成本优化的终点不是「省到极致」,而是「每一美元都对应一个明确的业务价值」——这需要持续的可见性而非一次性的运动。

8. 总结

成本治理把「账单」翻译成「配置」,让每一笔支出在产生之前就被看见:

环节手段关键点
预估Infracost + plan JSON贴到 PR 评论,评审时可见
规格变量化 + Graviton先看利用率再降配
存储gp3 + 生命周期策略冷数据分层归档
清理巡检脚本 + Owner 标签无主资源优先处理
护栏validation + Budgets事前拦截 + 事后告警
分摊成本分配标签按团队/环境归集
评审PR 模板 + 阈值有数据支撑决策
持续看板 + 定期复查周/季度节奏

一句话收尾:成本优化不是「省钱运动」,而是「让基础设施的每一分投入都可解释」。从 Infracost 在 plan 阶段亮出价格,到护栏挡住超规格配置,再到标签把账单归集到团队,成本从「财务部门的数字」变成了「工程团队每次提交都能看到的信号」。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「terraform」更多文章

  1. Helm Provider 与应用发布:值注入与回滚
  2. 模块注册表与分发:版本、文档与测试
  3. DNS 与证书编排:托管区域与自动验证