nslookup 有 IP 还是不通?跨 Namespace 调用的两个隐藏门

场景:微服务 A(ns-a)调用微服务 B(ns-b),curl 卡住直到超时——开发同学第一反应是"服务名写错了",改了三次名字没效果 路径:坐标(DNS 先查还是 NetPol 先查?)→ 分层(DNS → Service → NetworkPolicy 逐层排查)→ 路径(3 步排查链)→ 定位(查对一半的陷阱)→ 标点(修复 + Check-list) K8s 版本:v1.28,Calico CNI

上篇讲 Ingress 排查时提到了一个关键思路:现象在 A 层、根因在 B 层。这次也一样——现象是 curl 超时,但根因不是一个,是两个。

新服务上线,开发同学在代码里配了 http://service-b:8080——"同一个项目,名字不都这样写吗"——curl 过去直接超时。加了 Namespace 后缀能解析了——"找到 IP 了,通了吧"——curl 还是超时。不是名字写错了,是两个 Namespace 之间隔着两层门——你推开了 DNS 的门,但 NetworkPolicy 的门还锁着。

nslookup 解析成功但 curl 超时


【坐标】现象:两种不通,一种误判

跨 Namespace 调用的"不通"分两种——找不到过不去。最要命的是,这两种不通的现象是一样的(curl 超时),但根因完全不同。

Layer 1 — 部署背景

部署项 配置
消费方 Pod A,ns-a,consumer-app
提供方 Service B(ClusterIP: 10.96.123.100),ns-b,service-b
后端 Pod Pod B,端口 8080,label app: service-b
DNS 域名 service-b.ns-b.svc.cluster.local
网络插件 Calico

开发同学第一次写的 URL 是 http://service-b:8080——这在同一个 Namespace 内是对的,但跨 Namespace 必须带 Namespace 后缀。

Layer 2 — 两个事实的矛盾

改了名字格式之后,nslookup 返回了 IP,curl 依然卡死超时。同一个域名,DNS 说有这个地址——但网络层就是不让你过去。

这就是跨 Namespace 调用最迷惑人的特征:DNS 给你一个绿灯,但真正通不通,DNS 说了不算。

Layer 3 — 方向判断

DNS 能解析 → "第一个门开了"。但 K8s 的跨 Namespace 调用有两个独立门:

nslookup 出 IP      →  门 1(DNS)开了  ✅
curl 拿不到 200      →  门 2(NetworkPolicy)还锁着 ❌

问题不在"能不能找到",在"找到了但能不能过去"。 如果这时候你继续改名字、加 rewrite、换端口——方向就完全错了。

跨 Namespace 调用链路:两个断点——DNS 门 + NetworkPolicy 门


【分层】逐层排查:先查 DNS,再查策略,最后查依赖

排查顺序表

排查对象 关键命令 预期正常 异常信号
Layer 1 DNS 解析 nslookup <svc>.<ns>.svc 返回 ClusterIP NXDOMAIN → 名字格式错
Layer 2 Service 状态 kubectl get svc,endpoints -n <ns> 有 ClusterIP + Endpoints 无 Endpoints → selector 错
Layer 3 NetworkPolicy kubectl describe netpol -n <target-ns> ingress 规则含你的 ns 无 ingress / 没放行
Layer 4 实际通信 curl <svc>.<ns>.svc:port HTTP 200 timeout → 策略挡;refused → Pod 没监听

Layer 1: DNS 解析 — 名字格式是第一道坎

K8s 内部 DNS 由 CoreDNS(K8s 集群 DNS 服务器,默认部署在 kube-system,为所有 Pod 提供服务发现)负责解析。同一个 Namespace 内用 <service-name> 就行,跨 Namespace 必须用 <service>.<namespace>.svc.cluster.localcluster.local 可省略)。

NXDOMAIN — CoreDNS 告诉你不认识这个域名。

K8s 内部 DNS 的工作方式是这样的:Pod 的 /etc/resolv.conf 中配了 search ns-a.svc.cluster.local svc.cluster.local cluster.local,所以同 Namespace 内 curl service-b 会自动补全为 service-b.ns-a.svc.cluster.local——能找到。但跨 Namespace 时,DNS 解析器不知道你在找另一个命名空间的服务,只能返回 NXDOMAIN。

原因通常是下面几种:

错误写法 正确写法 原因
service-b service-b.ns-b.svc 缺 Namespace 后缀
service-b.ns-b service-b.ns-b.svc svc
svc-b.ns-b.svc.cluster svc-b.ns-b.svc.cluster.local cluster.local 是默认域名后缀

把 URL 改成 service-b.ns-b.svc.cluster.local,nslookup 返回 10.96.123.100——第一个门开了。接下来测 Service 本身。

DNS 解析过程:从 NXDOMAIN 到正确返回 ClusterIP

Layer 2: Service 层 — 确认后端在不在

DNS 能解析到 ClusterIP,不代表流量能到达后端 Pod。检查 Service 和 Endpoints(端点列表,记录 Service 后端 Pod 的 IP:Port,由 Endpoints Controller 自动维护):

Service 和 Endpoints 状态:ClusterIP 存在、Endpoints 有 IP——但直连 Pod IP 也超时

Service 有 ClusterIP、Endpoints 有 Pod IP——说明 selector 匹配上了,Endpoints Controller 正常工作。

但直连 Pod IP 也超时。这说明问题不在 Service 层——不是 kube-proxy 没生成规则,不是 conntrack 表满。是 比 Service 更底层的东西在拦

Layer 3: NetworkPolicy 层 — 默认全放行,加策略后全拒绝

这里是最关键、也最容易被忽略的一层。

K8s 网络默认行为:所有 Pod 之间可以互相通信。但一旦某个 Namespace 或 Pod 绑定了 NetworkPolicy(K8s 网络策略资源,控制 Pod 间通信的防火墙规则),行为变为:只允许被显式放行的流量。没有匹配规则的流量全部丢弃。

关键行是 Allowing ingress traffic: <none>——不接收任何入站流量。PodSelector: <none> 指这条策略作用于 ns-b 中的所有 Pod。

这就是为什么 curl 超时——不是 Service 名字写错了,不是 CoreDNS 坏了,是 ns-b 的 NetworkPolicy 把来自所有其他 Namespace 的入站流量全部丢弃了

这个点值得多说一句:K8s NetworkPolicy 的工作方式不是"加一条策略就多加一条允许",而是一旦加了策略,所有未显式允许的流量默认拒绝。这和大部分人熟悉的防火墙模型(默认放行 + 加阻止规则)正好相反。

默认情况下,K8s 集群内所有 Pod 互通。但安全团队介入后,他们给每个 Namespace 加了一条"默认拒绝入站"的策略——出于安全考虑,只允许明确授权的流量进入。问题是没有人记起来更新这条策略的 ingress 规则。

所以不是配置错了,是配置只做了一半。安全策略到位了,业务放行没跟上。

NetworkPolicy describe 显示 ingress 规则为空——所有入站流量被拦截

Layer 4: 最终验证

default-deny NetworkPolicy 加上 ingress 规则,显式放行 ns-a 的流量后,再测——通了。

最终验证:cross-namespace 调用返回 HTTP 200


【路径】🔍 下次先跑这个命令

跨 Namespace 的 3 步排查链

跨 Namespace 调用不通时,按这个顺序查,不用走弯路。拷贝这段直接跑:

# 全量排查命令,拷贝即用
# Step 1: 查 DNS —— 第一个门
kubectl run debug -it --image=busybox --rm \
  -- nslookup <svc>.<ns>.svc
# → 有 IP 继续,NXDOMAIN 查格式

# Step 2: 查 NetworkPolicy —— 第二个门
kubectl describe netpol -n <target-ns>
# → ingress 为空则策略在拦,加 namespaceSelector

# Step 3: 实际验证
kubectl run debug -it --image=busybox --rm \
  -- wget -qO- http://<svc>.<ns>.svc:port
# → HTTP 200 = 通了,timeout = 策略还在拦

异常判断快速表

命令 正常输出 异常信号 下一步
nslookup <svc>.<ns>.svc Address: 10.96.x.x NXDOMAIN 检查名字格式
kubectl get endpoints -n <ns> <svc> 10.244.x.x:port 检查 Service selector
kubectl describe netpol -n <ns> ingress 有来源列表 <none> 添加 ingress 规则
curl <svc>.<ns>.svc:port HTTP 200 timeout / refused 定位具体原因

跨 Namespace 调用 3 步排查命令 + 异常判断


【定位】查对一半比完全没查更危险

❌ 错误排查方向

"nslookup 出 IP 了,网络没问题"

典型的内心 OS:

curl 卡住了
  → 是不是名字不对?→ nslookup → NXDOMAIN
  → 改成全名 → nslookup 出 IP 了!
  → curl 试试 → 还是不通
  → 换个端口?换 http -> https?加个超时参数?
  → 还是不通
  → 是不是 Service 的 YAML 有问题?改了重建
  → 依然不通

这个过程消耗 15-30 分钟。走完一圈后回到原点——你查对了 DNS 就以为自己查完了,忘记检查第二个门

✅ 正确排查方向

curl 卡住了
  → Step 1: nslookup <svc>.<ns>.svc → 确认 DNS
  → Step 2: kubectl describe netpol -n <target-ns> → 查策略
  → Step 3: 找到规则缺 namespaceSelector → 补上
  → curl → 200 ✅

总耗时:2-3 分钟。

两者的本质差异:跨 Namespace 调用有两个独立的检查点——DNS 负责"能不能找到",NetworkPolicy 负责"找到了能不能过去"。开了一个门不等于门全开了。查对一半比完全没查更危险——因为它给你虚假的安全感。

错误排查(只在 DNS 层打转)vs 正确排查(逐个开门)


【标点】修复 + Check-list

场景 A:DNS 名称格式错误

# ❌ 错误
$ curl http://service-b:8080
# 等价于 curl http://service-b.ns-a.svc:8080 → 在 ns-a 里找 service-b → 找不到

# ✅ 正确
$ curl http://service-b.ns-b.svc:8080

场景 B:NetworkPolicy 没放行

修复前 — ingress 规则为空(全部入站流量拒绝):

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny
  namespace: ns-b
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress: []     # 不接收任何入站流量

修复后 — 显式放行 ns-a:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-from-ns-a
  namespace: ns-b
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: ns-a
  policyTypes:
  - Ingress

关键字段是 namespaceSelector——这是 NetworkPolicy 控制跨 Namespace 访问的唯一手段。podSelector: {} 表示作用于 ns-b 中的所有 Pod。

NetworkPolicy YAML 修复 + 完整 Check-list 拷贝即用

注意:matchLabels 的值是 Namespace 的真实名称(K8s 自动给每个 Namespace 打上 kubernetes.io/metadata.name label)。如果你的 Namespace 叫 ns-a,那 label 就是 kubernetes.io/metadata.name: ns-a

场景 C:两个都有问题(最真实场景)

最常遇到的情况是两个问题一起出现

  1. DNS 名字格式不对 → 改成 <svc>.<ns>.svc
  2. NetworkPolicy 阻挡 → 加上 ingress 规则
  3. 逐个修,逐个验证
# 修完 DNS 后测 → 超时(预料之中,继续修策略)
$ curl -v --connect-timeout 5 http://service-b.ns-b.svc:8080
*   Trying 10.96.123.100:8080...
* connect to 10.96.123.100 port 8080 failed: Connection timed out

# 修完 NetworkPolicy 后测 → 通
$ curl -s -o /dev/null -w "%{http_code}" http://service-b.ns-b.svc:8080
200

跨 Namespace 调用排查 Check-list

□ kubectl run debug --image=busybox -it --rm -- nslookup <svc>.<ns>.svc
  → 有 IP(无 IP → 检查 Service 名/Namespace 格式)

□ kubectl get svc -n <ns> -o wide
  → Service 存在,ClusterIP 已分配

□ kubectl get endpoints -n <ns> <svc>
  → 有后端 IP:Port(空 → 查 selector 匹配)

□ kubectl get netpol -n <target-ns>
  → 查看是否有 NetworkPolicy(无 → 默认所有 ns 都能通)
  → 有 → kubectl describe netpol -n <target-ns>
    → ingress 规则是否含你的 namespaceSelector

□ kubectl get ns --show-labels
  → 确认目标 ns 的 label
  → namespaceSelector 的 matchLabels 值要与此一致

□ curl -v --connect-timeout 5 <svc>.<ns>.svc:port
  → 200(通了)
  → timeout → NetworkPolicy 或路由问题
  → refused → Pod 端口没监听

□ 双门检查:
  ✓ 门 1(DNS):nslookup 能解析到 ClusterIP
  ✓ 门 2(NetworkPolicy):ingress 规则显式放行了来源 ns

附:完整命令清单

# === 1. DNS 解析 ===
kubectl run debug -it --image=busybox --rm -- nslookup <svc>.<ns>.svc
kubectl run debug -it --image=busybox --rm -- cat /etc/resolv.conf
kubectl -n kube-system logs deploy/coredns --tail=20

# === 2. Service 状态 ===
kubectl get svc -n <ns> -o wide
kubectl get endpoints -n <ns> <svc>
kubectl describe svc -n <ns> <svc>

# === 3. NetworkPolicy 检查 ===
kubectl get netpol -n <target-ns>
kubectl describe netpol -n <target-ns> <name>
kubectl get netpol -n <target-ns> -o yaml
kubectl get ns --show-labels

# === 4. 实际验证 ===
kubectl run debug -it --image=busybox --rm -- wget -qO- http://<svc>.<ns>.svc:port
kubectl run debug -it --image=busybox --rm -- curl -v --connect-timeout 5 http://<svc>.<ns>.svc:port

# === 5. 诊断辅助 ===
kubectl get pods -n <ns> -o wide
kubectl logs -n <ns> <pod-name> --tail=50
kubectl exec -n <ns> <pod-name> -- ss -tlnp

# === 6. NetworkPolicy 修复 ===
# 创建允许跨 Namespace 访问的策略
kubectl apply -f - <<EOF
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-from-<source-ns>
  namespace: <target-ns>
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: <source-ns>
  policyTypes:
  - Ingress
EOF

记住一句话:K8s 的跨 Namespace 调用有两个门——DNS 推开了第一扇,但 NetworkPolicy 还锁着第二扇。你只开一扇门就以为通了,比完全没开门更危险。

上篇我们聊 Ingress 端口映射,上上篇聊了 Service 内部不通。这次从"集群外"看到"跨 Namespace"。下篇我们聊 同 Namespace 内的 NetworkPolicy 自残——不是因为跨 ns 没配,是同一个 ns 里把自己拦死了。

🔗 个人博客:https://opencao.cn 📺 公众号:Ai拆代码的曹操 🌟 知识星球:Ai拆代码的曹操