Chapter 05

Service 与网络模型

理解 K8s 扁平网络模型、四种 Service 类型的使用场景、Ingress 控制器的流量入口配置。

Kubernetes 网络模型

K8s 要求网络实现满足以下约束(即"扁平网络"模型):

K8s 网络层次

┌──────────────────────────────────────────────────────────┐
  外部用户 / 互联网                                          
└──────────────────┬───────────────────────────────────────┘
                   
┌──────────────────▼───────────────────────────────────────┐
  Ingress  (L7 HTTP 路由,基于域名/路径分发)                 
└──────────────────┬───────────────────────────────────────┘
                   
┌──────────────────▼───────────────────────────────────────┐
  Service  (L4 TCP/UDP 负载均衡,稳定虚拟 IP)               
└────────┬─────────────────────────┬────────────────────────┘
                                  
┌────────▼────────┐       ┌────────▼────────┐
    Pod                     Pod          
  10.244.1.5              10.244.2.8     
└─────────────────┘       └─────────────────┘
  Node 192.168.1.10          Node 192.168.1.11
CNI
Container Network Interface。K8s 网络插件标准接口,实现 Pod 网络的第三方插件,如 Calico(支持网络策略)、Flannel(简单易用)、Cilium(eBPF 高性能)。
Pod CIDR
分配给 Pod IP 地址的子网段,如 10.244.0.0/16。每个 Node 分配一个子网(如 10.244.1.0/24),Node 上的 Pod 从中获取 IP。
Service CIDR
分配给 Service ClusterIP 的虚拟 IP 段,如 10.96.0.0/12。Service IP 是虚拟地址,不对应任何真实网卡,由 kube-proxy 通过 iptables/ipvs 实现。

四种 Service 类型

ClusterIP(默认)

在集群内分配一个虚拟 IP,只能在集群内访问。适合服务间内部通信。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP              # 默认类型,可省略
  selector:                   # 通过标签选择后端 Pod
    app: my-app
  ports:
  - name: http
    port: 80                   # Service 暴露的端口
    targetPort: 8080           # 转发到 Pod 的端口
    protocol: TCP
# 集群内访问:http://my-service.default.svc.cluster.local
# 简写(同 namespace):http://my-service

NodePort

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

spec:
  type: NodePort
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080            # 指定端口(可不填,随机分配 30000-32767)
# 外部访问:http://<任意节点IP>:30080

LoadBalancer

请求云厂商创建外部负载均衡器。是云上暴露服务的标准方式(每个 Service 会分配一个独立 LB,成本较高)。

spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
# 云厂商会分配公网 IP,存入 status.loadBalancer.ingress[0].ip

Headless Service

设置 clusterIP: None,不分配虚拟 IP。DNS 直接返回所有后端 Pod 的 IP。StatefulSet 专用,用于 Pod 直接寻址。

spec:
  clusterIP: None               # Headless:不分配 ClusterIP
  selector:
    app: mysql
  ports:
  - port: 3306
# DNS 查询 mysql.default.svc.cluster.local
# 返回:10.244.1.5, 10.244.2.8, 10.244.3.3 (所有 Pod IP)
# 单个 Pod 寻址:mysql-0.mysql.default.svc.cluster.local

Endpoints 机制

Service 通过 Endpoints 对象维护后端 Pod IP 列表。当 Pod 就绪状态变化时,Endpoint Controller 自动更新 Endpoints。

# 查看 Service 的 Endpoints
kubectl get endpoints my-service

# 输出示例:
# NAME         ENDPOINTS                                   AGE
# my-service   10.244.1.5:8080,10.244.2.8:8080,10.244.3.3:8080   5m
为什么 Pod 未通过 Readiness 时会从 Endpoints 移除

Readiness Probe 失败时,EndpointSlice Controller 将该 Pod IP 从 Service 的 Endpoints 中移除,kube-proxy 不再向其转发流量。这正是 readinessProbe 与 livenessProbe 的核心区别:前者控制流量,后者控制重启。

kube-proxy 工作模式

iptables 模式
默认模式。kube-proxy 将 Service/Endpoints 信息转换为 iptables 规则链。访问 ClusterIP 时,iptables 随机选择一个后端 Pod IP 进行 DNAT 转发。规则数量随 Service 增加而线性增长,大集群性能下降。
ipvs 模式
使用 Linux IPVS(IP Virtual Server)内核模块,基于哈希表实现,O(1) 查找复杂度。支持多种负载均衡算法(rr/lc/dh/sh/sed/nq)。大规模集群(1000+ Service)推荐使用。
eBPF 模式
Cilium CNI 提供的模式,完全绕过 kube-proxy,在内核层面通过 eBPF 程序直接处理网络包,性能最高,延迟最低。

Ingress 控制器

Service 提供 L4 负载均衡,而 Ingress 提供 L7 HTTP(S) 路由。一个 Ingress 资源可以根据域名和路径将流量路由到不同的 Service,共用一个 LoadBalancer,大幅降低成本。

Ingress 流量路由

  外部请求
     
     
┌─────────────────────────────────┐
  Ingress Controller              
  (如 ingress-nginx)              
└────────┬──────────┬──────────────┘
                   
  api.example.com  app.example.com/v1
                   
┌──────────────┐  ┌──────────────┐
 api-service     app-service  
└──────────────┘  └──────────────┘
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /          # 路径重写
    nginx.ingress.kubernetes.io/ssl-redirect: "true"       # 强制 HTTPS
    nginx.ingress.kubernetes.io/rate-limit: "100"          # 限流
spec:
  ingressClassName: nginx        # 指定使用哪个 Ingress Controller
  tls:                            # HTTPS 配置
  - hosts:
    - api.example.com
    secretName: api-tls-secret   # TLS 证书 Secret
  rules:
  - host: api.example.com         # 基于域名路由
    http:
      paths:
      - path: /v1
        pathType: Prefix          # Prefix/Exact/ImplementationSpecific
        backend:
          service:
            name: api-service-v1
            port:
              number: 80
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-service-v2
            port:
              number: 80

安装 ingress-nginx

# 使用 Helm 安装 ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace

# 查看 Ingress Controller 获得的外部 IP
kubectl get svc -n ingress-nginx ingress-nginx-controller
Ingress vs Gateway API

K8s 1.24+ 引入了 Gateway API 作为 Ingress 的下一代替代品,提供更强大的路由能力(流量权重、Header 修改、TLS 终止等)。目前两者并存,新项目可考虑直接使用 Gateway API(需要支持的 Ingress Controller,如 Contour、Envoy Gateway)。

NetworkPolicy 网络策略

默认情况下 K8s 中所有 Pod 可以互相通信。NetworkPolicy 允许定义基于 Pod/namespace 标签的流量访问控制规则(需要 CNI 支持,如 Calico/Cilium)。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-to-db-only
  namespace: production
spec:
  podSelector:                   # 规则应用到哪些 Pod
    matchLabels:
      role: database
  policyTypes:
  - Ingress                       # 控制入站流量
  ingress:
  - from:
    - podSelector:               # 只允许 role=api 的 Pod 访问
        matchLabels:
          role: api
    ports:
    - protocol: TCP
      port: 5432