阿里云 CEN 与腾讯云 CCN 跨云专线打通:context deadline exceeded 超时排查

阿里云 CEN 与腾讯云 CCN 跨云专线打通:context deadline exceeded 超时排查

文中所有 IP、网段、资源 ID、域名、服务名均为占位示意,替换成你自己的即可。涉及两朵云的私网网段,统一约定:阿里云海外侧用 10.10.0.0/16,阿里云北京侧用 10.30.0.0/16,腾讯云侧用 10.20.0.0/16

结论先行

一个跑在阿里云海外地域的 Go 服务,调用腾讯云北京 TKE 里的图片生成接口,偶发 context deadline exceeded,单次卡到 28s。根因不是后端,也不是 DNS——是这条调用走的公网域名裸奔跨境,而两朵云之间其实早就拉好了专线(阿里云 CEN + 腾讯云 CCN + 物理专线),只是没被这条调用用上。

最终的卡点精确到一个字段:腾讯云专线网关发布给云联网(CCN)的「静态网段列表」里,漏了阿里云海外那个网段,导致回程不通。补上网段、再建一个内网 Ingress 把流量切到专线,跨境超时根治,真实出图从公网的 2.2s 降到内网的 1.6s 且不再抖动。

下面是完整的排查路径,以及中途踩的四个坑。

环境

角色 说明
调用方 阿里云海外地域 ECS 上的 Go 服务(下称上游服务),VPC 网段 10.10.0.0/16
被调方 腾讯云北京 TKE 集群里的图片生成服务,CLB Ingress 暴露,域名 api.example.com,接口 POST /img/generate
已有网络 阿里云 CEN(云企业网,企业版转发路由器)跨地域 + 阿里云↔腾讯物理专线 + 腾讯 CCN(云联网)
排查工具 阿里云 SLS、腾讯云 CLS、aliyun CLI、tcclikubectl

链路总览

一张图看清「出问题的公网路径」和「改造后的内网专线路径」,以及本次根因卡在哪:

阿里云↔腾讯云跨云专线打通架构(超时排查)

对照上图,整条链路逐段说明:

  1. 现状 / 出问题的路(图中红色虚线):上游服务用公网域名 api.example.com,DNS 解析到公网 CLB,流量裸奔跨境公网 —— 高峰丢包、TCP 握手 SYN 重传,最终 28s context deadline exceeded
  2. 改造去程(绿线起步 + 蓝线):上游改走内网域名 api-inner,经阿里云 CEN 企业版转发路由器(TR) 跨地域传输,海外网段 10.10.xPropagated 到北京 TR 路由表,到达阿里云北京 VBR
  3. 物理专线(蓝线):北京 VBR 通过物理专线接入腾讯专线网关 dcg-xxxx,这一段的专用通道走 BGP 动态路由。
  4. 根因卡点(图中红框):专线网关发布给 CCN 的网段是 CcnRouteType=STATIC 手工静态列表,漏了海外 10.10.0.0/16 —— 腾讯侧没有回到海外的路由,去得到、回不来。补上这条网段后,CCN 学到、腾讯 VPC 路由表自动出现 10.10/16 → CCN,回程打通。
  5. 内网入口(绿线落点):腾讯 TKE 建内网 Ingress(内网 CLB 10.20.2.34),内网域名解析到它,流量打到后端 abc-svc(GPU 出图 ~1.4s)。至此上游全程走专线,跨境超时根治。

一句话:去程靠阿里云 CEN 把网段传过去(已通),回程靠腾讯专线网关把对端网段加进「发布给 CCN 的静态列表」——这一环漏了海外网段,所以只配一边等于没配。

数据流向动图(公网裸奔超时 → 切内网专线打通,逐段点亮):

跨云数据流向:公网超时→切专线打通

一、现象:context deadline exceeded

上游服务日志(阿里云 SLS)里一条典型报错:

HTTP request failed. URL:  http://api.example.com/img/generate ,
Total Time: 28.67s, Error: context deadline exceeded,
Trace: event=DNSDone cost=0.8ms; event=ConnectStart addr=<对端公网IP>:80;
       event=ConnectDone (耗时 3.46s); event=WroteRequest; <之后无响应>

拆开这条 trace,卡点一目了然:

  • DNS:0.8ms,正常;
  • TCP 建连:3.46s(正常应是毫秒级,说明握手就有 SYN 重传);
  • 请求写完后,服务端长时间不返回,直到 28s deadline 触发。

先在 SLS 里按报错聚合,确认量级和规律:

aliyun sls get-logs-v2 --region <REGION-A> --project <proj> --logstore <ls> \
  --from <ts1> --to <ts2> \
  --query '"/img/generate" and "context deadline exceeded" | select date_format(from_unixtime(__time__), '"'"'%H'"'"') as hr, count(*) c group by hr order by hr'

结果很有指向性,两个维度都指向跨境公网。

按天看 —— 爆发式,不是天天稳定:

日期 context deadline exceeded 次数
D1 38,222(单日大爆发)
D2 / D3 10 / 2(几乎为零)
D4 6,728
D5 1,292

按小时看 —— 只在中国网络高峰犯,深夜清零:超时几乎全部集中在北京白天到晚高峰(04:00–12:00 UTC,对应北京 12:00–20:00)。单分钟峰值在 07:05 UTC 一分钟 94 次07:09 UTC 又 40 次;06:30–07:30 UTC 一小时累计 204 次;而北京深夜(13:00 UTC 之后)基本为 0。

「只在中国流量高峰犯、深夜清零、日间量级差几个数量级」——这套特征几乎只有跨境公网链路在高峰拥塞/丢包能解释,基本排除了后端容量、DNS、固定故障。

二、第一刀:证明后端无辜

下游图片服务在腾讯云,日志在腾讯云 CLS。用同一时段的请求量和耗时核对后端表现:

tccli cls SearchLog --region <REGION-B> --TopicId <TOPIC_ID> \
  --From <ms1> --To <ms2> --Query '"abc" and "elapsed_ms"' --Limit 1000 --Sort asc

坑预告:CLS 的 SearchLog 默认 Limit 很小,且全文匹配 "ERROR" 会把日志里任何含该子串的行都带回来。要 Limit 拉满、按秒分桶看吞吐,不要拿全文匹配的命中数当"错误数"。

后端实测:出图 P50 约 1.1s、P95 约 2.3s,全部 SUCCESS,Pod 零重启。再用 kubectl 交叉验证 Pod 状态,全程 Running、无 OOM。后端无辜

对照之下,调用方侧成功的请求也要 4s、15s,失败的 28s——后端只花 1~2s,差额全在传输链路上。结合 TCP 握手 3.46s 的 SYN 重传特征,矛头指向跨境公网。

三、关键转折:专线明明有,却没走

聊到这里才发现:两朵云之间早就有专线(阿里云 CEN + 腾讯 CCN + 物理专线)。那为什么还在裸奔公网?

因为上游服务调的是公网域名 api.example.com,它解析到腾讯的公网 CLB IP,流量自然走公网出口。专线在那躺着没被这条调用用上。

于是问题转化为:能不能让这条调用走内网专线? 先做最朴素的连通性对比——从阿里云海外 ECS 去 ping 腾讯内网:

# 腾讯一台节点内网 IP
ping -c 3 10.20.0.18
# --- 100% packet loss

而同在阿里云北京的运维机(专线同侧)却 ping 得通这个 IP。北京通、海外不通——专线本身没问题,是海外这一段没接上。

四、逐层定位:从 CEN 到 CCN 到专线网关

先放一张完整的 CEN + 专线配置详图(可对照复刻):VPC/IP、CEN 的转发路由器(TR)/路由表/跨地域连接 100Mbps/路由策略,专线的 VBR/专用通道/BGP-ASN,腾讯侧的 CCN/VPC 路由表/子网/安全组,都标在图里。

阿里云 CEN + 跨云专线 配置详图(可照着复刻)

下面按这张图,从阿里云侧往腾讯侧逐层查。

4.1 阿里云侧:CEN 路由是通的

先在阿里云 CEN(企业版转发路由器,TR)上查路由表,确认海外网段有没有传到北京。这里踩了第一个坑:

# 错误:默认只返 20 条,海外网段 10.10.x 排在后面没返回,差点误判"没传过来"
aliyun cbn ListTransitRouterRouteEntries --TransitRouterRouteTableId <tr-rtb> --region <REGION-B>

# 正确:拉满分页
aliyun cbn ListTransitRouterRouteEntries --TransitRouterRouteTableId <tr-rtb> \
  --region <REGION-B> --MaxResults 100

拉满后看到 10.10.0.0/2410.10.1.0/24(海外网段)都已经 Propagated 到北京 TR 路由表。再用 tracepath 从海外 ECS 打腾讯内网,去程能跨境跑到腾讯骨干网段(220ms,跨境 RTT),说明阿里云侧路由、跨境带宽、专线去程全部正常

4.2 腾讯侧:回程缺了海外网段

去程通,那问题在回程。查腾讯 VPC 路由表:

tccli vpc DescribeRouteTables --region <REGION-B> --Limit 100
# 只有 10.30.0.0/16(阿里云北京) -> CCN,没有 10.10.x(阿里云海外)

回程路由只认了阿里云北京网段,没有海外网段。再往专线网关这一层挖:

# 专线网关发布给 CCN 的路由
tccli vpc DescribeDirectConnectGatewayCcnRoutes --DirectConnectGatewayId dcg-xxxxxxxx --region <REGION-B>
# 列出 7 条:6 个内部互联 /24 网段 + 10.30.0.0/16(阿里云北京),唯独没有 10.10.0.0/16(海外)

再确认这个专线网关到 CCN 的路由模式:

tccli vpc DescribeDirectConnectGateways --DirectConnectGatewayIds '["dcg-xxxxxxxx"]' --region <REGION-B>
# CcnRouteType = STATIC   EnableBGP = False

真相大白。要分清两层:

  • 专用通道(VBR ↔ 专线网关,物理层)是 BGP 动态;
  • 专线网关 → CCN 这一层是 STATIC——发布哪些网段给云联网,是手工维护的静态列表

而这个静态列表里只有阿里云北京网段,漏了阿里云海外 10.10.0.0/16。所以海外的包能跨境到腾讯,但腾讯不知道怎么把包送回海外——去得到、回不来,表现就是 ping 100% 丢包、TCP 黑洞。

五、打通方案:先修网络,再切流量

找到根因后,解决分两步——先在网络层把专线回程修通,再在应用层把流量切上去。两步缺一不可:只修路不改道,流量还在走公网;不修路,内网根本不通。

5.1 网络层:补专线网关静态网段,打通回程

腾讯云控制台 → 专线接入 → 专线网关 → 「IDC 网段 / 路由」→ 新增 10.10.0.0/16。等价的 CLI:

tccli vpc CreateDirectConnectGatewayCcnRoutes --DirectConnectGatewayId dcg-xxxxxxxx \
  --Routes '[{"DestinationCidrBlock":"10.10.0.0/16"}]' --region <REGION-B>

加完立刻验证,一条链路三处自动生效:

# 1. 专线网关已发布
tccli vpc DescribeDirectConnectGatewayCcnRoutes --DirectConnectGatewayId dcg-xxxxxxxx --region <REGION-B>   # 出现 10.10.0.0/16
# 2. CCN 学到
tccli vpc DescribeCcnRoutes --CcnId ccn-xxxxxxxx --region <REGION-B>                                          # 出现 10.10.0.0/16
# 3. VPC 路由表自动同步(无需手工加)
tccli vpc DescribeRouteTables --region <REGION-B> --Limit 100                                                 # 10.10.0.0/16 -> CCN

安全组也确认了一下:腾讯节点安全组里本就有 10.0.0.0/8 放通,海外网段在内,无需改安全组

回到海外 ECS 验证端到端:

ping -c 3 10.20.0.18
# 3 received, 0% packet loss, rtt 223 ms   ← 从 100% 丢包黑洞,到 0% 丢包稳定

跨境固有 RTT ~220ms(物理距离决定,专线也省不掉),但丢包没了——这才是 28s 超时的根因所在。

5.2 应用层:建内网 Ingress,把流量切到专线

专线通了,但上游服务还在走公网域名。要切到内网,需要给被调服务一个内网入口,并让上游用它。这里用「内网 Ingress + 独立内网域名」的方案,在腾讯 TKE 里照着现有的公网 Ingress 改一份:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-inner
  namespace: prod
  annotations:
    kubernetes.io/ingress.class: qcloud
    ingress.cloud.tencent.com/direct-access: "true"
    kubernetes.io/ingress.subnetId: subnet-xxxxxxxx          # 内网 CLB 落的子网
  labels:
    ingress.cloud.tencent.com/loadbalance-type: INTERNAL     # 关键:内网
spec:
  rules:
  - host: api-inner.example.com                              # 独立内网域名
    http:
      paths:
      - backend:
          service:
            name: abc-svc                           # 见下方「坑三」:必须是有后端的那个 service
            port:
              number: 80
        path: /img
        pathType: ImplementationSpecific

apply 后拿到一个内网 CLB VIP(10.20.2.34),配上内网 DNS api-inner.example.com → 10.20.2.34,上游服务把目标域名一换,流量就走专线了。

六、四个坑

坑一:用 GET 测一个 POST 接口,被 302/404 带偏

验证内网入口时,顺手 curl 了一下,公网返回 302、内网返回 404,于是一头扎进"为什么状态码不一致"猜了半天 Host、backend。真相是 /img/generate 是 POST 接口,GET 打它,后端返回什么都不奇怪(302/404 全是假象)。

教训:验证业务接口一定用真实的方法和 body。换成 POST + 真实参数后,内外网行为立刻一致。怎么拿到真实参数?去后端 CLS 日志里捞一条真实请求:

tccli cls SearchLog --region <REGION-B> --TopicId <TOPIC_ID> \
  --From <ms1> --To <ms2> --Query '"generate request"' --Limit 4 --Sort desc
# generate request={"prompt":"...","upload_key_prefix":"img/20260101/"}

坑二:内网 CLB 第一个实例创建卡住,重建才好

第一次建的内网 CLB,VIP ping 100% 丢包、80 端口 filtered,tracepath 到腾讯边界黑洞。一度怀疑"腾讯内网 CLB 不支持被 CCN 访问"。这个推断是错的——删掉重建一个新 CLB,VIP 立刻可达。就是第一个 CLB 实例 provision 卡住/异常,跟 CCN 支不支持无关。遇到内网 CLB VIP 不通,先重建一次再下结论。

坑三:backend 指向了一个「空 service」

腾讯 qcloud Ingress 实际转发看的是它的 http-rules/spec.backend。排查时发现集群里有两个相似的 service:

kubectl get endpoints abc-empty-svc          -n prod   # ENDPOINTS  <none>   ← 空壳,没有后端
kubectl get endpoints abc-svc -n prod   # 10.20.0.x:8000 ... (14 个 Pod)

abc-empty-svc 是个没有任何 endpoint 的空 service(从它的 ClusterIP 发起连接直接 connection refused)。内网 Ingress 的 backend 一旦指到它,就不通。必须指向有真实 Pod 的 abc-svc

坑四:ListTransitRouterRouteEntries 默认只返 20 条

前面提过——阿里云 CEN 查 TR 路由条目,aliyun cbn ListTransitRouterRouteEntries 默认分页 20 条,关键网段排在后面就被截断,直接误导出"路由没传过来"的错误结论。永远带 --MaxResults 100,并留意返回里的 NextToken

七、验证:真实出图,内网 1.6s vs 公网 2.2s

用从日志捞到的真实 body,内外网各打一发对照:

# 内网(走内网 VIP,--resolve 强制解析)
curl -X POST --resolve api-inner.example.com:80:10.20.2.34 \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"a 1970s discotheque in a snowy alpine landscape","upload_key_prefix":"img/20260101/"}' \
  -w '\n[code=%{http_code} total=%{time_total}s]\n' \
  http://api-inner.example.com/img/generate
# {"code":0,"data":{"out_imgs":["img/20260101/xxxx.jpg"]}}   [code=200 total=1.63s]

# 公网对照
# {"code":0,"data":{"out_imgs":["img/20260101/yyyy.jpg"]}}   [code=200 total=2.24s]

两边都 code=0 真实出图、功能完全一致;内网 1.63s vs 公网 2.24s。GPU 出图本身 ~1.4s 是一样的,差在网络:内网专线握手快且稳定——切内网解决的是"高峰偶发暴雷(28s)",而不是日常快几百毫秒。

再用两次请求的 trace_id 去 CLS 反查落点,确认内外网打到的是同一套 abc deployment 的 Pod(只是不同副本),后端一致。收工。

八、三天后复查:这个"超时归零"是真的吗?

修复当天的漂亮数字不算数——改完容易一时好转、过几天又回潮。隔了三天回头查,这次带着一个问题:这个"0"会不会是假象?

先看趋势,还是那条 SLS 聚合,按天数 /img/generate 的跨境超时:

日期      context deadline exceeded 次数
06-02       30
06-03      380
06-04     1390   ← 峰值(故障当天)
06-05       22   ← 修复落地,收尾的零星
06-06        0
06-07        0
06-08        0   ← 连续三天归零

峰值 1390 一路降到连续三天 0。但"日志查不到"有三种假象,逐一排除才敢说真归零:

① 采集断了? 查同一个 logstore 的全量超时(不限这个接口),06-06/07/08 每天仍有几万条(66330 / 20317 / 11266)——日志在正常进来,不是采集挂了导致的空。

② 没流量了? 万一这几天根本没人出图,自然不超时。查监控里的出图请求量,每天增量稳定在 ~9 万(90526 / 58809 / 89247),出图一直在正常跑。

③ 真切到专线了,还是公网碰巧通畅? 这点最该坐实——前两条排除了"假 0",但"超时为 0"既可能是流量切到了内网专线,也可能是公网这几天恰好没拥塞。登上游服务所在的海外 ECS 实测:

# 内网域名现在解析到哪
$ getent hosts api-inner.example.com
10.20.2.34      api-inner.example.com        # ← 内网 VIP(专线)

# /etc/hosts 里那行其实还是注释态 —— 靠内网 DNS(PrivateZone)生效,不是 hosts
$ grep api-inner /etc/hosts
#10.20.2.34     api-inner.example.com

# 当前出图的 established 连接,到底连内网还是公网
$ ss -tn | grep -E '10.20.2.34|<对端公网IP>' | awk '{print $5}' | sort | uniq -c
      4 10.20.2.34:80              # 内网专线:4 个活跃连接
#       公网 <对端公网IP>:80 —— 0 个

内网 4 个活跃连接、公网 0 个——流量是真切到专线了。归零的真因是"残留流量切上了内网",不是"公网碰巧没犯病"。修复当天内网 DNS 还没配好、上游还在用公网域名(所以 06-05 还剩 22 条);这三天 DNS 落地、上游用上内网域名,才彻底干净。

一个教训:改完别只看当天数字。几天后带着"会不会是假 0"的怀疑再复查一遍,把采集没断、流量正常、链路实际走向三样都验证过,才算真闭环——尤其"超时归零"这种结论,既可能是真修好,也可能是诱因恰好消失,不实测链路根本分不清。

快速参考

排查链路(从现象到根因)

  1. SLS 聚合超时,看时间规律(高峰才犯 → 怀疑跨境公网);
  2. CLS + kubectl 证明后端无辜(后端 1~2s,差额在传输);
  3. 海外 ping 腾讯内网对比"北京通/海外不通" → 专线没接上海外;
  4. 阿里云 CEN 路由 → 腾讯 VPC 路由 → CCN 路由 → 专线网关静态网段列表,逐层下钻;
  5. 找到 CcnRouteType=STATIC 的网段列表漏了海外网段。

关键命令速查

# 阿里云 CEN:查 TR 路由条目(务必带 --MaxResults)
aliyun cbn ListTransitRouterRouteEntries --TransitRouterRouteTableId <tr-rtb> --region <R> --MaxResults 100
# 腾讯:VPC 路由 / CCN 路由 / 专线网关发布的网段
tccli vpc DescribeRouteTables --region <R> --Limit 100
tccli vpc DescribeCcnRoutes --CcnId ccn-xxxx --region <R>
tccli vpc DescribeDirectConnectGatewayCcnRoutes --DirectConnectGatewayId dcg-xxxx --region <R>
tccli vpc DescribeDirectConnectGateways --DirectConnectGatewayIds '["dcg-xxxx"]' --region <R>  # 看 CcnRouteType
# 专线网关补一条静态网段(打通回程的关键)
tccli vpc CreateDirectConnectGatewayCcnRoutes --DirectConnectGatewayId dcg-xxxx \
  --Routes '[{"DestinationCidrBlock":"10.10.0.0/16"}]' --region <R>

铁律 / 易错点

  • 跨云专线双向都要通:去程靠阿里云 CEN 把网段传过去,回程靠腾讯专线网关把对端网段加进发布给 CCN 的静态列表——只配一边等于没配。
  • 专用通道 BGP ≠ 专线网关→CCN 的 STATIC,别被"专线是 BGP"误导,后者是手工网段列表。
  • 验证业务接口用真实方法 + 真实 body,别用 GET 测 POST,状态码会骗你。
  • 内网 CLB 的 backend 必须指向有 endpoint 的 service(kubectl get endpoints 先看一眼,<none> 的是空壳)。
  • 内网 CLB VIP 不通先重建一次再排查。
  • aliyun cbn ListTransitRouterRouteEntries 默认 20 条,--MaxResults 100,别被分页截断误判。
  • 跨境专线根治的是丢包/抖动/超时,固有 RTT(物理距离)省不掉。
posted @ 2026-06-05 09:58  Hello_worlds  阅读(41)  评论(0)    收藏  举报