Kubernetes 网络模型
K8s 要求网络实现满足以下约束(即"扁平网络"模型):
- 每个 Pod 都有唯一的 IP 地址
- 所有 Pod 无需 NAT 就能互相通信(跨节点也行)
- 节点与 Pod 无需 NAT 就能互相通信
- Pod 看到自己 IP 和别人看到它的 IP 是同一个
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
四种 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
Readiness Probe 失败时,EndpointSlice Controller 将该 Pod IP 从 Service 的 Endpoints 中移除,kube-proxy 不再向其转发流量。这正是 readinessProbe 与 livenessProbe 的核心区别:前者控制流量,后者控制重启。
kube-proxy 工作模式
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
K8s 1.24+ 引入了 Gateway API 作为 Ingress 的下一代替代品,提供更强大的路由能力(流量权重、Header 修改、TLS 终止等)。Ingress 资源本身不会被移除,但新功能不会再添加到 Ingress API。新项目推荐直接使用 Gateway API。
Gateway API v1.2(K8s 1.32 GA)
Gateway API 是 K8s 1.32 正式稳定(GA)的新一代入站流量 API,由 SIG Network 主导开发。相比 Ingress,它实现了职责分离(基础设施团队管理 Gateway,应用团队管理 Route),并原生支持更丰富的路由语义。
Gateway API 三层资源的职责分工:
Gateway API 职责分层 ┌──────────────────────────────────────────────────┐ │ 基础设施提供商 (Cloud / CNI 厂商) │ │ GatewayClass: envoy-gateway │ └───────────────────────┬──────────────────────────┘ │ 引用 ┌───────────────────────▼──────────────────────────┐ │ 集群运维团队 │ │ Gateway: prod-gateway (80/443 监听) │ └───────────┬──────────────────────┬───────────────┘ │ 附着 │ 附着 ┌───────────▼──────────┐ ┌──────▼─────────────────┐ │ 应用团队 A │ │ 应用团队 B │ │ HTTPRoute: /api/* │ │ HTTPRoute: /shop/* │ └──────────────────────┘ └────────────────────────┘
完整的 Gateway API 示例(包含金丝雀发布):
# 1. Gateway:由运维团队创建,监听 80 端口
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: prod-gateway
namespace: infra
spec:
gatewayClassName: envoy-gateway # 引用 GatewayClass
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All # 允许所有 namespace 的 Route 附着
---
# 2. HTTPRoute:由应用团队创建,实现金丝雀发布(90% 稳定版 + 10% 新版)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app-route
namespace: production
spec:
parentRefs: # 附着到哪个 Gateway
- name: prod-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs: # 按权重分流(金丝雀发布)
- name: my-app-stable
port: 80
weight: 90 # 90% 流量到稳定版
- name: my-app-canary
port: 80
weight: 10 # 10% 流量到金丝雀版
- matches: # Header 匹配路由(内测用户)
- headers:
- name: X-Beta-User
value: "true"
backendRefs:
- name: my-app-canary
port: 80
weight: 100 # Header 匹配到的全部走金丝雀
---
# 3. GRPCRoute:gRPC 服务路由(K8s 1.32 GA)
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-route
spec:
parentRefs:
- name: prod-gateway
namespace: infra
rules:
- matches:
- method:
service: com.example.UserService # gRPC 服务名
method: GetUser # gRPC 方法名(可省略匹配全部方法)
backendRefs:
- name: user-service
port: 50051
# 安装 Envoy Gateway(Gateway API 实现之一)
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.2.0 \
-n envoy-gateway-system \
--create-namespace
# 查看 GatewayClass 是否就绪
kubectl get gatewayclass
# 查看 Gateway 状态(观察是否分配到外部 IP)
kubectl get gateway -n infra
# 查看 HTTPRoute 是否成功附着到 Gateway
kubectl get httproute -n production
Gateway API 资源(GatewayClass/Gateway/HTTPRoute)仅定义 API schema,本身不包含任何网络实现。必须先安装一个支持 Gateway API 的 Controller(如 Envoy Gateway、Nginx Gateway Fabric、Contour、Traefik v3)才能生效。安装了 ingress-nginx 不代表支持 Gateway API。
kube-proxy nftables 后端(K8s 1.32 Beta)
kube-proxy 负责在每个节点上维护 Service 的网络规则,将访问 ClusterIP/NodePort 的流量转发到实际的 Pod。K8s 1.32 将 nftables 后端升级为 Beta,作为传统 iptables 后端的现代替代方案。
# 在 kubeadm 集群中启用 nftables 后端
# 修改 kube-proxy ConfigMap
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "nftables" # 切换为 nftables 模式
nftables:
masqueradeAll: false
masqueradeBit: 14
# 查看当前 kube-proxy 模式
kubectl get configmap kube-proxy -n kube-system -o jsonpath='{.data.config\.conf}'
# 验证 nftables 规则是否生效(在节点上执行)
nft list ruleset | grep kubernetes
# 对比:查看 iptables 模式的规则数量
iptables-save | wc -l # 大集群可能有数万条规则
nftables 后端要求 Linux 内核 5.13+(部分特性需要 5.4+)。在生产环境切换前,需确认所有节点的内核版本满足要求(uname -r)。Ubuntu 20.04 默认内核为 5.4,建议升级到 22.04(内核 5.15+)再启用 nftables 模式。
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
Service 是 K8s 中稳定的网络端点,通过 Label Selector 关联 Pod,解决 Pod IP 动态变化的问题。四种 Service 类型满足不同场景:ClusterIP(集群内通信)、NodePort(节点端口暴露)、LoadBalancer(云端负载均衡器)、ExternalName(CNAME 别名)。Ingress 提供七层路由能力,Gateway API(K8s 1.32 GA)是其现代替代品,通过 GatewayClass/Gateway/HTTPRoute 三层职责分离实现更强大的路由控制和原生金丝雀发布。kube-proxy nftables 模式(K8s 1.32 Beta)在大规模集群中有显著性能优势。NetworkPolicy 通过白名单机制实现 Pod 间的网络隔离,需要 CNI 插件支持。