ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【Kubernetes从入门到精通】第43篇:K8s网络模型——“每个Pod一个独立IP“背后的大智慧

【Kubernetes从入门到精通】第43篇:K8s网络模型——“每个Pod一个独立IP“背后的大智慧 上一篇【第42篇】CSI——容器存储接口标准深度解析下一篇【第44篇】Flannel——最简单的K8s网络方案但别小看它摘要恭喜你存储模块终于通关了CSI让你见识了K8s是怎么用一套gRPC接口搞定万国牌存储的。现在咱们进入一个更魔幻的领域——K8s网络。你有没有想过一个问题K8s集群里可能有几千个Pod这些Pod分布在几十台Node上它们是怎么互相找到彼此的在传统的Docker世界里容器跟容器通信得靠端口映射、NAT、网桥搞得巨复杂。但K8s的做法简单粗暴——“每个Pod一个独立IP所有Pod之间直连不搞NAT那套虚的”。这篇文章是模块5的开篇宪法——咱们不聊具体哪个网络插件Flannel、Calico、Cilium后面有的是篇幅先把K8s网络的基础逻辑掰扯清楚(1)三大基本原则为什么这么设计(2)NAT在什么层次存在不在Pod层面在Service层面(3)CNI标准到底定义了哪些接口(4)选网络插件时你应该关心什么。读完这篇你就知道为什么每个Pod一个IP是K8s网络最精妙的设计——它的核心思想不是帮你节省IP而是让你简化网络。一、K8s网络的三大基本原则——“网络的宪法”1.1 先来个全景图在聊细节之前先看一张图理解K8s网络到底要解决什么问题【K8s网络模型全景——三通一平】 ┌──────────────┐ │ Pod A │ │ 10.244.1.5 │ └──────┬───────┘ │ ① Pod-to-Pod (同Node) │ 同一个网桥上直接通信 ▼ ┌──────────────┐ │ Pod B │ │ 10.244.1.6 │ └──────────────┘ ┌─────────────────────────────────────────────────┐ │ Node 1 (10.0.0.1) │ │ ┌────────────────────────────────────────────┐ │ │ │ cni0 / docker0 网桥 │ │ │ │ 10.244.1.0/24 │ │ │ └────────────────────────────────────────────┘ │ └──────────────────────┬──────────────────────────┘ │ ② Pod-to-Pod (跨Node) │ 走网络插件隧道/路由 ▼ ┌──────────────────────┴──────────────────────────┐ │ Node 2 (10.0.0.2) │ │ ┌────────────────────────────────────────────┐ │ │ │ cni0 / docker0 网桥 │ │ │ │ 10.244.2.0/24 │ │ │ └────────────────────────────────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ Pod C │ ③ Pod │ Pod D │ │ │ │ 10.244.2.5 │◄───to───►│ 10.244.2.6 │ │ │ └──────────────┘ Service └──────────────┘ │ │ │ │ │ │ └──── ④ Pod-to-外部 ──────┘ │ └─────────────────────────────────────────────────┘要点K8s网络有四个核心场景——同Node Pod通信、跨Node Pod通信、Pod到Service、Pod到外部。其中前三个是K8s的宪法级要求——必须实现不得有误。第四个出站/入站是CNI插件的可选能力。1.2 三大原则原文翻译Kubernetes官方对网络模型的要求就三句话但它们比看起来深刻得多原则官方定义大白话翻译为什么这么设计原则1Pod互通Pods can communicate with all other Pods on any other Node without NATPod A在Node1上Pod B在Node2上它们必须能用各自的Pod IP直接通信中间不能做任何地址转换如果做NATPod就不知道谁在跟我说话了——源IP被改了服务端拿到的是Node的IP不是Pod的IP原则2Agent互通Agents on a Node (e.g. system daemons, kubelet) can communicate with all Pods on that NodeNode上的kubelet、监控Agent等系统进程必须能用Pod IP直接访问本节点的Pod健康检查(kubelet→Pod)、日志采集(agent→Pod)都需要直接通信做NAT会疯掉原则3宿主机互通Pods in the host network can communicate with all Pods on all Nodes without NAT用了hostNetwork的Pod跟其他Pod通信也不需要NAT保持一致——不能有的Pod要NAT有的不要那网络插件就乱套了要点这三条原则的核心精髓就一个词——扁平网络。K8s希望把整个集群变成一个大二层网络所有Pod IP在集群内都是直接可达的就像所有Pod都插在同一个交换机的不同端口上一样。二、为什么不做NAT——“kube-proxy的职责边界”2.1 NAT在哪里不在Pod层面在Service层面这是最多人搞混的地方。很多人刚学K8s时会问“K8s不是有个叫kube-proxy的东西做NAT吗那怎么又说Pod间不做NAT”答案是——两个层面的NAT是两码事【NAT的两个层面——Pod层的NAT vs Service层的NAT】 ┌─────────────────────────────────────────────────────────────────┐ │ Pod 层的通信 (K8s宪法禁止NAT) │ │ │ │ Pod A ──── 直接用Pod IP ────► Pod B │ │ 10.244.1.5 │ 10.244.2.5 │ │ │ │ │ [CNI 插件负责] │ │ (Flannel/Calico/Cilium) │ │ ───────────────────── │ │ 源IP: 10.244.1.5 ← 不变 │ │ 目的IP: 10.244.2.5 ← 不变 │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ Service 层的通信 (kube-proxy做NAT) │ │ │ │ Pod A ──── 访问 ClusterIP ────► kube-proxy ────► Pod B │ │ 10.244.1.5 10.96.0.1 (iptables/IPVS) 10.244.2.5 │ │ │ │ │ [kube-proxy负责] │ │ ────────────── │ │ DNAT: 10.96.0.1 → 10.244.2.5 │ │ 源IP: 10.244.1.5 → 不变 │ └─────────────────────────────────────────────────────────────────┘要点Pod间直接通信走的是CNI插件的路由/隧道IP地址原封不动——这叫源IP保留。而Service的ClusterIP是一个虚拟IP需要通过kube-proxy做DNAT把ClusterIP转换成后端Pod的IP——但这只改目的IP不改源IP。所以K8s说的是Pod层面不做NATService层面该做还是做。2.2 为什么保留源IP这么重要你可能会问“反正数据包能送到就行了管它源IP改不改呢”来看看如果Pod间也做NAT会怎样【NAT vs 不NAT——谁在叫我的问题】 ▸ 不做NAT (K8s的方式) Pod B 的日志里看到的来源是 10.244.1.5 (Pod A的IP) → 做网络策略、做审计日志、做调用链追踪所有东西清清楚楚 ▸ 如果做NAT (Docker默认方式) Pod B 的日志里看到的来源是 10.0.0.1 (Node 1的IP) → 到底是谁在调我Node 1上有20个Pod我tm怎么知道是哪个 → NetworkPolicy 直接废了——因为所有Pod经过NAT后看起来都一样现在你明白了吧——保留源IP不是K8s的强迫症而是NetworkPolicy、审计、分布式追踪这些上层功能的基础。三、CNI标准解读——“网络的USB接口”3.1 CNI是什么——“不是你写插件是插件配合你”CNIContainer Network Interface跟上一篇文章讲的CSI是一个路子——都是定义了一套标准接口让实现方和调用方解耦。但CNI和CSI有个关键区别CNI是CNCF主导的通用标准不光K8s用Mesos、Cloud Foundry也用。它的设计哲学极其简洁【CNI 接口全景——就两个操作但涵盖了一切】 ┌─────────────────────────────────────────────────────────┐ │ CNI 规范 (Spec) │ │ ───────────────── │ │ │ │ ┌─────────────────────────────────────────────────────┐│ │ │ 核心操作 (就两个) ││ │ │ ││ │ │ ADD: 创建容器时调用 ││ │ │ • 分配IP地址 ││ │ │ • 创建网络接口 (veth pair) ││ │ │ • 配置路由 ││ │ │ • 返回结果 (IP/网关/DNS等) ││ │ │ ││ │ │ DEL: 删除容器时调用 ││ │ │ • 回收IP地址 ││ │ │ • 删除网络接口 ││ │ │ • 清理路由 ││ │ │ ││ │ │ CHECK: (可选) 检查容器网络是否正常 ││ │ │ VERSION: 查询插件支持的CNI版本 ││ │ └─────────────────────────────────────────────────────┘│ │ │ │ ┌─────────────────────────────────────────────────────┐│ │ │ 输入/输出格式 ││ │ │ ││ │ │ 输入: 通过 stdin 传入 JSON 配置 环境变量 ││ │ │ 输出: 通过 stdout 返回 JSON 结果 ││ │ │ 错误: 通过 stderr 输出错误信息 ││ │ └─────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────┘要点CNI的设计比你想象的简单得多——它就定义了一个可执行文件的调用规范。kubelet在创建Pod时会调用类似这样的命令echo {cniVersion:0.3.1,name:mynet,...} | /opt/cni/bin/bridge然后插件干活返回结果。没有gRPC、没有长连接、没有守护进程——就是一个简单的命令行调用。这种极简主义让CNI插件的开发门槛极低。3.2 CNI配置文件在哪里每家CNI插件的配置方式都差不多但格式各有不同# CNI配置文件默认位置ls/etc/cni/net.d/# 输出示例:# 10-flannel.conflist (Flannel)# 10-calico.conflist (Calico)# 05-cilium.conf (Cilium)# CNI插件二进制文件位置ls/opt/cni/bin/# 输出示例:# bridge host-local loopback portmap bandwidth flannel calico cilium-cni3.3 CNI配置示例Flannel{name:cbr0,cniVersion:0.3.1,plugins:[{type:flannel,delegate:{hairpinMode:true,isDefaultGateway:true}},{type:portmap,capabilities:{portMappings:true}}]}3.4 网络插件的职责边界——“CNI管什么kube-proxy管什么”这是很多新手最困惑的地方。看了前面的内容你应该知道了——网络通信涉及的组件不止一个。组件负责的事不负责的事CNI插件(Flannel/Calico/Cilium)① Pod IP分配 ② 创建Pod网络接口(veth) ③ 跨Node路由/隧道 ④ NetworkPolicy执行① Service ClusterIP ② 负载均衡 ③ DNS解析kube-proxy① Service → Pod的负载均衡 ② ClusterIP→Pod IP的DNAT ③ NodePort的监听① Pod间直连路由 ② Pod IP分配 ③ 网络策略CoreDNS① Service名称→ClusterIP的DNS解析 ② 外部域名转发 ③ 自定义域名解析① 网络连通性 ② 负载均衡 ③ 安全策略【分工协作全景图——谁管哪一段】 用户请求 │ ▼ ┌─────────┐ DNS解析 ┌──────────┐ │ CoreDNS │◄──────────────────►│ Service │ └─────────┘ svc.ns.local │ 10.96.x.x│ │ → 10.96.x.x └────┬─────┘ │ │ │ ┌─────▼─────┐ │ │ kube-proxy │ ← 这段我管 │ │ iptables/ │ 做 DNAT 负载均衡 │ │ IPVS │ │ └─────┬─────┘ │ │ DNAT → Pod IP │ ┌─────▼─────┐ │ │ CNI 插件 │ ← 这段我管 │ │ Flannel/ │ 负责把包送到Pod │ │ Calico/ │ │ │ Cilium │ │ └─────┬─────┘ │ │ ▼ ▼ ┌──────────────────────────────────────────┐ │ 目标 Pod │ │ 10.244.2.5 │ └──────────────────────────────────────────┘要点把这个职责边界刻在脑子里——CNI管Pod到Pod的原始IP包传输kube-proxy管Service到Pod的负载均衡和地址转换CoreDNS管名字到IP的翻译。三者各司其职、互不越界。很多人排查网络问题时抓瞎就是因为没搞清楚现在该找谁。四、网络插件的选型框架——“斗地主指南”4.1 三大流派的对比【K8s网络插件三大流派——你选哪条路】 ┌─────────────────────────────────────────────────────────────────┐ │ 流派1: Overlay (叠加网络) │ │ ───────────────────────── │ │ 代表: Flannel(VXLAN), Calico(IPIP/VXLAN), Weave, Cilium(VXLAN) │ │ │ │ 原理: 在宿主机网络上再包一层虚拟网络 │ │ Pod 包 → 封装 → 走物理网络 → 解封装 → Pod 包 │ │ │ │ 优点: 不依赖物理网络对底层设备无要求部署简单 │ │ 缺点: 封包/解封有性能损耗带宽打折扣(通常5-15%) │ │ 适合: 公有云环境、对底层网络不可控的场景 │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 流派2: Underlay (底层网络) │ │ ───────────────────────── │ │ 代表: Calico(BGP), Macvlan, SR-IOV │ │ │ │ 原理: Pod直接使用物理网络通信没有封装开销 │ │ Pod 包 → 直接走物理网络 → Pod 包 │ │ │ │ 优点: 性能最佳几乎没有损耗 │ │ 缺点: 需要物理网络支持(BGP/路由配置)IP地址规划要求高 │ │ 适合: 自建机房、对网络性能有极致要求的场景 │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 流派3: eBPF (内核级网络) │ │ ──────────────────────── │ │ 代表: Cilium(eBPF) │ │ │ │ 原理: 用eBPF在内核里直接处理数据包连iptables/IPVS都省了 │ │ 包 → eBPF程序 → 直接转发 │ │ │ │ 优点: 性能最佳可观测性最强支持L7策略 │ │ 缺点: 要求内核4.9学习曲线陡峭 │ │ 适合: 新项目、对可观测性和安全有高要求的场景 │ └─────────────────────────────────────────────────────────────────┘4.2 核心能力对比能力FlannelCalicoCiliumWeaveOverlay支持VXLAN/UDPIPIP/VXLANVXLANVXLANUnderlay支持host-gwBGP原生路由❌NetworkPolicy❌✅ 深度集成✅ L3/L4/L7✅eBPF❌❌✅ 核心❌加密❌WireGuardWireGuard/IPSec✅部署复杂度低中中高中性能中等(封装损耗)高(BGP模式)极高(eBPF)中等适用规模中小集群中大集群各种规模小集群五、实战验证你的K8s网络模型5.1 验证Pod IP的全局唯一且可达# 1. 获取所有namespace下所有Pod的IPkubectl get pods-A-owide|awk{print $1, $6, $7}# 输出示例# NAMESPACE IP NODE# default 10.244.1.5 node1# default 10.244.2.5 node2# kube-system 10.244.1.3 node1# kube-system 10.244.2.3 node2# 2. 在Pod A里直接ping Pod B的IPkubectlexec-itpod-a --ping-c310.244.2.5# PING 10.244.2.5 (10.244.2.5): 56 data bytes# 64 bytes from 10.244.2.5: icmp_seq0 ttl62 time0.523 ms# ✅ 能通验证了跨Node Pod直接通信# 3. 查看Pod的网络接口kubectlexec-itpod-a --ipaddr show# 1: lo: LOOPBACK,UP,LOWER_UP# 3: eth0if7: BROADCAST,MULTICAST,UP,LOWER_UP# inet 10.244.1.5/32 brd 10.244.1.5 scope global eth0# # ↑ 注意 /32 掩码——每个Pod只看得到自己的IP# 4. 查看Pod的路由表kubectlexec-itpod-a -- route-n# Kernel IP routing table# Destination Gateway Genmask Flags Metric Ref Use Iface# 0.0.0.0 10.244.1.1 0.0.0.0 UG 0 0 0 eth0# 10.244.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0# # 默认网关指向 Node 上的网桥 (10.244.1.1)# # 所有出Pod的流量都先到网桥然后由CNI决定怎么走5.2 在Node上观察veth pair# 在Node上查看veth设备iplinkshow|grepveth# 输出示例# 7: veth1234if3: BROADCAST,MULTICAST,UP,LOWER_UP# # ↑ veth一端在host(namespace)另一端在Pod(namespace)# # if3 表示对端的interface index是3就是Pod里的eth0# 查看哪个Pod对应哪个vethforvethin$(iplinkshow|grep-oPveth[^]);dopod$(crictl pods--name.*-q2/dev/null|head-1)echo$veth-$poddone六、IP地址规划的坑——“/24到底够不够”6.1 默认配置的隐藏限制很多人用Flannel默认配置时看到Pod CIDR是10.244.0.0/16就以为能跑65535个Pod。但实际上【Flannel默认配置的真实含义】 --pod-cidr10.244.0.0/16 ← 整个集群的Pod IP池 (65536个IP) 每个Node分配一个 /24 子网 Node1 → 10.244.1.0/24 (254个Pod) Node2 → 10.244.2.0/24 (254个Pod) Node3 → 10.244.3.0/24 (254个Pod) ... 最多支持 256 个Node (因为 /16 ÷ /24 2^8 256) 每个Node最多 254 个Pod (因为 /24 减去网络地址和广播地址) 所以 最大Pod数 256 Node × 254 Pod 65024 最大Node数 256要点Flannel默认/16的Pod CIDR意味着最多256个Node。如果你预计集群会超过这个数部署时要改--pod-cidr10.244.0.0/12这样就有1048576个Node子网可用。但更大的CIDR意味着更大的路由表——Calico的BGP模式尤其受此影响每个Node的子网都是一条BGP路由。6.2 跟现有网络冲突怎么办# 当你的办公网络也在10.0.0.0/8里时# 用默认的10.244.0.0/16可能恰好不冲突# 但如果Pod CIDR和宿主机网络、VPN网络路由冲突——# 解法1: 换IP段# kubeadm init --pod-network-cidr172.16.0.0/12# 或者用 192.168.0.0/16# 解法2: 确认当前所有网络的CIDRiproute show# default via 192.168.1.1 dev wlan0# 10.0.0.0/8 via 10.0.0.1 dev tun0 ← VPN占用了10段# 172.17.0.0/16 dev docker0 ← Docker占用了172.17段# 所以 Pod CIDR 只能选 172.18-31 或 192.168 段# 解法3: 用Cilium的cluster-pool模式# Cilium可以单独指定IPv4和IPv6的CIDR更灵活本篇小结K8s网络模型的宪法其实就三句话所有Pod可以直连、不要NAT、Node也能直连Pod。这三条原则的核心目的是保留源IP——没了源IP你的NetworkPolicy废了、审计日志废了、调用链废了。这个设计思想跟Docker那种NAT糊一脸的方式完全相反——K8s选择了用更多IP地址来换取更干净的网络语义。CNI是这个模型的执行器——两个核心接口ADD/DEL一个可执行文件的简单调用就让几十种网络插件百花齐放。但你要记住职责边界CNI管Pod到Pod的包怎么传kube-proxy管Service到Pod的负载均衡CoreDNS管名字翻译。后面几篇文章咱们挨个拆解Flannel、Calico、Cilium看看这些执行器是怎么各显神通的。上一篇【第42篇】CSI——容器存储接口标准深度解析下一篇【第44篇】Flannel——最简单的K8s网络方案但别小看它
返回列表