Qingular

Pod 网络连通性

·CKAk8s练习

CKA Domain 3 — Pod 网络通信机制、kube-proxy 模式与 CNI 插件对比

← 返回 CKA 练习目录

概述

Kubernetes 网络模型要求:

  • 所有 Pod 可以不使用 NAT 直接相互通信
  • 所有节点可以不使用 NAT 直接与所有 Pod 通信
  • Pod 看到的自身 IP 与其他 Pod 看到的它相同

1. 同一 Pod 内容器通信

Pod 内的所有容器共享同一个网络命名空间(Network Namespace)。

┌─────────────────────────────┐
│          Pod                │
│  ┌─────────┐  ┌─────────┐  │
│  │ Container│  │ Container│  │
│  │   App A  │  │   App B  │  │
│  │ localhost:8080  │  │
│  └─────────┘  └─────────┘  │
│       lo interface          │
│       eth0: 10.244.1.5     │
└─────────────────────────────┘
apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
spec:
  containers:
    - name: nginx
      image: nginx
      ports:
        - containerPort: 80
    - name: sidecar
      image: busybox:1.28
      command: ["sleep", "3600"]
      # sidecar 可以通过 localhost:80 访问 nginx

关键点

  • 通过 localhost 通信
  • 共享同一 IP 地址和端口空间
  • 不需要 Service 即可互相访问

2. 同一节点上 Pod 通信

同一节点上的 Pod 通过节点的网络桥接(通常是 cbr0CNI 创建的网桥)通信。

┌───────────── Node ─────────────┐
│                                │
│  ┌─────────┐  ┌─────────┐     │
│  │ Pod A   │  │ Pod B   │     │
│  │ veth pair│  │ veth pair│    │
│  └────┼────┘  └────┼────┘     │
│       │           │          │
│    ┌──┴───────────┴──┐       │
│    │   cni0 (bridge)  │       │
│    └────────┬─────────┘       │
│             │ eth0            │
│    Node IP: 192.168.1.10      │
└────────────────────────────────┘
  • Pod 通过虚拟网卡对(veth pair)连接到网桥
  • 通过网桥直接通信,不需要封装
  • 延迟低,性能好

3. 不同节点上 Pod 通信

跨节点 Pod 通信依赖 CNI 插件的 Overlay 网络实现。

┌────── Node 1 ──────┐    ┌────── Node 2 ──────┐
│ Pod A: 10.244.1.5  │    │ Pod C: 10.244.2.7  │
│    │               │    │    │               │
│ ┌──┴──┐            │    │ ┌──┴──┐            │
│ │cni0 │            │    │ │cni0 │            │
│ └──┬──┘            │    │ └──┬──┘            │
│    │               │    │    │               │
│ eth0: 192.168.1.10 │    │ eth0: 192.168.2.10│
└────────┬───────────┘    └────────┬───────────┘
         │                        │
         └──── Overlay (VXLAN) ───┘
                或直接路由

4. Service ClusterIP 通信机制

当 Pod 通过 Service ClusterIP 通信时,kube-proxy 负责将流量转发到后端 Pod。

kube-proxy 模式

iptables 模式(默认)

# 查看 iptables 规则
kubectl get svc

# 在节点上查看 Service 的 iptables 规则
sudo iptables -t nat -L -n | grep <cluster-ip>

# 查看 KUBE-SERVICES 链
sudo iptables -t nat -L KUBE-SERVICES -n

# 查看具体的 Service 规则链
sudo iptables -t nat -L KUBE-SVC-XXXXXXXX -n

# 查看 Endpoints 负载均衡规则
sudo iptables -t nat -L KUBE-SEP-XXXXXXXX -n

iptables 转发流程

  1. PREROUTINGKUBE-SERVICES
  2. 匹配 ClusterIP 目标 → 跳转到对应的 KUBE-SVC-*
  3. KUBE-SVC-* 链执行随机负载均衡 → 跳转到 KUBE-SEP-*
  4. KUBE-SEP-* 链执行 DNAT → 将目标 IP 替换为 Pod IP

特点

  • 默认模式,成熟稳定
  • 规则数量与 Service+Endpoint 数量成正比
  • 大规模集群下 iptables 规则更新可能变慢

IPVS 模式(推荐大规模集群)

# 查看 kube-proxy 当前模式
kubectl get configmap -n kube-system kube-proxy -o yaml | grep mode

# 切换到 IPVS 模式(修改 ConfigMap)
kubectl edit configmap -n kube-system kube-proxy
# 设置 mode: ipvs

# 重启 kube-proxy
kubectl rollout restart -n kube-system daemonset kube-proxy

# 查看 IPVS 规则
sudo ipvsadm -L -n

# 查看 IPVS 连接
sudo ipvsadm -L -n -c

特点

  • 基于内核的 IPVS 模块
  • 更高效:哈希表查找,时间复杂度 O(1)
  • 支持更多负载均衡算法(rr, wrr, lc, wlc, sh, dh 等)
  • 适合大规模集群(数千 Service)

负载均衡算法

算法说明
rr轮询(Round Robin,默认)
wrr加权轮询
lc最少连接
wlc加权最少连接
sh源地址哈希
dh目标地址哈希

5. CNI 插件对比

特性CalicoFlannelWeaveCilium
基础技术BGP + iptablesVXLAN/Host-GWVXLAN/Weave MesheBPF
NetworkPolicy完全支持有限支持(需配合其他插件)完全支持完全支持
加密WireGuard 可选不内置内置加密WireGuard 可选
性能中低极高
复杂度中高
推荐场景生产环境,需 NetworkPolicy小集群,简单网络小到中型集群高性能生产环境
eBPF支持(eBPF 模式)不支持不支持原生 eBPF

安装 Calico(CKA 重点)

# 安装 Calico(基于 Tigera Operator)
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/tigera-operator.yaml

# 配置文件
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/custom-resources.yaml

# 等待所有 Pod 就绪
kubectl get pods -n calico-system -w

安装 Flannel

kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

6. 网络排查命令

连通性测试

# 使用 busybox 测试网络连通性
kubectl run test-pod --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://<service-ip>

# 使用 netshoot(推荐,包含更多工具)
kubectl run netshoot --image=nicolaka/netshoot --rm -it --restart=Never -- curl http://my-svc:80

# 测试 DNS 解析
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-svc

# 测试 Pod IP 直接通信
kubectl run test-pod --image=busybox:1.28 --rm -it --restart=Never -- sh
# 在容器内
ping <target-pod-ip>
wget -qO- http://<target-pod-ip>:8080

网络诊断工具 Pod

apiVersion: v1
kind: Pod
metadata:
  name: network-tools
spec:
  containers:
    - name: netshoot
      image: nicolaka/netshoot
      command: ["sleep", "3600"]
# 创建后进入容器
kubectl exec -it network-tools -- sh

# 在容器内可以使用以下工具
curl http://my-svc:80
dig my-svc.default.svc.cluster.local
nslookup kubernetes.default
ping 10.96.0.1
traceroute 10.244.1.5
mtr 10.244.1.5
tcpdump -i eth0
ip addr
ip route
ss -tunap

节点级排查

# 查看 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy

# 查看节点路由表
ip route show

# 查看网桥
brctl show
ip link show type bridge

# 查看网络接口和 veth
ip link

# 查看 iptables NAT 规则(节点上执行)
sudo iptables-save | grep <service-name>


🧪 完整操作实例:验证跨节点 Pod 连通性

场景描述

在多节点集群的不同节点上创建 Pod,验证它们可以通过 Pod IP 直接通信,也可以通过 Service ClusterIP 进行服务发现。

前置条件

  • 集群有至少 2 个可用节点
  • 所有节点上的 CNI 插件正常工作
  • kubectl 已配置好集群访问

操作步骤

Step 1: 查看集群节点拓扑

# 查看节点列表及标签
kubectl get nodes -o wide
# NAME       STATUS   ROLES           INTERNAL-IP      OS-IMAGE
# node-1     Ready    <none>          192.168.1.10     Ubuntu 22.04
# node-2     Ready    <none>          192.168.1.20     Ubuntu 22.04

# 查看节点标签,确定可以用哪些标签调度 Pod
kubectl get nodes --show-labels
# 记录节点名称,为后续指定调度做准备

Step 2: 在节点 1 上创建 Pod

# 在 node-1 上创建 Pod,使用节点选择器指定调度
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-node1
  labels:
    app: test
    node: node1
spec:
  nodeName: node-1          # 强制调度到 node-1
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80
EOF
# 预期输出:pod/pod-node1 created

# 验证 Pod 在 node-1 上运行
kubectl get pod pod-node1 -o wide
# NAME        READY   STATUS    RESTARTS   AGE   IP              NODE
# pod-node1   1/1     Running   0          10s   10.244.1.10     node-1

Step 3: 在节点 2 上创建 Pod

# 在 node-2 上创建 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: pod-node2
  labels:
    app: test
    node: node2
spec:
  nodeName: node-2
  containers:
    - name: nginx
      image: nginx:1.25
      ports:
        - containerPort: 80
EOF
# 预期输出:pod/pod-node2 created

# 验证 Pod 在 node-2 上运行
kubectl get pod pod-node2 -o wide
# NAME        READY   STATUS    RESTARTS   AGE   IP              NODE
# pod-node2   1/1     Running   0          10s   10.244.2.20     node-2

Step 4: 通过 Pod IP 跨节点直连

# 记录两个 Pod 的 IP
POD1_IP=$(kubectl get pod pod-node1 -o jsonpath='{.status.podIP}')
POD2_IP=$(kubectl get pod pod-node2 -o jsonpath='{.status.podIP}')
echo "Pod1 IP: $POD1_IP, Pod2 IP: $POD2_IP"
# 预期输出:Pod1 IP: 10.244.1.10, Pod2 IP: 10.244.2.20

# 从 pod-node1 访问 pod-node2(跨节点)
kubectl exec pod-node1 -- curl -s -m 5 http://$POD2_IP
# 预期输出:nginx 欢迎页面 HTML

# 从 pod-node2 访问 pod-node1(反向跨节点)
kubectl exec pod-node2 -- curl -s -m 5 http://$POD1_IP
# 预期输出:nginx 欢迎页面 HTML

# 使用 ping 测试 ICMP 连通性(某些 CNI 可能禁用 ICMP)
kubectl exec pod-node1 -- ping -c 2 $POD2_IP
# 预期输出:2 packets transmitted, 2 received(可能超时取决于 CNI 配置)

Step 5: 通过 Service ClusterIP 跨节点通信

# 创建 Service 暴露两个 Pod
kubectl expose pod pod-node1 --name=test-svc --port=80 --target-port=80
# 或使用 label selector
kubectl create service clusterip test-svc --tcp=80:80

# 手动设置 selector 指向两个 Pod
kubectl patch svc test-svc -p '{"spec":{"selector":{"app":"test"}}}'
# 预期输出:service/test-svc patched

# 查看 Service ClusterIP
kubectl get svc test-svc
# NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# test-svc    ClusterIP   10.96.200.50    <none>        80/TCP    10s

SVC_IP=$(kubectl get svc test-svc -o jsonpath='{.spec.clusterIP}')
echo "Service ClusterIP: $SVC_IP"
# 预期输出:Service ClusterIP: 10.96.200.50

# 从 pod-node1 通过 Service 名称访问(DNS 解析)
kubectl exec pod-node1 -- curl -s -m 5 http://test-svc
# 预期输出:nginx 欢迎页面 HTML

# 从 pod-node2 通过 Service ClusterIP 访问
kubectl exec pod-node2 -- curl -s -m 5 http://$SVC_IP
# 预期输出:nginx 欢迎页面 HTML

Step 6: 完整的网络调试工作流

# 创建包含完整网络工具的诊断 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: network-debug
spec:
  containers:
    - name: netshoot
      image: nicolaka/netshoot
      command: ["sleep", "3600"]
EOF
# 预期输出:pod/network-debug created

# 进入诊断 Pod
kubectl exec -it network-debug -- sh

# 在诊断容器内执行以下调试命令:
# 1. IP 与路由信息
ip addr
ip route

# 2. DNS 解析测试
dig test-svc.default.svc.cluster.local
nslookup kubernetes.default

# 3. 连通性测试
curl -m 5 http://$POD1_IP          # 访问 pod-node1
curl -m 5 http://$POD2_IP          # 访问 pod-node2
curl -m 5 http://test-svc          # 通过 Service DNS 访问

# 4. 追踪路由
traceroute $POD2_IP

# 5. 抓包分析(可选)
tcpdump -i eth0 -c 10

# 退出容器
exit

验证结果

# 检查 Endpoints 确认 Service 后端
kubectl get endpoints test-svc
# NAME        ENDPOINTS                           AGE
# test-svc    10.244.1.10:80,10.244.2.20:80      2m

# 在节点上查看 iptables 转发规则(需 sudo)
# sudo iptables -t nat -L KUBE-SERVICES -n | grep $SVC_IP

# 查看 kube-proxy 日志(排查 Service 不通时)
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=10

# 清理资源
kubectl delete pod pod-node1 pod-node2 network-debug
kubectl delete svc test-svc

考试提示