Kubernetes网络模型深度解析:从Pod通信到Ingress网关实战
一、为什么K8s网络让无数人头疼
上午聊完 Istio 服务网格,很多同学会问:Istio 的 Sidecar 拦截的是"服务间流量",那流量在进入服务网格之前,是怎么在 Kubernetes 集群里流动的?Pod 之间到底怎么通信?为什么 Service 的 IP 有时 ping 不通却能用?Ingress 和 Service 又是什么关系?
这一篇,我们把 Kubernetes 网络这条主线彻底捋清楚:从 Pod 的 IP 是怎么来的,到跨节点流量怎么走,再到外部流量怎么进来,最后附上实战排查手册。
1.1 传统网络与K8s网络的本质差异
在传统虚拟机/物理机架构里,网络模型非常简单:一台机器一个 IP,防火墙规则写在主机上,端口冲突自己管理。运维对网络有绝对的掌控感。
但 K8s 把这一切打碎了:
| 维度 | 传统主机网络 | Kubernetes 网络 |
|---|---|---|
| IP 分配 | 固定、手工规划 | Pod IP 动态分配、随时漂移 |
| 通信主体 | 主机 ↔ 主机 | Pod ↔ Pod、Pod ↔ Service |
| 网络边界 | 物理网卡/交换机 | 虚拟网络命名空间 + veth |
| 服务发现 | 固定 IP + 端口 | Service + DNS,动态解析 |
| 安全控制 | 防火墙规则 | NetworkPolicy(一层层策略) |
核心矛盾:业务代码还在用"IP:端口"的思维写调用,但 Pod 的 IP 是"临时的"——扩容、故障迁移、滚动更新都会让 IP 变。K8s 网络要解决的第一件事,就是在 IP 随时变化的前提下,让通信依然可靠。
1.2 K8s网络要解决的四个问题
- Pod 之间的通信:同节点 Pod、跨节点 Pod 怎么互通?
- Pod 与 Service 的通信:IP 会变的 Pod,怎么稳定地被访问?
- Service 与外部的通信:集群外部(比如数据库、第三方 API)怎么访问集群内服务?
- 南北向流量入口:用户的浏览器请求,怎么到达集群里的应用?
这四个问题,就是 K8s 网络的四个层次。下面一层一层拆。
二、K8s网络模型:一条必须遵守的"宪法"
K8s 对网络有一条硬性要求,称为CNI(Container Network Interface)规范,核心就三条:
- 每个 Pod 拥有独立的 IP(集群内唯一)
- Pod 之间可以直接通信,不需要 NAT(无论是否同节点)
- Pod 访问节点、节点访问 Pod,也不需要 NAT
为什么这三条如此重要?因为只有"IP 全网可达、不搞地址转换",才能让 Service、Ingress、Istio 这些上层组件基于一个统一、简单的地址空间做文章。任何 CNI 插件(Flannel、Calico、Cilium……)都必须满足这个模型。
2.1 网络命名空间与veth pair
先看最小单元:一个 Pod 的网络长什么样。
┌─────────────────────────── Pod ───────────────────────────┐ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 容器 A │ │ 容器 B │ │ │ │ (业务进程) │ │ (Sidecar) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ eth0 │ │ │ └────────┬─────────┘ │ │ ┌────┴─────┐ │ │ │ pause │ ← Pod 的"网络根容器" │ │ │ 容器 │ 持有 eth0 + IP │ │ └────┬─────┘ │ └──────────────────┼─────────────────────────────────────────┘ │ veth0(veth pair 的一端) ▼ ┌─────────────────────┐ │ cni0 网桥/隧道 │ ← 由 CNI 插件创建 └─────────────────────┘关键点:Pod 里的所有容器共享一个网络命名空间——这个命名空间由 pause(沙箱)容器持有,业务容器(包括 Sidecar)都把自己的 eth0 挂进去。所以 Pod 内容器通过 localhost 就能互相访问(这也是 Istio Sidecar 能拦截流量的基础)。
而 Pod 的 eth0 另一端,是通过veth pair(虚拟网线)连到 CNI 插件创建的虚拟网络设备上。数据从容器 eth0 出来,就到了 CNI 插件的管辖范围。
2.2 三大主流CNI实现怎么选
| CNI 插件 | 数据面技术 | 性能 | 网络策略 | 适用场景 |
|---|---|---|---|---|
| Flannel | VXLAN / Host-GW | 中 | ❌ 不支持 | 小集群、快速上手、学习环境 |
| Calico | BGP / IPIP | 高 | ✅ 完整 | 生产主流,策略要求高 |
| Cilium | eBPF | 最高 | ✅ 极强 | 大规模、高吞吐、可观测性 |
选型建议:
- 学习/试验:Flannel,一条命令装完,原理简单。
- 生产通用:Calico,网络策略成熟,社区大,坑少。
- 性能极致/大规模:Cilium,eBPF 内核态转发,延迟低一个量级,还自带可观测性(Hubble),但内核版本要求较高(5.8+ 更佳)。
注意:网络策略(NetworkPolicy)不是 CNI 自带的,Flannel 就不支持。如果你需要"默认拒绝 + 白名单"的安全隔离,直接选 Calico 或 Cilium。
三、Pod间通信原理:从同节点到跨节点
3.1 同节点通信:网桥转发
两个 Pod 在同一个节点上时,通信路径非常短:
Pod A(10.244.1.2) ──veth──> cni0 网桥 ──veth──> Pod B(10.244.1.3)数据包从 Pod A 的 eth0 出来,经 veth 到达节点的 cni0 网桥,网桥查 MAC 地址表直接转发给 Pod B 的 veth。全程不离开节点,性能损耗几乎为零。
3.2 跨节点通信:隧道封装
Pod 在节点 1(10.244.1.0/24),要访问节点 2 的 Pod(10.244.2.0/24),两个网段不同,数据包怎么过去?
以 Flannel VXLAN 模式为例:
节点1 节点2 ┌──────────────────────────┐ UDP 8484 ┌──────────────────────────┐ │ Pod A 10.244.1.2 │ │ Pod B 10.244.2.3 │ │ │ │ │ ▲ │ │ ▼ │ │ │ │ │ cni0 网桥 │ │ cni0 网桥 │ │ │ │ │ │ │ │ ▼ │ │ │ │ │ flannel.1 (VTEP) │ │ flannel.1 (VTEP) │ │ │ │ │ │ │ │ ▼ 原始包 + VXLAN头 │ │ │ 解封装还原原始包 │ │ eth0 10.0.0.11 ──────────►│──────────────────│◄─ eth0 10.0.0.12 │ └──────────────────────────┘ 物理网络 └──────────────────────────┘VXLAN 的做法是:把 Pod 的原始数据包整个"装进"一个 UDP 包里(外层目的地址是目标节点的物理 IP),通过物理网络传输,到达后再解封装还原。对底层网络完全无感——哪怕物理交换机不支持任何特殊协议也能跑。
Calico 的 BGP 模式则不同:它把节点当作 BGP 路由器,直接广播"这些 Pod 网段在这个节点",数据包不封装、直接路由,性能更好,但要求物理网络允许(云厂商 VPC 通常需要配置路由表或 IPIP 模式)。
四、Service与kube-proxy:稳定访问的基石
Pod IP 会漂移,那业务怎么稳定访问?答案是Service。
Service 是一个虚拟的"稳定入口":它有一个固定的虚拟 IP(ClusterIP),通过 Label Selector 动态关联一组 Pod。Pod 挂了、扩了、变了,Service 的 IP 不变。
4.1 Service三种类型
| 类型 | 作用 | 访问方式 | 适用场景 |
|---|---|---|---|
| ClusterIP | 集群内部虚拟 IP | 集群内访问 | 服务间调用(默认) |
| NodePort | 每个节点开放一个端口 | 节点IP:端口 外部访问 | 测试、非云环境暴露 |
| LoadBalancer | 云厂商 LB + NodePort | 负载均衡器 IP | 云上对外暴露 |
4.2 kube-proxy三种转发模式
kube-proxy 负责把访问 ClusterIP 的流量转发到后端 Pod,有三种实现:
| 模式 | 原理 | 性能 | 特点 |
|---|---|---|---|
| userspace | 用户态代理转发 | 差 | 老古董,已弃用 |
| iptables | 内核 Netfilter 规则链 | 中 | 默认模式,规则多时延迟升高 |
| IPVS | 内核 LVS 负载均衡 | 高 | 规则少、支持多种调度算法 |
生产建议:直接开 IPVS。iptables 模式下,Service 数量一多(上千条规则),每条新连接都要遍历规则链,延迟明显;IPVS 基于哈希表,O(1) 查找,性能稳定。
4.3 ClusterIP转发链路拆解
访问my-service:8080(ClusterIP 10.96.0.10)时发生了什么:
客户端 Pod │ 访问 10.96.0.10:8080 ▼ PREROUTING ──> KUBE-SERVICES 链 │ 匹配 ClusterIP 10.96.0.10 ▼ KUBE-SVC-XXXX 链(负载均衡) │ 按权重跳转到后端 ▼ KUBE-SEP-YYYY(Endpoint:Pod A / Pod B / Pod C) │ DNAT 改写目的地址为 Pod IP ▼ 真实 Pod(10.244.1.2:8080)这就是为什么 ClusterIP “ping 不通却能用”——ClusterIP 是 iptables/IPVS 里的一条虚拟规则,不是真实网卡地址,ICMP 没人应答;但 TCP 流量会被规则 DNAT 到真实 Pod,所以业务访问完全正常。
五、DNS与服务发现:让调用方不再硬编码IP
Service 解决了"稳定 IP",但代码里写死 ClusterIP 依然不优雅——换个环境 IP 就变了。K8s 的答案是DNS 服务发现(通常由 CoreDNS 提供)。
5.1 自动生成的DNS记录
K8s 会自动为每个 Service 生成 DNS 记录:
<service-name>.<namespace>.svc.cluster.local比如 namespaceprod下的order-service,集群内任何 Pod 都可以直接:
curl http://order-service.prod.svc.cluster.local:8080/api/orders同一 namespace 内还能简写:
curl http://order-service:8080/api/orders5.2 服务发现实战示例
apiVersion:v1kind:Servicemetadata:name:order-servicenamespace:prodspec:selector:app:order-serviceports:-port:8080# Service 端口targetPort:8080# Pod 容器端口配合 Deployment 的app: order-service标签,这套配置就完成了:DNS 解析 → Service → 后端 Pod的完整链路。代码里只需要写服务名,IP 的事全交给 K8s。
六、南北向流量:Ingress实战
Service 解决了集群内部和"节点级"的暴露,但生产环境的需求是:一个域名 + 80/443 端口,路由到多个服务。用 NodePort 一个个暴露端口显然不行(端口冲突、无法按域名分流)。这就轮到Ingress登场。
6.1 Ingress vs LoadBalancer
| 维度 | LoadBalancer | Ingress |
|---|---|---|
| 层级 | L4(TCP/UDP) | L7(HTTP/HTTPS) |
| 按域名/路径路由 | ❌ | ✅ |
| TLS 终结 | 部分 | ✅ |
| 成本 | 每个服务一个 LB,贵 | 一个 LB 入口,多个服务共用 |
| 典型场景 | 数据库、gRPC、非 HTTP | Web 应用、API 网关 |
6.2 部署Nginx Ingress Controller
Ingress 本身只是一个 API 对象,真正干活的是Ingress Controller(社区主流是 ingress-nginx,底层是 Nginx + Lua):
# 以 Helm 方式部署 ingress-nginxhelm repoaddingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update helminstallingress-nginx ingress-nginx/ingress-nginx\--namespaceingress-nginx\--create-namespace\--setcontroller.service.type=LoadBalancer部署后,所有 Ingress 规则由这个控制器监听并动态生成 Nginx 配置。
6.3 Ingress规则配置实战
假设有两个服务:blog-service(博客)和api-service(API),要求:
www.example.com→ blog-serviceapi.example.com→ api-service(带 TLS)
apiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:example-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target:/$2spec:rules:-host:www.example.comhttp:paths:-path:/pathType:Prefixbackend:service:name:blog-serviceport:number:80-host:api.example.comhttp:paths:-path:/v1(/|$)(.*)pathType:ImplementationSpecificbackend:service:name:api-serviceport:number:8080tls:-hosts:-api.example.comsecretName:api-tls-secret请求链路全貌:
用户浏览器 │ https://api.example.com/v1/orders ▼ 云LB(LoadBalancer) ▼ Ingress Controller(Nginx,443/80) │ 按 Host=api.example.com + 路径 /v1/ 匹配规则 │ TLS 终结,rewrite 为 /orders ▼ api-service:8080(ClusterIP) ▼ kube-proxy → 真实 Pod一句话总结 Ingress 的价值:把"域名、路径、TLS、灰度、限流"这些七层能力集中在一个入口统一管理,业务团队只管写 Ingress 规则,不用碰 LB。
七、网络安全:NetworkPolicy实战
K8s 网络默认"全通"——所有 Pod 之间都能互访。生产环境这显然不行:支付服务凭什么能被任意 Pod 调用?此时需要NetworkPolicy做精细化隔离。
7.1 默认拒绝一切入站
apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:default-deny-ingressnamespace:prodspec:podSelector:{}# 匹配命名空间所有 PodpolicyTypes:-Ingress# 只管控入站这条策略一落地,prod命名空间所有 Pod 的入站流量全部被拒——任何新服务上线默认零暴露,必须显式加白名单。
7.2 只允许网关访问订单服务
apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:allow-gateway-to-ordernamespace:prodspec:podSelector:matchLabels:app:order-servicepolicyTypes:-Ingressingress:-from:-podSelector:matchLabels:app:api-gatewayports:-protocol:TCPport:8080效果:只有带app: api-gateway标签的 Pod 能访问 order-service 的 8080 端口,其他全部隔离。默认拒绝 + 显式放行是生产集群的标准姿势。
注意:NetworkPolicy 依赖 CNI 支持(Calico/Cilium 均可),Flannel 不生效,且策略要按命名空间维度规划好,否则排查起来非常痛苦。
八、网络排查实战手册
网络问题占了 K8s 排障的一半。按下面的顺序排查,能少走很多弯路。
8.1 Pod之间不通
# 1. 确认 Pod 状态和 IPkubectl get pod-owide-nprod# 2. 进入源 Pod 测试连通性kubectlexec-it<src-pod>-nprod --ping<dst-pod-ip># 3. 不通则查 CNI 状态kubectl get pods-nkube-system|grep-E"flannel|calico|cilium"kubectl logs-nkube-system<cni-pod># 4. 检查 NetworkPolicy 是否误拦kubectl get networkpolicy-A常见原因:CNI 组件异常、NetworkPolicy 拦截、节点路由表缺失(Calico BGP 未建立)。
8.2 DNS解析失败
# 在 Pod 内测试 DNSkubectlexec-it<pod>--nslookuporder-service.prod.svc.cluster.local# 检查 CoreDNSkubectl get pods-nkube-system|grepcoredns kubectl logs-nkube-system<coredns-pod>--tail=50常见原因:CoreDNS 副本数不足(建议≥2)、resolv.conf里的search域被覆盖(重点查dnsPolicy)、上游 DNS 不通。
8.3 Service访问不通
# 1. 检查 Endpoints 是否有后端kubectl get endpoints<service>-nprod# 如果 ENDPOINTS 为空 → selector 没匹配上 Pod,最经典的坑!# 2. 检查 kube-proxy 模式与状态kubectl get pods-nkube-system|grepkube-proxy# 3. 检查 iptables/IPVS 规则# IPVS 模式:ipvsadm-ln|grep<cluster-ip>最常见的坑:Service 的selector标签和 Pod 标签不一致,导致 Endpoints 为空,Service 看起来存在但永远连不通。
九、总结
把 K8s 网络这条主线串起来:
- CNI 插件解决"Pod 怎么拿到 IP、怎么互通"——Flannel/Calico/Cilium 按需选型;
- Service + kube-proxy解决"Pod IP 漂移怎么稳定访问"——ClusterIP 是虚拟规则,生产开 IPVS;
- CoreDNS解决"代码不写死 IP"——服务名就是地址;
- Ingress解决"外部流量怎么按域名/路径进来"——七层统一入口;
- NetworkPolicy解决"谁能访问谁"——默认拒绝 + 白名单。
回到开头的问题:Istio 建立在 K8s 网络之上——K8s 网络保证"包能到",Istio 保证"流量怎么治理"。理解了这一层网络地基,再看服务网格、再看云原生架构,都会有豁然开朗的感觉。
下一篇可以聊聊Kubernetes 调度与资源管理,把 Pod 是怎么被"安放"到节点上的原理讲透,敬请期待。