在一套生产级 Kubernetes 集群里,我踩过一个隐蔽却杀伤力巨大的坑:为命名空间加上 NetworkPolicy 的 默认拒绝
,却忘了为 egress
明确放行 DNS。结果是整片业务 Pod
同时失明,外部 API
、内部 Service
的域名解析统统失败,症状传播极快,告警像雪崩一样涌来。把这次事故拆开看,背后是 NetworkPolicy 关于 egress
的隔离语义、默认值推断,以及 kube-dns/CoreDNS
的访问模式共同作用的连锁反应。
为了让排障记录更具可复现性,下面先交代技术环境,再给出故障现象、真实报错与调用栈、完整排查步骤,以及可直接 kubectl apply
的修复策略和防坑清单。
v1.26.x
(网络策略语义同样适用于 v1.20+
)v3.26
(开启 Kubernetes NetworkPolicy
支持,不使用 Calico GlobalNetworkPolicy
进行跨命名空间全局拦截)1.10.x
,kube-dns
ClusterIP
位于 10.96.0.10
169.254.20.10
payments
payments
命名空间引入 默认拒绝
的 NetworkPolicy,仅写了 ingress
的允许规则,遗漏了 egress
放行 DNS关于 NetworkPolicy 的基本模型、默认拒绝
建议和 egress
一旦生效即转入白名单模式的语义,可参考 Kubernetes 官方文档与厂商最佳实践。官方文档明确:NetworkPolicy 控制 L3/L4 流量,策略是 允许列表
语义;一旦某 Pod
被 egress
类型的策略选择到,该 Pod
的出站流量默认被隔离,除非被某条策略显式放行。
变更落地几分钟后,业务 Pod
陆续出现外部依赖访问失败与内部服务发现失败。典型的报错来自应用日志与 CoreDNS 日志。
API
、第三方支付网关等外部域名时报:Get https://api.github.com: dial tcp: lookup api.github.com on 10.96.0.10:53: read udp 10.244.3.57:57589->10.96.0.10:53: i/o timeout
这类 dial tcp ... i/o timeout
或 no such host
是 kube-dns/Cloud DNS 故障或不可达时的典型表征,GKE 的官方排障页也将其列为常见症状。
http: proxy error: dial tcp: lookup kubernetes.default.svc on 10.96.0.10:53: read udp 10.244.0.4:57589->10.96.0.10:53: i/o timeout
(Server Fault)
CoreDNS Pod
日志出现大量 i/o timeout
:
[ERROR] plugin/errors: 2 node3. A: read udp 10.244.0.25:43976->8.8.8.8:53: i/o timeout
[ERROR] plugin/errors: 2 example.com. A: read udp 10.244.0.25:55132->8.8.8.8:53: i/o timeout
这类错误意味着业务 Pod
到 kube-dns
的查询报文要么发不出去,要么回包被丢,通常与网络策略或底层网络到 DNS 上游的连通性相关。
一条来自社区 issue 的 Python 调用栈精准命中我们的症状:应用通过 Service DNS
访问 Redis,最终因为 DNS 解析失败异常退出。我们现场采集到的栈与其高度一致:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/redis/connection.py", line 544, in connect
raise ConnectionError(self._error_message(e))
ConnectionError: Error -3 connecting to o2o-redis-service.o2o-sales.svc.cluster.local:6379. Temporary failure in name resolution.
说明:上面这类错误在不同语言栈都会出现对应形态,例如 Go 会打印
lookup <host> on 10.96.0.10:53: i/o timeout
,Node.js 常见ENOTFOUND
,Java 则多见UnknownHostException
。共同点是 DNS 解析失败。
下列截图来自社区的同类现场,可与文中错误互相印证(i/o timeout
集中涌现、DNS 查询超时):
默认允许
→ 默认拒绝
的转变:Kubernetes 在未命中任何 NetworkPolicy 时,Pod
的入站与出站都是允许的。一旦某个 Pod
被 egress
类型策略选中,即刻进入 出站白名单
模式——只允许策略显式放行的目标、端口与协议。 ingress
放行、未定义 egress
放行,等价于将 egress
完全关死。egress
到集群内的 kube-dns
Service
(UDP/TCP 53
),若未放行,所有需要域名解析的出站流量都会失败。Calico、Cilium 的最佳实践都明确强调:有 egress
限制时必须显式允许到 DNS。 169.254.20.10/32
,这不是普通 Pod
IP
,用 namespaceSelector/podSelector
选不出来,策略中要改用 ipBlock
放行这个链路。很多项目忽略了这一差异,导致即使写了 kube-dns
的选择器,解析依旧失败。 现场我采用了 自上而下 + 对照实验
的策略,把问题快速定位到 egress DNS
。
busybox
在出问题命名空间做对照测试:kubectl -n payments run dns-test --image=busybox:1.36 --restart=Never -it --rm -- \
sh -c 'nslookup kubernetes.default && nslookup api.github.com || true'
若返回 ;; connection timed out; no servers could be reached
或 i/o timeout
,说明命名空间里的 Pod
已经解析失败,与业务现象一致。官方调试文档也建议用这种方式核验 CoreDNS
工作状态。
Pod
与 Service
:kubectl -n kube-system get svc,ep -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
日志若出现大量 i/o timeout
,进一步印证 DNS 报文未达或回包被拦。
kubectl -n payments get networkpolicy
kubectl -n payments describe networkpolicy <name>
只要存在任何一个 egress
类型策略选中业务 Pod
,该 Pod
的出站即进入白名单模式。
dns-test
Pod
临时打上不被策略选中的标签,或临时移除策略,nslookup
立即恢复。这是证明 NetworkPolicy 造成隔离的最直接实验。dns-test
的 resolv.conf
指向 169.254.20.10
,进而验证策略中是否放行了该地址段流量。 YAML
与验证方法放行到 kube-system
命名空间、带标签 k8s-app: kube-dns
的 Pod
,UDP/TCP 53
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: payments
spec:
podSelector: {} # 作用于命名空间内所有 Pod
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
这条策略只负责放行 DNS;其他业务 egress
规则可以按需另行补充。NetworkPolicy
的规则是相加的,写多条 allow
没问题。
由于 NodeLocal DNSCache 暴露在本机 169.254.20.10/32
,需要改用 ipBlock
放行这个地址(选择器匹配不到):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress-nodelocal
namespace: payments
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- ipBlock:
cidr: 169.254.20.10/32 # NodeLocal DNS 缓存地址
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
实践里也可以两者都放行,兼容不同环境的 DNS 路径。关于 NodeLocal DNSCache 场景需要 ipBlock
的原因,可参考社区答复。
kubectl apply -f allow-dns-egress.yaml
kubectl -n payments get networkpolicy
nslookup
、dig
:kubectl -n payments run dns-ok --image=busybox:1.36 --restart=Never -it --rm -- \
sh -c 'nslookup kubernetes.default && nslookup api.github.com && wget -qO- https://api.github.com/zen'
当你看到 nslookup
正常返回 10.96.0.1xx
,以及获取到 zen
的一句话输出,egress DNS
已恢复。官方与厂商文档都建议在建立 默认拒绝
后优先补齐 DNS 放行。
Deployment
(触发可观测错误)如果想在测试命名空间快速复现 egress
被拦时的典型错误,可以部署一个极简容器,定时做外部域名的 HTTP GET
与内部 Service
的解析并打印错误。下面是 BusyBox
版本的 CronJob
,避免语言栈依赖,也无英文双引号:
apiVersion: batch/v1
kind: CronJob
metadata:
name: dns-probe
namespace: payments
spec:
schedule: '*/1 * * * *'
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: dns-probe
image: busybox:1.36
command:
- sh
- -c
- |
echo '--- start probe ---'
date
echo 'nslookup kubernetes.default.svc'
nslookup kubernetes.default.svc || true
echo 'wget https://api.github.com/zen'
wget -qO- https://api.github.com/zen || true
echo '--- end probe ---'
在没有 allow-dns-egress
的情况下,日志会稳定打印 connection timed out
、i/o timeout
。修复策略落地后,日志恢复正常。
默认拒绝
要配套 DNS 放行
模板。团队可以把 allow-dns-egress.yaml
作为命名空间基线策略的一部分,任何新业务命名空间默认携带,避免遗忘。egress
的隔离语义:只要某个 Pod
被任何 egress
策略选中,它的出站立刻进入白名单模式。把这句话贴在变更单模板上,能少很多事故。 UDP 53
,也要放行 TCP 53
。在应对响应分片、重试与部分上游行为时,TCP
通道同样会被使用,单放 UDP
可能会出现偶发解析失败。CNI 与公有云文档均有类似建议。 Pod
的 resolv.conf
指向 169.254.20.10
,策略需要 ipBlock
放行;仅靠 kube-dns
的选择器不生效。 验证用例
固化成自动化检查。在 gitops
管道或 admission
阶段,对带 egress
的 NetworkPolicy 自动扫描是否包含 DNS 放行
,缺失则阻断合入。Pod
的 error
日志关键字(如 i/o timeout
、SERVFAIL
)及业务侧 lookup ... on 10.96.0.10:53
等模式纳入告警,能在事故扩大前给出强提示。 FQDN
域名级 egress
放行(通过 DNS 解析到 IP
后动态放通),这对访问外部 SaaS 非常有用;但这只适用于 egress
allow
,并且需要信任的 DNS 源。 dial tcp: i/o timeout
、no such host
的常见场景。 egress
指南与最佳实践,强调 默认拒绝
后按需放行。 默认拒绝
、策略排障、DNS 域名级 egress
。 这次事故对团队的冲击不小,却也让我们的 NetworkPolicy 策略从 能用
走向 可演进
。当你在 零信任
的道路上收紧流量时,别忘了为 DNS 留一条明路;当你希望通过 默认拒绝
构筑边界时,也要把 egress
的白名单做成一张可信、可维护的配置表。用成熟的模板、自动化校验和可观测信号,把这类高频且隐蔽的问题挡在上线之前。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。