Skip to content

Kubernetes Pod网络、Service、DNS与NetworkPolicy

应用容器 Running 只说明进程已经启动,不说明其他 Pod、Service 或集群外客户端能够访问它。Kubernetes 网络是一条分层链路:Pod 先获得网络命名空间和 IP,Service Selector 生成 EndpointSlice,CoreDNS提供名称发现,节点数据面把虚拟 Service 地址转发到后端,NetworkPolicy再约束允许的通信。

本页不把 Service 简化成“负载均衡到Pod”,而是从一个请求的数据包视角解释:名称怎样解析、目标IP是否真实存在于网卡、后端列表从哪里来、连接为什么会固定在某个Pod、跨节点为什么可能失败、策略为什么两边都要允许。HTTP/HTTPS域名和路径入口放在独立的 Ingress专栏 中。

学习目标

学完后应能:

  1. 解释 Kubernetes Pod 网络模型和 CNI 的职责。
  2. 解释同Pod localhost、同节点Pod通信和跨节点Pod通信的差异。
  3. 从 Service Selector 追踪到 EndpointSlice 地址、端口与Ready条件。
  4. 解释 ClusterIP 为什么通常不是某张网卡上的普通监听地址。
  5. 解释 kube-proxy 的 iptables/IPVS 思路、NAT和Conntrack边界。
  6. 区分 ClusterIP、Headless、NodePort、LoadBalancer和ExternalName。
  7. 区分 porttargetPortnodePort 和容器监听端口。
  8. 解释 CoreDNS、搜索域、ndots、Headless DNS 和 ExternalName CNAME。
  9. 解释 externalTrafficPolicyinternalTrafficPolicy 和 ClientIP Session Affinity。
  10. 正确设计默认拒绝、DNS放行和应用最小通信 NetworkPolicy。
  11. 解释为什么 Ingress/Service有配置仍可能404、503、拒绝或超时。
  12. 按 DNS、Service、EndpointSlice、Pod、Node数据面、CNI和策略逐层排查。

一、先建立分层模型

mermaid
flowchart TD
    A["客户端使用Service DNS名称"] --> B["CoreDNS返回ClusterIP或Pod地址"]
    B --> C["客户端向目标IP和port发起连接"]
    C --> D["节点Service数据面选择Endpoint"]
    D --> E["CNI网络把包送到目标Pod IP"]
    E --> F["NetworkPolicy判断通信是否允许"]
    F --> G["Pod容器在targetPort处理请求"]

不同故障发生在不同层:

现象可能层次
服务名解析失败CoreDNS、搜索域、NetworkPolicy、上游DNS
Service没有EndpointSlice地址Selector、Pod Label、Readiness、端口
ClusterIP连接超时节点数据面、策略、CNI、后端无响应
Connection refused目标地址可达但没有进程监听/端口错误
同节点可通、跨节点不通CNI跨节点路由/隧道/安全组/MTU
外部NodePort不通防火墙、安全组、监听地址、TrafficPolicy
偶发失败部分Endpoint异常、连接复用、版本差异、网络丢包

二、Kubernetes Pod网络模型

经典 Kubernetes 网络模型希望满足:

  • 每个 Pod 拥有自己的 IP。
  • 同一集群中 Pod 可以按 Pod IP 通信,不要求应用自己做端口映射。
  • Node能够与其上的Pod通信。
  • Pod看到的自身IP应与其他Pod用来访问它的地址一致,特殊实现边界除外。

Kubernetes定义模型和插件接口,不规定所有集群必须使用同一组路由、隧道或 eBPF 程序。Calico、Cilium、云厂商 VPC CNI 等实现的数据路径可能完全不同。

三、Pod网络命名空间怎样建立

kubelet通过 CRI 创建 Pod Sandbox,运行时建立并持有 Pod 网络命名空间,再调用 CNI 插件配置网络。

mermaid
flowchart TD
    A["kubelet请求创建Pod Sandbox"] --> B["Runtime建立网络命名空间"]
    B --> C["CNI ADD接收Pod和Namespace信息"]
    C --> D["创建veth或云网卡等接口"]
    D --> E["分配Pod IP并写入路由"]
    E --> F["配置网关、策略或隧道数据面"]
    F --> G["Runtime在该Namespace启动业务容器"]

典型 Linux veth 思路中:

text
Pod网络命名空间中的eth0
        ↕ veth pair
Node网络命名空间中的对端接口

路由、Bridge、隧道或eBPF数据面

具体接口名、Bridge和路由取决于 CNI,不能把某个插件的 cni0flannel.1 当成所有集群共同结构。

四、同一个Pod中的容器怎样通信

同一个 Pod 的普通容器共享 Pod 网络命名空间,因此:

  • 共享同一个 Pod IP。
  • 可以通过 localhost 互相访问。
  • 共享端口空间,两个容器不能同时监听同一个IP:Port。
  • 网络设备和路由视图通常一致。

例如业务容器监听 0.0.0.0:8080,同 Pod Sidecar 可以访问:

text
http://127.0.0.1:8080

但不同 Pod 中的 127.0.0.1 各自指向自己,不能用它调用另一个 Pod。文件系统也不会因为共享网络自动共享,必须显式挂载同一个 Volume。

五、同节点和跨节点Pod通信

5.1 同节点

数据包可能经本地 veth、Bridge、路由或 eBPF 转发到另一 Pod。即使都在同节点,NetworkPolicy和安全数据面仍可能生效。

5.2 跨节点

常见实现思路:

  • 直接路由:节点/网络知道各Pod CIDR下一跳。
  • Overlay:把Pod包封装在 VXLAN、Geneve 等外层包中穿过节点网络。
  • 云VPC原生地址:Pod获得VPC可路由地址。
  • eBPF数据面:通过内核程序完成转发、策略和Service能力。

跨节点失败而同节点正常时,应检查:

  • Node之间路由和安全组。
  • 隧道端口/协议。
  • CNI Agent状态。
  • Pod CIDR冲突。
  • MTU和分片/PMTU。
  • 反向路径过滤。
  • NetworkPolicy。

六、MTU为什么会导致小包通大包不通

Overlay封装会增加外层头部。如果底层网络 MTU 为1500,Pod仍发送1500字节包,封装后可能超过底层限制。若分片或 ICMP“Packet Too Big”被阻断,可能出现:

  • ping 小包正常。
  • TCP握手正常。
  • 小HTTP响应正常。
  • TLS、大响应、文件传输卡住或超时。

排查需要比较 Pod接口、隧道接口和底层网卡 MTU,并验证路径 MTU。不能因为 curl /health 成功就证明所有业务流量网络正常。

七、为什么需要Service

Pod是可替换实例:滚动更新、扩容、驱逐和故障恢复会产生新Pod、UID和IP。调用方若保存Pod IP,会遇到地址过期和负载分配问题。

Service提供:

  • 稳定名称。
  • 稳定虚拟IP(ClusterIP类型)。
  • 一组动态后端的服务发现。
  • 端口映射。
  • 节点数据面的四层转发。

Service本身不检查业务数据库是否正确,也不保证请求严格轮询。它依赖 Pod Ready、EndpointSlice和数据面实现。

八、Selector怎样变成EndpointSlice

示例 Service:

yaml
apiVersion: v1
kind: Service
metadata:
  name: order-api
  namespace: commerce
spec:
  selector:
    app: order-api
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: http

配套 Pod:

yaml
metadata:
  labels:
    app: order-api
spec:
  containers:
    - name: order-api
      ports:
        - name: http
          containerPort: 8080

控制链:

mermaid
flowchart TD
    A["Service selector app=order-api"] --> B["EndpointSlice Controller观察Pod"]
    B --> C["筛选Namespace内匹配Label的Pod"]
    C --> D["读取Pod IP、命名端口和Ready条件"]
    D --> E["创建或更新EndpointSlice"]
    E --> F["kube-proxy或其他数据面观察EndpointSlice"]
    F --> G["编程Service到Pod的转发规则"]

Service Selector只在自己的 Namespace 内选择 Pod。另一个 Namespace 中标签相同的 Pod 不会被普通 Selector Service自动加入。

九、EndpointSlice里有什么

查看:

bash
kubectl get endpointslice -n commerce \
  -l kubernetes.io/service-name=order-api -o yaml

简化结构:

yaml
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 8080
endpoints:
  - addresses: ["10.244.2.17"]
    conditions:
      ready: true
      serving: true
      terminating: false
    nodeName: worker-2
    targetRef:
      kind: Pod
      name: order-api-7d8f-a

9.1 Ready、Serving和Terminating

  • ready:端点是否可作为普通就绪后端。
  • serving:端点是否仍在服务,尤其用于终止语义。
  • terminating:关联Pod是否正在终止。

具体数据面怎样使用这些Condition受版本和实现影响。排查必须看实际 EndpointSlice,而不是只看 Pod Phase。

9.2 Readiness失败为什么会摘除Endpoint

Pod Readiness 变为False后,EndpointSlice Controller更新端点条件,数据面随后更新规则。这个过程有传播延迟,已有连接也可能继续存在,所以 Readiness不是瞬时切断所有流量的防火墙。

9.3 EndpointSlice为什么替代单一Endpoints扩展思路

大量后端全部塞在一个 Endpoints 对象会造成大对象更新和传播压力。EndpointSlice把端点分片,便于扩展、拓扑和双栈等能力。兼容场景中仍可能看到 Endpoints 对象,但新排查应优先理解 EndpointSlice。

十、port、targetPort、nodePort和containerPort

字段所在对象含义
containerPortPod Container文档化容器预期端口,可供命名引用;本身不创建进程监听
targetPortService转发到后端Pod的端口或端口名
portService客户端访问ClusterIP时使用的Service端口
nodePortServiceNodePort/LoadBalancer时节点暴露的端口

示例:

text
客户端访问 order-api:80
→ Service port 80
→ EndpointSlice target port 8080
→ Pod内应用实际监听 0.0.0.0:8080

containerPort: 8080 不会让应用自动监听。Java应用若实际监听9090,而Service targetPort指向8080,会得到拒绝或超时。

10.1 命名端口的价值

yaml
targetPort: http

后端不同Pod模板可以把名字 http 映射到实际端口。命名必须一致且协议正确。滚动期间新旧版本端口号不同但端口名相同,可以降低Service同时兼容的配置复杂度。

十一、ClusterIP是什么

ClusterIP通常是集群 Service CIDR 中的虚拟地址,不一定配置在某张普通网卡上,也通常没有一个用户态进程对每个 ClusterIP 执行 listen()

创建 Service 后:

  1. API Server从Service CIDR分配ClusterIP。
  2. Service和EndpointSlice被节点Service数据面观察。
  3. 数据面编程规则,匹配目标ClusterIP:Port。
  4. 连接被转发/NAT到某个PodIP:targetPort。

因此在节点执行 ip addr 找不到 ClusterIP,不代表 Service 不存在。

十二、kube-proxy做什么

kube-proxy通常观察 Service 和 EndpointSlice,在每个Node维护四层转发规则。请求不一定经过一个中心代理进程,具体取决于模式。

12.1 iptables思路

iptables模式会创建规则链,匹配Service VIP/Port并按规则选择后端,再执行DNAT等处理。第一批数据包经过规则和Conntrack,后续同一连接通常沿已建立的映射。

mermaid
flowchart TD
    A["连接首包到ClusterIP:80"] --> B["iptables Service规则匹配"]
    B --> C["按统计规则选择Endpoint"]
    C --> D["DNAT为PodIP:8080"]
    D --> E["路由/CNI送到目标Pod"]
    E --> F["Conntrack保存该连接映射"]

它不是每个HTTP请求都重新严格轮询。HTTP Keep-Alive、HTTP/2 和 gRPC 长连接会在同一连接上发送很多请求,仍然落在同一后端。

12.2 IPVS思路

IPVS模式在内核维护虚拟服务和真实后端,适合大量Service/Endpoint,并支持若干调度算法。它仍依赖底层路由、Conntrack和同步;看到 IPVS Virtual Server不代表后端应用健康。

12.3 eBPF或其他替代数据面

部分CNI/发行版可用 eBPF 等实现 Service转发并替代部分或全部 kube-proxy。此时排查 iptables/IPVS可能找不到预期规则。第一步要确认集群实际数据面,而不是照搬别的集群命令。

12.4 userspace模式

历史上存在 userspace proxy 模式,但现代生产集群通常不以它作为默认。面试回答应讲清当前目标集群实现,不要把“kube-proxy一定是一个用户态进程转发每个包”当通用原理。

十三、连接级负载与连接复用

Service通常是四层连接转发:

text
TCP连接A → Pod 1
TCP连接B → Pod 3
TCP连接C → Pod 2

但一个连接内可能包含几千个HTTP请求。以下情况会导致流量看起来不均匀:

  • HTTP Keep-Alive。
  • HTTP/2多路复用。
  • gRPC长连接。
  • 数据库连接池。
  • 少量客户端产生大部分流量。
  • Session Affinity。
  • 后端Ready变化。

扩容Pod后,已有长连接不会自动均匀迁移。需要客户端连接生命周期、服务端优雅排水和协议级负载策略共同配合。

十四、Conntrack为什么影响故障切换

Conntrack记录连接状态和NAT映射。某Pod退出后:

  • 新连接会根据新规则选择健康Endpoint。
  • 已有连接可能继续尝试原映射,直到关闭、重置或超时。
  • 大量短连接可能耗尽Conntrack表。

Conntrack压力可能表现为间歇丢包和新连接失败。需要监控表使用量、丢弃、连接创建速率和超时,而不是只看Pod CPU。

十五、Service类型对比

类型/模式返回/暴露典型用途重要边界
ClusterIP集群内虚拟IP和DNS微服务内部调用依赖集群数据面
Headless不分配ClusterIP,DNS返回端点信息StatefulSet成员发现客户端自己选择端点
NodePort每个Node IP上的指定端口外部LB后端、测试防火墙、源IP、节点转发
LoadBalancer云/实现提供外部LB,通常叠加NodePort或直连生产四层暴露依赖Cloud Controller/LB实现
ExternalNameDNS CNAME到外部名称外部服务别名没有Selector/Endpoint转发

十六、ClusterIP Service

默认类型:

yaml
apiVersion: v1
kind: Service
metadata:
  name: order-api
  namespace: commerce
spec:
  type: ClusterIP
  selector:
    app: order-api
  ports:
    - name: http
      port: 80
      targetPort: http

同Namespace访问:

text
http://order-api:80

跨Namespace:

text
http://order-api.commerce.svc.cluster.local:80

是否能从Node或集群外直接访问ClusterIP取决于数据面和网络,不应将其作为外部稳定入口。

十七、Headless Service

yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: data
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
    - name: mysql
      port: 3306

Headless Service没有普通 ClusterIP 负载转发,DNS可返回后端Pod地址或相关记录。客户端/中间件负责成员选择、连接和故障切换。

这适合 StatefulSet成员发现,不代表普通HTTP服务使用Headless就更高性能。客户端必须正确处理DNS TTL、地址变化和失败重试。

十八、NodePort数据路径

yaml
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: http
      nodePort: 31080

外部访问:

text
NodeIP:31080

典型路径:

mermaid
flowchart TD
    A["外部客户端"] --> B["任一可达NodeIP:31080"]
    B --> C["节点Service数据面"]
    C --> D["选择Endpoint"]
    D --> E["本节点或跨节点Pod"]

NodePort不自动打开云安全组、机房防火墙或主机防火墙。部分实现还会限制可接受NodePort的节点地址范围。

十九、LoadBalancer Service

yaml
spec:
  type: LoadBalancer
  selector:
    app: order-api
  ports:
    - name: http
      port: 443
      targetPort: http

Service对象只是声明需求。Cloud Controller或LB实现需要:

  1. 观察LoadBalancer Service。
  2. 创建/配置云负载均衡器。
  3. 配置监听、后端、健康检查和安全组。
  4. 把外部地址写回Service Status。

EXTERNAL-IP 长期 Pending时检查控制器、权限、配额、Annotation、子网和云事件,不是删除Pod能解决。

四层LoadBalancer与七层Ingress职责不同:前者常按TCP/UDP端口转发,后者理解HTTP Host、Path、TLS和七层策略。

二十、ExternalName Service

yaml
apiVersion: v1
kind: Service
metadata:
  name: legacy-payment
  namespace: commerce
spec:
  type: ExternalName
  externalName: payment.legacy.example.com

DNS通常返回CNAME。它没有Selector、ClusterIP和EndpointSlice负载转发。注意:

  • 外部DNS仍可能失败。
  • HTTPS证书和Host/SNI仍针对真实域名。
  • 某些HTTP客户端用Service别名做Host时可能与证书/虚拟主机不匹配。
  • NetworkPolicy对外部地址的控制取决于CNI和解析后的IP。

二十一、没有Selector的Service

Service可以没有Selector,由平台或管理员维护 EndpointSlice,适合迁移到集群外后端等受控场景。手工端点必须:

  • 与Service名称Label正确关联。
  • 地址和端口准确。
  • 有健康管理和变更审计。
  • 避免把Service VIP、回环地址等不合法目标当Endpoint。

普通业务优先使用Selector自动维护,避免Pod变化后手工端点过期。

二十二、CoreDNS怎样提供服务发现

CoreDNS通常观察 Kubernetes Service、EndpointSlice、Namespace等对象并回答集群域查询。Pod的 /etc/resolv.conf 常由kubelet按DNS策略生成。

mermaid
flowchart TD
    A["应用查询order-api"] --> B["Pod resolver读取search和ndots"]
    B --> C["向CoreDNS Service发DNS查询"]
    C --> D["CoreDNS根据Kubernetes对象生成回答"]
    D --> E["返回ClusterIP、Pod地址或CNAME"]
    E --> F["客户端按TTL缓存并建立连接"]

查看:

bash
kubectl exec <pod> -n commerce -- cat /etc/resolv.conf
kubectl get service -n kube-system
kubectl get deployment,pod -n kube-system -l k8s-app=kube-dns

Label可能因发行版不同而变化,应先列出实际对象。

二十三、Service DNS名称和搜索域

完整名称通常:

text
order-api.commerce.svc.cluster.local

同Namespace可简写:

text
order-api

其他Namespace可使用:

text
order-api.commerce

集群域不一定永远是 cluster.local,应以Pod resolv.conf 和集群配置为准。

23.1 ndots为什么会增加DNS查询

典型Pod可能有:

text
search commerce.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

一个点数少于 ndots 的外部域名可能先拼接多个搜索域尝试,再查询绝对名称,造成额外DNS QPS和延迟。外部FQDN可在适合的Resolver中使用尾点明确绝对名称:

text
api.example.com.

是否这样配置还取决于应用DNS库。不能在不了解客户端Resolver行为时只修改CoreDNS扩容来掩盖查询放大。

二十四、DNS缓存和旧地址

DNS记录有TTL,客户端、JVM、Sidecar和本地缓存可能各自缓存。Headless Service后端变化后:

  • CoreDNS记录已更新。
  • 某客户端仍可能使用缓存旧Pod IP。
  • 已建立连接更不会因为DNS变化自动迁移。

Java DNS缓存策略受JDK、安全属性和运行参数影响。应设置与服务发现需求相符的有界缓存,并结合连接池生命周期,不能每次请求都强制DNS查询,也不能永久缓存动态Pod地址。

二十五、DNS排查Demo

使用受控调试镜像:

bash
kubectl run dns-debug -n commerce --rm -it --restart=Never \
  --image=registry.example.com/ops/dns-tools:approved -- sh

容器中:

bash
cat /etc/resolv.conf
nslookup order-api
nslookup order-api.commerce.svc.cluster.local
nslookup kubernetes.default.svc.cluster.local

若精简业务镜像没有 nslookup,不要在线安装不受控软件;使用批准的临时调试Pod/Ephemeral Container。

判断:

  • 集群内置 kubernetes.default 也失败:先查DNS链路/策略。
  • 只有目标Service失败:查Service名称、Namespace和对象。
  • 能解析但连接失败:DNS已完成,继续查Endpoint、端口和数据面。
  • 间歇慢:查CoreDNS负载、上游DNS、查询放大、缓存和丢包。

二十六、Session Affinity

yaml
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800

它尝试让同一源IP在一段时间内选择同一后端。边界:

  • 多个用户经过同一NAT/代理时可能共享源IP。
  • 外部TrafficPolicy和SNAT会影响看到的源地址。
  • 后端删除/不Ready后映射会变化。
  • 它不是用户登录Session的可靠存储。
  • 数据面实现和超时应按目标集群验证。

商业应用应把Session放在共享存储或使用无状态Token,不依赖Service粘性维持正确性。

二十七、externalTrafficPolicy

NodePort/LoadBalancer常用:

yaml
spec:
  externalTrafficPolicy: Cluster

Cluster

  • 外部流量进入任一Node后可转到集群任意健康Endpoint。
  • 后端分布更灵活。
  • 某些路径可能发生SNAT,应用看到的源IP可能不是原始客户端。
  • 可能多一次跨节点转发。

Local

yaml
externalTrafficPolicy: Local
  • Node只把外部流量交给本节点Endpoint。
  • 常用于保留客户端源IP和减少跨节点跳转。
  • 进入没有本地健康Endpoint的Node时可能被丢弃。
  • 外部LB必须通过健康检查只送到有本地后端的Node。
  • Pod分布不均会导致节点间负载不均。

不要为了获取源IP直接改Local而不验证LoadBalancer健康检查、Daemon/Pod分布和容量。

二十八、internalTrafficPolicy

部分版本支持:

yaml
spec:
  internalTrafficPolicy: Local

它限制集群内部流量只使用本节点Endpoint,可减少跨节点通信,但本节点没有后端时请求会失败。默认Cluster更强调可达性。字段支持与具体行为必须按目标版本验证。

二十九、拓扑感知流量

Kubernetes和部分数据面支持 Endpoint拓扑提示、Traffic Distribution等能力,让客户端倾向同Zone/近端Endpoint,降低跨区延迟和费用。它不是绝对保证:

  • 各Zone容量必须足够。
  • 后端不均衡时可能回退或失衡。
  • 字段和Feature成熟度随版本变化。
  • 强制Local可能牺牲可用性。

商业设计需要在延迟、成本和故障域可用性之间权衡。

三十、NetworkPolicy是什么

NetworkPolicy声明允许哪些Pod流量进入或离开。它是否生效取决于 CNI 是否实现 NetworkPolicy;API对象创建成功不代表网络数据面一定执行了规则。

它通常是三/四层策略,按Pod、Namespace、IP块和端口控制,不等同于HTTP路径、JWT或业务权限。

三十一、策略隔离是怎样发生的

对某方向,如果没有任何策略选择某Pod,该方向通常默认不隔离;一旦至少一个策略选择该Pod并声明Ingress/Egress,该方向进入隔离,只允许所有适用策略规则的并集。

规则是累加允许,不是按文件顺序“后一个覆盖前一个”。

一条连接要成功,通常需要:

text
源Pod的Egress允许
AND
目标Pod的Ingress允许
AND
中间网络可达
AND
应用端口监听

只在目标端允许 Ingress,但源 Pod 已被 Egress 默认拒绝,连接仍失败。

三十二、先做Namespace默认拒绝

Ingress默认拒绝:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: commerce
spec:
  podSelector: {}
  policyTypes:
    - Ingress

Ingress和Egress都拒绝:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: commerce
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

部署默认拒绝前必须先盘点 DNS、监控、日志、控制面代理、数据库、MQ和外部API流量,否则会造成大面积超时。

三十三、允许Gateway访问Order API

给Namespace加稳定Label:

bash
kubectl label namespace gateway access-role=gateway --overwrite

策略:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-gateway-to-order-api
  namespace: commerce
spec:
  podSelector:
    matchLabels:
      app: order-api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              access-role: gateway
          podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - protocol: TCP
          port: 8080

同一个 from 条目里同时出现 namespaceSelector 和 podSelector,通常表示 AND:指定Namespace中的指定Pod。若写成两个数组项,则通常是 OR,可能扩大授权范围。

策略端口针对目标 Pod 端口,不是调用方看到的 Service port: 80。数据面在策略和DNAT处理的先后细节可能影响某些IP/Port匹配,设计应使用Pod Selector和目标容器端口并在实际CNI验证。

三十四、Egress默认拒绝后必须考虑DNS

只允许访问数据库但忘记DNS,应用使用域名时会先解析失败。

DNS放行示意:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: commerce
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

实际 CoreDNS Label、Namespace、NodeLocal DNS地址和端口可能不同,先查看集群对象再配置。DNS不仅使用UDP,较大响应、截断和区域传送等场景可能使用TCP,应按Resolver链路放行。

三十五、允许Order API访问数据库

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-order-to-mysql
  namespace: commerce
spec:
  podSelector:
    matchLabels:
      app: order-api
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: data
          podSelector:
            matchLabels:
              app: mysql
      ports:
        - protocol: TCP
          port: 3306

目标 MySQL 若也被 Ingress Policy隔离,还必须在 data Namespace添加允许来自 commerce/order-api 的 Ingress规则。源Egress和目标Ingress必须同时满足。

三十六、ipBlock和NAT边界

yaml
to:
  - ipBlock:
      cidr: 203.0.113.0/24
      except:
        - 203.0.113.128/25

ipBlock适合相对稳定的外部CIDR,不适合用Pod IP替代Pod Selector。数据包在NetworkPolicy处理前后是否已做SNAT/DNAT,取决于CNI、云网络和流量路径,因此:

  • 外部源IP可能已被Node/LB改写。
  • Service VIP可能在策略点前后转换。
  • 云元数据地址和Node流量有实现边界。

必须在目标CNI测试,不能只根据YAML推断最终看到的IP。

三十七、NetworkPolicy不能解决什么

  • 不理解HTTP Path和Method。
  • 不验证JWT和用户权限。
  • 不自动加密流量。
  • 不替代Service Mesh mTLS。
  • 不保证CNI一定支持所有字段。
  • 不一定覆盖Node本机、hostNetwork和特殊系统流量的所有路径。
  • 不阻止应用通过已允许数据库端口执行越权SQL。

网络策略是纵深防御的一层,不是业务认证授权。

三十八、完整商业Service Demo

yaml
apiVersion: v1
kind: Service
metadata:
  name: order-api
  namespace: commerce
  labels:
    app: order-api
spec:
  type: ClusterIP
  selector:
    app: order-api
  sessionAffinity: None
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: http
---
apiVersion: v1
kind: Service
metadata:
  name: order-api-metrics
  namespace: commerce
spec:
  type: ClusterIP
  selector:
    app: order-api
  ports:
    - name: metrics
      port: 8081
      targetPort: metrics

业务和Metrics拆Service可分别设置发现、策略和访问者。Pod Template需要定义对应命名端口:

yaml
ports:
  - name: http
    containerPort: 8080
  - name: metrics
    containerPort: 8081

不要因为 containerPort 声明了8081就认为指标端点一定启动;仍需验证进程监听、认证和Service EndpointSlice。

三十九、从客户端到Pod的验证顺序

39.1 确认客户端身份和Namespace

bash
kubectl get pod <client-pod> -n <client-namespace> -o wide --show-labels

NetworkPolicy按Pod/Namespace Label判断,必须知道请求真正从哪个Pod发出。Ingress Controller、Service Mesh Egress或Node SNAT可能改变观察到的源。

39.2 验证DNS

bash
nslookup order-api.commerce.svc.cluster.local

解析失败先解决DNS;解析成功后记录返回的ClusterIP/地址,再进入连接层。

39.3 检查Service

bash
kubectl get service order-api -n commerce -o yaml

检查:

  • ClusterIP。
  • port/targetPort/protocol。
  • Selector。
  • TrafficPolicy和SessionAffinity。

39.4 检查Pod Label和Ready

bash
kubectl get pod -n commerce -l app=order-api \
  -o wide --show-labels

39.5 检查EndpointSlice

bash
kubectl get endpointslice -n commerce \
  -l kubernetes.io/service-name=order-api -o yaml

确认地址、端口、Ready、Node、Zone和TargetRef。

39.6 分别直连Pod IP和Service

在批准的调试Pod中:

bash
curl -sv --connect-timeout 2 http://<pod-ip>:8080/actuator/health/readiness
curl -sv --connect-timeout 2 http://order-api.commerce.svc.cluster.local/

解释:

  • Pod IP也失败:应用监听、策略、CNI或Pod本身。
  • Pod IP成功、Service失败:Service端口、节点数据面或策略/NAT路径。
  • Service成功、Ingress失败:进入独立Ingress链路排查。

四十、场景一:Service没有Endpoint

bash
kubectl get service order-api -n commerce -o yaml
kubectl get pod -n commerce --show-labels
kubectl get endpointslice -n commerce \
  -l kubernetes.io/service-name=order-api -o yaml

常见原因:

  • Selector键/值拼错。
  • Pod在不同Namespace。
  • Pod没有匹配Label。
  • 命名targetPort在Pod中不存在/不一致。
  • Pod未Ready且不发布未就绪地址。
  • 手工EndpointSlice关联Label错误。

不要通过把Readiness固定为成功来制造Endpoint,这会把不健康实例送入生产流量。

四十一、场景二:有Endpoint但Connection Refused

Refused通常意味着数据包到达某个网络栈,但目标端口没有监听或主动拒绝。检查:

bash
kubectl get pod <pod> -n commerce -o wide
kubectl logs <pod> -n commerce -c order-api --tail=200
kubectl exec <pod> -n commerce -c order-api -- \
  sh -c 'ss -lnt 2>/dev/null || netstat -lnt 2>/dev/null'

精简镜像没有工具时使用受控Debug Container。重点:

  • 应用实际端口。
  • 监听 0.0.0.0/Pod地址还是只监听 127.0.0.1
  • targetPort是否正确。
  • TCP/UDP协议是否一致。
  • 应用是否启动后立即退出。

四十二、场景三:连接超时

超时更像包被丢弃、路由不通、策略拒绝无响应、后端完全无响应或返回路径失败。检查:

  • 源Egress和目标Ingress Policy。
  • 同节点/跨节点差异。
  • CNI Agent和Node网络。
  • 路由、隧道、安全组和MTU。
  • kube-proxy/eBPF规则是否同步。
  • Pod是否因CPU、线程池或GC无法响应。

超时与拒绝不是绝对一一对应,但可帮助确定第一检查方向。

四十三、场景四:只有部分请求失败

先按Endpoint分组:

bash
kubectl get endpointslice -n commerce \
  -l kubernetes.io/service-name=order-api -o wide
kubectl get pod -n commerce -l app=order-api -o wide

逐个Pod IP验证,关联:

  • Pod版本/Digest。
  • Node和Zone。
  • Ready变化。
  • 应用错误率和日志。
  • Sidecar版本。
  • 是否只有跨节点路径失败。

已有长连接可能持续打到旧/异常Pod,因此 Endpoint列表修复后错误不会瞬间归零。需要观察连接超时、客户端连接池和优雅排水。

四十四、场景五:DNS偶发超时

检查:

bash
kubectl get pod -n kube-system -o wide
kubectl get service -n kube-system
kubectl logs -n kube-system deployment/coredns --tail=300

对象名称和Label按发行版调整。关注:

  • CoreDNS副本、CPU、内存、Throttle。
  • 上游DNS延迟。
  • DNS QPS和Cache命中。
  • ndots导致的查询放大。
  • UDP丢包与TCP回退。
  • NetworkPolicy是否同时允许UDP/TCP 53。
  • NodeLocal DNS健康。
  • Conntrack压力。

不要只无限扩CoreDNS,如果根因是每个外部域名产生四五次搜索域查询,先修正客户端查询方式和缓存。

四十五、场景六:NodePort外部访问失败

按顺序:

  1. Service确实是NodePort,记录nodePort。
  2. Node IP从客户端网络可达。
  3. 云安全组、ACL和主机防火墙放行。
  4. kube-proxy/数据面在该Node正常。
  5. externalTrafficPolicy=Local时该Node有Ready本地Endpoint。
  6. Endpoint到Pod网络可达。
  7. 返回路由和源IP策略正确。

NodePort对象存在不代表互联网一定可以访问。

四十六、场景七:NetworkPolicy上线后全超时

立即保留策略和流量路径,检查:

bash
kubectl get networkpolicy -A
kubectl describe networkpolicy -n commerce
kubectl get namespace --show-labels
kubectl get pod -n commerce --show-labels

高频错误:

  • Egress默认拒绝后忘记DNS。
  • 只允许目标Ingress,源Egress仍拒绝。
  • namespaceSelector Label不存在。
  • 把AND写成两个OR数组项,授权过宽;或反过来。
  • 策略端口写Service port而非目标Pod端口。
  • CNI不支持或尚未同步策略。
  • 外部IP经过NAT后不匹配ipBlock。

止损应回退有问题的策略版本,而不是永久删除所有策略。修复后用允许和应拒绝两组测试同时验证,防止“恢复通信但权限全放开”。

四十七、抓包和节点级诊断何时使用

只有对象、Event、日志和指标已把问题指向数据面时,再使用:

  • 受控Debug Container。
  • CNI专用诊断命令。
  • tcpdump/eBPF观测工具。
  • iptables/IPVS/Conntrack检查。
  • Node路由和接口。

抓包前确认权限、目标接口、过滤条件和敏感数据。TLS内容通常不可见,但IP、端口、SNI等元数据仍可能敏感。不要在生产长时间无过滤抓全流量。

典型定位:

text
源Pod是否发出SYN
→ 源Node是否转发/NAT
→ 目标Node是否收到
→ 目标Pod是否收到
→ SYN-ACK是否沿正确路径返回

四十八、商业监控指标

至少监控:

  • CoreDNS请求率、错误、延迟、Cache和上游失败。
  • Service无Ready Endpoint数量。
  • Endpoint Ready变化和版本分布。
  • CNI Agent健康与策略同步错误。
  • Node网络丢包、错误、重传。
  • Conntrack使用率和插入失败。
  • 跨Zone流量与成本。
  • Ingress/LB 4xx、5xx、连接和后端健康。
  • 应用按Pod/Node/Zone的RED指标。

告警必须关联集群、Namespace、Service、版本、Node和Runbook。

四十九、常见误区

  1. Service保存固定Pod IP:后端由EndpointSlice动态维护。
  2. ClusterIP一定存在于某张网卡:它通常是虚拟地址和规则匹配目标。
  3. kube-proxy用户态转发每个包:iptables/IPVS等模式主要在内核数据面。
  4. Service严格按HTTP请求轮询:通常按连接转发,长连接会偏斜。
  5. containerPort会启动监听:监听由应用进程完成。
  6. Endpoint存在就代表业务健康:Readiness可能是假阳性。
  7. DNS变化会迁移已有连接:缓存和连接都需独立过期。
  8. NodePort会自动开安全组:外部网络仍需配置。
  9. ExternalName会代理流量:它主要提供DNS CNAME。
  10. NetworkPolicy创建成功就一定生效:取决于CNI实现。
  11. 只允许目标Ingress就够:源Egress也可能被隔离。
  12. NetworkPolicy是业务权限:它不理解用户和HTTP语义。

五十、面试标准回答

Service从Selector到Pod经历什么

EndpointSlice Controller根据Service Selector选择同Namespace匹配Label的Pod,读取Pod IP、命名targetPort和Ready条件,生成EndpointSlice;kube-proxy或其他数据面观察Service和EndpointSlice,在节点编程ClusterIP/NodePort到PodIP的四层转发。Pod或Ready变化会异步更新端点和规则。

ClusterIP为什么没有进程监听也能访问

ClusterIP通常是Service CIDR中的虚拟地址,不一定配置在网卡,也没有每个VIP对应的用户态监听进程。节点数据面匹配目标VIP和Port,在iptables、IPVS或eBPF等机制中选择Endpoint并DNAT/转发到PodIP。

Service怎样负载均衡

常见数据面在新连接上选择后端,Conntrack让同一连接保持映射。它不是每个HTTP请求严格轮询,Keep-Alive、HTTP/2和gRPC长连接会让大量请求固定到一个Pod;扩容也不会迁移已有连接。

ClusterIP、NodePort、LoadBalancer区别

ClusterIP提供集群内VIP;NodePort在NodeIP上暴露端口并转发到Service后端;LoadBalancer由云控制器或LB实现创建外部入口,常叠加NodePort或直连数据面。对象存在不代表防火墙、健康检查和外部路由已完成。

externalTrafficPolicy Cluster和Local区别

Cluster可把外部流量转到任意Node的健康Endpoint,可达性好但可能跨节点并发生SNAT;Local只使用入口Node本地Endpoint,常能保留源IP和减少一跳,但没有本地后端的Node不能承载流量,必须配合LB健康检查和均匀Pod分布。

NetworkPolicy为什么两边都允许仍可能不通

一条连接需要源Egress允许、目标Ingress允许、中间CNI/路由可达、DNS成功且应用端口监听。策略是所有适用规则的允许并集;还要检查Namespace/Pod Label、目标Pod端口、NAT后的IP以及CNI是否支持该策略。

Service有Endpoint仍访问失败怎么查

先验证Endpoint地址、端口和Ready,再从同一客户端分别直连PodIP和Service:PodIP也失败查应用监听、Policy和CNI;PodIP成功但Service失败查port/targetPort、kube-proxy或eBPF规则和Conntrack;只有跨节点失败查路由、隧道、安全组和MTU。

五十一、学习实验与验收

实验一:观察EndpointSlice变化

  1. 创建两个Ready Pod和Selector Service。
  2. 查看EndpointSlice地址、端口、TargetRef。
  3. 让一个Pod Readiness失败。
  4. 观察Condition和数据面传播。
  5. 恢复后验证新连接重新进入该端点。

实验二:证明Service是连接级转发

使用Keep-Alive/HTTP2客户端在每次响应返回Pod名称,比较单长连接和大量新连接的后端分布,解释为什么扩容后旧连接不迁移。

实验三:端口错配

让应用监听8080,Service targetPort错误指向9090。比较DNS成功、Endpoint存在、PodIP:8080成功和Service失败,形成分层证据。

实验四:跨节点故障

在隔离环境分别测试同节点与跨节点Pod IP,调整测试MTU或阻断CNI隧道,观察小包/大包、路由和Event/指标差异。

实验五:默认拒绝和最小放行

先记录Gateway→Order→MySQL和DNS流量,再实施默认拒绝;逐条加入DNS、Gateway Ingress、MySQL Egress和目标Ingress。用允许/拒绝测试证明策略没有过宽。

验收清单

  1. 能画出Pod Sandbox、CNI、Pod IP形成过程。
  2. 能解释同Pod localhost和跨Pod通信边界。
  3. 能从Service Selector找到EndpointSlice和目标Pod。
  4. 能区分四类端口字段。
  5. 能解释ClusterIP虚拟转发和Conntrack。
  6. 能解释iptables、IPVS和eBPF实现边界。
  7. 能解释长连接为什么导致负载不均。
  8. 能诊断CoreDNS、搜索域、ndots和缓存问题。
  9. 能比较五类Service模式。
  10. 能解释Cluster/Local TrafficPolicy取舍。
  11. 能设计双向最小NetworkPolicy和DNS放行。
  12. 能根据拒绝、超时、偶发失败定位到正确层。

关联知识点