Kubernetes Pod网络、Service、DNS与NetworkPolicy
应用容器 Running 只说明进程已经启动,不说明其他 Pod、Service 或集群外客户端能够访问它。Kubernetes 网络是一条分层链路:Pod 先获得网络命名空间和 IP,Service Selector 生成 EndpointSlice,CoreDNS提供名称发现,节点数据面把虚拟 Service 地址转发到后端,NetworkPolicy再约束允许的通信。
本页不把 Service 简化成“负载均衡到Pod”,而是从一个请求的数据包视角解释:名称怎样解析、目标IP是否真实存在于网卡、后端列表从哪里来、连接为什么会固定在某个Pod、跨节点为什么可能失败、策略为什么两边都要允许。HTTP/HTTPS域名和路径入口放在独立的 Ingress专栏 中。
学习目标
学完后应能:
- 解释 Kubernetes Pod 网络模型和 CNI 的职责。
- 解释同Pod
localhost、同节点Pod通信和跨节点Pod通信的差异。 - 从 Service Selector 追踪到 EndpointSlice 地址、端口与Ready条件。
- 解释 ClusterIP 为什么通常不是某张网卡上的普通监听地址。
- 解释 kube-proxy 的 iptables/IPVS 思路、NAT和Conntrack边界。
- 区分 ClusterIP、Headless、NodePort、LoadBalancer和ExternalName。
- 区分
port、targetPort、nodePort和容器监听端口。 - 解释 CoreDNS、搜索域、
ndots、Headless DNS 和 ExternalName CNAME。 - 解释
externalTrafficPolicy、internalTrafficPolicy和 ClientIP Session Affinity。 - 正确设计默认拒绝、DNS放行和应用最小通信 NetworkPolicy。
- 解释为什么 Ingress/Service有配置仍可能404、503、拒绝或超时。
- 按 DNS、Service、EndpointSlice、Pod、Node数据面、CNI和策略逐层排查。
一、先建立分层模型
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 插件配置网络。
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 思路中:
Pod网络命名空间中的eth0
↕ veth pair
Node网络命名空间中的对端接口
↓
路由、Bridge、隧道或eBPF数据面具体接口名、Bridge和路由取决于 CNI,不能把某个插件的 cni0、flannel.1 当成所有集群共同结构。
四、同一个Pod中的容器怎样通信
同一个 Pod 的普通容器共享 Pod 网络命名空间,因此:
- 共享同一个 Pod IP。
- 可以通过
localhost互相访问。 - 共享端口空间,两个容器不能同时监听同一个IP:Port。
- 网络设备和路由视图通常一致。
例如业务容器监听 0.0.0.0:8080,同 Pod Sidecar 可以访问:
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:
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:
metadata:
labels:
app: order-api
spec:
containers:
- name: order-api
ports:
- name: http
containerPort: 8080控制链:
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里有什么
查看:
kubectl get endpointslice -n commerce \
-l kubernetes.io/service-name=order-api -o 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-a9.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
| 字段 | 所在对象 | 含义 |
|---|---|---|
containerPort | Pod Container | 文档化容器预期端口,可供命名引用;本身不创建进程监听 |
targetPort | Service | 转发到后端Pod的端口或端口名 |
port | Service | 客户端访问ClusterIP时使用的Service端口 |
nodePort | Service | NodePort/LoadBalancer时节点暴露的端口 |
示例:
客户端访问 order-api:80
→ Service port 80
→ EndpointSlice target port 8080
→ Pod内应用实际监听 0.0.0.0:8080containerPort: 8080 不会让应用自动监听。Java应用若实际监听9090,而Service targetPort指向8080,会得到拒绝或超时。
10.1 命名端口的价值
targetPort: http后端不同Pod模板可以把名字 http 映射到实际端口。命名必须一致且协议正确。滚动期间新旧版本端口号不同但端口名相同,可以降低Service同时兼容的配置复杂度。
十一、ClusterIP是什么
ClusterIP通常是集群 Service CIDR 中的虚拟地址,不一定配置在某张普通网卡上,也通常没有一个用户态进程对每个 ClusterIP 执行 listen()。
创建 Service 后:
- API Server从Service CIDR分配ClusterIP。
- Service和EndpointSlice被节点Service数据面观察。
- 数据面编程规则,匹配目标ClusterIP:Port。
- 连接被转发/NAT到某个PodIP:targetPort。
因此在节点执行 ip addr 找不到 ClusterIP,不代表 Service 不存在。
十二、kube-proxy做什么
kube-proxy通常观察 Service 和 EndpointSlice,在每个Node维护四层转发规则。请求不一定经过一个中心代理进程,具体取决于模式。
12.1 iptables思路
iptables模式会创建规则链,匹配Service VIP/Port并按规则选择后端,再执行DNAT等处理。第一批数据包经过规则和Conntrack,后续同一连接通常沿已建立的映射。
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通常是四层连接转发:
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实现 |
| ExternalName | DNS CNAME到外部名称 | 外部服务别名 | 没有Selector/Endpoint转发 |
十六、ClusterIP Service
默认类型:
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访问:
http://order-api:80跨Namespace:
http://order-api.commerce.svc.cluster.local:80是否能从Node或集群外直接访问ClusterIP取决于数据面和网络,不应将其作为外部稳定入口。
十七、Headless Service
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: data
spec:
clusterIP: None
selector:
app: mysql
ports:
- name: mysql
port: 3306Headless Service没有普通 ClusterIP 负载转发,DNS可返回后端Pod地址或相关记录。客户端/中间件负责成员选择、连接和故障切换。
这适合 StatefulSet成员发现,不代表普通HTTP服务使用Headless就更高性能。客户端必须正确处理DNS TTL、地址变化和失败重试。
十八、NodePort数据路径
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: http
nodePort: 31080外部访问:
NodeIP:31080典型路径:
flowchart TD
A["外部客户端"] --> B["任一可达NodeIP:31080"]
B --> C["节点Service数据面"]
C --> D["选择Endpoint"]
D --> E["本节点或跨节点Pod"]NodePort不自动打开云安全组、机房防火墙或主机防火墙。部分实现还会限制可接受NodePort的节点地址范围。
十九、LoadBalancer Service
spec:
type: LoadBalancer
selector:
app: order-api
ports:
- name: http
port: 443
targetPort: httpService对象只是声明需求。Cloud Controller或LB实现需要:
- 观察LoadBalancer Service。
- 创建/配置云负载均衡器。
- 配置监听、后端、健康检查和安全组。
- 把外部地址写回Service Status。
EXTERNAL-IP 长期 Pending时检查控制器、权限、配额、Annotation、子网和云事件,不是删除Pod能解决。
四层LoadBalancer与七层Ingress职责不同:前者常按TCP/UDP端口转发,后者理解HTTP Host、Path、TLS和七层策略。
二十、ExternalName Service
apiVersion: v1
kind: Service
metadata:
name: legacy-payment
namespace: commerce
spec:
type: ExternalName
externalName: payment.legacy.example.comDNS通常返回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策略生成。
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缓存并建立连接"]查看:
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-dnsLabel可能因发行版不同而变化,应先列出实际对象。
二十三、Service DNS名称和搜索域
完整名称通常:
order-api.commerce.svc.cluster.local同Namespace可简写:
order-api其他Namespace可使用:
order-api.commerce集群域不一定永远是 cluster.local,应以Pod resolv.conf 和集群配置为准。
23.1 ndots为什么会增加DNS查询
典型Pod可能有:
search commerce.svc.cluster.local svc.cluster.local cluster.local
options ndots:5一个点数少于 ndots 的外部域名可能先拼接多个搜索域尝试,再查询绝对名称,造成额外DNS QPS和延迟。外部FQDN可在适合的Resolver中使用尾点明确绝对名称:
api.example.com.是否这样配置还取决于应用DNS库。不能在不了解客户端Resolver行为时只修改CoreDNS扩容来掩盖查询放大。
二十四、DNS缓存和旧地址
DNS记录有TTL,客户端、JVM、Sidecar和本地缓存可能各自缓存。Headless Service后端变化后:
- CoreDNS记录已更新。
- 某客户端仍可能使用缓存旧Pod IP。
- 已建立连接更不会因为DNS变化自动迁移。
Java DNS缓存策略受JDK、安全属性和运行参数影响。应设置与服务发现需求相符的有界缓存,并结合连接池生命周期,不能每次请求都强制DNS查询,也不能永久缓存动态Pod地址。
二十五、DNS排查Demo
使用受控调试镜像:
kubectl run dns-debug -n commerce --rm -it --restart=Never \
--image=registry.example.com/ops/dns-tools:approved -- sh容器中:
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
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800它尝试让同一源IP在一段时间内选择同一后端。边界:
- 多个用户经过同一NAT/代理时可能共享源IP。
- 外部TrafficPolicy和SNAT会影响看到的源地址。
- 后端删除/不Ready后映射会变化。
- 它不是用户登录Session的可靠存储。
- 数据面实现和超时应按目标集群验证。
商业应用应把Session放在共享存储或使用无状态Token,不依赖Service粘性维持正确性。
二十七、externalTrafficPolicy
NodePort/LoadBalancer常用:
spec:
externalTrafficPolicy: ClusterCluster
- 外部流量进入任一Node后可转到集群任意健康Endpoint。
- 后端分布更灵活。
- 某些路径可能发生SNAT,应用看到的源IP可能不是原始客户端。
- 可能多一次跨节点转发。
Local
externalTrafficPolicy: Local- Node只把外部流量交给本节点Endpoint。
- 常用于保留客户端源IP和减少跨节点跳转。
- 进入没有本地健康Endpoint的Node时可能被丢弃。
- 外部LB必须通过健康检查只送到有本地后端的Node。
- Pod分布不均会导致节点间负载不均。
不要为了获取源IP直接改Local而不验证LoadBalancer健康检查、Daemon/Pod分布和容量。
二十八、internalTrafficPolicy
部分版本支持:
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,该方向进入隔离,只允许所有适用策略规则的并集。
规则是累加允许,不是按文件顺序“后一个覆盖前一个”。
一条连接要成功,通常需要:
源Pod的Egress允许
AND
目标Pod的Ingress允许
AND
中间网络可达
AND
应用端口监听只在目标端允许 Ingress,但源 Pod 已被 Egress 默认拒绝,连接仍失败。
三十二、先做Namespace默认拒绝
Ingress默认拒绝:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: commerce
spec:
podSelector: {}
policyTypes:
- IngressIngress和Egress都拒绝:
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:
kubectl label namespace gateway access-role=gateway --overwrite策略:
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放行示意:
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访问数据库
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边界
to:
- ipBlock:
cidr: 203.0.113.0/24
except:
- 203.0.113.128/25ipBlock适合相对稳定的外部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
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需要定义对应命名端口:
ports:
- name: http
containerPort: 8080
- name: metrics
containerPort: 8081不要因为 containerPort 声明了8081就认为指标端点一定启动;仍需验证进程监听、认证和Service EndpointSlice。
三十九、从客户端到Pod的验证顺序
39.1 确认客户端身份和Namespace
kubectl get pod <client-pod> -n <client-namespace> -o wide --show-labelsNetworkPolicy按Pod/Namespace Label判断,必须知道请求真正从哪个Pod发出。Ingress Controller、Service Mesh Egress或Node SNAT可能改变观察到的源。
39.2 验证DNS
nslookup order-api.commerce.svc.cluster.local解析失败先解决DNS;解析成功后记录返回的ClusterIP/地址,再进入连接层。
39.3 检查Service
kubectl get service order-api -n commerce -o yaml检查:
- ClusterIP。
- port/targetPort/protocol。
- Selector。
- TrafficPolicy和SessionAffinity。
39.4 检查Pod Label和Ready
kubectl get pod -n commerce -l app=order-api \
-o wide --show-labels39.5 检查EndpointSlice
kubectl get endpointslice -n commerce \
-l kubernetes.io/service-name=order-api -o yaml确认地址、端口、Ready、Node、Zone和TargetRef。
39.6 分别直连Pod IP和Service
在批准的调试Pod中:
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
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通常意味着数据包到达某个网络栈,但目标端口没有监听或主动拒绝。检查:
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分组:
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偶发超时
检查:
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外部访问失败
按顺序:
- Service确实是NodePort,记录nodePort。
- Node IP从客户端网络可达。
- 云安全组、ACL和主机防火墙放行。
- kube-proxy/数据面在该Node正常。
- externalTrafficPolicy=Local时该Node有Ready本地Endpoint。
- Endpoint到Pod网络可达。
- 返回路由和源IP策略正确。
NodePort对象存在不代表互联网一定可以访问。
四十六、场景七:NetworkPolicy上线后全超时
立即保留策略和流量路径,检查:
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等元数据仍可能敏感。不要在生产长时间无过滤抓全流量。
典型定位:
源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。
四十九、常见误区
- Service保存固定Pod IP:后端由EndpointSlice动态维护。
- ClusterIP一定存在于某张网卡:它通常是虚拟地址和规则匹配目标。
- kube-proxy用户态转发每个包:iptables/IPVS等模式主要在内核数据面。
- Service严格按HTTP请求轮询:通常按连接转发,长连接会偏斜。
containerPort会启动监听:监听由应用进程完成。- Endpoint存在就代表业务健康:Readiness可能是假阳性。
- DNS变化会迁移已有连接:缓存和连接都需独立过期。
- NodePort会自动开安全组:外部网络仍需配置。
- ExternalName会代理流量:它主要提供DNS CNAME。
- NetworkPolicy创建成功就一定生效:取决于CNI实现。
- 只允许目标Ingress就够:源Egress也可能被隔离。
- 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变化
- 创建两个Ready Pod和Selector Service。
- 查看EndpointSlice地址、端口、TargetRef。
- 让一个Pod Readiness失败。
- 观察Condition和数据面传播。
- 恢复后验证新连接重新进入该端点。
实验二:证明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。用允许/拒绝测试证明策略没有过宽。
验收清单
- 能画出Pod Sandbox、CNI、Pod IP形成过程。
- 能解释同Pod localhost和跨Pod通信边界。
- 能从Service Selector找到EndpointSlice和目标Pod。
- 能区分四类端口字段。
- 能解释ClusterIP虚拟转发和Conntrack。
- 能解释iptables、IPVS和eBPF实现边界。
- 能解释长连接为什么导致负载不均。
- 能诊断CoreDNS、搜索域、ndots和缓存问题。
- 能比较五类Service模式。
- 能解释Cluster/Local TrafficPolicy取舍。
- 能设计双向最小NetworkPolicy和DNS放行。
- 能根据拒绝、超时、偶发失败定位到正确层。
