ACK 跨 CEN 拉取 ACR VPC 镜像 ImagePullBackOff: dockerauth-ee-vpc 超时排查

以下命令里的地域、VPC、TR、路由表、域名和 IP 都做了占位处理。为了让链路方向更容易读,本文用“杭州侧”表示 ACR 所在地域,用“泰国侧”表示 ACK 集群所在地域。落地时把 <REGION_A><REGION_B><CEN_ID><TR_*><VPC_*><ROUTE_TABLE_*><ACR_AUTH_IP><ACR_OSS_INTERNAL_CIDR> 等替换成自己的真实值即可。

一次 ACK 发布失败时,业务 Pod 卡在 ImagePullBackOff。表面看是镜像拉不下来,但真正的断点不在镜像仓库域名,而在 ACR VPC 鉴权域名:

failed to fetch oauth token:
Post "https://dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/auth":
dial tcp <ACR_AUTH_IP>:443: i/o timeout

这类问题容易误判成“镜像太大”“节点拉镜像慢”“公网链路抖动”。实际排查时要把镜像拉取拆成三条链路:

registry-vpc.<REGION_A>.cr.aliyuncs.com:443                    镜像仓库
dockerauth-ee-vpc.<REGION_A>.aliyuncs.com:443                  ACR OAuth 鉴权
<acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com:443  blob layer 下载

只要鉴权域名不通,镜像再小也拉不下来;鉴权通了以后,真正下载 layer 时还可能卡在 OSS internal 地址。

ACK 跨地域拉取 ACR VPC 镜像的关键路径

环境和现象

环境抽象如下:

项目 示例
ACK 集群所在地域 泰国侧,命令中写作 <REGION_B>,RegionId 示例 ap-southeast-7
ACR 企业版实例所在地域 杭州侧,命令中写作 <REGION_A>,RegionId 示例 cn-hangzhou
ACK 节点示例 IP 192.168.1.1
ACR VPC Registry <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com
ACR VPC Auth dockerauth-ee-vpc.<REGION_A>.aliyuncs.com
ACR Auth 私网 IP <ACR_AUTH_IP>
ACR OSS Internal <acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com
ACR OSS Internal 网段 <ACR_OSS_INTERNAL_CIDR>
打通方式 CEN / 企业版转发路由器 TR

先用一个很小的 busybox 镜像测试,不要一开始就拿几百 MB 或几十 GB 的业务镜像测。测试 Pod 固定到问题节点上:

apiVersion: v1
kind: Pod
metadata:
  name: acr-vpc-pull-test
  namespace: test
spec:
  nodeName: <ACK_NODE_NAME>
  restartPolicy: Never
  containers:
  - name: busybox
    image: <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/devops/busybox:latest
    imagePullPolicy: Always
    command: ["sh", "-c", "sleep 3600"]

Pod 事件里看到的错误:

kubectl -n test get pod acr-vpc-pull-test -o wide

kubectl -n test get pod acr-vpc-pull-test \
  -o jsonpath='phase={.status.phase}{"\n"}{range .status.containerStatuses[*]}name={.name} ready={.ready} imageID={.imageID} state={.state}{"\n"}{end}'

kubectl -n test get events \
  --field-selector involvedObject.name=acr-vpc-pull-test \
  --sort-by=.lastTimestamp

关键输出类似:

STATUS: ImagePullBackOff

failed to pull and unpack image ".../devops/busybox:latest":
failed to authorize:
failed to fetch oauth token:
Post "https://dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/auth":
dial tcp <ACR_AUTH_IP>:443: i/o timeout

这里已经能确认:失败发生在 ACR 鉴权阶段,而不是业务容器启动、readiness、镜像命令或应用本身。

一、先在 ACK 节点上拆开测 registry 和 auth

登录到实际承载 Pod 的 ACK 节点,只测 TCP 443:

getent hosts <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com
getent hosts dockerauth-ee-vpc.<REGION_A>.aliyuncs.com

ip route get <ACR_AUTH_IP>

timeout 8 bash -lc '</dev/tcp/<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/443' \
  && echo REGISTRY_VPC_443_OK || echo REGISTRY_VPC_443_FAIL

timeout 8 bash -lc '</dev/tcp/dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/443' \
  && echo DOCKERAUTH_VPC_443_OK || echo DOCKERAUTH_VPC_443_FAIL

这次现场的关键结果是:

<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com:443  OK
dockerauth-ee-vpc.<REGION_A>.aliyuncs.com:443          FAIL

所以不能再说“VPC ACR 都不通”。它只是不通鉴权服务。

为了排除 ACR 自身故障,再找一台 ACR 所在地域 VPC 内的 ECS 做同样测试:

getent hosts <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com
getent hosts dockerauth-ee-vpc.<REGION_A>.aliyuncs.com

timeout 8 bash -lc '</dev/tcp/<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/443' \
  && echo REGISTRY_VPC_443_OK || echo REGISTRY_VPC_443_FAIL

timeout 8 bash -lc '</dev/tcp/dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/443' \
  && echo DOCKERAUTH_VPC_443_OK || echo DOCKERAUTH_VPC_443_FAIL

如果同地域 VPC 内结果是两个都 OK,而跨地域 ACK 节点只有 auth FAIL,问题边界就收敛到 CEN/TR 路由。

二、确认 ACK 节点属于哪个 VPC 和安全组

先拿到节点对应 ECS 实例的 VPC、交换机、安全组:

aliyun ecs DescribeInstances \
  --RegionId <REGION_B> \
  --InstanceIds '["<ACK_NODE_INSTANCE_ID>"]' \
| jq -r '.Instances.Instance[] | {
    InstanceId,
    InstanceName,
    ZoneId,
    VpcId:.VpcAttributes.VpcId,
    VSwitchId:.VpcAttributes.VSwitchId,
    PrivateIp:.VpcAttributes.PrivateIpAddress.IpAddress[0],
    SecurityGroups:.SecurityGroupIds.SecurityGroupId
  }'

再查安全组出方向,避免被 egress 限制误导:

aliyun ecs DescribeSecurityGroupAttribute \
  --RegionId <REGION_B> \
  --SecurityGroupId <ACK_NODE_SECURITY_GROUP_ID> \
  --Direction egress \
| jq -r '.Permissions.Permission[]? |
  [.Direction,.IpProtocol,.PortRange,.DestCidrIp,.Policy,.Priority,.Description] | @tsv'

如果出方向已经放行 0.0.0.0/0 或至少放行 <ACR_AUTH_IP>:443,就继续看路由。

三、VPC 控制台:确认泰国侧 ACK VPC 路由表是否把杭州侧 ACR Auth IP 指向 TR

这一节配置的是 泰国侧 ACK 节点所在 VPC 的路由表。入口在 VPC 控制台,不在 CEN 控制台。

配置入口:

阿里云控制台
-> 专有网络 VPC
-> 切到泰国侧 ACK 集群所在地域 <REGION_B>
-> 专有网络
-> 进入泰国侧 ACK 节点所在 VPC <VPC_ACK>
-> 路由表
-> 选择 ACK 节点交换机绑定的路由表 <ACK_VPC_ROUTE_TABLE_ID>

查询 ACK VPC 的路由表:

aliyun vpc DescribeRouteTableList \
  --RegionId <REGION_B> \
  --VpcId <VPC_ACK> \
| jq -r '.RouterTableList.RouterTableListType[]? |
  [.RouteTableId,.RouteTableName,.RouteTableType,.VpcId,(.VSwitchIds.VSwitchId|tostring)] | @tsv'

查询路由条目:

aliyun vpc DescribeRouteEntryList \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --MaxResult 100 \
| jq -r '.RouteEntrys.RouteEntry[]? |
  [.DestinationCidrBlock,.NextHopType,(.NextHops.NextHop[0].NextHopId // .NextHopId // ""),.Status,.RouteEntryType,.ServiceType,.RouteEntryName] | @tsv' \
| sort

这里要重点找:

<ACR_AUTH_IP>/32 -> <SG_TR_VPC_ATTACHMENT>

阿里云会提示:

100.64.0.0/10 及其子集是云服务网段,在配置跨地域访问云服务时建议配置服务所在地域的精细 IP,
避免 IP 配置范围过大导致本地域云服务不通。

所以不要粗暴把 100.64.0.0/10 全部扔进 CEN。这里应该只配 dockerauth-ee-vpc 解析出来的精细 IP,例如 <ACR_AUTH_IP>/32

如果 ACK VPC 路由表缺这条,就在 ACK 节点交换机绑定的路由表里补。节点池如果跨多个交换机,要确认这些交换机是不是绑定同一张路由表;如果不是,就逐张补同一条路由。

新增路由条目的控制台入口:

VPC 控制台
-> 路由表 <ACK_VPC_ROUTE_TABLE_ID>
-> 路由条目列表
-> 添加路由条目

表单这样填:

字段
名称 acr-dockerauth-vpc-to-tr
资源组 可不选
目标网段 <ACR_AUTH_IP>/32
下一跳类型 转发路由器
转发路由器 <VPC_ATTACHMENT_REGION_B>,也就是泰国侧 ACK VPC 连接到泰国侧 TR 的 VPC Attachment
描述 Route ACR VPC dockerauth traffic to TR

CLI 模板:

aliyun vpc CreateRouteEntry \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_AUTH_IP>/32 \
  --NextHopType Attachment \
  --NextHopId <VPC_ATTACHMENT_REGION_B> \
  --RouteEntryName acr-dockerauth-vpc-to-tr \
  --Description 'Route ACR VPC dockerauth traffic to TR'

如果 ACK VPC 路由表已经有 <ACR_AUTH_IP>/32 -> TR Attachment,但节点仍然超时,说明问题不在 VPC 路由表本身,而是在 TR 路由表里。

四、CEN 控制台:确认泰国侧 / 杭州侧 VPC Attachment 和跨地域 Peer

这一节配置的是 CEN / 企业版转发路由器 TR 的连接关系。入口在 CEN 控制台,不在 VPC 路由表里。

配置入口:

阿里云控制台
-> 云企业网 CEN
-> 转发路由器
-> 选择对应地域的 TR,例如泰国侧 <TR_REGION_B> 或杭州侧 <TR_REGION_A>
-> 网络实例连接 / 跨地域连接

列出 CEN:

aliyun cbn DescribeCens --PageSize 50 \
| jq -r '.Cens.Cen[]? | [.CenId,.Name,.Status,.CreationTime] | @tsv'

确认两个地域的 VPC 都已挂载:

aliyun cbn DescribeCenAttachedChildInstances \
  --CenId <CEN_ID> \
  --ChildInstanceType VPC \
  --ChildInstanceRegionId <REGION_A> \
  --PageNumber 1 \
  --PageSize 50 \
| jq -r '.ChildInstances.ChildInstance[]? |
  [.ChildInstanceId,.ChildInstanceRegionId,.ChildInstanceType,.Status,.ChildInstanceOwnerId] | @tsv'

aliyun cbn DescribeCenAttachedChildInstances \
  --CenId <CEN_ID> \
  --ChildInstanceType VPC \
  --ChildInstanceRegionId <REGION_B> \
  --PageNumber 1 \
  --PageSize 50 \
| jq -r '.ChildInstances.ChildInstance[]? |
  [.ChildInstanceId,.ChildInstanceRegionId,.ChildInstanceType,.Status,.ChildInstanceOwnerId] | @tsv'

企业版转发路由器场景下,再查两个地域的 TR:

aliyun cbn ListTransitRouters \
  --CenId <CEN_ID> \
| jq -r '.TransitRouters[]? | [.TransitRouterId,.TransitRouterName,.RegionId,.Status,.Type] | @tsv'

查 ACK 所在地域的 VPC attachment:

aliyun cbn ListTransitRouterVpcAttachments \
  --CenId <CEN_ID> \
  --RegionId <REGION_B> \
  --MaxResults 100 \
| jq -r '.TransitRouterAttachments[]? |
  [.TransitRouterAttachmentId,.TransitRouterId,.RegionId,.VpcId,.VpcOwnerId,.Status,.TransitRouterAttachmentName] | @tsv'

查 ACR 所在地域的 VPC attachment:

aliyun cbn ListTransitRouterVpcAttachments \
  --CenId <CEN_ID> \
  --RegionId <REGION_A> \
  --MaxResults 100 \
| jq -r '.TransitRouterAttachments[]? |
  [.TransitRouterAttachmentId,.TransitRouterId,.RegionId,.VpcId,.VpcOwnerId,.Status,.TransitRouterAttachmentName] | @tsv'

如果某个 VPC 没挂到对应地域的 TR,需要先创建 VPC Attachment。控制台入口:

阿里云控制台
-> 云企业网 CEN
-> 转发路由器
-> 选择对应地域的 TR <TR_REGION_X>
-> 网络实例连接
-> 创建网络实例连接
-> 网络实例类型选择 VPC

泰国侧 ACK 所在地域 VPC Attachment 的表单重点:

字段
地域 泰国侧 <REGION_B>
转发路由器 泰国侧 <TR_REGION_B>
网络实例类型 VPC
专有网络 <VPC_ACK>
交换机 选择 ACK VPC 内用于连接 TR 的可用区和交换机
名称 cen-vpc-ack
自动发布路由 按现网策略选择;不确定时先关闭,后续用静态路由精细控制

杭州侧也按同样方式把 ACR 所在 VPC 挂到杭州侧 <TR_REGION_A>

CLI 预检模板:

aliyun cbn CreateTransitRouterVpcAttachment \
  --DryRun true \
  --CenId <CEN_ID> \
  --RegionId <REGION_B> \
  --TransitRouterId <TR_REGION_B> \
  --VpcId <VPC_ACK> \
  --ZoneMappings.1.ZoneId <ZONE_ID_1> \
  --ZoneMappings.1.VSwitchId <VSWITCH_ID_1> \
  --TransitRouterAttachmentName cen-vpc-ack \
  --TransitRouterAttachmentDescription 'Attach ACK VPC to TR'

查两个地域之间的 peer attachment:

aliyun cbn ListTransitRouterPeerAttachments \
  --CenId <CEN_ID> \
  --RegionId <REGION_B> \
  --MaxResults 100 \
| jq -r '.TransitRouterAttachments[]? |
  [.TransitRouterAttachmentId,.TransitRouterId,.PeerTransitRouterRegionId,.PeerTransitRouterId,.Status,.TransitRouterAttachmentName] | @tsv'

aliyun cbn ListTransitRouterPeerAttachments \
  --CenId <CEN_ID> \
  --RegionId <REGION_A> \
  --MaxResults 100 \
| jq -r '.TransitRouterAttachments[]? |
  [.TransitRouterAttachmentId,.TransitRouterId,.PeerTransitRouterRegionId,.PeerTransitRouterId,.Status,.TransitRouterAttachmentName] | @tsv'

需要看到跨地域 peer attachment 是 Attached

如果两个地域 TR 之间没有 peer attachment,需要创建跨地域连接。控制台入口:

阿里云控制台
-> 云企业网 CEN
-> 转发路由器
-> 选择泰国侧本端地域 TR <TR_REGION_B>
-> 跨地域连接
-> 创建跨地域连接

表单重点:

字段
本端地域 泰国侧 <REGION_B>
本端转发路由器 泰国侧 <TR_REGION_B>
对端地域 杭州侧 <REGION_A>
对端转发路由器 杭州侧 <TR_REGION_A>
带宽分配方式 按现网计费和带宽规划选择
名称 cen-peer-region-b-region-a
自动发布路由 按现网策略选择;排障时建议先用静态路由精细控制

CLI 预检模板:

aliyun cbn CreateTransitRouterPeerAttachment \
  --DryRun true \
  --CenId <CEN_ID> \
  --RegionId <REGION_B> \
  --TransitRouterId <TR_REGION_B> \
  --PeerTransitRouterRegionId <REGION_A> \
  --PeerTransitRouterId <TR_REGION_A> \
  --BandwidthType DataTransfer \
  --TransitRouterAttachmentName cen-peer-region-b-region-a \
  --TransitRouterAttachmentDescription 'Peer TR between ACK region and ACR region'

五、CEN 控制台:查泰国侧 / 杭州侧 TR 路由表有没有 ACR Auth IP

这一节配置的是 转发路由器 TR 的路由表。入口仍然在 CEN 控制台,但它和第四节不同:第四节看连接是否存在,第五节看路由条目是否存在。

配置入口:

阿里云控制台
-> 云企业网 CEN
-> 转发路由器
-> 选择泰国侧 <TR_REGION_B> 或杭州侧 <TR_REGION_A>
-> 路由表
-> 选择路由表 <TR_RT_REGION_B 或 TR_RT_REGION_A>
-> 路由条目

列出泰国侧 ACK 所在地域 TR 的路由表:

aliyun cbn ListTransitRouterRouteTables \
  --TransitRouterId <TR_REGION_B> \
  --MaxResults 100 \
| jq -r '.TransitRouterRouteTables[]? |
  [.TransitRouterRouteTableId,.TransitRouterRouteTableName,.TransitRouterRouteTableType,.TransitRouterRouteTableStatus] | @tsv'

列出杭州侧 ACR 所在地域 TR 的路由表:

aliyun cbn ListTransitRouterRouteTables \
  --TransitRouterId <TR_REGION_A> \
  --MaxResults 100 \
| jq -r '.TransitRouterRouteTables[]? |
  [.TransitRouterRouteTableId,.TransitRouterRouteTableName,.TransitRouterRouteTableType,.TransitRouterRouteTableStatus] | @tsv'

然后查泰国侧 ACK 所在地域 TR 是否有杭州侧 ACR Auth IP <ACR_AUTH_IP>/32

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_B> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

再查杭州侧 ACR 所在地域 TR 是否有 <ACR_AUTH_IP>/32

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_A> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

这次现场就是两边 TR 路由表都没有 <ACR_AUTH_IP>/32。ACK VPC 路由表虽然把流量送进了 TR,但 TR 自己没有下一跳,最终表现为节点访问 dockerauth-ee-vpc 超时。

同时可以查回程路由作为旁证:

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_A> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACK_NODE_CIDR> \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

例如 <ACK_NODE_CIDR> 是 ACK 节点所在交换机或 VPC 网段。回程路由存在时,问题通常集中在去程的云服务 IP 路由。

ACK 跨 CEN 拉取 ACR VPC 镜像: 从超时到 Running

如果缺失,就在第五步直接补两边 TR 静态路由。

1. 泰国侧 ACK 所在地域 TR:把 Auth IP 指向跨地域 Peer

控制台添加入口:

CEN 控制台
-> 转发路由器
-> 泰国侧 <TR_REGION_B>
-> 路由表
-> <TR_RT_REGION_B>
-> 路由条目
-> 添加路由条目

控制台字段:

字段
路由表 <TR_RT_REGION_B>
所属转发路由器 泰国侧 <TR_REGION_B>
路由条目名称 acr-dockerauth-vpc-to-region-a
目的地址 CIDR <ACR_AUTH_IP>/32
是否为黑洞路由
下一跳连接 <PEER_ATTACHMENT_REGION_B_TO_A>
描述 Route ACR VPC dockerauth to region A via CEN

CLI 模板:

aliyun cbn CreateTransitRouterRouteEntry \
  --TransitRouterRouteTableId <TR_RT_REGION_B> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryNextHopType Attachment \
  --TransitRouterRouteEntryNextHopId <PEER_ATTACHMENT_REGION_B_TO_A> \
  --TransitRouterRouteEntryName acr-dockerauth-vpc-to-region-a \
  --TransitRouterRouteEntryDescription 'Route ACR VPC dockerauth to region A via CEN'

2. 杭州侧 ACR 所在地域 TR:把 Auth IP 指向 ACR 所在 VPC Attachment

控制台添加入口:

CEN 控制台
-> 转发路由器
-> 杭州侧 <TR_REGION_A>
-> 路由表
-> <TR_RT_REGION_A>
-> 路由条目
-> 添加路由条目

控制台字段:

字段
路由表 <TR_RT_REGION_A>
所属转发路由器 杭州侧 <TR_REGION_A>
路由条目名称 acr-dockerauth-vpc
目的地址 CIDR <ACR_AUTH_IP>/32
是否为黑洞路由
下一跳连接 <VPC_ATTACHMENT_REGION_A>
描述 Route ACR VPC dockerauth to ACR VPC

CLI 模板:

aliyun cbn CreateTransitRouterRouteEntry \
  --TransitRouterRouteTableId <TR_RT_REGION_A> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryNextHopType Attachment \
  --TransitRouterRouteEntryNextHopId <VPC_ATTACHMENT_REGION_A> \
  --TransitRouterRouteEntryName acr-dockerauth-vpc \
  --TransitRouterRouteEntryDescription 'Route ACR VPC dockerauth to ACR VPC'

如果担心误操作,先加 --DryRun true 做预检。

六、验证:先测节点,再看 Pod

先查两条路由都已经 Active:

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_B> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_A> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

期望看到:

TransitRouterRouteEntryStatus: Active
TransitRouterRouteEntryType: Static
TransitRouterRouteEntryNextHopType: Attachment

再回到 ACK 节点测:

getent hosts dockerauth-ee-vpc.<REGION_A>.aliyuncs.com
ip route get <ACR_AUTH_IP>

timeout 8 bash -lc '</dev/tcp/dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/443' \
  && echo DOCKERAUTH_VPC_443_OK || echo DOCKERAUTH_VPC_443_FAIL

timeout 8 bash -lc '</dev/tcp/<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/443' \
  && echo REGISTRY_VPC_443_OK || echo REGISTRY_VPC_443_FAIL

修复后应该是:

DOCKERAUTH_VPC_443_OK
REGISTRY_VPC_443_OK

最后看测试 Pod:

kubectl -n test get pod acr-vpc-pull-test -o wide

kubectl -n test get pod acr-vpc-pull-test \
  -o jsonpath='phase={.status.phase}{"\n"}{range .status.containerStatuses[*]}name={.name} ready={.ready} restart={.restartCount} imageID={.imageID} state={.state}{"\n"}{end}'

kubectl -n test get events \
  --field-selector involvedObject.name=acr-vpc-pull-test \
  --sort-by=.lastTimestamp \
| tail -30

期望状态:

STATUS: Running
READY: 1/1
imageID: <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/devops/busybox@sha256:...

这时才说明 ACR VPC 拉取链路真的打通了。

七、auth 通了以后,还要检查 OSS internal blob 下载

有一种容易漏的情况:registry-vpcdockerauth-ee-vpc 都通了,小镜像也可能拉通,但业务镜像仍然失败。Pod 事件里会出现类似错误:

failed to copy:
httpReadSeeker: failed open:
failed to do request:
Get "https://<acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com/docker/registry/v2/blobs/.../data?...":
dial tcp <ACR_OSS_INTERNAL_IP>:443: i/o timeout

这说明已经越过 OAuth 鉴权,卡在下载镜像 layer 的 OSS internal 地址上。先在 ACK 节点上拆开测:

OSS_HOST=<acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com

getent hosts "$OSS_HOST"

for ip in <ACR_OSS_INTERNAL_IP_1> <ACR_OSS_INTERNAL_IP_2> <ACR_OSS_INTERNAL_IP_3> <ACR_OSS_INTERNAL_IP_4>; do
  echo "ip route get $ip"
  ip route get "$ip" 2>&1 || true
done

timeout 8 bash -lc "</dev/tcp/$OSS_HOST/443" \
  && echo OSS_INTERNAL_HOST_443_OK || echo OSS_INTERNAL_HOST_443_FAIL

for ip in <ACR_OSS_INTERNAL_IP_1> <ACR_OSS_INTERNAL_IP_2> <ACR_OSS_INTERNAL_IP_3> <ACR_OSS_INTERNAL_IP_4>; do
  timeout 8 bash -lc "</dev/tcp/$ip/443" && echo "$ip OK" || echo "$ip FAIL"
done

如果 DNS 返回多个 IP,不要只给失败日志里出现的那个 IP 加 /32。更稳妥的是确认这些 IP 落在哪个精细网段,例如:

<ACR_OSS_INTERNAL_CIDR> -> 例如某个 /24

然后分别核对三层路由。

ACK VPC 路由表:

aliyun vpc DescribeRouteEntryList \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --MaxResult 100

ACK 所在地域 TR 路由表:

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_B> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

ACR 所在地域 TR 路由表:

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_RT_REGION_A> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

如果两边 TR 已经有 <ACR_OSS_INTERNAL_CIDR>,但泰国侧 ACK VPC 路由表没有,就在泰国侧 ACK VPC 路由表补一条到本地域 TR attachment。注意这一步不是去 CEN/TR 路由表里加,而是去泰国侧 ACK 节点所在地域的 VPC 路由表里加。

先确认要改哪张 VPC 路由表。ACK 节点本质上是 ECS,路由表是跟节点所在交换机绑定的:

# 1. 从 Pod 反查它落在哪个 ACK 节点
kubectl -n <NAMESPACE> get pod <POD_NAME> -o wide

# 2. 看节点的 providerID,里面通常能看到 ECS 实例 ID
kubectl get node <ACK_NODE_NAME> -o jsonpath='{.spec.providerID}{"\n"}'

# 3. 查这个 ECS 实例属于哪个 VPC / VSwitch
aliyun ecs DescribeInstances \
  --RegionId <REGION_B> \
  --InstanceIds '["<ECS_INSTANCE_ID>"]'

# 4. 查这个 VSwitch 绑定的路由表
aliyun vpc DescribeVSwitchAttributes \
  --RegionId <REGION_B> \
  --VSwitchId <ACK_NODE_VSWITCH_ID>

如果 ACK 节点池跨了多个交换机,就把每个节点交换机对应的路由表都查出来。生产里常见的是多个交换机共用同一张系统路由表;如果有自定义路由表,就要按实际绑定关系逐张补。

控制台操作路径:

阿里云控制台
-> 专有网络 VPC
-> 切到泰国侧 ACK 集群所在地域 <REGION_B>
-> 专有网络
-> 进入泰国侧 ACK 节点所在 VPC <VPC_ACK>
-> 路由表
-> 选择 ACK 节点交换机绑定的路由表 <ACK_VPC_ROUTE_TABLE_ID>
   如果节点跨多个交换机,逐个确认这些交换机绑定的路由表
-> 路由条目列表
-> 添加路由条目

表单这样填:

字段
名称 acr-oss-vpc
资源组 可不选
目标网段 <ACR_OSS_INTERNAL_CIDR>
下一跳类型 转发路由器
转发路由器 <VPC_ATTACHMENT_REGION_B>,也就是泰国侧 ACK VPC 连接到泰国侧 TR 的 VPC Attachment
描述 Route ACR OSS internal blob traffic via CEN

用这次这种场景举例,表单含义是:

目标网段: 杭州侧 ACR 后端 OSS internal 域名解析出的精细网段
下一跳类型: 转发路由器
转发路由器: 泰国侧 ACK VPC 挂到泰国侧 TR 的 VPC attachment

提交后,泰国侧 ACK 节点访问这个杭州侧 OSS internal 网段的流量才会从 VPC 路由表进入 CEN/TR。否则即使两边 TR 路由已经存在,ACK 节点本地也不会把流量送进去。

CLI 模板:

aliyun vpc CreateRouteEntry \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --NextHopType Attachment \
  --NextHopId <VPC_ATTACHMENT_REGION_B> \
  --RouteEntryName acr-oss-vpc \
  --Description 'Route ACR OSS internal blob traffic via CEN'

这里的风险和前面的 auth IP 一样:不要为了省事把 100.64.0.0/10 整段加进去。优先使用 OSS internal 域名当前解析结果所在的精细网段;如果追求最小影响,可以先加多个 /32 做验证,但后续 DNS 漂移到同网段其它 IP 时可能再次失败。

补完后再测:

timeout 8 bash -lc "</dev/tcp/$OSS_HOST/443" \
  && echo OSS_INTERNAL_HOST_443_OK || echo OSS_INTERNAL_HOST_443_FAIL

最后用业务镜像而不是 busybox 再测一次:

kubectl -n test run acr-vpc-business-pull-test \
  --image=<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/prod/public:<business-tag> \
  --restart=Never \
  --overrides='{
    "apiVersion":"v1",
    "spec":{
      "nodeName":"<ACK_NODE_NAME>",
      "containers":[{
        "name":"acr-vpc-business-pull-test",
        "image":"<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/prod/public:<business-tag>",
        "imagePullPolicy":"Always",
        "command":["/bin/sh","-c","sleep 3600"]
      }]
    }
  }'

kubectl -n test get events \
  --field-selector involvedObject.name=acr-vpc-business-pull-test \
  --sort-by=.lastTimestamp

看到业务镜像 Successfully pulled image 后,才算完整验证了 registry、auth、OSS blob 下载三段链路。

八、最后再改 Jenkins 或发布系统

不要在 busybox 没测通前直接把发布系统切到 VPC 地址。推荐顺序是:

1. 小镜像 busybox 走 VPC 地址拉取成功
2. ACK 节点能连通 auth 和 OSS internal blob 地址
3. 业务镜像指定 tag 走 VPC 地址拉取成功
4. 再改 Jenkins / 发布系统里的 IMAGE_REPOSITORY
5. 发一次真实发布,观察 rollout 和 Pod 事件

仓库地址切换通常只是 registry 域名不同:

非 VPC:
<acr-name>-registry.<REGION_A>.cr.aliyuncs.com/prod/public

VPC:
<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/prod/public

但鉴权域名也会跟着变成 VPC 侧:

dockerauth-ee-vpc.<REGION_A>.aliyuncs.com

而 blob 下载会访问 ACR 后端的 OSS internal 域名:

<acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com

所以只测 registry 域名不够,必须一起测 auth 域名和 OSS internal 域名。

快速参考

节点侧最小验证:

getent hosts <acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com
getent hosts dockerauth-ee-vpc.<REGION_A>.aliyuncs.com
getent hosts <acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com
ip route get <ACR_AUTH_IP>
ip route get <ACR_OSS_INTERNAL_IP>

timeout 8 bash -lc '</dev/tcp/<acr-name>-registry-vpc.<REGION_A>.cr.aliyuncs.com/443' \
  && echo REGISTRY_VPC_443_OK || echo REGISTRY_VPC_443_FAIL

timeout 8 bash -lc '</dev/tcp/dockerauth-ee-vpc.<REGION_A>.aliyuncs.com/443' \
  && echo DOCKERAUTH_VPC_443_OK || echo DOCKERAUTH_VPC_443_FAIL

timeout 8 bash -lc '</dev/tcp/<acr-instance>-registry.oss-<REGION_A>-internal.aliyuncs.com/443' \
  && echo OSS_INTERNAL_443_OK || echo OSS_INTERNAL_443_FAIL

ACK Pod 事件:

kubectl -n test get pod <POD> -o wide
kubectl -n test get events --field-selector involvedObject.name=<POD> --sort-by=.lastTimestamp

CEN/TR 路由查询:

aliyun cbn ListTransitRouterRouteEntries \
  --TransitRouterRouteTableId <TR_ROUTE_TABLE_ID> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryStatus All \
  --MaxResults 100

ACK VPC 路由查询:

aliyun vpc DescribeRouteEntryList \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --MaxResult 100

新增 TR 静态路由:

aliyun cbn CreateTransitRouterRouteEntry \
  --TransitRouterRouteTableId <TR_ROUTE_TABLE_ID> \
  --TransitRouterRouteEntryDestinationCidrBlock <ACR_AUTH_IP>/32 \
  --TransitRouterRouteEntryNextHopType Attachment \
  --TransitRouterRouteEntryNextHopId <NEXT_HOP_ATTACHMENT_ID> \
  --TransitRouterRouteEntryName acr-dockerauth-vpc \
  --TransitRouterRouteEntryDescription 'Route ACR VPC dockerauth via CEN'

新增 ACK VPC 到 TR 的 Auth 路由:

aliyun vpc CreateRouteEntry \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_AUTH_IP>/32 \
  --NextHopType Attachment \
  --NextHopId <VPC_ATTACHMENT_REGION_B> \
  --RouteEntryName acr-dockerauth-vpc-to-tr \
  --Description 'Route ACR VPC dockerauth traffic to TR'

新增 ACK VPC 到 TR 的 OSS internal 路由:

aliyun vpc CreateRouteEntry \
  --RegionId <REGION_B> \
  --RouteTableId <ACK_VPC_ROUTE_TABLE_ID> \
  --DestinationCidrBlock <ACR_OSS_INTERNAL_CIDR> \
  --NextHopType Attachment \
  --NextHopId <VPC_ATTACHMENT_REGION_B> \
  --RouteEntryName acr-oss-vpc \
  --Description 'Route ACR OSS internal blob traffic via CEN'

几条经验:

  • ImagePullBackOff 里如果出现 failed to fetch oauth token,优先查 dockerauth,不要只查 registry。
  • registry-vpc:443 OK 不代表 ACR VPC 拉取链路完整。
  • dockerauth 通了也不代表完整,业务镜像下载大 layer 时还可能卡在 OSS internal。
  • 100.64.0.0/10 是云服务网段,跨地域访问云服务时尽量用精细 IP,例如 <ACR_AUTH_IP>/32
  • VPC 路由表有下一跳到 TR,不代表 TR 路由表也有下一跳。
  • 发布系统切 VPC 地址前,先用小镜像和业务镜像各测一次真实拉取。
posted @ 2026-07-07 16:03  Hello_worlds  阅读(1)  评论(0)    收藏  举报