这是一篇偏实战的排障笔记:描述一次在生产集群引入命名空间级 default deny
之后,CoreDNS 解析失败、kubectl top
挂掉、业务 curl
/nslookup
频繁报错的连锁故障。
文章根据我在公司实际运维时处理过的一个真实故障写作,给出可复现实验、真实报错与日志、定位路径、可运行的修复策略,以及上线时容易遗漏的策略清单。
v1.28.x
(集群开启 NetworkPolicy
支持)v3.27+
,启用了 Kubernetes NetworkPolicy
与 Calico GlobalNetworkPolicy
kube-system
命名空间内 CoreDNS
,Service
名称 kube-dns
(UDP/TCP 53
)metrics-server
作为资源度量聚合层ingress-nginx
控制器prod
(本文在该命名空间启用 default deny
)官方 NetworkPolicy
文档明确指出:只有当 CNI 实现支持策略时,NetworkPolicy
才会生效;并且策略是 L3/L4
语义,需要显式放行必要端口与方向,否则会被默认拒绝拦截。Calico 文档也强烈建议使用默认拒绝,但同时要配好白名单流量,避免把系统组件与基础设施一刀切掉。CNCF、Red Hat、Isovalent 的最佳实践文章都反复强调:在启用 egress 限制时,要显式允许对 kube-dns
的 53/UDP
(必要时也包含 53/TCP
)访问,并尽量收敛到集群内的 kube-dns
Service
/Pod
而不是放开任意 53
端口。
在 prod
命名空间落地如下默认拒绝策略后(既拒入也拒出):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod
spec:
podSelector: {}
policyTypes: ['Ingress','Egress']
业务 Pod 里出现大面积 DNS 解析失败与外联中断,典型报错采样如下(均为业内高频报错原文):
curl
报错:
curl: (6) Could not resolve host: example.com
—— 常见于 DNS 不可达或被策略阻断时 dial tcp: lookup github.com on 10.96.0.10:53: i/o timeout
或 ...: no such host
—— 明确指向 53
端口解析超时/失败,极可能是到 kube-dns
的 egress 被拦截 dial tcp: lookup ... on <cluster-dns-ip>:53: no such host
,业务连 RDS 等外部域名解析失败kubectl top pods/nodes
失败:
Error from server (ServiceUnavailable): the server is currently unable to handle the request
—— metrics-server
无法被访问或其到 API 的聚合被网络限制影响时,常见此错误。在 CoreDNS
/GKE 的排障页中,也明确把 dial tcp: i/o timeout
、no such host
列为 DNS 组件异常或网络连通受阻的典型征兆。阿里云 ACK 的 DNS 故障文档则系统性地罗列了常见客户端报错与可能的网络策略成因,包括 curl: (6) Could not resolve host
、dial tcp: lookup ...: i/o timeout
等,用来对照本次现象非常贴切。
文档地址:
# 在 prod 命名空间业务 Pod 内:
nslookup kubernetes.default.svc.cluster.local
;; connection timed out; no servers could be reached
curl -sS https://www.google.com
curl: (6) Could not resolve host: www.google.com
# Go 服务日志片段(示例)
2025-09-28T03:18:22Z error httpclient: request failed:
Get "https://api.github.com": dial tcp: lookup api.github.com on 10.96.0.10:53: i/o timeout
落地 default deny
后,prod
内所有 Pod 的入站与出站都默认被拒。没有额外 egress 规则时,连 CoreDNS
的 53/UDP
都到不了,自然会出现 no such host
、i/o timeout
等解析错误,这是各大云厂与社区文档里反复强调的经典坑位 (Kubernetes)。与此同时,kubectl top
依赖的 metrics.k8s.io
聚合层若被策略间接影响,也会给出 ServiceUnavailable
一类错误提示。
定位时序建议:
kube-system
中 coredns
状态与 kube-dns
服务端口是否就绪。
官方调试指引给出了标准命令集,可快速确认 CoreDNS 是否健康运行、Service
/Endpoints
是否存在。nslookup
/dig
指向 kube-dns
ClusterIP
。
若超时或 no servers could be reached
,结合 default deny
推断 egress 被拦。CNCF/Red Hat/Isovalent 的最佳实践均要求允许对 kube-dns
的 53/UDP
(必要时也含 53/TCP
)的 egress。metrics-server
聚合 API:kubectl get apiservices | grep metrics
,并查看 metrics-server
Deployment/Pod
与其网络路径。若 kubectl top
报 ServiceUnavailable
,多见网络路径或聚合层未打通。GlobalNetworkPolicy
做全局默认拒绝,要确认系统命名空间的例外与顺序优先级,避免把 kube-system
等关键命名空间锁死在全局层面。default deny
不区分业务与系统流量,prod
内 Pod 的 egress 全被拦截。kube-dns
Service
的 53/UDP
/53/TCP
,未显式放行时,客户端会报 curl: (6) Could not resolve host
、Go 应用 dial tcp: lookup ...: i/o timeout/no such host
等典型错误。metrics-server
及其聚合 API 若跨命名空间、跨节点通信也受限,会出现 ServiceUnavailable
与 unable to handle the request
的错误呈现。下述清单在 prod
命名空间启用默认拒绝,并精确定义允许列表:允许访问集群内 kube-dns
,允许访问公司外部代理(示例),允许访问必要的观测与出站端点。策略写法遵循官方/Calico/社区博文建议,且在端口上既包含 UDP 53
也包含 TCP 53
,避免少数域传输或重试路径走 TCP 53
时被拦。
# 1) 默认拒绝(既拒入也拒出)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod
spec:
podSelector: {}
policyTypes: ['Ingress','Egress']
---
# 2) 允许对 CoreDNS 的 DNS 解析 egress(收敛到 kube-system 命名空间 & kube-dns Pod)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: prod
spec:
podSelector: {}
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
---
# 3) 允许访问公司外部代理(示例:egress 到 10.20.30.40:3128)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-proxy
namespace: prod
spec:
podSelector: {}
policyTypes: ['Egress']
egress:
- to:
- ipBlock:
cidr: 10.20.30.40/32
ports:
- protocol: TCP
port: 3128
可选:如果使用 Calico 的全局默认拒绝,可在全局层用
GlobalNetworkPolicy
做default deny
,并为系统命名空间配置更高优先级的allow
规则,避免把kube-system
、ingress-nginx
、observability
等命名空间锁死。
# 0) 基础:准备一个测试命名空间与调试 Pod
kubectl create ns prod || true
kubectl -n prod run netshoot --image=nicolaka/netshoot -it --restart=Never -- sleep 365d
# 1) 未放行前的观测(此时若已应用 default deny,下面命令大概率失败)
kubectl -n prod exec -it netshoot -- sh -c 'nslookup kubernetes.default.svc.cluster.local || true'
kubectl -n prod exec -it netshoot -- sh -c 'curl -sS https://www.google.com || true'
# 2) 应用上文 YAML(default deny + allow-dns + allow-egress-proxy)
kubectl apply -f networkpolicy-prod.yaml
# 3) 再次验证:DNS 应恢复可用
kubectl -n prod exec -it netshoot -- sh -c 'nslookup kubernetes.default.svc.cluster.local'
kubectl -n prod exec -it netshoot -- sh -c 'dig +short www.cncf.io'
# 4) 验证 kubectl top(若仍失败,检查 metrics-server 与聚合层是否被策略影响)
kubectl top nodes || true
kubectl top pods -A || true
如果在第 4 步 kubectl top
仍报错 the server is currently unable to handle the request
,需要继续检查 metrics-server
的网络路径与聚合 API 的连通性。社区经验贴与 metrics-server
仓库 issue 指出,网络与聚合层没打通时常会出现此类 ServiceUnavailable
,应根据部署方式考虑 hostNetwork
、--kubelet-insecure-tls
、DNS 解析等因素,并结合 NetworkPolicy 放行必要通信路径。
curl: (6) Could not resolve host
、dial tcp: lookup ...: i/o timeout/no such host
等错误形式,被 Kubernetes/GKE/ACK 等官方或云厂文档列为 DNS 受阻的常见症状,可对照核实;这些页面包含终端输出与图例截图,可作为运维现场的对照参考:
GKE kube-dns
排障页(列举 dial tcp: i/o timeout
、no such host
;
阿里云 ACK DNS 故障页(系统性罗列 curl
、dig
、Go 客户端错误示例与成因,包括 i/o timeout
、no such host
。kubectl top
报 ServiceUnavailable
的社区案例:ServerFault/metrics-server issue 供比对。贴士:如果你的业务是 Go 编写,日志中出现
dial tcp: lookup ...: i/o timeout
、no such host
,这通常对应DialContext
超时与 DNS 解析阶段失败。Better Stack 的 Go 超时指南对各层超时的语义有一页式总结,方便交叉验证与设置合理的Transport
超时参数(虽然这不是本次根因,但能帮助减少误判。
在 egress 策略里仅仅放开 :53
的所有目标 IP,看似能恢复业务,但会带来数据外泄面。CNCF 与 Red Hat 的 egress 实战文章建议尽量收敛到 kube-dns
的 Service
或 Pod
,并标注通过 namespaceSelector + podSelector
来圈定目标,这样在换 IP 或节点漂移时也能稳定命中,且不放开任意外部 53
端口。Isovalent 的 Cilium 教程同样示例了如何只允许对集群内 kube-dns
的 53 访问,避免策略过宽。
default deny
一定要与 allow-dns-egress
成对上线;必要时同时放开 TCP 53
。这不是玄学,而是来自大量线上案例与官方/社区文档的共识。GlobalNetworkPolicy
做默认拒绝,给 kube-system
、ingress-nginx
、observability
、gateways
等系统命名空间加显式 allow
,并检查策略优先级,防止自锁 。metrics-server
/聚合 API 涉及到 apiserver
、kubelet
与聚合层通信,受策略影响时 kubectl top
会直接失败,报 ServiceUnavailable
;将其必要端口与命名空间纳入白名单,或评估 hostNetwork
与访问路径。namespaceSelector + podSelector
锁定 kube-dns
,而不是 ipBlock
指向 ClusterIP
;后者在负载均衡或 IP 变化时更脆弱,前者跟随标签更稳健。nslookup
,要压测一段时间观察 i/o timeout
抖动,确认没有遗漏例如 TCP 53
、node-local-dns
等变体路径。A. 复现 default deny
误杀 DNS
kubectl create ns prod || true
kubectl -n prod run dns-test --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl -n prod exec dns-test -- nslookup kubernetes.default.svc.cluster.local || true
# 应用 default deny
cat > /tmp/np-deny.yaml <<'YAML'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod
spec:
podSelector: {}
policyTypes: ['Ingress','Egress']
YAML
kubectl apply -f /tmp/np-deny.yaml
# 观察典型错误(和云厂/官方文档一致)
kubectl -n prod exec dns-test -- nslookup kubernetes.default.svc.cluster.local || true
kubectl -n prod exec dns-test -- wget -qO- https://www.google.com || true
B. 添加精确白名单后恢复
cat > /tmp/np-allow-dns.yaml <<'YAML'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: prod
spec:
podSelector: {}
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
YAML
kubectl apply -f /tmp/np-allow-dns.yaml
# 再次验证,DNS 恢复
kubectl -n prod exec dns-test -- nslookup kubernetes.default.svc.cluster.local
kubectl -n prod exec dns-test -- wget -qO- https://www.cncf.io | head -n 3
C. kubectl top
验证与排障指引
kubectl get apiservices | grep metrics
kubectl -n kube-system get deploy metrics-server -o wide
kubectl top nodes || echo 'metrics aggregation likely blocked'
若依然失败,结合社区与官方问题单,从 metrics-server
的 Deployment
、hostNetwork
、访问 kubelet
/聚合 API 的路径与策略逐项核查,参照错误表现 ServiceUnavailable
的案例进行逐项放行与验证 (Stack Overflow)。
把 default deny
当成开关,会很快收获一堆 curl: (6) Could not resolve host
、dial tcp: lookup ...: i/o timeout
、kubectl top ... ServiceUnavailable
的报警;把 allow
白名单当成清单,按命名空间与组件粒度逐步补齐,问题就会以同样的速度消失。以上做法与 Kubernetes 官方概念、Calico 的最佳实践、主流厂商的排障指引完全一致,可直接用于生产落地与回归校验。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。