背景脱敏:在某云厂商的托管 K8s 上部署一套海外集群,玩家流量走 CDN 接入,管理面(ArgoCD、Grafana、运营后台)要限制来源。本文记录入口拓扑定型过程中踩的两个坑,以及最终的拓扑。
坑一:Ingress 复用已有负载均衡器,只认「手动购买」的实例
托管 K8s 的 Ingress controller 支持通过注解指定「复用某个已有的负载均衡器(CLB)」。我们希望多个 Ingress 共用一个 CLB,好把安全组、DNS 都指到一个固定 IP。
实际行为:如果指定的 CLB 是之前由其他 Ingress 自动创建的,注解会被静默忽略,controller 自动购买一个新 CLB,并把新 CLB 的 ID 回写到 Ingress 注解里。
后果连环:
- git 里的注解和集群里的不一样,GitOps 报 OutOfSync;
- selfHeal 一同步,注解又被改回 git 版本,controller 再买一个;
- 自动创建的 CLB 生命周期跟随 Ingress,Ingress 删了 CLB 也没了,IP 不固定。
结论:凡是要配安全组、指 DNS、需要固定 IP 的场景,CLB 一律手动购买再让 Ingress 复用。 自动创建的 CLB 只适合一次性的、无状态的入口。
附带的第三个坑
Ingress 上加了 modification-protection 类注解(防止控制台误改)。在「多 Ingress 共用 CLB」的 Group 模式下,controller 不支持这个注解,于是拒绝同步整个 Ingress,不只是忽略那一个注解。
表现非常迷惑:CLB 上的转发规则一直是某天手工在控制台建的那份,钉死了当时的 pod IP。每次滚动更新,新 pod 都 ReadinessGateFailed,报 ingress rs not found。手工在控制台补后端能救当前 pod,下次滚动再挂。运维「修复」了两次,都是治标。
排障配方:别只看负载均衡器控制台,先读 Ingress 对象的 status.conditions 注解,controller 把拒绝同步的原因写在那里(AnnotationError / 错误码)。删掉不支持的注解后,卡住的 pod 自动就绪,旧 pod 下线,全程不用重发。
坑二:CDN 层的 IP 白名单可以被「直连 + Host 头」绕过
管理面域名最初和玩家域名一样走 CDN,靠 CDN 的 IP 白名单限制来源。
一条命令复现绕过:
curl -H "Host: admin.example.com" https://<回源负载均衡器IP>/
CDN 的白名单只在 CDN 边缘生效。回源 IP 是公网可达的,直接打回源 IP 并带上 Host 头,白名单形同虚设。这是结构性问题,不是配置错误。
根治:管理面域名从 CDN 摘掉,DNS 直指一个专属的手购 CLB,安全组白名单是唯一门禁。 玩家域名继续走 CDN。两个入口物理分离,各自的安全模型才成立。
切换时踩了一次:把玩家域名的回源切到新 CLB 之前,忘了先在安全组放行 CDN 的回源网段,正常玩家全断了 10 分钟。顺序必须是:先放行回源网段 → 再切回源。
附:三个排障陷阱
- 本机 hosts 脏条目。之前为了临时访问在 hosts 加过
IP 域名映射,后来 IP 换了。curl 走 hosts,浏览器走 DoH 不走 hosts,于是「curl 断了、浏览器正常」,误报一轮。探测 DNS 一律nslookup 域名 公共DNS。 - CDN 边缘配置分发有延迟且节点轮换。硬编码某个旧边缘 IP 复测,会撞上配置已摘除的节点,返回 CDN 默认证书,误判成证书故障。复测必须按当下解析。
- 控制台手工改 live = 埋雷。运维在控制台给 CLB 加了 TLS 段,GitOps 下次 sync 就抹掉。发现 OutOfSync 先 diff live,把手工改动收编进 git,而不是关掉 selfHeal。
最终拓扑
玩家:域名 → CDN(TLS/WSS 在边缘终结)→ 回源 CLB(自动创建即可)→ ingress-nginx → 服务
管理:域名 → DNS 直指手购 CLB(443 证书 + 安全组白名单)→ Ingress → ArgoCD / Grafana / 后台
教训
- 云厂商的「支持复用」要读细则,自动创建和手动购买的资源常常是两个生命周期模型。
- 白名单要问一句「绕过这一层能不能直达下一层」。CDN、WAF、API 网关都有同样的问题。
- 声明式系统里任何一个字段被 controller 拒绝,都可能导致整个对象不同步。看 status 比看控制台可靠。