在华为云 ECS 上配一套真正有用的告警:CES 指标、阈值与通知落地

举报
yd_42677603 发表于 2026/11/02 10:28:02 2026/11/02
【摘要】 盘被日志撑爆,却过了八小时才发现。这篇把华为云 CES 告警从零配起来:查指标清单、拉历史数据算 P95 定阈值、建 SMN 主题接通知、验证与清理,全程 Python SDK 调用,字段名和几个反直觉的坑都标了出来。

一个真实的教训

去年 9 月的一个早上,我那台 2 核 2G 的 ECS 上跑着的几个 Docker 容器全挂了,访问服务全是 502。登上去一看,根分区 100% 满,日志文件把盘撑爆,Docker 连新容器都起不来。

最气人的不是坏了,而是坏了八个小时我才知道。

日志写得再详细也是事后的东西:它记录已经发生的事故,不会告诉我"再过十分钟盘就要满了"。指标是实时的,阈值一越线就有人被叫醒——这才是我真正缺的那块拼图。

那次之后我给这台机器配了云监控告警。这篇写的是这套东西怎么从零落地,以及中间那些文档上不写、只能自己踩出来的坑。

一、先想清楚:告警不是越多越好

装监控很容易,难的是装完之后没人看、看了也不动手,最后所有告警都被静音,监控变成摆设。

我给自己定了三条规矩:

  1. 一条告警必须能回答"我现在该做什么"。回答不了的,先别加。
  2. 宁可少而准,不要多而全。起步只配三条:内存、磁盘、CPU。
  3. 每条告警都要有对应的处置动作,写下来放在手边。告警本身只是通知,动作得是人定的。

二、CES 的三层结构

云监控服务(CES,Cloud Eye)的模型其实只有三层。理解了这三层,剩下的就全是填参数。

第一层:命名空间 + 指标 + 维度。

ECS 的基础监控指标都挂在 SYS.ECS 命名空间下,维度只有一个:instance_id。

第二层:告警规则。

一条规则由四部分组成:作用对象(资源)+ 触发条件(指标 + 聚合方式 + 阈值 + 连续次数)+ 告警级别 + 通知方式。

第三层:通知。

规则本身不发消息,它只负责"判定"。真正把消息送出去的是通知渠道——SMN 主题、消息中心这些。

这三层是解耦的,实践中最常见的翻车就发生在第三层:规则建成功了,却没人收到通知。原因通常是 SMN 主题没订阅端点,或者订阅还在"待确认"状态。

三、第一步:先摸清有哪些指标

不要凭记忆写指标名。CES 有接口可以直接查。

import os

from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcore.region.region import Region
from huaweicloudsdkces.v1.ces_client import CesClient
from huaweicloudsdkces.v1 import ListMetricsRequest

REGION = "cn-east-3"

creds = BasicCredentials(
    ak=os.environ["HW_AK"],
    sk=os.environ["HW_SK"],
)

# ces / smn 的 SDK 都没有内置区域常量,endpoint 必须自己拼
region = Region(REGION, f"ces.{REGION}.myhuaweicloud.com")
ces = CesClient.new_builder().with_credentials(creds).with_region(region).build()

resp = ces.list_metrics(ListMetricsRequest(namespace="SYS.ECS", limit=100))
for m in resp.metrics:
    print(m.metric_name, "|", m.unit, "|", [d.name for d in (m.dimensions or [])])

跑出来的指标清单大致是这样(ECS 基础监控):

指标名 含义 单位
cpu_util CPU 使用率 %
mem_util 内存使用率 %
disk_util_inband 磁盘使用率 %
disk_read_bytes_rate / disk_write_bytes_rate 磁盘读/写带宽 byte/s
disk_read_requests_rate / disk_write_requests_rate 磁盘读/写 IOPS Request/s
network_incoming_bytes_rate_inband / network_outgoing_bytes_rate_inband 带内网络流入/流出速率 byte/s
network_vm_connections 网络连接数 Count
network_vm_bandwidth_in / network_vm_bandwidth_out 虚拟机整机入/出方向带宽 Byte/s

注意命名规律:cpu_util、mem_util 是不带后缀的,磁盘和带内网络这几个则带 inband(另有 aggregate 一套,是从虚拟化层统计的)。指标名写错,规则就永远不会触发——这是配告警时最常见的"配了没反应"。

四、第二步:别拍脑袋定阈值

阈值拍脑袋的结果只有两种:太低了天天告警,太高了出事不告警。

靠谱的做法是先用历史数据算出基线。CES 能拉历史指标数据,拉过去一周,算个 P95,比阈值高一点就行。

import time

from huaweicloudsdkces.v1 import ShowMetricDataRequest

instance_id = "<你的 ECS 实例 ID>"
now = int(time.time())

resp = ces.show_metric_data(ShowMetricDataRequest(
    namespace="SYS.ECS",
    metric_name="mem_util",
    dim_0=instance_id,      # v1 里维度值直接用 dim_0 / dim_1 / dim_2 / dim_3
    filter="average",
    period=3600,            # 聚合粒度,单位是秒
    _from=now - 7 * 24 * 3600,
    to=now,
))

values = sorted(p.average for p in resp.datapoints if p.average is not None)
if values:
    p95 = values[int(len(values) * 0.95)]
    print("mem_util 基线 P95 =", p95, "%")

几个参数要注意:

  • dim_0 就是维度值,这里填 instance_id 的取值。v1 用 dim_0~dim_3 这种扁平参数,最多四个维度;v2 才改成结构化的 Dimension 列表。
  • period 是聚合粒度,单位秒,3600 表示按小时聚合。不是"多少分钟"。
  • _from / to 是毫秒级时间戳。参数名带下划线是因为 from 是 Python 关键字,SDK 只能这么写。
  • 返回的 datapoints 里同时给 average / max / min / sum / variance,按你请求的 filter 取对应字段。

我的经验值:2 核 2G 的小机器,内存基线 P95 大概在 60% 上下,阈值定 85%;磁盘使用率定 80%,因为磁盘一满,很多服务会直接写不进去,比内存耗尽更难恢复。

五、第三步:先把通知通道准备好

告警规则里的 notification_list 填的是 SMN 主题的 URN,形如 smn:cn-east-3:<project_id>:<topic_id>。主题和订阅端点要先在 SMN 里准备好,下一步写规则时才有 URN 可填。

from huaweicloudsdksmn.v2 import (
    AddSubscriptionRequest,
    AddSubscriptionRequestBody,
    CreateTopicRequest,
    CreateTopicRequestBody,
)
from huaweicloudsdksmn.v2.smn_client import SmnClient

smn_region = Region(REGION, f"smn.{REGION}.myhuaweicloud.com")
smn = SmnClient.new_builder().with_credentials(creds).with_region(smn_region).build()

topic = smn.create_topic(CreateTopicRequest(body=CreateTopicRequestBody(
    name="ecs-alarm",
    display_name="ECS 告警",
)))
topic_urn = topic.topic_urn        # 这就是 notification_list 要填的东西

smn.add_subscription(AddSubscriptionRequest(
    topic_urn=topic_urn,
    body=AddSubscriptionRequestBody(
        protocol="email",
        endpoint="ops@example.com",
        remark="值班邮箱",
    ),
))

两个提醒:

  • 加完订阅要去控制台点确认。邮件订阅会先发一封确认信,不点确认,订阅一直是"待确认"状态,告警永远发不出来。
  • SMN 的 endpoint 和 CES 不一样(smn. 前缀 vs ces. 前缀),但要在同一个区域。在华东-上海二建 CES 规则,就得用华东-上海二的 SMN 主题,跨区域填不进去。

六、第四步:写告警规则

规则放在 v2 SDK 里创建。注意 v2 的客户端要单独建一个——指标查询在 v1,告警规则在 v2。

from huaweicloudsdkces.v2.ces_client import CesClient as CesClientV2
from huaweicloudsdkces.v2 import (
    AlarmRulePolicy,
    CreateAlarmRulesRequest,
    Dimension,
    Notification,
    PostAlarmsReqV2,
)

ces_v2 = CesClientV2.new_builder().with_credentials(creds).with_region(region).build()

rule = PostAlarmsReqV2(
    name="ecs-mem-high",
    description="内存使用率连续 10 分钟高于 85%",
    namespace="SYS.ECS",
    type="RESOURCE_GROUP",
    resources=[[Dimension(name="instance_id", value=instance_id)]],
    policies=[AlarmRulePolicy(
        metric_name="mem_util",
        period=300,              # 5 分钟聚合,单位是秒
        filter="average",
        comparison_operator=">=",
        value=85,
        unit="%",
        count=2,                 # 连续 2 个周期都满足才告警
        level=2,                 # 1=紧急 2=重要 3=次要 4=提示
        suppress_duration=3600,  # 同一告警 1 小时内不重复通知,单位秒
    )],
    enabled=True,
    notification_enabled=True,
    alarm_notifications=[Notification(
        type="notification",
        notification_list=[topic_urn],   # 上一步拿到的 SMN 主题 URN
    )],
    effective_timezone="Asia/Shanghai",
)

resp = ces_v2.create_alarm_rules(CreateAlarmRulesRequest(body=rule))
print("新建告警规则 ID:", resp.alarm_id)

几个参数容易踩坑:

  • resources 是二维数组:list[list[Dimension]]。外层是"或"关系(满足任意一组即命中),内层是"且"关系(一组里的所有维度都要满足)。只写一维会直接报错。
  • period 单位是秒:300 = 5 分钟,3600 = 1 小时。写 period=5 不是 5 分钟,是 5 秒。
  • count 是"连续几个周期":period=300, count=2 才是"连续 10 分钟"。很多人在这里配了一次性告警。
  • comparison_operator 可选:>、<、>=、<=、=、!=,还有 cycle_decrease、cycle_increase、cycle_wave 三个周期性比较。
  • filter 可选:average、variance、min、max、sum。
  • suppress_duration 是抑制时间,单位秒,用来防止阈值在临界点反复横跳时刷屏。
  • level 是告警级别:1=紧急、2=重要、3=次要、4=提示。
  • 接口名是复数,body 是单个:CreateAlarmRules(复数)接收的却是单个 PostAlarmsReqV2,返回也只有一个 alarm_id。这个命名不一致很容易让人以为能一次建一批。

七、第五步:验证与清理

配完一定要验证,别相信"创建成功"等于"能告警"。

from huaweicloudsdkces.v2 import (
    BatchDeleteAlarmRulesRequest,
    BatchDeleteAlarmsRequestBody,
    ListAlarmHistoriesRequest,
    ListAlarmRulesRequest,
)

# 看规则
rules = ces_v2.list_alarm_rules(ListAlarmRulesRequest(namespace="SYS.ECS", limit=100))
for r in rules.alarms:
    print(r.alarm_id, r.name, r.enabled, r.notification_enabled)

# 看告警历史
hist = ces_v2.list_alarm_histories(ListAlarmHistoriesRequest(
    namespace="SYS.ECS",
    status=["alarm"],
    _from="2026-10-25 00:00:00",
    to="2026-10-26 00:00:00",
    limit=50,
))
for h in hist.alarm_histories:
    print(h.record_id, h.name, h.status, h.level, h.first_alarm_time)

# 删除
ces_v2.batch_delete_alarm_rules(BatchDeleteAlarmRulesRequest(
    body=BatchDeleteAlarmsRequestBody(alarm_ids=["al1234567890"])))

注意 ListAlarmHistoriesRequest 的 _from / to 是字符串时间("2026-10-25 00:00:00"),和 v1 拉指标数据时的毫秒时间戳不一样。同一套 SDK 里两种时间格式并存,翻车率很高。

还有 BatchDeleteAlarmsRequestBody——名字里是 Alarms 不是 AlarmRules,跟外层的 BatchDeleteAlarmRulesRequest 差一个字母。

八、几个更隐蔽的坑

v1 和 v2 要混着用。 指标查询(list_metrics、show_metric_data)只在 v1 里,告警规则(create_alarm_rules 等)只在 v2 里。SDK 是同一个包,客户端要建两个。

Region 的导入路径很反直觉。 正确的是:

from huaweicloudsdkcore.region.region import Region

huaweicloudsdkcore.region 这个包的 __init__.py 是空的,直接 from huaweicloudsdkcore.region import Region 会报 ImportError。同一个包里 BasicCredentials 也一样,得写全 huaweicloudsdkcore.auth.credentials。

区域常量不要指望。 huaweicloudsdkces.v2.region 和 huaweicloudsdksmn.v2.region 里都是空的,没有 CN_EAST_3 之类的现成常量,只能自己 Region(id, endpoint) 拼。

全局服务在另一个区域。 告警历史这类全局资源,数据默认落在华北-北京四,在别的区域查不到,会误以为"没有告警记录"。

规则是资源级的。 规则建在哪个区域,就只能监控那个区域的资源。跨区域部署的业务,得在每个区域各建一套。

九、我的实践清单

现在这台机器上常驻的告警就三条:

告警 指标 条件 级别 触发后我做什么
内存吃紧 mem_util 5 分钟均值 ≥ 85%,连续 2 次 重要 查容器内存占用,必要时重启服务
磁盘告急 disk_util_inband 5 分钟均值 ≥ 80%,连续 2 次 紧急 清理日志和 Docker 悬空镜像
CPU 持续打满 cpu_util 5 分钟均值 ≥ 90%,连续 3 次 次要 看是不是有定时任务或压测

三条规则,两个半小时的活配出来的。但它们覆盖了这类小机器上最常见的三种死法。

另外三个我觉得值得单独说一句的习惯:

  • 告警一定要写 description。 控制台上一排 alarm_ecs_xxx,事后根本分不清是哪个。没描述的告警,等于没配。
  • 先建看板,后建告警。 告警告诉你"出事了",看板告诉你"到哪一步了"。没看板的告警,人被叫醒之后还是得现查。
  • 告警规则纳入代码管理。 规则本身是配置,扔进 Git,用 IaC 部署到新机器。靠控制台手点出来的规则,换个账号就没了。

小结

给云主机配监控告警这件事,技术难度不高,难的是想清楚三件事:

  1. 配什么:从少量高价值指标开始,别一上来就上几十条。
  2. 阈值怎么定:用历史数据的 P95 当基线,不要拍脑袋。
  3. 通知怎么送达:SMN 主题 + 订阅 + 确认,三步都做完才算数。

把这套流程走通之后,最直接的收益是:机器出问题的那天,你是十分钟内知道的,而不是第二天早上。

---

*文中所有 SDK 调用均已用 huaweicloudsdkces / huaweicloudsdksmn 实际校验过字段名与结构;实例 ID、项目 ID、密钥等均使用占位符。*

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。