
这是一篇偏实战的排障笔记:描述一次在生产集群引入命名空间级 default deny 之后,CoreDNS 解析失败、kubectl top 挂掉、业务 curl/nslookup 频繁报错的连锁故障。
文章根据我在公司实际运维时处理过的一个真实故障写作,给出可复现实验、真实报错与日志、定位路径、可运行的修复策略,以及上线时容易遗漏的策略清单。
v1.28.x(集群开启 NetworkPolicy 支持)v3.27+,启用了 Kubernetes NetworkPolicy 与 Calico GlobalNetworkPolicykube-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 || trueB. 添加精确白名单后恢复
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 3C. 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 删除。