Kubernetes网络模型详解Pod通信Service负载均衡CNI插件核心原理与日常使用技巧
先跟我聊个事儿。你有没有在幼儿园的时候玩过那个”传声筒”游戏?孩子们排成一排,第一个人对着最后一个人说话,声音从这头传到那头。Kubernetes的网络就像是这么个游戏,只不过这个游戏的规则写得更精细,而且传的是数据而不是悄悄话。
今天咱们就聊聊这个”传声筒”到底是怎么工作的。
一、Pod:你的第一个小房间
想象一下,你是一个搬家公司。老板让你把一个容器(比如一个装满了应用的小盒子)搬到公司里。问题来了——这个盒子怎么找到正确的房间?它得有个门牌号对吧?
在Kubernetes里,每个Pod都有自己的IP地址,这就是它的门牌号。这个IP不是随便编的,而是由一个叫CNI的”管家”来分配的。
# 一个简单的Pod示例
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
这个Pod启动后,你就会看到一个10.244.1.5这样的IP地址。这个IP在整个集群里都是唯一的,就像每间教室都有唯一的教室号一样。
为什么Pod需要有IP?
因为在Kubernetes的世界里,Pod随时可能被创建、销毁、迁移。今天这个Pod在Node1上,明天可能就跑到Node2上了。如果IP是固定的,那迁移后就找不到了。所以每个Pod都要有自己的IP,这样不管它搬到哪个Node上,都能被找到。
Pod之间怎么通信?
同一个集群里的Pod,不管在哪个Node上,都可以通过IP直接访问。这就像你们公司里的同事,不管坐在哪个工位,都知道对方的工号,可以直接打电话联系。
# 查看Pod的IP地址
kubectl get pod -o wide
# 在一个Pod里访问另一个Pod
kubectl exec -it my-pod -- curl http://10.244.1.5:80
二、Service:稳定的”前台接待处”
现在问题来了。Pod的IP是变化的,就像你公司的同事今天坐在这里,明天可能就换座位了。如果别人要找你,每次都要问”你今天坐哪?”那得多麻烦?
Service就是这个问题的解决方案。你可以把Service想象成公司前台。不管你的工位怎么变,前台都能找到你。同事只需要打电话到前台,前台就会帮你转接。
Service的几种类型
ClusterIP是默认的,只在集群内部有效。它就像一个内部电话号码,外面的人打不进来。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
selector:
app: nginx
ports:
- port: 80
targetPort: 80
NodePort会在每个Node上开放一个端口,这样外部就可以通过Node的IP加上这个端口来访问服务。
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
LoadBalancer则会调用云服务商的API,创建一个真正的负载均衡器。
spec:
type: LoadBalancer
Service是怎么工作的?
当你对Service发起请求时,流量会经过一个叫做kube-proxy的组件。kube-proxy就像是交通指挥员,它维护着一套规则,告诉你”到10.244.1.5的请求应该转到哪个Pod”。
现在有两种主要的实现方式:iptables和IPVS。
iptables是传统的方式,规则是一系列的匹配条件。当请求进来时,kube-proxy会遍历这些规则,找到匹配的那一条。
# 查看iptables规则
sudo iptables -t nat -L KUBE-SERVICES -n -v
IPVS是更现代的方式,性能更好。它使用哈希表来查找规则,而不是逐一遍历。
# 查看IPVS规则
sudo ipvsadm -Ln
负载均衡算法
Service支持几种负载均衡算法。默认是轮询,也就是每个Pod轮流接收请求。你也可以设置为随机、最少连接等。
apiVersion: v1
kind: Service
metadata:
name: my-service
annotations:
service.kubernetes.io/traffic-server: "ipvs"
spec:
type: ClusterIP
selector:
app: nginx
ports:
- port: 80
targetPort: 80
三、CNI:幕后英雄
现在该说说CNI了。CNI的全称是Container Network Interface,容器网络接口。你可以把它理解成一套”网络插件标准”。
为什么需要CNI?
Kubernetes本身不负责网络,它只定义了”网络应该长什么样”。至于”网络怎么实现”,那是CNI插件的工作。这就好比你不需要自己造房子,只需要告诉装修工人你想要什么样的布局,他们来帮你实现。
主流的CNI插件
Flannel是最简单的一个。它用一个简单的方式实现Pod之间的通信:给每个Node分配一个子网,然后在这两个子网之间建立隧道。
# Flannel的配置
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
Calico更强大一些。它使用BGP协议来分发路由信息,这样不同Node上的Pod可以直接通信,不需要隧道。
# Calico的配置
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
Weave Net也是一个不错的选择,它有自己的加密机制,适合对安全有要求的场景。
# Weave Net的配置
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
CNI是怎么工作的?
当一个Pod被创建时,Kubernetes会调用CNI插件来配置网络。插件会做这几件事:
- 创建一个veth pair(虚拟以太网设备)
- 把一端放进Pod的网络命名空间
- 把另一端放在Node上
- 给Pod分配IP地址
- 设置路由规则
# 查看Pod的网络配置
kubectl exec -it my-pod -- ip addr
# 你会看到类似这样的输出
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
inet 127.0.0.1/8 scope host lo
2: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP
inet 10.244.1.5/24 brd 10.244.1.255 scope global eth0
四、日常使用技巧
1. 网络调试工具
kubectl有内置的网络诊断工具。
# 检查Service是否正常
kubectl describe service my-service
# 检查Endpoint是否正确
kubectl get endpoints my-service
# 在Pod里测试网络连通性
kubectl exec -it my-pod -- curl -v http://my-service:80
2. 常见的网络问题
问题1:Pod无法访问Service
这种情况通常是kube-proxy没有正确配置。
# 检查kube-proxy是否运行
kubectl get pods -n kube-system | grep kube-proxy
# 查看kube-proxy的日志
kubectl logs -n kube-system kube-proxy-xxxxx
问题2:Service没有Endpoint
这说明Service的selector没有匹配到任何Pod。
# 检查Pod的label
kubectl get pod --show-labels
# 检查Service的selector
kubectl describe service my-service
问题3:跨Node的Pod通信慢
这可能是因为你的CNI插件使用了隧道模式,导致性能下降。考虑切换到Calico的BGP模式。
3. 性能优化技巧
使用IPVS而不是iptables
IPVS的性能更好,尤其是在大规模集群中。
# 在kube-proxy配置中使用IPVS
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
调整MTU值
不同的CNI插件可能有不同的MTU要求。如果网络出现丢包,可以尝试调整MTU。
# 查看当前MTU
kubectl exec -it my-pod -- ip link show
# 修改MTU(需要重启Pod)
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
hostNetwork: true
hostPID: false
containers:
- name: app
image: myapp:latest
启用网络策略
网络策略不仅可以用来限制流量,还可以帮助诊断问题。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
4. 网络策略的高级用法
网络策略是Kubernetes提供的安全机制,可以用来控制Pod之间的流量。
允许特定命名空间的访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: frontend
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
project: myproject
只允许特定端口的访问
spec:
ingress:
- ports:
- port: 80
- port: 443
限制出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
spec:
podSelector: {}
egress:
- to:
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
五、完整的网络配置案例
让我给你展示一个完整的配置,涵盖Pod、Service和NetworkPolicy。
# 命名空间
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
project: myproject
---
# 应用Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp-frontend
namespace: myapp
labels:
app: frontend
tier: web
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
---
# 后端Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp-backend
namespace: myapp
labels:
app: backend
tier: api
spec:
containers:
- name: api
image: myapi:latest
ports:
- containerPort: 8080
---
# 前端Service
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
namespace: myapp
spec:
type: ClusterIP
selector:
app: frontend
ports:
- port: 80
targetPort: 80
---
# 后端Service
apiVersion: v1
kind: Service
metadata:
name: backend-svc
namespace: myapp
spec:
type: ClusterIP
selector:
app: backend
ports:
- port: 8080
targetPort: 8080
---
# 网络策略:只允许前端访问后端
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: myapp
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
六、排查问题的思维框架
当你遇到网络问题时,可以按照这个顺序来排查:
第一步:确认Pod是否正常运行
kubectl get pods -n myapp
kubectl describe pod myapp-frontend -n myapp
第二步:确认Service是否正确配置
kubectl get svc -n myapp
kubectl describe svc frontend-svc -n myapp
kubectl get endpoints frontend-svc -n myapp
第三步:检查网络连通性
kubectl exec -it myapp-frontend -n myapp -- curl http://backend-svc:8080
kubectl exec -it myapp-frontend -n myapp -- ping 10.244.1.5
第四步:检查网络策略
kubectl get networkpolicy -n myapp
kubectl describe networkpolicy allow-frontend-to-backend -n myapp
第五步:检查底层网络
# 在Node上检查路由
ip route
# 检查iptables规则
sudo iptables -L -n -v
# 或者检查IPVS规则
sudo ipvsadm -Ln
七、一些实用的小技巧
技巧1:使用hostNetwork调试
当你怀疑网络配置有问题时,可以临时给Pod设置hostNetwork,这样Pod就能使用Node的网络,帮助你排除问题。
spec:
hostNetwork: true
技巧2:查看Pod的网络命名空间
# 找到Pod的PID
kubectl get pod myapp-frontend -o jsonpath='{.status.podIP}'
# 在Node上查找网络命名空间
sudo nsenter -t $(sudo cat /proc/$(kubectl get pod myapp-frontend -o jsonpath='{.spec.hostNetwork}' | head -1)/root) -n ip addr
技巧3:监控网络流量
# 安装监控工具
kubectl apply -f https://github.com/containernetworking/plugins/releases/latest/download/cni-plugins-linux-amd64.tgz
# 使用tcpdump抓包
kubectl exec -it myapp-frontend -- tcpdump -i eth0 port 80
技巧4:测试DNS解析
# 测试Service的DNS
kubectl exec -it myapp-frontend -- nslookup backend-svc.myapp.svc.cluster.local
# 测试外部DNS
kubectl exec -it myapp-frontend -- nslookup www.baidu.com
八、理解Kubernetes网络的关键点
说到这里,我想帮你理一理思路。Kubernetes的网络设计有几个核心思想:
每个Pod都有独立的IP
这意味着Pod之间的通信就像传统网络一样简单。你不需要在应用层做任何特殊处理,直接使用IP地址即可。
Pod的IP是临时的
Pod可能被重建、迁移,所以IP会变化。这就是为什么需要Service来提供稳定的访问入口。
网络是跨Node的
不管Pod在哪个Node上,都能通过IP直接通信。这背后是CNI插件在起作用,它配置了路由和隧道,让不同Node的网络能够互通。
安全是由策略控制的
默认情况下,所有Pod都可以互相访问。如果需要限制,可以配置NetworkPolicy。这给了你很大的灵活性。
九、实际工作中的经验
在我实际使用Kubernetes的过程中,遇到过不少网络相关的问题。下面分享几个常见的坑。
坑1:iptables模式在高并发下性能下降
如果你在大规模集群中使用iptables模式的kube-proxy,可能会发现网络延迟增加。这时候切换到IPVS模式会有明显改进。
坑2:MTU不匹配导致丢包
不同的网络环境可能有不同的MTU要求。如果你的Pod网络MTU设置不当,可能会出现大流量时丢包的情况。
坑3:网络策略配置过于严格
有时候为了安全,你会配置很严格的网络策略。但如果配置不当,可能会导致正常的业务流量被阻断。建议在测试环境充分验证后再应用到生产环境。
坑4:忽略DNS缓存
Kubernetes的DNS服务(CoreDNS)有缓存机制。有时候你修改了Service,但Pod里的DNS缓存还没有更新,导致访问的还是旧的IP。
# 调整CoreDNS的缓存时间
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
十、总结
聊了这么多,我希望你对Kubernetes的网络有了一个比较清晰的认识。
Pod是每个应用的运行单元,有自己的IP。Service提供稳定的访问入口,背后有kube-proxy来转发流量。CNI插件负责底层的网络配置。NetworkPolicy则让你可以精细控制网络访问。
这些组件协同工作,构成了Kubernetes强大而灵活的网络能力。
学习Kubernetes网络的关键在于理解它的设计思想:简单、灵活、安全。每一个设计选择都有它的道理,理解了这些道理,你就能更好地使用它,也能在遇到问题时更快地找到解决方案。
网络这东西,看似复杂,其实就像一个城市的交通系统。有道路、有路口、有交通规则。只要理解了它们是怎么工作的,你就能在这个系统里畅通无阻。
好了,今天就聊到这里。如果你在实践中遇到什么问题,随时可以再来聊聊。网络这东西,越用越熟,多实践就好了。
