跳过正文

Kubernetes 网络模型与 Service 详解

·1197 字·6 分钟·
目录

Kubernetes 网络模型四大要求
#

Kubernetes 对网络有四条核心约束,所有 CNI 插件必须满足:

  1. Pod 间通信不需要 NAT:任意 Pod 可以用对方的 Pod IP 直接通信
  2. Node 与 Pod 通信不需要 NAT:节点可以直接访问任何 Pod IP
  3. Pod 看到的自身 IP 与外界看到的一致:不存在 IP 伪装问题
  4. 跨节点 Pod 通信:不同节点上的 Pod 也能直接互访

这意味着每个 Pod 有独立 IP,且整个集群共享一个扁平网络(flat network),这与 Docker 的 NAT 模型完全不同。

节点A                          节点B
┌─────────────────┐            ┌─────────────────┐
│  Pod-A          │            │  Pod-B          │
│  10.0.1.5:8080  │◄──────────►│  10.0.2.7:8080  │
│                 │  直接通信   │                 │
└─────────────────┘            └─────────────────┘
    eth0: 192.168.1.10              eth0: 192.168.1.11

CNI 插件对比
#

插件工作模式网络策略性能适用场景
FlannelOverlay (VXLAN/host-gw)不支持(需配合 Canal)中等简单集群,快速搭建
CalicoBGP 路由 / Overlay支持(NetworkPolicy)生产级,大规模集群
CiliumeBPF支持(L3-L7)最高高性能,安全要求高
TerwayVPC 弹性网卡支持极高阿里云 ACK 专用
AWS VPC CNIENI 直通支持(SG for Pods)极高AWS EKS 专用

Calico 安装示例
#

# 使用 operator 安装 Calico
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml

cat <<EOF | kubectl apply -f -
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    ipPools:
    - blockSize: 26
      cidr: 10.244.0.0/16
      encapsulation: VXLANCrossSubnet  # 同子网用 BGP,跨子网用 VXLAN
      natOutgoing: Enabled
      nodeSelector: all()
EOF

Cilium 安装示例(Helm)
#

helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium \
  --namespace kube-system \
  --set kubeProxyReplacement=true \   # 替换 kube-proxy
  --set k8sServiceHost=<API_SERVER_IP> \
  --set k8sServicePort=6443

Service 四种类型
#

ClusterIP(默认)
#

集群内部虚拟 IP,只能在集群内访问。kube-proxy 通过 iptables/IPVS 将流量转发到后端 Pod。

apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - name: http
      port: 80          # Service 端口
      targetPort: 8080  # Pod 端口
      protocol: TCP

访问方式:curl http://my-app.production.svc.cluster.local

NodePort
#

在每个节点上开放一个端口(30000-32767),通过 NodeIP:NodePort 从外部访问。

apiVersion: v1
kind: Service
metadata:
  name: my-app-nodeport
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 31080   # 不指定则随机分配
# 从集群外访问
curl http://192.168.1.10:31080

# 查看 NodePort 分配
kubectl get svc my-app-nodeport -o jsonpath='{.spec.ports[0].nodePort}'

LoadBalancer
#

在 NodePort 基础上,由云厂商自动创建外部负载均衡器(CLB/ALB/NLB)。

apiVersion: v1
kind: Service
metadata:
  name: my-app-lb
  annotations:
    # AWS NLB
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    # 阿里云 SLB
    # service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: "slb.s2.small"
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
# 等待 EXTERNAL-IP 分配
kubectl get svc my-app-lb -w

# 输出示例
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)        AGE
my-app-lb   LoadBalancer   10.96.45.123   a1b2c3.elb.amazonaws.com   80:31234/TCP   2m

ExternalName
#

将 Service 映射到外部 DNS 名称,不创建任何代理,纯粹是 CNAME 记录。

apiVersion: v1
kind: Service
metadata:
  name: external-db
  namespace: production
spec:
  type: ExternalName
  externalName: my-rds.abc123.us-west-2.rds.amazonaws.com

应用通过 external-db.production.svc.cluster.local 访问,实际解析为 RDS 地址,便于后续迁移。


kube-proxy 工作模式
#

iptables 模式(默认)
#

每个 Service 创建一组 iptables 规则,随机选择后端 Pod。规则数量随 Service 数线性增长,1000+ Service 时性能下降明显。

# 查看 iptables 规则(以 my-app Service 为例)
iptables -t nat -L KUBE-SERVICES | grep my-app
iptables -t nat -L KUBE-SVC-XXXXXXXX   # 查看对应 chain

# 当前 iptables 规则数
iptables -t nat -L | wc -l

IPVS 模式(推荐生产使用)
#

基于 Linux IPVS(内核级负载均衡),哈希表查找复杂度 O(1),支持多种调度算法。

# 检查节点 IPVS 支持
lsmod | grep ip_vs

# 切换到 IPVS 模式(修改 kube-proxy ConfigMap)
kubectl -n kube-system edit configmap kube-proxy
# kube-proxy ConfigMap 关键配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-proxy
  namespace: kube-system
data:
  config.conf: |
    mode: "ipvs"
    ipvs:
      scheduler: "rr"          # round-robin,也支持 lc/dh/sh/sed/nq
      strictARP: true          # LoadBalancer 模式必须开启
    iptables:
      masqueradeAll: false
# 重启 kube-proxy DaemonSet 生效
kubectl -n kube-system rollout restart daemonset kube-proxy

# 验证 IPVS 规则
ipvsadm -Ln | grep -A3 "10.96.45.123:80"
对比项iptablesIPVS
查找复杂度O(n)O(1)
调度算法随机RR/LC/DH/SH 等
规则更新全量刷新增量更新
适用规模< 1000 Service无限制
健康检查不支持支持

Endpoints 与 EndpointSlice
#

Service 选中的 Pod 列表存储在 Endpoints/EndpointSlice 对象中。

# 查看 Service 对应的 Endpoints
kubectl get endpoints my-app -o yaml

# 输出示例
subsets:
  - addresses:
    - ip: 10.244.1.5
      targetRef:
        kind: Pod
        name: my-app-7d6b9f-abc12
    - ip: 10.244.2.8
      targetRef:
        kind: Pod
        name: my-app-7d6b9f-xyz89
    ports:
    - port: 8080
      protocol: TCP

EndpointSlice(K8s 1.17+ 默认启用)将 Endpoints 分片存储,每片最多 100 个端点,解决大规模集群的性能问题:

kubectl get endpointslices -l kubernetes.io/service-name=my-app

DNS 服务发现(CoreDNS)
#

服务名解析规则
#

访问方式解析结果适用场景
my-app同 Namespace(需同 NS)同命名空间内
my-app.productionproduction 命名空间跨命名空间
my-app.production.svc同上明确指定
my-app.production.svc.cluster.local完整 FQDN最明确
# 查看 CoreDNS 配置
kubectl -n kube-system get configmap coredns -o yaml

# 在 Pod 内调试 DNS
kubectl run dnsutils --image=gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 -it --rm -- bash
nslookup my-app.production.svc.cluster.local
dig my-app.production.svc.cluster.local

CoreDNS 自定义配置
#

# 添加自定义 hosts 或转发规则
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        # 自定义转发:内部域名走私有 DNS
        forward internal.company.com 10.0.0.53
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }

Headless Service
#

不分配 ClusterIP(clusterIP: None),DNS 直接返回所有 Pod IP,适用于 StatefulSet 和需要客户端负载均衡的场景。

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  namespace: production
spec:
  clusterIP: None        # 关键:不分配 VIP
  selector:
    app: mysql
  ports:
    - port: 3306
      targetPort: 3306
# Headless Service DNS 返回多个 A 记录
nslookup mysql-headless.production.svc.cluster.local
# Server:  10.96.0.10
# Address: 10.96.0.10#53
# Name: mysql-headless.production.svc.cluster.local
# Address: 10.244.1.5
# Address: 10.244.2.8
# Address: 10.244.3.2

# StatefulSet Pod 通过固定 DNS 访问
# mysql-0.mysql-headless.production.svc.cluster.local
# mysql-1.mysql-headless.production.svc.cluster.local

NetworkPolicy 网络策略
#

默认所有 Pod 互通,NetworkPolicy 用于实现网络隔离:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-only-frontend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend      # 只允许 frontend Pod 访问
        - namespaceSelector:
            matchLabels:
              name: monitoring   # 允许 monitoring 命名空间访问(Prometheus 抓取)
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: mysql
      ports:
        - protocol: TCP
          port: 3306
    - to: []                     # 允许 DNS 查询
      ports:
        - protocol: UDP
          port: 53

Service 故障排查
#

ClusterIP 不通
#

# 1. 确认 Service 存在且 Selector 正确
kubectl get svc my-app -o yaml
kubectl describe svc my-app

# 2. 确认 Endpoints 不为空
kubectl get endpoints my-app
# 如果 ENDPOINTS 列为 <none>,说明没有匹配的 Pod

# 3. 确认 Pod 正在运行且标签匹配
kubectl get pods -l app=my-app
kubectl get pods --show-labels

# 4. 直接访问 Pod IP 测试
kubectl get pods -l app=my-app -o jsonpath='{.items[0].status.podIP}'
kubectl exec -it test-pod -- curl http://10.244.1.5:8080

# 5. 通过 ClusterIP 访问测试
kubectl exec -it test-pod -- curl http://10.96.45.123:80

# 6. 检查 kube-proxy 状态
kubectl -n kube-system get pods -l k8s-app=kube-proxy
kubectl -n kube-system logs daemonset/kube-proxy | tail -50

外部无法访问 LoadBalancer
#

# 1. 检查 EXTERNAL-IP 是否分配
kubectl get svc my-app-lb

# 2. 如果 EXTERNAL-IP 一直是 <pending>,检查云厂商 LB 控制器
kubectl -n kube-system get pods | grep aws-load-balancer
kubectl -n kube-system logs deployment/aws-load-balancer-controller | tail -50

# 3. 检查安全组是否放通了 NodePort 范围 (30000-32767)
# AWS: 检查节点 SG 的入站规则

# 4. 检查节点是否可达
curl http://<NodeIP>:<NodePort>

# 5. 检查 Pod readiness probe
kubectl describe pod <pod-name> | grep -A10 "Readiness"

DNS 解析失败
#

# 1. 检查 CoreDNS Pod 状态
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs deployment/coredns

# 2. 在问题 Pod 内测试 DNS
kubectl exec -it <pod-name> -- nslookup kubernetes.default.svc.cluster.local

# 3. 检查 Pod 的 /etc/resolv.conf
kubectl exec -it <pod-name> -- cat /etc/resolv.conf
# 应该包含:
# nameserver 10.96.0.10
# search production.svc.cluster.local svc.cluster.local cluster.local

# 4. 检查 CoreDNS ConfigMap
kubectl -n kube-system get configmap coredns -o yaml

# 5. 测试外部 DNS 解析
kubectl exec -it <pod-name> -- nslookup google.com

生产配置建议
#

# Service 生产配置模板
apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: production
  annotations:
    # AWS NLB 跨可用区
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec:
  type: LoadBalancer
  selector:
    app: my-app
  # 流量策略:Local 保留客户端源 IP,但可能不均衡
  # Cluster 均衡分发但会做 SNAT
  externalTrafficPolicy: Cluster
  sessionAffinity: None
  ports:
    - name: http
      port: 80
      targetPort: 8080
    - name: https
      port: 443
      targetPort: 8443
  # 健康检查端口(给 LB 探活用)
  healthCheckNodePort: 31234
# 生产常用排查命令速查
kubectl get svc,endpoints,endpointslices -n production
kubectl describe svc <name> -n production
kubectl get events -n production --sort-by='.lastTimestamp' | tail -20
# 查看 iptables/IPVS 规则
iptables -t nat -L KUBE-SERVICES -n | grep <ClusterIP>
ipvsadm -Ln | grep -A5 <ClusterIP>
Wenzhuo Huang
作者
Wenzhuo Huang
搞运维的工程师,写代码的运维人。专注 Kubernetes、AWS、GitOps 与基础设施可靠性。这个博客既是我的技术笔记本,也是我踩过的坑的受害者档案。

相关文章

Kubernetes 网络深度解析——CNI、kube-proxy、NetworkPolicy 完全指南

·962 字·5 分钟
K8s 网络是很多工程师的知识盲区,平时不出问题就忽略,一出问题就完全不知道从哪下手。我在多次生产网络故障的排查中,深刻理解了 K8s 网络的每一层。这篇文章从 Pod 网络模型讲到 NetworkPolicy 实战,帮你建立完整的 K8s 网络知识体系。

Kubernetes HPA/VPA 弹性伸缩配置

·1145 字·6 分钟
从 HPA v2 到 KEDA 事件驱动伸缩,覆盖 CPU/内存/自定义指标配置、防抖参数调优、VPA 推荐器集成和生产级弹性伸缩最佳实践。

Kubernetes RBAC 权限管理实践

·1069 字·6 分钟
从 RBAC 核心概念到生产级多租户权限设计,涵盖 ServiceAccount 最小权限、kubectl auth can-i 排查和命名空间隔离实践。