Kubernetes网络模型深入解析常见网络故障排查与Pod通信Service配置完整指南
一、为什么Kubernetes的网络总让人头疼
说实话,我刚接触K8s的时候,也被它的网络搞蒙了好几次。明明Pod之间应该能互通,结果curl就是超时;Service配置看起来没毛病,可就是访问不到后端Pod。那种感觉就像你在修一个隐形的迷宫,每次以为找到了出口,结果又撞墙。
但K8s的网络设计其实是有章可循的,只要理解了它的核心设计理念,剩下的就是熟能生巧了。今天咱们就从头到尾捋一捋,从网络模型到故障排查,再到Service的配置,争取让你以后遇到网络问题不再抓瞎。
二、Kubernetes网络模型的核心设计哲学
2.1 每个Pod都拥有独立的IP地址
这是K8s网络模型的第一条铁律。不管你的集群部署在哪个云厂商,不管用了哪种CNI插件(Calico、Flannel、 Cilium、Weave等等),K8s都保证:集群内的每一个Pod都能拥有一个唯一的IP地址,且所有Pod之间无需通过NAT就能直接通信。
听起来简单对吧?但背后涉及的东西其实不少。我们来拆解一下:
(1)Pod IP的生命周期
Pod的IP在创建时分配,Pod销毁时释放。这个IP是由你选定的CNI插件负责的。比如你用Calico,那IPAM(IP地址管理)模块就会从你配置的网段中取出一个IP,绑定到Pod的网络命名空间上。
(2)Pod网络的隔离
每个Pod都拥有独立的网络命名空间(network namespace)。这意味着你可以用下面的命令验证:
# 进入Pod内部查看网络接口
kubectl exec -it my-pod -- ip addr
# 或者在当前节点查看Pod的网络命名空间
ls /var/run/netns/
你会看到类似这样的输出:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
2: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP
link/ether 0a:58:0a:84:02:0b brd ff:ff:ff:ff:ff:ff
inet 10.132.2.11/24 brd 10.132.2.255 scope global eth0
注意到那个10.132.2.11了吗?这就是这个Pod的IP。而eth0@if10表示这是一个veth pair(虚拟以太网对),一端在Pod内,另一端在节点上,通过这种方式实现了网络隔离和连通。
2.2 容器与Pod共享网络命名空间
在K8s中,Pod是最小的调度单元,而不是容器。一个Pod可以包含一个或多个容器,但这些容器共享同一个网络命名空间。这意味着:
- 同一Pod内的所有容器可以使用
localhost互相通信 - 它们共享同一个IP地址和端口空间
- 如果两个容器都需要监听80端口,就必须使用不同的端口号
我们来用一个实际例子验证:
# 创建一个包含两个容器的Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: dual-container-pod
spec:
containers:
- name: container-a
image: nginx:alpine
command: ["sh", "-c", "while true; do sleep 3600; done"]
- name: container-b
image: busybox
command: ["sh", "-c", "while true; do sleep 3600; done"]
EOF
# 从container-b访问container-a的nginx
kubectl exec -it dual-container-pod -c container-b -- wget -qO- http://localhost
你会发现,通过localhost就能直接访问到nginx,这就是共享网络命名空间的威力。
2.3 Pod到Pod的网络连通性无需NAT
这是K8s网络模型最”反直觉”的地方。在传统数据中心或者VM环境中,不同机器之间的通信通常需要NAT(网络地址转换),因为IP地址空间是有限的。但K8s要求:
集群内任意两个Pod之间可以直接通信,不需要任何额外的配置或转换。
要做到这一点,K8s需要解决几个核心问题:
(1)Pod IP的全局唯一性
每个节点上的CNI插件必须从不同的IP段中分配地址,确保整个集群内没有IP冲突。
(2)跨节点的路由
Pod分布在不同的物理节点上,节点之间需要有路由表或者overlay网络来实现互通。
(3)网络策略的执行
如果启用了NetworkPolicy,就需要在正确的位置拦截和过滤流量。
不同的CNI插件用不同的方式解决这些问题。比如Flannel用VXLAN overlay,Calico用BGP路由,Cilium用eBPF。理解这些差异对排查网络问题很有帮助,咱们后面会详细讲。
三、Service:连接Pod的”中转站”
3.1 为什么需要Service
前面说了,Pod的IP是 ephemeral(临时的)——Pod重启后IP会变,Pod被删了IP就没了。如果你直接通过Pod IP来访问服务,一旦Pod重建,你的客户端就断联了。
Service的作用就是提供一个稳定的访问入口。它把一个或多个Pod抽象成一个虚拟的服务端点,客户端只需要知道Service的IP(ClusterIP)或者DNS名称,不需要关心背后具体是哪个Pod在提供服务。
3.2 Service的工作原理
Service的核心组件有两个:kube-proxy 和 Endpoints。
(1)Endpoints:Service到Pod的映射
当你在集群中创建一个Service时,K8s的控制器会查找具有匹配label的Pod,并自动创建对应的Endpoints对象。你可以用下面的命令查看:
# 查看某个Service的Endpoints
kubectl get endpoints my-service -o yaml
# 或者更详细地查看
kubectl describe svc my-service
你会看到类似这样的输出:
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 10.132.2.11
targetRef:
kind: Pod
name: my-service-abc12
uid: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
nodeName: node-1
- ip: 10.133.5.22
targetRef:
kind: Pod
name: my-service-def34
uid: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
nodeName: node-2
ports:
- port: 80
protocol: TCP
targetPort: 8080
这说明Service my-service 当前有两个后端Pod:my-service-abc12(在node-1上)和my-service-def34(在node-2上),流量会被转发到这两个Pod的8080端口。
(2)kube-proxy:流量的实际转发
kube-proxy运行在每一个节点上,它会监听API Server,当Service或Endpoints发生变化时,kube-proxy会相应地更新节点上的网络规则。
根据配置的不同,kube-proxy有三种工作模式:
iptables模式(默认):kube-proxy会在节点上创建iptables规则,将发往Service ClusterIP的流量转发到后端的Pod IP。
# 查看kube-proxy使用的模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 查看iptables规则中与服务相关的条目
iptables -t nat -L KUBE-SERVICE -n -v --line-numbers | head -50
ipvs模式:性能更好,特别是当后端Pod数量很多时。它使用Linux内核的IPVS(IP Virtual Server)模块来实现负载均衡。
# 切换到ipvs模式
kubectl edit configmap kube-proxy -n kube-system
# 将 mode: "" 改为 mode: "ipvs"
# 重启kube-proxy
kubectl delete pod -n kube-system -l app=kube-proxy
# 查看ipvs规则
ipvsadm -Ln
userspace模式:最老的模式,性能最差,基本不会被使用。
3.3 Service的类型详解
K8s提供了四种Service类型,每种适用于不同的场景:
(1)ClusterIP(默认)
这是最常见的类型,Service会分配一个仅集群内部可访问的虚拟IP。
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
(2)NodePort
在ClusterIP的基础上,每个节点会开放一个静态端口,外部可以通过<NodeIP>:<NodePort>访问服务。
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 可选,不指定则自动分配30000-32767范围内的端口
protocol: TCP
查看分配的NodePort:
kubectl get svc my-nodeport-service
# 输出示例:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# my-nodeport-service NodePort 10.96.45.123 <none> 80:30080/TCP 5m
(3)LoadBalancer
在NodePort的基础上,K8s会请求云服务商创建一个外部负载均衡器,将流量转发到NodePort。
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
annotations:
# AWS示例
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
(4)ExternalName
将Service映射到一个外部DNS名称,主要用于访问集群外的服务。
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: my.database.example.com
使用时,你在集群内访问my-external-service.default.svc.cluster.local,DNS会返回my.database.example.com的地址。
3.4 Service的负载均衡策略
Service默认使用轮询(Round Robin)的方式将流量分发到后端Pod。但你也可以通过设置session affinity来实现会话保持:
apiVersion: v1
kind: Service
metadata:
name: my-sticky-service
spec:
type: ClusterIP
sessionAffinity: ClientIP # 同一客户端IP的请求总是转发到同一个Pod
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
对于需要会话保持的场景(比如购物车、登录状态),这个功能非常有用。
四、Pod之间的通信方式
4.1 Pod到Pod的直接通信
根据K8s的设计,Pod之间可以直接通过IP通信,不需要任何特殊配置。
# 在Pod A中访问Pod B
kubectl exec -it pod-a -- curl http://10.132.2.11:8080
# 使用DNS名称访问(更推荐)
kubectl exec -it pod-a -- curl http://pod-b.default.svc.cluster.local:8080
K8s默认提供了一个DNS服务(CoreDNS或kube-dns),每个Service和Pod都可以通过DNS名称访问:
- Service:
<service-name>.<namespace>.svc.cluster.local - Pod:
<pod-ip>.<namespace>.pod.cluster.local(需要额外配置)
4.2 跨Namespace的通信
默认情况下,不同Namespace中的Pod是无法直接通过Service名称通信的。你需要使用完整的DNS名称:
# 访问其他namespace的Service
kubectl exec -it pod-a -n namespace-a -- curl http://my-service.namespace-b.svc.cluster.local
4.3 Headless Service(无头服务)
有些场景下,你不需要Service的负载均衡功能,而是希望客户端能够直接发现所有的后端Pod。这时可以使用Headless Service:
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 关键:不分配ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
使用Headless Service时,DNS查询会返回所有后端Pod的IP列表,客户端可以自己决定连接哪个Pod。这在StatefulSet场景中非常常见:
# 查询Headless Service的DNS
kubectl exec -it pod-a -- nslookup my-headless-service.default.svc.cluster.local
# 输出示例:
# Server: 10.96.0.10
# Address: 10.96.0.10#53
#
# Name: my-headless-service.default.svc.cluster.local
# Address: 10.132.2.11
# Name: my-headless-service.default.svc.cluster.local
# Address: 10.133.5.22
# Name: my-headless-service.default.svc.cluster.local
# Address: 10.134.8.33
4.4 Sidecar模式的网络通信
在Service Mesh(如Istio、Linkerd)中,每个Pod会注入一个Sidecar代理容器。流量路径会变成:
客户端Pod → Sidecar代理 → 网络 → Sidecar代理 → 目标Pod
这种情况下,Pod内的应用实际监听的是localhost(127.0.0.1),因为Sidecar代理会拦截所有进出Pod的流量。
# 查看注入了Sidecar的Pod的容器列表
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{range .spec.containers[*]} {.name}{end}{"\n"}{end}'
# 进入Pod查看网络命名空间
kubectl exec -it my-pod -- ip addr
五、CNI插件的工作原理
5.1 什么是CNI
CNI(Container Network Interface)是K8s定义的一个网络插件标准。它规定了插件需要实现什么接口,以及如何被调用。常见的CNI插件有:
- Flannel:最简单的overlay网络,使用VXLAN实现跨节点通信
- Calico:基于BGP的全功能网络方案,支持NetworkPolicy
- Cilium:基于eBPF的最新一代网络方案,性能出色
- Weave:自愈的overlay网络
5.2 CNI插件的安装位置
CNI插件的二进制文件通常位于:
ls /opt/cni/bin/
# 输出示例:
# bridge firewall flannel host-device host-local ipvlan loopback macvlan portmap tuning vlan veth
插件的配置文件位于:
ls /etc/cni/net.d/
# 输出示例:
# 10-flannel.conflist 10-calico.conflist 10-cilium.conflist
5.3 网络配置示例
Flannel配置:
{
"name": "flannel.1",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
Calico配置:
{
"name": "calico",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "calico",
"log_level": "info",
"datastore_type": "kubernetes",
"nodename": "__KUBERNETES_NODE_NAME__",
"mtu": 1440,
"ipam": {
"type": "calico-ipam"
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
}
]
}
5.4 不同CNI插件的路由方式对比
| CNI插件 | 路由方式 | 支持NetworkPolicy | 性能 | 复杂度 |
|---|---|---|---|---|
| Flannel | VXLAN Overlay | 有限支持 | 中等 | 低 |
| Calico | BGP + IPIP | 完整支持 | 高 | 中等 |
| Cilium | eBPF | 完整支持 | 非常高 | 高 |
| Weave | Overlay | 支持 | 中等 | 中等 |
六、NetworkPolicy:精细化的网络隔离
6.1 什么是NetworkPolicy
NetworkPolicy是K8s提供的网络隔离机制,类似于传统数据中心的安全组。通过NetworkPolicy,你可以控制哪些Pod可以互相通信,哪些流量可以进出Pod。
注意: NetworkPolicy需要你的CNI插件支持才能生效。Flannel、Calico、Cilium都支持,但默认安装的kubenet不支持。
6.2 NetworkPolicy的基本语法
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
- namespaceSelector:
matchLabels:
team: platform
ports:
- protocol: TCP
port: 8080
这个Policy的意思是:在production命名空间中,标记为app=backend的Pod只接受来自标记为app=frontend的Pod或者team为platform的命名空间中的Pod的8080端口TCP流量。
6.3 常见的NetworkPolicy模式
(1)隔离所有入站流量(默认拒绝所有)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
spec:
podSelector: {} # 空选择器表示匹配所有Pod
policyTypes:
- Ingress
(2)允许所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {} # 允许所有出站流量
(3)只允许访问数据库
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-database-access
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 5432
protocol: TCP
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432
protocol: TCP
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
这个Policy允许backend Pod只与frontend Pod通信(入站),并且只能访问database Pod的5432端口(出站),同时还允许访问DNS(53端口)。
6.4 排查NetworkPolicy问题
NetworkPolicy配置错误是导致K8s网络问题的常见原因。以下是一些排查技巧:
# 查看当前命名空间的所有NetworkPolicy
kubectl get networkpolicy -n production
# 查看详细的Policy规则
kubectl describe networkpolicy allow-database-access -n production
# 检查CNI插件是否支持NetworkPolicy
kubectl get pods -n kube-system -l k8s-app=calico-node
# 或者
kubectl get pods -n kube-system -l k8s-app=cilium
如果你发现应用之间无法通信,首先检查是否有NetworkPolicy在阻止流量:
# 临时删除Policy测试(仅用于排查,不要在生产环境长期使用)
kubectl delete networkpolicy --all -n production
# 如果删除后通信恢复,说明是Policy配置问题
# 然后逐个添加Policy,定位具体是哪个规则导致的问题
七、常见网络故障排查指南
7.1 Pod无法访问外部网络
这是最常见的问题之一。可能的原因和排查步骤:
(1)检查节点的出站路由
# 在节点上检查默认路由
ip route show default
# 检查DNS解析是否正常
nslookup google.com
# 检查iptables规则是否阻止了出站流量
iptables -L POSTROUTING -t nat -n -v
iptables -L OUTPUT -t filter -n -v
(2)检查CNI插件的配置
# 查看CNI插件的日志
kubectl logs -n kube-system -l k8s-app=calico-node | tail -100
# 或者
kubectl logs -n kube-system -l k8s-app=cilium | tail -100
# 检查CNI插件的Pod是否正常运行
kubectl get pods -n kube-system | grep -E 'calico|flannel|cilium'
(3)检查IPVS规则(如果使用ipvs模式)
# 在节点上检查IPVS规则
ipvsadm -Ln
# 查看是否有规则丢失或异常
(4)云服务商的特殊配置
在AWS、阿里云等云环境中,可能需要配置安全组或网络ACL:
# AWS:检查安全组规则
aws ec2 describe-security-groups --group-ids <sg-id>
# 阿里云:检查安全组规则
aliyun ecs DescribeSecurityGroupAttribute --SecurityGroupId <sg-id>
7.2 Pod之间无法通信
(1)确认Pod在同一集群
# 检查Pod所在的节点
kubectl get pod -o wide
# 检查节点是否在同一个集群
kubectl get nodes
(2)检查网络策略
# 查看源Pod和目标Pod所在命名空间的NetworkPolicy
kubectl get networkpolicy -A -o wide
# 查看具体的Policy详情
kubectl describe networkpolicy -n <namespace>
(3)测试连通性
# 在Pod A中测试到Pod B的连通性
kubectl exec -it pod-a -- ping 10.132.2.11
kubectl exec -it pod-a -- curl -v http://10.132.2.11:8080
kubectl exec -it pod-a -- nc -zv 10.132.2.11 8080
# 在Pod B中检查监听状态
kubectl exec -it pod-b -- netstat -tlnp
kubectl exec -it pod-b -- ss -tlnp
(4)检查iptables/ipvs规则
# 查看iptables规则中的KUBE-SERVICES链
iptables -t nat -L KUBE-SERVICES -n -v --line-numbers
# 查看ipvs规则
ipvsadm -Ln
7.3 Service无法访问后端Pod
(1)检查Endpoints是否正确
# 查看Service的Endpoints
kubectl get endpoints <service-name> -o wide
# 检查Endpoints是否包含后端Pod
kubectl describe endpoints <service-name>
# 检查Selector是否正确匹配Pod
kubectl get pods -l app=my-app
如果Endpoints为空,说明没有Pod匹配Service的Selector。检查Pod的label是否正确:
# 查看Pod的label
kubectl get pods --show-labels
# 查看Service的Selector
kubectl get svc <service-name> -o yaml
(2)检查kube-proxy是否正常运行
# 查看所有kube-proxy Pod的状态
kubectl get pods -n kube-system -l app=kube-proxy
# 检查kube-proxy的日志
kubectl logs -n kube-system kube-proxy-xxxxx
# 如果Pod不在运行,可能需要手动重启
kubectl delete pod -n kube-system -l app=kube-proxy
(3)检查节点上的网络规则
# 在运行后端Pod的节点上检查iptables规则
iptables -t nat -L KUBE-SERVICES -n -v --line-numbers | grep <service-ip>
# 检查kube-proxy的配置
cat /etc/kubernetes/proxy.config 2>/dev/null || echo "配置文件不存在"
# 检查IPVS规则(如果使用ipvs模式)
ipvsadm -Ln | grep <service-ip>
(4)检查Pod是否监听正确的端口
# 进入Pod检查监听状态
kubectl exec -it pod-b -- netstat -tlnp
kubectl exec -it pod-b -- ss -tlnp
kubectl exec -it pod-b -- curl http://localhost:8080/healthz
# 检查应用日志
kubectl logs pod-b
7.4 外部无法访问Service
(1)NodePort类型
# 检查NodePort是否开放
kubectl get svc <service-name>
# 在节点上检查端口是否监听
ss -tlnp | grep <nodeport>
netstat -tlnp | grep <nodeport>
# 检查节点防火墙
ufw status
firewall-cmd --list-ports
iptables -L INPUT -n -v --line-numbers
(2)LoadBalancer类型
# 检查Service状态
kubectl get svc <service-name>
# 如果EXTERNAL-IP是<pending>,说明云服务商还没有创建负载均衡器
# 检查事件
kubectl describe svc <service-name>
# 查看云服务商的控制台,确认负载均衡器是否创建成功
(3)Ingress资源
如果你使用Ingress来控制外部访问,需要检查Ingress Controller是否正常工作:
# 检查Ingress Controller的Pod
kubectl get pods -n ingress-nginx
kubectl logs -n ingress-nginx <ingress-controller-pod>
# 检查Ingress资源
kubectl get ingress
kubectl describe ingress <ingress-name>
# 检查Ingress Controller的配置文件
kubectl get configmap nginx-config -n ingress-nginx -o yaml
7.5 DNS解析问题
K8s集群内的DNS解析是Pod通信的关键环节。常见的DNS问题包括:
(1)CoreDNS Pod不运行
# 检查CoreDNS Pod状态
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get pods -n kube-system -l app=kube-dns
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
(2)DNS解析超时
# 在Pod中测试DNS解析
kubectl exec -it pod-a -- nslookup kubernetes.default.svc.cluster.local
kubectl exec -it pod-a -- nslookup my-service.default.svc.cluster.local
# 检查DNS解析配置
kubectl exec -it pod-a -- cat /etc/resolv.conf
# 检查Service的ClusterIP是否正确
kubectl get svc kube-dns -n kube-system
(3)DNS缓存问题
如果DNS解析偶尔失败偶尔成功,可能是缓存问题:
# 检查CoreDNS的缓存配置
kubectl get configmap coredns -n kube-system -o yaml | grep -A 10 cache
# 清除CoreDNS缓存(需要重启Pod)
kubectl rollout restart deployment coredns -n kube-system
7.6 性能问题排查
当网络出现高延迟或丢包时,可以使用以下工具排查:
# 使用ping测试延迟
kubectl exec -it pod-a -- ping -c 10 10.132.2.11
# 使用iperf3测试带宽
# 在目标Pod中启动服务器
kubectl exec -it pod-b -- iperf3 -s -p 5001
# 在源Pod中测试
kubectl exec -it pod-a -- iperf3 -c 10.132.2.11 -p 5001
# 使用tcpdump抓包分析
kubectl exec -it pod-a -- tcpdump -i any -n port 8080
kubectl exec -it pod-b -- tcpdump -i any -n port 8080
# 在节点上使用tcpdump
tcpdump -i any -n host 10.132.2.11 and port 8080
# 检查网络统计信息
kubectl exec -it pod-a -- cat /proc/net/dev
kubectl exec -it pod-a -- netstat -s
八、实战案例:排查一个复杂的网络问题
让我分享一个真实的排查经历。
场景描述
我们有一个生产环境的K8s集群,运行着微服务架构的应用。某天早上,运维团队报告说,前端服务无法访问后端的支付服务,但其他服务之间的通信是正常的。
排查过程
第一步:确认问题范围
# 检查支付服务的Pod是否运行
kubectl get pods -l app=payment-service
# 输出:3个Pod都在Running状态
# 检查支付服务的Service是否正常
kubectl get svc payment-service
# 输出:ClusterIP为10.96.45.100,端口8080
# 检查Endpoints
kubectl get endpoints payment-service
# 输出:3个后端Pod的IP都在列表中
第二步:测试连通性
# 从其他服务访问支付服务
kubectl exec -it user-service-xxx -- curl -v http://payment-service:8080/health
# 输出:Connection refused
# 直接访问Pod IP
kubectl exec -it user-service-xxx -- curl -v http://10.132.5.22:8080/health
# 输出:Connection timed out
# 在支付服务的Pod中检查监听状态
kubectl exec -it payment-service-xxx -- netstat -tlnp
# 输出:应用监听在127.0.0.1:8080,而不是0.0.0.0:8080
第三步:发现问题根源
原来,支付服务的开发人员最近更新了配置,将应用的绑定地址从0.0.0.0改成了127.0.0.1,导致应用只监听localhost,无法接受来自其他Pod的请求。
第四步:修复并验证
# 修改配置,将绑定地址改回0.0.0.0
kubectl edit deploy payment-service
# 在环境变量中添加:
# - name: BIND_ADDRESS
# value: "0.0.0.0"
# 重启Pod
kubectl rollout restart deploy payment-service
# 验证修复
kubectl exec -it payment-service-xxx -- netstat -tlnp
# 确认应用现在监听在0.0.0.0:8080
kubectl exec -it user-service-xxx -- curl -v http://payment-service:8080/health
# 输出:200 OK
经验总结
这个案例说明,很多网络问题其实不是K8s本身的问题,而是应用配置的问题。排查时不仅要关注网络层面,还要深入到应用层面。
九、最佳实践建议
9.1 网络规划
(1)合理设计Pod IP网段
- 确保各节点的Pod网段不重叠
- 预留足够的IP地址空间
- 考虑未来集群扩容的需求
(2)选择合适的CNI插件
- 小规模集群:Flannel(简单)
- 需要NetworkPolicy:Calico或Cilium
- 高性能要求:Cilium
- 兼容性问题少:Calico
9.2 Service设计
(1)合理选择Service类型
- 内部服务通信:ClusterIP
- 需要NodePort访问:NodePort(谨慎使用,仅用于调试)
- 外部流量入口:LoadBalancer或Ingress
- 访问外部服务:ExternalName
(2)使用标签选择器
确保Service的Selector能准确匹配目标Pod,避免将流量转发到错误的Pod。
9.3 安全加固
(1)启用NetworkPolicy
对所有命名空间启用默认的deny-all策略,然后按需放行流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
(2)定期审计NetworkPolicy
# 查看所有命名空间的NetworkPolicy
kubectl get networkpolicy --all-namespaces
# 导出Policy配置进行审计
kubectl get networkpolicy --all-namespaces -o yaml > network-policies.yaml
9.4 监控和日志
(1)监控关键指标
- Pod网络吞吐量
- 网络延迟
- 丢包率
- DNS解析成功率
- Service连接数
(2)集中日志收集
# 收集kube-proxy日志
kubectl logs -n kube-system -l app=kube-proxy --tail=100
# 收集CNI插件日志
kubectl logs -n kube-system -l k8s-app=calico-node --tail=100
# 或者
kubectl logs -n kube-system -l k8s-app=cilium --tail=100
十、总结
Kubernetes的网络模型看似复杂,但只要理解了其核心设计哲学——每个Pod拥有独立IP、Pod间直接通信、Service提供稳定入口——剩下的就是熟悉各种组件和工具的使用了。
排查网络问题时,建议按照以下思路进行:
- 确认问题范围:是单个Pod的问题,还是整个Service的问题,或者是集群级别的问题
- 检查基础组件:CNI插件、kube-proxy、CoreDNS是否正常运行
- 验证网络配置:检查Endpoints、NetworkPolicy、Service配置是否正确
- 深入应用层面:确认应用监听地址、端口、防火墙规则等
- 使用工具定位:ping、curl、nc、tcpdump、iperf3等工具可以帮助快速定位问题
希望这篇指南能帮助你更好地理解Kubernetes网络,遇到问题时不再手足无措。记住,实践是最好的老师,多在实验环境中折腾,积累的经验会在关键时刻帮上大忙。
