阿里云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", "未知实例"))
网关按 insname → instance → "未知实例" 的顺序取。主机告警天然有 instance,所以显示正常;而 DTS 这种指标告警两个标签都没有,就落到了"未知实例"。
修法不动网关(网关是公共服务,所有告警都走它,改它影响面大),而是在规则的 PromQL 里用 label_replace 把 instanceName 复制成 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.com→100.103.x)是地域级 anycast,跨地域读不到,与 CEN 无关;加路由没用,DNS 已把你导向本地死胡同。 - ARMS 公网域名(
<region>.arms.aliyuncs.com)跨地域可读,URL path 里的 token 即凭证、免认证。 - ARMS 托管实例只有 Inter(公网)+ Intra(内网 anycast)两类地址,没有 VPC IP;
arms GetPrometheusInstance看RemoteReadInterUrl/RemoteReadIntraUrl。 - 区分"网络不通"还是"DNS 导错":
ping看 RTT(亚毫秒=本地 VIP),curl看是 timeout(被挡/无后端)还是 refused(端口关)。 - 一个数据源合并多地域 ARMS:
remote_read各加一条,用instanceName!=""过滤去重并保完整标签。 - 自研消息网关渲染的告警卡片,"告警实例"常取
insname/instance;指标告警缺这俩会显示"未知实例",用label_replace注入即可,别改公共网关。
n9e 建规则的两个坑
POST /v1/n9e/alert-rules收 JSON 数组;改规则没有 PUT,要删了重建。- 规则名不能含
>=/>等字符(报Name has invalid characters),阈值写进备注/描述。

浙公网安备 33010602011771号