云项目可观测性与成本治理:告警、日志、Dashboard 与预算标签

本文讲云项目的可观测性与成本治理,覆盖日志字段与保留期、告警规则、Dashboard 分层、预算预警、成本分摊标签。

环境:Terraform 1.9.5 + AWS provider 5.60,CloudWatch Logs/Alarm、AWS Budgets、Cost Allocation Tags,AWS CLI v2。

起因:上月账单比前月高 47%,没人说得清是哪个服务涨的;线上 502 挂了 26 分钟才被发现。

一、五个资产的边界

云上项目上线后最容易欠的两笔账:出问题看不见,花超了说不清。五个资产各管一段,缺哪段都会在故障单或账单上体现出来。

资产 解决什么 落地资源 缺了的后果
日志 事后定位根因 CloudWatch Logs 日志组 + 指标过滤 出事只能靠猜
告警 主动发现故障 Metric Alarm + SNS 主题 用户比你先知道
Dashboard 3 秒看清全局 CloudWatch Dashboard 每次排查从零拼图
预算 花钱前拦住 AWS Budgets 预测型通知 月底才看账单
标签 成本能分摊到服务 Cost Allocation Tags + SCP 账单只剩一个总数

二、整体链路

观测链路和成本链路共用一套标签和一个通知主题,两条链在 SNS 汇合。

上排是故障响应链路,下排是花钱预警链路。两者的汇合点只有一个 SNS 主题,值班同学不用盯两个后台。

三、日志:先定字段,再定保留期

日志格式不统一,指标过滤就写不出来。业务日志统一输出单行 JSON,字段固定成下面七个。

json 复制代码
{"ts":"2026-10-29T10:12:33.481Z","level":"ERROR","svc":"order-api","trace_id":"9f2c1a7e","route":"/api/v1/orders","status":502,"latency_ms":1830,"msg":"upstream timeout"}
字段 类型 必填 用途
ts ISO8601 是 跨服务对齐时间轴
level ERROR/WARN/INFO 是 指标过滤的第一道条件
svc 字符串 是 多服务共用一个日志组时区分来源
trace_id 字符串 是 一次请求跨服务串起来
route 字符串 是 定位到具体接口
status 整数 是 5xx 比例的分母与分子
latency_ms 整数 是 P99 延迟的自算兜底

字段定完再谈保留期。保留期直接决定存储费,按环境分三档就够。

环境 日志组 保留天数 理由
prod /aws/app/order-api 30 覆盖一个完整排障窗口
staging /aws/app/order-api-staging 7 只验证发布后的短期行为
dev /aws/app/order-api-dev 3 留三天够查联调问题
plain 复制代码
resource "aws_cloudwatch_log_group" "app" {
  name              = "/aws/app/order-api"
  retention_in_days = 30
  kms_key_id        = aws_kms_key.logs.arn
}

摄入费是存储费的 16 倍。按 us-east-1 刊例价,摄入 0.50 美元/GB,存储 0.03 美元/GB·月,20 GB/天的 INFO 日志一个月摄入 300 美元,存 30 天只要 18 美元。

INFO 不进 CloudWatch,只把 WARN/ERROR 送进去,INFO 落 S3 走生命周期,摄入费能压到 1/10。

落地方式是采集端做过滤,应用侧不用改代码:CloudWatch Agent 的 filters 用 exclude_at_match 正则丢掉 "level":"INFO" 行,进 S3 的那份由 Firehose 直投。

四、告警:三条就够

告警数量上去以后,值班会开始忽略通知。四条告警覆盖住入口、延迟、后台任务和数据断流,剩下的等真出事再补。

告警 指标(命名空间) 阈值 评估 动作
5xx 突增 HTTPCode_Target_5XX_Count(AWS/ApplicationELB) Sum > 20 / 5min 1/1 SNS 值班
P99 延迟 TargetResponseTime p99(AWS/ApplicationELB) > 2s 3/3 SNS 值班
任务失败 JobFailure(自定义,由日志过滤产出) Sum > 3 / 5min 1/1 SNS + 工单
日志断流 IncomingBytes(AWS/Logs) < 1024 / 5min 2/2 SNS 值班

前两条直接建在 ALB 的现成指标上,不用自己算。

plain 复制代码
resource "aws_cloudwatch_metric_alarm" "alb_5xx" {
  alarm_name          = "prod-alb-5xx-high"
  namespace           = "AWS/ApplicationELB"
  metric_name         = "HTTPCode_Target_5XX_Count"
  dimensions          = { LoadBalancer = aws_lb.app.arn_suffix }
  statistic           = "Sum"
  period              = 300
  evaluation_periods  = 1
  threshold           = 20
  comparison_operator = "GreaterThanThreshold"
  treat_missing_data  = "notBreaching"
  alarm_actions       = [aws_sns_topic.oncall.arn]
  ok_actions          = [aws_sns_topic.oncall.arn]
}

P99 用 extended_statistic,和 statistic 二选一,同时写会报冲突。

plain 复制代码
resource "aws_cloudwatch_metric_alarm" "alb_p99" {
  alarm_name          = "prod-alb-p99-slow"
  namespace           = "AWS/ApplicationELB"
  metric_name         = "TargetResponseTime"
  dimensions          = { LoadBalancer = aws_lb.app.arn_suffix }
  extended_statistic  = "p99"
  period              = 300
  evaluation_periods  = 3
  threshold           = 2
  comparison_operator = "GreaterThanThreshold"
  alarm_actions       = [aws_sns_topic.oncall.arn]
}

自定义指标由日志过滤产出,一条过滤只出一个指标,字段名要和日志里的键一致。

plain 复制代码
resource "aws_cloudwatch_log_metric_filter" "job_failed" {
  name           = "job-failed"
  log_group_name = aws_cloudwatch_log_group.app.name
  pattern        = "{ $.level = \"ERROR\" && $.route = \"/job/settle\" }"

  metric_transformation {
    name      = "JobFailure"
    namespace = "App/OrderApi"
    value     = "1"
  }
}

日志断流这条用 treat_missing_data = "breaching":没有数据点本身就是故障信号,不能按正常处理。

参数 取值 场景
notBreaching 缺数据算正常 低峰期本来就没有 5xx
breaching 缺数据算告警 心跳、日志断流、定时任务
missing 进 INSUFFICIENT_DATA 新上线指标还没数据

告警阈值先按现状 P99 的 2 倍设,跑一周看误报率再收紧,一开始就按理想值设会天天响。

变更期要静默。部署前 disable-alarm-actions 关掉动作,部署后 enable-alarm-actions 打开,中间漏掉的告警会在下一个评估周期重新触发。

五、Dashboard:三块面板

一块面板回答一个问题:现在健康吗、慢在哪、花了多少。三块并排,值班扫一眼就有判断。

面板 指标 回答什么
流量与错误 RequestCount、5XX、4XX 堆叠图 是故障还是流量变化
延迟分布 p50/p90/p99 三条线 慢在平均值还是长尾
成本趋势 按 Service 分组的预估日花费 哪个服务在涨
plain 复制代码
resource "aws_cloudwatch_dashboard" "main" {
  dashboard_name = "prod-overview"
  dashboard_body = jsonencode({
    widgets = [{
      type = "metric", x = 0, y = 0, width = 12, height = 6,
      properties = {
        title   = "流量与错误"
        region  = "ap-northeast-1"
        period  = 300
        view    = "timeSeries"
        stacked = true
        metrics = [
          ["AWS/ApplicationELB", "RequestCount", "LoadBalancer", aws_lb.app.arn_suffix],
          ["AWS/ApplicationELB", "HTTPCode_Target_5XX_Count", "LoadBalancer", aws_lb.app.arn_suffix]
        ]
      }
    }]
  })
}

region 必须显式写在 properties 里,缺了这块面板在跨区看板里是空白,且不报错。

六、预算:预测型比超支型有用

超支型通知在钱已经花完之后才响,预测型在当月跑到 80% 时就响,中间差出一周的处理时间。

plain 复制代码
resource "aws_budgets_budget" "prod" {
  name         = "prod-monthly"
  budget_type  = "COST"
  limit_amount = "800"
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  cost_filter {
    name   = "TagKeyValue"
    values = ["user:Environment$prod"]
  }

  notification {
    comparison_operator       = "GREATER_THAN"
    threshold                 = 80
    threshold_type            = "PERCENTAGE"
    notification_type         = "FORECASTED"
    subscriber_sns_topic_arns = [aws_sns_topic.oncall.arn]
  }
}
通知类型 触发时机 适合
ACTUAL 实际花费越过阈值 硬上限兜底
FORECASTED 预测月末花费越过阈值 月中干预
CREDIT 抵扣额度变化 有预付账单的账号

预算按标签过滤,标签没打全的账号预算会漏算,上线前先核对 cost_filter 的标签键拼写。

七、标签:成本能不能分摊就看这一步

账单只有一个总数的时候,任何优化都无从下手。标签规范要在资源创建之前定,事后补标签的历史账单是补不回来的。

标签键 取值示例 必填 用途
Environment prod/staging/dev 是 预算过滤、告警路由
Service order-api 是 成本按服务分摊
Owner team-pay 是 超支时找谁
CostCenter CC-1042 是 财务对账
ExpireAt 2026-12-31 否 临时资源回收

只靠规范文档约束不住人,用 SCP 在组织层拦截缺标签的创建请求。

json 复制代码
{"Version":"2012-10-17","Statement":[{"Sid":"DenyRunWithoutTags","Effect":"Deny",
"Action":["ec2:RunInstances","rds:CreateDBInstance"],
"Resource":"*",
"Condition":{"Null":{"aws:RequestTag/Service":"true","aws:RequestTag/Environment":"true"}}}]}

SCP 会连 Terraform 一起拦,default_tags 里必须把这两个键补上,否则 plan 通过、apply 被拒。

plain 复制代码
provider "aws" {
  default_tags {
    tags = {
      Environment = var.env
      Service     = "order-api"
      Owner       = "team-pay"
      CostCenter  = "CC-1042"
    }
  }
}

标签只在 Billing 控制台激活为成本分配标签之后才进账单,激活后约 24 小时生效,历史数据不会追溯。

八、告警闭环

告警响了之后要有人走完流程,否则告警只是噪音来源。

回环箭头那一步是重点:每次误报都要落成阈值调整或过滤条件,否则同一条告警会一直响到被静音。

九、验收

部署完按下面四项核对,全部通过才算补齐。

  • 日志组保留期与规划一致,IncomingBytes 有数据
  • 四条告警状态为 OK,不是 INSUFFICIENT_DATA
  • Dashboard 三块面板都有曲线,无空白块
  • 预算出现在 Billing 控制台,标签已激活为成本分配标签
bash 复制代码
aws cloudwatch describe-alarms --state-value INSUFFICIENT_DATA \
  --query 'MetricAlarms[].AlarmName' --output table
aws logs describe-log-groups --query 'logGroups[].[logGroupName,retentionInDays]' --output table
aws budgets describe-budgets --account-id 123456789012 --query 'Budgets[].BudgetName'
latex 复制代码
-----------------------------------------
|            DescribeAlarms             |
+---------------------------------------+
|  prod-alb-p99-slow                    |
+---------------------------------------+

出现 INSUFFICIENT_DATA 说明指标还没上报,先查命名空间和维度,别急着调阈值。

十、排障

现象 根因 处理
告警长期 INSUFFICIENT_DATA 指标名或维度拼错 aws cloudwatch get-metric-statistics 手工取一次
日志组里没数据 实例缺 logs:PutLogEvents 权限 查 Agent 日志与实例角色策略
指标过滤不命中 JSON 里字段名大小写不一致 aws logs test-metric-filter 用真实日志样本验
Budgets 金额偏小 资源缺标签,cost_filter 漏算 补标签后重算,历史账期无法修正
Cost Explorer 看不到标签 未激活为成本分配标签 Billing → Cost allocation tags 手动激活

十一、成本口径

这套配置本身的月度成本很低,大头在日志摄入,压住它就压住了整体。

项目 单价(us-east-1 刊例) 优化手段
日志摄入 0.50 美元/GB 只送 WARN/ERROR,INFO 走 S3
日志存储 0.03 美元/GB·月 保留期分环境,prod 30 天
自定义指标 0.30 美元/个·月,前 10 个免费 一条过滤只出一个指标,别按日志行建
告警 前 10 个免费 合并同类告警,按服务不按实例建
Dashboard 前 3 个免费 一块看板三块面板,不按团队拆

可观测性的成本结构是摄入 >> 存储 >> 告警,先砍摄入量,再谈保留期。

参考

相关推荐
4SAPI3 小时前
多国产模型混合调度与企业级治理生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
大数据·人工智能·架构·php
无人生还别怕3 小时前
如何搭建k8s服务(containerd部署)
linux·云原生·容器·kubernetes·openanolisos
智购科技自动售货机厂家3 小时前
让AI经营自动售货机:从Project Vend看工具权限与业务规则~YH
大数据·服务器·单片机·嵌入式硬件·深度学习
数字新视界3 小时前
空调控制器AIRC800的智能化管理与应用实践
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
知已难寻3 小时前
01 · OpenStack的认证管理 Keystone
运维·服务器·云原生·云计算·openstack
分布式存储与RustFS4 小时前
模型权重散落在每台 GPU?用 RustFS 给 vLLM 做共享权重库
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准
isfox4 小时前
ReduceTask工作机制
大数据
蹄蹄4 小时前
从“一次性检测“到“年度服务“:检测机构收入结构变了,合同和应收账款也要跟着变
大数据·数据库·人工智能
YIN_尹5 小时前
【git原理与使用】git初识与安装
大数据·git·elasticsearch