你好呀!我是Agnes。说到Kubernetes(简称K8s)的网络,很多新手朋友——甚至一些资深运维——都会觉得头疼。毕竟,网络这东西本身就复杂,再加上K8s这种容器编排的抽象层,简直像是给迷宫又加了一层地下室。
但别怕,今天咱们不整那些晦涩的教科书定义,我就像坐在你对面,一边喝咖啡一边跟你拆解这套体系。我会尽量用大白话,配合真实的代码例子,让你从“为什么需要它”到“怎么配置它”都门儿清。咱们这就开始。
一、 先别急着动手,得先看懂“地图”:K8s网络的基本哲学
在深入CNI(Container Network Interface)之前,你必须理解Kubernetes设计网络时的几个核心原则。这些原则是Google在设计容器网络时经过深思熟虑的,违背它们,你的集群会很难维护。
1. 每个Pod都有一个唯一的IP地址
这是最关键的一点。在传统的虚拟化环境中,虚拟机(VM)可能有多个网卡,可能共享IP。但在K8s里,Pod是网络的最小单元。
想象一下:
- 你有一个Pod,里面跑着两个容器(一个主业务容器,一个sidecar日志收集容器)。
- 对于K8s网络来说,这两个容器共享同一个网络命名空间(通过
podNetwork字段配置)。 - 这意味着,从外部看,这个Pod就像一台普通的物理机或虚拟机,有一个专属的IP。
- Pod内部的两个容器,通过
localhost就能互相通信。
为什么要这样设计? 为了简化应用开发。你可以像部署一个单进程进程一样去部署一个多容器应用,而不需要担心复杂的容器间网络配置。
2. Pod之间可以无需NAT直接通信
在旧架构中,不同服务器上的服务通信往往需要通过复杂的负载均衡器或NAT(网络地址转换)。K8s要求:
- 所有Pod,无论是否在同一个节点上,都能直接通过IP互访。
- 不需要进行端口映射或NAT。
这听起来很理想,但实现起来需要一些魔法,这就是CNI插件的工作。
3. 每个Pod都可以通过所有容器的IP直接访问
无论你是从本节点、其他节点,还是从集群外部,只要你知道了Pod的IP,你就能访问它上面的所有容器(因为它们共享网络命名空间)。
4. 客户端视角与服务端视角一致
这是一个容易被忽视的原则。意思是:一个Pod看到自己的IP,和另一个Pod看到它的IP,应该是同一个IP。不存在“我在本机看到是A,别人看我却是B”的情况。
二、 揭开神秘面纱:什么是CNI?
现在,我们来到了核心主角——CNI (Container Network Interface)。
2.1 为什么需要CNI?
Kubernetes本身并不“实现”网络。它只定义了接口。也就是说,K8s说:“嘿,我需要你给这个Pod分配一个IP,并把它连接到网络。”至于怎么分配、怎么连接,K8s不管,它交给一个标准的插件系统来处理。
这个标准就是CNI。
CNI由CNCF(云原生计算基金会)维护,它定义了一个简单的规范:
- Plugin:一个可执行文件,负责配置Pod的网络接口。
- Runtime:比如Kubernetes,负责调用这些Plugin。
- Configuration:JSON或YAML文件,描述网络拓扑。
当你安装K8s时,你必须选择一个CNI插件。常见的有:
- Flannel:简单,适合小集群,性能一般。
- Calico:功能强大,支持网络策略(Network Policy),性能优秀,生产环境首选。
- Canal:Flannel + Calico的组合。
- Weave Net:易于使用,支持加密。
- Cilium:基于eBPF,新兴明星,性能极佳,支持高级安全策略。
2.2 CNI的工作原理(以Calico为例)
当一个Pod被调度到某个节点上时,Kubelet会做以下几件事:
- 创建网络命名空间:为Pod创建一个独立的网络环境。
- 调用CNI插件:Kubelet读取
/etc/cni/net.d/目录下的配置,找到对应的CNI插件(比如calico)。 - 插件执行:CNI插件(一个二进制文件)被调用,它负责:
- 在Pod的网络命名空间中创建一个虚拟网卡(veth pair)。
- 将这个veth连接到宿主机的Linux网桥或隧道中。
- 从IPAM(IP地址管理)池中分配一个IP地址给这个Pod。
- 配置路由,确保这个Pod的流量能被正确转发。
- 结果返回:CNI插件返回配置好的网络信息给Kubelet。
- Pod启动:Kubelet启动Pod中的容器,容器就可以使用分配到的IP通信了。
三、 深入实战:网络策略与安全防护
网络模型不只是“通不通”的问题,更是“谁能和谁通”的问题。这就是NetworkPolicy(网络策略)的用武之地。
3.1 为什么需要NetworkPolicy?
默认情况下,K8s集群中的Pod是全通的。任何Pod都可以访问任何其他Pod。这在生产环境中是巨大的安全隐患。
NetworkPolicy允许你定义规则,限制Pod之间的流量。
3.2 实战:配置一个基本的网络策略
假设我们有一个名为backend的应用,它只允许来自frontend Pod的访问。
首先,我们需要确保我们的CNI插件支持NetworkPolicy(Calico和Cilium都支持,Flannel默认不支持)。
创建一个名为backend-policy.yaml的文件:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
解读这个YAML:
podSelector: 应用这个策略的目标Pod,即标签为app: backend的Pod。policyTypes: 这个策略是入站(Ingress)规则。ingress: 定义允许的入站流量。from: 只允许来自标签为app: frontend的Pod的流量。ports: 只允许访问8080端口。
应用这个策略:
kubectl apply -f backend-policy.yaml
现在,尝试从其他非frontend Pod访问backend Pod,你会发现连接被拒绝。
3.3 使用Cilium进行更细粒度的控制
Cilium不仅支持基于标签的策略,还支持基于安全标识(Security Identity)和L7(应用层)的策略。
例如,你可以限制HTTP请求的方法:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: http-policy
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/*"
这个策略只允许frontend Pod通过GET方法访问/api/*路径。
四、 高级话题:Service与负载均衡
Pod的IP是易变的(Pod重启后IP会变),所以我们不能直接用Pod IP来提供服务。K8s引入了Service抽象,它提供了一个稳定的VIP(虚拟IP)和DNS名称,将流量负载均衡到后端的多个Pod上。
4.1 Service的类型
- ClusterIP:默认类型,只在集群内部可访问。
- NodePort:在每个节点上打开一个端口,通过
<NodeIP>:<NodePort>从集群外部访问。 - LoadBalancer:在NodePort基础上,请求云服务商创建一个外部负载均衡器。
- ExternalName:将服务映射到DNS名称。
4.2 Service背后的机制:kube-proxy
Service如何实现负载均衡?这依赖于kube-proxy。
kube-proxy运行在每个节点上,它会监听API Server,当Service或Endpoint变化时,kube-proxy会更新节点的iptables规则或IPVS表,将流向Service VIP的流量转发到后端的Pod IP。
4.2.1 iptables模式(传统)
这是默认模式。当你在Service上配置type: LoadBalancer时,云平台会在外部创建一个LB,并将流量转发到节点上的NodePort。在节点内部,iptables规则确保流量从NodePort被正确路由到后端Pod。
4.2.2 IPVS模式(高性能)
IPVS(IP Virtual Server)是Linux内核的一种负载均衡技术,性能比iptables更高,尤其是当Service和Endpoint数量很大时。
要在K8s中使用IPVS,你需要:
- 确保内核支持IPVS模块(大多数现代Linux发行版都支持)。
- 配置
kube-proxy使用IPVS模式。
编辑kube-proxy的配置(通常在/etc/kubernetes/kube-proxy-config.yaml):
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
ipvs:
scheduler: "rr" # 轮询算法,也可以是"lc", "dh", "sh", "sed", "nq"
重启kube-proxy:
# 如果是静态Pod部署
killall kube-proxy
# 或者
kubectl delete pod -n kube-system -l app=kube-proxy
现在,你的Service负载均衡将使用IPVS,性能会有显著提升。
五、 常见问题与排查技巧
即使了解了理论,实践中也会遇到各种网络问题。以下是一些常见问题及排查思路。
5.1 Pod无法访问外部网络
现象:Pod内执行curl http://example.com超时。
排查步骤:
- 检查DNS:确保
/etc/resolv.conf配置正确。尝试用IP访问,排除DNS问题。 - 检查路由:在Pod内执行
ip route,看默认路由是否存在。 - 检查iptables/IPVS规则:在节点上检查是否有规则阻止了出站流量。
- 检查CNI插件状态:确认CNI插件正常运行,IPAM池是否有足够IP。
- 检查NAT规则:如果是从Pod访问外部,可能需要MASQUERADE规则。iptables模式通常会自动处理,但IPVS模式需要额外配置。
解决方案: 确保CNI插件正确配置了出站NAT。对于Calico,这通常是默认的。如果使用了自定义CNI,检查其配置。
5.2 Pod之间无法通信
现象:Pod A可以ping通Pod B,但TCP连接失败。
排查步骤:
- 检查NetworkPolicy:是否有策略阻止了通信?
- 检查安全组/防火墙:云厂商的安全组是否允许节点间通信?
- 检查MTU:如果使用了 VXLAN 或 IP-in-IP 隧道,MTU不匹配会导致大包丢弃。在节点上执行
ip link show,检查MTU。通常需要设置为1450或更低。 - 检查CNI插件日志:查看CNI插件的日志(如
/var/log/calico或/var/log/cilium)。
解决方案: 调整MTU,或修改NetworkPolicy。
5.3 Service无法访问
现象:curl http://<Service-ClusterIP>:<Port>超时。
排查步骤:
- 检查Service和Endpoint:执行
kubectl get svc <service-name>和kubectl get endpoints <service-name>,确认Endpoint不为空。 - 检查kube-proxy:确认kube-proxy进程在运行,并且模式正确(iptables/IPVS)。
- 检查iptables/IPVS规则:在节点上执行
iptables-save或ipvsadm -L,看是否有对应的规则。 - 检查Pod的端口:确认后端Pod确实在监听指定的端口。
解决方案: 重启kube-proxy,或检查Pod配置。
六、 总结与建议
Kubernetes的网络模型看似复杂,但其核心思想是简单和灵活。通过CNI插件,K8s解耦了网络实现,让你可以根据需求选择合适的网络方案。
给你的建议:
- 从小规模开始:如果是学习或小型项目,Flannel足够。如果需要生产环境的安全性和性能,选择Calico或Cilium。
- 理解你的CNI:不要只把它当作黑盒。了解它的工作原理,有助于排查问题。
- 重视安全:尽早引入NetworkPolicy,哪怕是最简单的规则,也比完全没有好。
- 监控网络:使用工具如Cilium Hubble、Calico流量监控等,可视化网络流量,快速定位问题。
希望这篇文章能帮你拨开K8s网络的神秘面纱。记住,网络是K8s的基石,理解它,你就掌握了构建可靠云原生应用的钥匙。如果还有疑问,欢迎随时交流!
