Kubernetes的网络体系就像一个庞大城市的交通系统,每一辆车(Pod)都有自己专属的车道(IP),道路之间通过立交桥(Service)和隧道(Network Policy)相连。理解这套系统,不能只靠死记硬背概念,而要从最基础的Pod通信开始,一步步搭建认知框架。这篇文章会带你走过从理论基础到实战排查的完整路径,中间穿插真实案例和可直接运行的命令,帮你把网络这块硬骨头啃透。
基础概念:Kubernetes网络模型的四条铁律
每个Kubernetes集群都必须满足四个基本原则,这是所有网络设计的地基。你可以把这四条规则想象成交通规则,不管开什么车、走什么路,都必须遵守。
第一条,每个Pod都有自己的IP地址,Pod内的所有容器共享这个IP和端口空间。这意味着同一个Pod里的容器可以直接用localhost通信,但不同Pod即使部署在同一台Node上,也各自拥有独立的IP。
第二条,所有Pod都能与其他Pod直接通信,不需要NAT。这点和传统虚拟机网络不同,传统环境里跨网段通信往往需要网关做地址转换,Kubernetes设计时就要求Pod-to-Pod直接互通,这也是为什么它强调扁平网络的重要性。
第三条,每个Pod看到的自己IP和它被其他Pod看到的IP是一致的。这条规则确保了网络语义的透明性,你的应用不需要关心自己跑在哪个节点上,IP就是IP。
第四条,Node Agent和Pod之间可以自由通信。这条规则保障了集群内部的监控、日志收集和调度指令能够顺畅传递。
这四条规则听起来简单,但要在生产环境里完全落实,需要CNI插件、kube-proxy、Service IPAM等多组件协同工作。理解了这个框架,后面讨论的具体机制才能对号入座。
Pod通信的完整路径:从发送到接收
当一个Pod向另一个Pod发送数据包时,数据包会经历一系列跳转才能到达目的地。了解这个路径,是排查网络问题的关键。
假设Node A上的Pod 1(IP 10.244.1.5)要访问Node B上的Pod 2(IP 10.244.2.8)。数据包首先从Pod 1的eth0接口发出,进入veth pair的另一端,也就是node A的kube-ipvs0或cni0网桥。接着数据包进入内核网络栈,路由表会根据目标IP查找出接口,通常是通过flannel.1或vxlan.1这样的虚拟隧道接口发出。
如果底层网络插件使用的是VXLAN模式(比如Flannel的vxlan后端),数据包会被封装在UDP报文里,源端口和目的端口默认是8472。外层IP头部使用宿主机的物理IP,这样数据包就能穿越物理网络到达Node B。Node B收到后,解封装取出内层IP,再转发到Pod 2的网桥接口。
整个过程对应用完全透明,这就是Kubernetes网络抽象的魅力。但透明也意味着问题难以定位,当通信失败时,你需要像侦探一样层层剥开这些封装。
CNI插件:网络插件化的核心机制
Container Network Interface(CNI)是Kubernetes网络插件化的标准接口。它把网络配置和具体实现分离,让你可以替换不同的网络插件而不需要修改Kubernetes核心代码。
CNI插件的工作流程分为两个阶段:Add和Check。Add阶段在Pod创建时执行,负责为Pod配置网络,比如创建veth pair、分配IP、写入路由等。Check阶段用于验证网络配置是否正确。删除时执行Del阶段,清理网络资源。
社区里常用的CNI插件有Flannel、Calico、Canal、Weave和 Cilium。每种插件有不同的设计哲学和适用场景。Flannel主打简单轻量,适合小规模集群;Calico功能丰富,支持复杂的网络策略; Cilium基于eBPF,性能卓越且可编程性强。
配置CNI插件通常只需一个YAML文件。比如安装Flannel:
apiVersion: policy.v1beta1.k8s.io/v1
kind: PodSecurityPolicy
metadata:
name: flannel
spec:
privileged: false
allowPrivilegeEscalation: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
hostNetwork: false
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: flannel
rules:
- apiGroups: ['']
resources: ['pods']
verbs: ['get']
- apiGroups: ['']
resources: ['nodes']
verbs: ['list', 'watch', 'update']
- apiGroups: ['']
resources: ['nodes/status']
verbs: ['patch']
- apiGroups: ['networking.k8s.io']
resources: ['clusternetworks']
verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: flannel
namespace: kube-system
data:
cni-config: |
{
"name": "cni0",
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: flannel
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-ready
operator: Exists
effect: NoExecute
serviceAccountName: flannel
initContainers:
- name: install-cni
image: flannel:v0.24.0
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-config.yaml
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: flannel
image: flannel:v0.24.0
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
- --iface=eth0
securityContext:
privileged: false
capabilities:
add:
- NET_ADMIN
- NET_RAW
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: '5000'
volumeMounts:
- name: run
mountPath: /run/flannel
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: lib-modules
readOnly: true
mountPath: /lib/modules
resources:
requests:
cpu: '100m'
memory: '50Mi'
volumes:
- name: run
hostPath:
path: /run/flannel
type: DirectoryOrCreate
- name: cni
hostPath:
path: /etc/cni/net.d
type: DirectoryOrCreate
- name: flannel-cfg
configMap:
name: flannel
- name: lib-modules
hostPath:
path: /lib/modules
这个配置定义了ServiceAccount、权限、配置映射和DaemonSet。关键点在于--iface=eth0参数指定了用于Pod通信的物理网卡,/etc/cni/net.d目录存放CNI配置文件,/run/flannel存放运行时状态。
kube-proxy:Service流量转发的幕后英雄
Service是Kubernetes提供的抽象,它让一组Pod拥有统一的访问入口。但Service本身不处理流量,真正做转发的是kube-proxy。
kube-proxy有三种工作模式:userspace、iptables和ipvs。userspace模式已经废弃,iptables模式是大多数集群的默认选择,ipvs模式性能更好但需要内核支持。
在iptables模式下,kube-proxy会监控Service和Endpoint的变化,动态生成iptables规则。当流量到达Node的虚拟网桥时,内核会根据这些规则将流量转发到后端的Pod IP。这个过程对用户空间透明,性能接近原生。
查看当前的iptables规则可以帮助你理解流量走向:
# 查看kube-proxy生成的规则
sudo iptables -t nat -L KUBE-SERVICES -n -v --line-numbers
# 查看具体的Service规则
sudo iptables -t nat -L KUBE-SVC-xxxxxx -n -v --line-numbers
# 查看Endpoint规则
sudo iptables -t nat -L KUBE-SEP-xxxxxx -n -v --line-numbers
在ipvs模式下,命令会有所不同:
# 查看IPVS规则
sudo ipvsadm -Ln
# 查看IPVS服务
sudo ipvsadm -Ln | grep -A 5 "VIP"
理解这些规则的结构,当Service无法访问时,你就能快速判断是规则缺失、Endpoint错误还是转发异常。
Network Policy:精细化的网络访问控制
默认情况下,Kubernetes集群里的Pod可以自由通信。但生产环境往往需要隔离,比如前端Pod只能访问后端Pod,不能直接访问数据库。Network Policy就是实现这种隔离的工具。
一个典型的Network Policy示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
这个Policy的含义是:在production命名空间里,标记为app=backend的Pod只接受来自标记为app=frontend的Pod的TCP 8080端口流量。其他所有入站流量都被拒绝。
需要注意的是,Network Policy不是内置功能,它依赖于CNI插件的支持。Flannel默认不支持Network Policy,需要使用Flannel的Calico集成版本或换成Calico。 Cilium、Weave等插件原生支持。
实战排查:从现象到根因的完整路径
网络问题排查最忌讳盲目猜测。正确的做法是从现象出发,层层定位,用证据说话。下面通过几个真实案例演示排查思路。
案例一:Pod无法访问Service
现象:Pod里curl Service ClusterIP超时。
第一步,确认Pod本身网络正常。进入Pod执行:
# 检查Pod IP
hostname -i
# 测试DNS解析
nslookup kubernetes.default.svc.cluster.local
# 测试同节点其他Pod连通性
curl http://<同节点Pod IP>:<port>
# 测试跨节点Pod连通性
curl http://<跨节点Pod IP>:<port>
如果DNS解析失败,检查CoreDNS Pod状态和配置:
# 查看CoreDNS Pod
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 查看CoreDNS日志
kubectl logs -n kube-system <coredns-pod-name>
# 检查CoreDNS配置
kubectl get configmap coredns -n kube-system -o yaml
如果Pod间无法通信,检查CNI插件状态:
# 查看CNI Pod日志
kubectl logs -n kube-system -l app=flannel
kubectl logs -n kube-system -l app=calico-node
# 检查网桥和接口
ip link show
ip addr show cni0
ip addr show flannel.1
如果Pod间正常但Service无法访问,检查kube-proxy:
# 查看kube-proxy Pod
kubectl get pods -n kube-system -l app=kube-proxy
# 检查iptables规则是否存在
sudo iptables -t nat -L KUBE-SERVICES -n | grep <service-name>
# 检查Endpoint是否生成
kubectl get endpoints <service-name>
案例二:跨节点Pod通信延迟高
现象:同节点Pod延迟1ms,跨节点Pod延迟200ms。
这种情况通常是VXLAN封装开销导致。检查隧道接口状态:
# 查看VXLAN接口
ip link show flannel.1
# 查看ARP表
ip neigh show dev flannel.1
# 检查MTU
ip link show flannel.1 | grep mtu
# 测试封装开销
ping -c 5 -s 1400 <跨节点Pod IP>
如果MTU不匹配导致分片,会显著降低性能。解决方案是在CNI配置中设置正确的MTU,或者在物理网络中启用Jumbo Frame。对于Flannel,可以在ConfigMap中添加:
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan",
"DirectRouting": true
}
}
DirectRouting参数会让节点间通过物理网络直连,跳过VXLAN封装,大幅提升性能。
案例三:Network Policy生效后流量仍可达
现象:配置了拒绝所有入站的Network Policy,但Pod仍然能收到流量。
这种情况可能是Policy未正确应用。检查Policy状态:
# 查看Policy是否生效
kubectl get networkpolicy -n <namespace>
# 检查Pod是否匹配selector
kubectl get pods -n <namespace> --show-labels
# 查看CNI插件的Policy日志
kubectl logs -n kube-system -l app=calico-node | grep -i policy
也可能是Pod的label没有正确设置。Network Policy通过podSelector匹配Pod,如果标签不匹配,Policy就不会生效。使用--show-labels确认标签是否正确。
性能优化:从内核参数到插件配置
Kubernetes网络性能优化涉及多个层面,从内核参数调到插件配置,每一步都可能带来提升。
内核参数调优
生产集群建议调整以下sysctl参数:
# 增加TCP缓冲区
sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sudo sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
# 启用TCP快速打开
sudo sysctl -w net.ipv4.tcp_fastopen=3
# 增加conntrack表大小(重要!)
sudo sysctl -w net.netfilter.nf_conntrack_max=655360
# 启用TCP BBR拥塞控制
sudo sysctl -w net.core.default_qdisc=fq
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
其中conntrack表大小容易被忽视。kube-proxy使用iptables时,每条规则都会触发conntrack记录。高并发场景下,默认的65536可能不够,导致新连接被丢弃。
CNI插件性能对比
不同CNI插件在性能上有显著差异。基准测试显示,在相同硬件条件下:
| 插件 | 吞吐量 | 延迟 | CPU占用 |
|---|---|---|---|
| Flannel (vxlan) | 8 Gbps | 150μs | 中 |
| Calico (iptables) | 12 Gbps | 80μs | 中高 |
| Calico (eBPF) | 18 Gbps | 40μs | 低 |
| Cilium | 20 Gbps | 35μs | 低 |
如果集群规模超过100节点或QPS要求高,建议迁移到Cilium或Calico eBPF模式。迁移前需要评估业务影响,因为网络插件替换可能导致短暂的断连。
服务网格的开销
Istio、Linkerd等服务网格通过sidecar代理实现流量管理,但会带来额外延迟。基准测试显示,Istio会增加约1-2ms延迟,吞吐下降10-20%。如果性能敏感,可以考虑:
- 使用Cilium Service Mesh替代Istio,性能更好
- 关闭不必要的特性,如mTLS、 tracing
- 使用Host Network模式部署sidecar
监控与诊断工具
良好的可观测性是网络运维的基础。以下是常用的诊断工具和使用场景。
kubectl网络诊断命令
”`bash
查看Pod网络信息
kubectl get pod -o wide kubectl describe
