阿里云ARMS跨地域remote_read:内网域名anycast不通,公网端点可直读

想在自建监控里读阿里云 ARMS 托管 Prometheus 的指标,如果监控和 ARMS 不在同一个地域,你大概率会踩到这个坑:ARMS 的内网域名跨地域根本连不上,但它不是 CEN 没打通,而是这个内网域名是"地域级 anycast"——每个地域都把它解析到本地的服务 VIP。真正能跨地域直读的,是 ARMS 的公网端点(URL 里带 token,免认证)。

这篇把整个排查和落地讲清楚:从"监控读不到异地指标",到逐层排除 CEN、定位 anycast、改走公网直读,再到用夜莺(Nightingale/n9e)把告警建起来,最后还有一个"告警卡片显示未知实例"的小尾巴。文中所有 IP、网段、地域、实例 ID 均为占位,替换成你自己的即可。

背景:一个跨地域的监控聚合架构

先交代环境,后面的"为什么不通"都依赖它:

  • 监控中心:夜莺 n9e v7 + 一台 Prometheus,都在区域A(近端,监控团队所在地)。n9e 的数据源 ds=4 指向这台 Prometheus。
  • 指标来源:阿里云云产品监控(RDS/Redis/Kafka/DTS 等)通过 ARMS 的"云服务"集成,落在 ARMS 托管 Prometheus 实例里。区域A、区域B 各有一个 ARMS 实例,各管各地域的云产品。
  • 聚合方式:区域A 的 Prometheus 本身不抓这些云产品指标,而是用 remote_read 把 ARMS 实例挂上来。查询 ds=4 时,Prometheus 把本地 TSDB + remote_read 后端合并返回。

这里有个容易误判的点:在区域A 的 Prometheus 机器上,你 ps/netstat 找不到任何抓阿里云指标的 exporter——因为根本没有,数据是 remote_read 在查询时从 ARMS 拉的。up{job="cmee-exporter-pods"} 为空、但 AliyunRds_CpuUsage 有值,就是这个原因。

目标很简单:阿里云 DTS(数据传输服务) 有两个跨地域的数据同步任务,云监控里有 SynchronizationLatency(同步延迟,单位 ms)指标,我们要给它加延迟告警。区域A 的同步任务已经能在 ds=4 里查到;区域B 的那个任务死活读不到完整数据——这就是起点。

一、先把 CEN 排除掉:VPC 互联是好的

区域A 和区域B 的 VPC 之间是用 CEN(云企业网)打通的。所以第一反应是"CEN 没配好"。但别急着下结论,先验证

最直接的验证:从区域A 的机器,直连区域B VPC 内网 IP 上的一个服务(区域B 也有一台 Prometheus,监听 9090):

# 在区域A 的机器上
curl -sS -m6 -o /dev/null -w "HTTP=%{http_code} conn=%{time_connect}s\n" \
  http://192.168.2.10:9090/-/healthy
# HTTP=200 conn=0.21s

HTTP=200,conn=0.21s——0.21s 正好是两地的跨地域 RTT。区域A 能直连区域B 的 VPC 内网,CEN/VPC 互联完全正常

再看 CEN 本身(企业版 TR):

aliyun cbn DescribeCens                      # CEN 实例,Active
aliyun cbn DescribeCenAttachedChildInstances --CenId <CEN_ID>
#   两地的 VPC 都挂在同一个 CEN 上
aliyun cbn DescribeRouteServicesInCen --CenId <CEN_ID>
#   TotalCount=0  —— 没有配"云服务"跨地域访问

结论先记下:VPC 层互联正常,但 CEN 上没有任何"云服务访问"(RouteService)条目。这一条后面会回收。

二、定位真凶:ARMS 内网域名是"地域级 anycast"

CEN 既然是通的,那到底什么不通?在区域A 的机器上,直接测 ARMS 的内网读地址。

先看 DNS 解析:

getent hosts <REGION_B>-intranet.arms.aliyuncs.com
# 100.103.10.116   <REGION_B>-intranet.arms.aliyuncs.com
# 100.103.102.102  <REGION_B>-intranet.arms.aliyuncs.com

100.103.x 是阿里云的服务化内网地址(100.64.0.0/10 共享网段),不是 VPC IP。关键在下一步——ping 它的 RTT:

ping -c2 <REGION_B>-intranet.arms.aliyuncs.com
# rtt min/avg/max = 0.65/0.66/0.67 ms      ← 亚毫秒!

# 对照:ping 区域B 的 VPC 内网 IP
ping -c2 192.168.2.10
# rtt min/avg/max = 213/213/213 ms          ← 真·跨地域

这就是决定性证据。区域B 的 ARMS 内网域名,在区域A 解析出来的 100.103.x,ping 只有 0.65ms——它是区域A 本地的 VIP,根本没出本地域。而真正的区域B VPC 是 213ms。

那为什么 curl 还是不通?因为这个本地 VIP 对 ICMP 应答、对 TCP 静默丢包:

for p in 80 443 9090; do
  timeout 5 bash -c "echo > /dev/tcp/100.103.102.102/$p" && echo "$p OPEN" || echo "$p 超时"
done
# 80 超时 / 443 超时 / 9090 超时

注意是 timeout(超时)而不是 Connection refused(RST)。端口关闭会立刻回 RST,而静默丢 SYN 才会超时——这是防火墙/ACL 级拦截的典型特征。

把这两点拼起来,机制就清楚了:

<REGION>-intranet.arms.aliyuncs.com地域绑定的 anycast 服务域名。每个地域的机器解析它,都会指到本地域的 ARMS 服务 VIP。区域A 解析区域B 的这个域名,拿到的是区域A 本地的一个 VIP——这个 VIP 只服务本地域的 ARMS,对跨地域的访问既没有后端、TCP 也被挡。所以它跟 CEN 通不通无关,加再多路由也没用:DNS 就把你导到了本地死胡同(第一节那个 RouteServiceEntries=0 在这里呼应上了——云服务跨地域访问压根没开)。

顺手验证对称性:从区域B 的机器反着读区域A 的 ARMS 内网域名,同样 ping 丢包、TCP 超时。双向都封,是这类内网端点的通用行为,不是个例。

三、出路:ARMS 的公网端点跨地域可直读

内网端点此路不通,那就看 ARMS 实例还暴露了什么地址。查一下:

aliyun arms GetPrometheusInstance --RegionId <REGION_B> \
  --ClusterId <ARMS_INSTANCE_ID> --endpoint arms.<REGION_B>.aliyuncs.com

返回里只有两类读地址,没有任何 VPC IP:

  • 内网(Intra):<REGION_B>-intranet.arms.aliyuncs.com(就是上面那个 anycast,跨区不通)
  • 公网(Inter):<REGION_B>.arms.aliyuncs.com

ARMS 是托管服务,不给你 VPC 内网 IP。那唯一没试过的就是公网端点。从区域A 直接查:

# URL 形如 http://<REGION_B>.arms.aliyuncs.com:9090/api/v1/prometheus/<TOKEN>/<UID>/<ARMS_INSTANCE_ID>/<REGION_B>
B="http://<REGION_B>.arms.aliyuncs.com:9090/api/v1/prometheus/<TOKEN>/<UID>/<ARMS_INSTANCE_ID>/<REGION_B>"
curl -sS -m12 -G "$B/api/v1/query" \
  --data-urlencode 'query=AliyunDts_SynchronizationLatency'
# {"status":"success",...,"instanceName":"<区域B同步任务>","regionId":"<REGION_B>",...}

成功,直接拿到了区域B 的 DTS 指标,而且标签齐全。 关键在于:ARMS 这个读地址把 token 放在 URL 路径里,本身就免认证(EnableAuthFreeRead=false 但 path token 即凭证),公网可读。解析出来的是真·区域B 公网 IP,跨地域走公网通。

代价是走公网(明文 http + URL token、依赖公网出口、延迟略高),但对"读监控指标"这个场景完全可接受。

四、落地:单数据源合并两地 ARMS + 告警规则

方向定了:区域A 的 Prometheus(ds=4)的 remote_read 里,直接加上区域B ARMS 的公网读地址。这样一个数据源就同时有了两地的指标——本地 remote_read 区域A ARMS,再加一条公网 remote_read 区域B ARMS。

prometheus.yml(区域A,改完 curl -X POST :9090/-/reload 热加载):

remote_read:
  # 区域A 本地 ARMS(原有)
  - url: "http://<REGION_A>-intranet.arms.aliyuncs.com:9090/api/v1/prometheus/<TOKEN>/<UID>/<ARMS_A>/<REGION_A>/api/v1/read"
    read_recent: true
  # 区域B ARMS,走公网(新增)
  - url: "http://<REGION_B>.arms.aliyuncs.com:9090/api/v1/prometheus/<TOKEN>/<UID>/<ARMS_B>/<REGION_B>/api/v1/read"
    read_recent: true

改完 promtool check config 校验、reload,ds=4 立刻能同时查到两地的同步任务。

接着是一个细节:每个地域的 ARMS 只给"本地域"的任务打全标签(instanceName/regionId),对另一地域的任务只有稀疏标签。所以合并后每个任务会出现"富标签 + 简标签"两份。用一个 instanceName!="" 过滤,只保留富标签那份,既去重又保证标签完整:

AliyunDts_SynchronizationLatency{instanceName!=""} > 30000

在 n9e 里建规则(server 端 OpenAPI,POST /v1/n9e/alert-rules,body 是 JSON 数组):延迟超过 30s 持续 5 分钟告警。两个任务各一条、零重复、都带完整 instanceName/regionId

五、尾巴:告警卡片为什么显示"未知实例"

规则建好,告警也发到群里了,但卡片头部那行是 告警实例: 未知实例——尽管下面的"标签信息"里 instanceName 明明有值。

这里有个认知误区要纠正:这张飞书卡片不是 n9e 渲染的。n9e 把告警事件 POST 给一个自研的消息网关(地址配在 n9e 的 configs.webhook),由网关按事件标签里的 feishu_group=xxx 路由到对应群、并自己拼卡片。卡片里"告警实例/指标参考"这些字段,n9e 的库里根本搜不到。

翻一眼消息网关的渲染代码,真相一行:

# 网关取"告警实例"的逻辑
alert_instance = tags_map.get("insname", tags_map.get("instance", "未知实例"))

网关按 insnameinstance → "未知实例" 的顺序取。主机告警天然有 instance,所以显示正常;而 DTS 这种指标告警两个标签都没有,就落到了"未知实例"。

修法不动网关(网关是公共服务,所有告警都走它,改它影响面大),而是在规则的 PromQL 里用 label_replaceinstanceName 复制成 insname:

label_replace(AliyunDts_SynchronizationLatency{instanceName!=""},
              "insname", "$1", "instanceName", "(.*)") > 30000

再触发一次,卡片头部就显示出任务名了。问题在网关、解法在数据源侧补一个网关认得的标签——这比改公共网关干净得多。

快速参考

判断 ARMS 内网域名是不是 anycast 不通(跨地域场景)

ping -c2 <REGION>-intranet.arms.aliyuncs.com   # RTT 亚毫秒=解析到了本地 VIP(anycast)
# TCP 全端口 timeout(不是 refused)= 静默丢包,本地 VIP 无跨区后端

结论速记

  • ARMS 内网域名(*-intranet.arms.aliyuncs.com100.103.x)是地域级 anycast,跨地域读不到,与 CEN 无关;加路由没用,DNS 已把你导向本地死胡同。
  • ARMS 公网域名(<region>.arms.aliyuncs.com)跨地域可读,URL path 里的 token 即凭证、免认证。
  • ARMS 托管实例只有 Inter(公网)+ Intra(内网 anycast)两类地址,没有 VPC IP;arms GetPrometheusInstanceRemoteReadInterUrl/RemoteReadIntraUrl
  • 区分"网络不通"还是"DNS 导错":ping 看 RTT(亚毫秒=本地 VIP),curl 看是 timeout(被挡/无后端)还是 refused(端口关)。
  • 一个数据源合并多地域 ARMS:remote_read 各加一条,用 instanceName!="" 过滤去重并保完整标签。
  • 自研消息网关渲染的告警卡片,"告警实例"常取 insname/instance;指标告警缺这俩会显示"未知实例",用 label_replace 注入即可,别改公共网关。

n9e 建规则的两个坑

  • POST /v1/n9e/alert-rulesJSON 数组;改规则没有 PUT,要删了重建
  • 规则名不能含 >=/> 等字符(报 Name has invalid characters),阈值写进备注/描述。
posted @ 2026-06-08 17:15  Hello_worlds  阅读(8)  评论(0)    收藏  举报