K8s Service 核心原理
📖 K8s 网络模型(4 层通信契约)
K8s 官方规定集群四层流量模型,Pod 独占集群唯一 IP,同集群所有 Pod 直连互通、无 NAT:
1. Pod 内部通信(容器 ↔ 同 Pod 容器)
同一 Pod 所有容器共享同一个 network namespace,通过 localhost/127.0.0.1 本地通信,无需走网卡/网桥。Pause 容器占位网络栈,业务容器共用 pause 的 net ns。
2. Pod ↔ Pod 通信(CNI 实现)
| 场景 | 实现方式 |
|---|---|
| 同节点 | veth pair + Linux 网桥(cni0/bridge)二层转发 |
| 跨节点 | CNI 插件(Flannel/Calico/Cilium)完成跨主机路由/隧道转发,源 Pod IP 不变、不 NAT |
3. Pod ↔ Service 通信
kube-proxy 实现,三种代理模式:userspace(淘汰)、iptables(默认)、IPVS(生产首选)。
4. 集群外部 ↔ Service 通信
四种暴露方式:NodePort、LoadBalancer、Ingress、ExternalIPs。
Pod 访问外网:Pod 出网经过节点 SNAT,源 IP 转为宿主机 IP。
⚡ veth pair(容器网络基础)
veth pair 是成对出现的虚拟网卡设备,像一根网线两端各插一块网卡,一端进数据另一端立刻出数据。
在 K8s 中的作用
- 一端留在宿主机 netns(挂在网桥 cni0/主机路由)
- 另一端放进 Pod 的 netns,作为 Pod 网卡 eth0
- 实现 Pod 和宿主机互通
数据流:Pod eth0 → veth peer → 宿主机网桥/协议栈 → 跨节点/外网
🔧 Pod 端口命名
在 Pod 的 yaml 中给容器端口起名字,Service 的 targetPort 直接引用名字而非端口号:
## 📝 Pod 定义
ports:
- name: http
containerPort: 80
## 🔗 Service 引用
targetPort: http
优势:端口更改时(如 80→8080)只需改 Pod,Service 无需修改。
⚙️ 4 种 Service 类型底层原理
1️⃣ ClusterIP(默认,最基础)
- 作用:集群内部访问,外部无法访问
- 实现:kube-proxy 生成虚拟 IP(ClusterIP),内核规则拦截访问该 IP 的包,做 DNAT 转发到 Pod IP
- 数据流:
Pod → ClusterIP → DNAT → PodIP
2️⃣ NodePort(对外暴露最常用)
- 作用:让集群外部能访问服务
- 实现:自动创建 ClusterIP,在每个节点打开相同端口(30000-32768),节点端口收到流量 → 转发给 ClusterIP → 转发给 Pod
- 数据流:
浏览器 → 节点IP:NodePort → ClusterIP → Pod - 关键:本质是节点端口 → 转发到 ClusterIP
3️⃣ LoadBalancer(云厂商入口)
- 作用:给服务独立公网 IP
- 实现:自动创建 ClusterIP + NodePort,调用云厂商 API 创建负载均衡器,后端绑定所有节点的 NodePort
- 数据流:
公网用户 → 公网LB IP → 节点NodePort → ClusterIP → Pod - 限制:依赖云厂商,裸机 K8s 无法使用
4️⃣ ExternalName(特殊类型)
- 作用:把 K8s 内部域名映射到外部域名
- 实现:没有 ClusterIP,没有 kube-proxy 规则,仅在 CoreDNS 生成一条 CNAME 记录
- 数据流:
Pod 请求 service 域名 → CoreDNS 返回外部域名 → Pod 直接访问外部服务
包含关系
LoadBalancer 包含 NodePort → NodePort 包含 ClusterIP → ExternalName 独立
🚀 Service 负载均衡完整流程
- Service 是抽象资源 — 拥有固定 ClusterIP,但没有进程监听
- Endpoint / EndpointSlice — 通过标签选择器自动收集后端 Pod IP,形成可用后端列表
- kube-proxy 监听变化 — 一旦 Pod 增删,立刻更新节点内核规则
- 流量被内核规则拦截 — 拦截访问 ClusterIP 的包,负载均衡算法选择后端 Pod,执行 DNAT
- 返回流量原路返回(自动 SNAT 修正)
关键特性
| 特性 | 说明 |
|---|---|
| 转发方式 | DNAT(目标地址 ClusterIP → PodIP) |
| 层级 | 4 层转发(TCP/UDP),不解析 HTTP |
| 会话保持 | 默认无,需开启 sessionAffinity: ClientIP |
| 转发位置 | 节点内核态,非中心化转发 |
| 健康检查 | 依赖 Pod 的 readinessProbe,不可用 Pod 被移出 Endpoint |
🌐 K8s 服务发现
环境变量方式
Pod 启动时,集群把同命名空间已存在的 Service 信息注入容器环境变量:
SVC_NAME_SERVICE_HOST=ClusterIP
缺点:Service 晚于 Pod 创建则无变量、不能跨命名空间、不动态刷新,生产极少使用。
CoreDNS 域名解析(主流方案)
- 集群内置 CoreDNS,监听域名解析请求
- Service 域名规则:
- 同命名空间:
服务名 - 跨命名空间:
服务名.命名空间.svc.cluster.local
- 同命名空间:
- 流程:Pod 发起解析 → CoreDNS 查询 → 返回 ClusterIP → kube-proxy 转发到 Pod
Headless Service(
clusterIP: None):DNS 直接返回全部 Pod IP,不用 ClusterIP 与 kube-proxy,多用于有状态应用。
💾 Pod 访问外网完整链路
Pod 内部 → PodIP 发出报文(10.244.x.x:公网IP)
→ 通过 veth pair 到达宿主机
→ 宿主机路由匹配默认网关(0.0.0.0/0)
→ iptables SNAT(源 PodIP 替换为节点物理 IP)
→ 经过物理网卡 → 机房网关 → 公网
→ 回程报文原路返回(DNAT 还原为 PodIP)
关键点
- NodePort/LoadBalancer 入站 ≠ 出站:Pod 主动出外网是 SNAT;外网主动进集群是 DNAT
- hostNetwork: true 的 Pod 共享宿主机网络栈,直接使用节点 IP 出网,无额外 SNAT
🔒 静态 Pod
静态 Pod = kubelet 直接管理、不经过 APIServer、配置存在节点本地 yaml 的 Pod。
五大特点
- 管理权在 kubelet — 不经过 APIServer、scheduler、controller-manager
- 镜像 Pod(Mirror Pod) — kubelet 向 APIServer 注册只读镜像,
kubectl delete删不掉 - 固定节点 — 不能调度漂移
- 自愈 — 容器崩溃自动重启
- 限制 — 不能引用 ConfigMap/Secret/SA
典型场景
Master 控制平面组件全是静态 Pod:kube-apiserver、kube-controller-manager、kube-scheduler、etcd
管理方式
## 放 yaml 到 manifest 目录自动创建
cp pod.yaml /etc/kubernetes/manifests/
## 删掉 yaml 自动删除 pod
rm /etc/kubernetes/manifests/pod.yaml
kubelet 默认 20s 扫描一次目录,改文件自动更新 Pod。
📊 无头服务(Headless Service)
Headless Service 是将 .spec.clusterIP 设置为 "None" 的 Service,Kubernetes 不会为其分配 ClusterIP。
| 特性 | 普通 Service | Headless Service |
|---|---|---|
| ClusterIP | ✅ 分配虚拟 IP | ❌ 不分配(None) |
| 负载均衡 | ✅ kube-proxy DNAT | ❌ 无负载均衡 |
| DNS 解析 | 解析到 ClusterIP | 返回所有 Pod IP 列表 |
| 适用场景 | 普通服务发现 | StatefulSet、需要直连 Pod 的场景 |
典型场景:StatefulSet 使用 Headless Service,每个 Pod 通过固定 DNS 名称直连:
pod-0.svc-name.ns.svc.cluster.localpod-1.svc-name.ns.svc.cluster.local
🎯 流量策略
internalTrafficPolicy(内部流量策略)
控制集群内部流量如何路由到端点:
| 值 | 行为 | 适用场景 |
|---|---|---|
Cluster(默认) | 流量可路由到集群中任意节点的端点 | 默认行为 |
Local | 流量仅路由到本地节点的端点 | 减少跨节点转发,保留源 IP |
externalTrafficPolicy(外部流量策略)
控制外部流量如何路由到端点:
| 值 | 行为 | 源 IP |
|---|---|---|
Cluster(默认) | 流量可能被转发到其他节点 | 丢失源 IP(SNAT) |
Local | 流量仅路由到本地节点的端点 | 保留客户端源 IP |
trafficDistribution(流量分发)
特性状态:Kubernetes v1.33 [stable]
控制流量路由偏好,不同于流量策略的严格语义保证,流量分发表达偏好:
| 值 | 行为 |
|---|---|
PreferSameZone | 优先路由到同区域的端点 |
PreferSameNode | 优先路由到同节点的端点 |
📋 会话亲和性(Session Affinity)
确保来自同一客户端的连接总是转发到同一个 Pod。
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 默认 3 小时
适用场景:有状态应用(如 Web 会话)、需要本地缓存的场景。
🔄 应用协议(appProtocol)
特性状态:Kubernetes v1.20 [stable]
appProtocol 字段为每个 Service 端口设置应用协议:
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
appProtocol: kubernetes.io/h2c # HTTP/2 明文
常用取值:kubernetes.io/h2c(HTTP/2 明文)
关联文档
- K8s Service DNS 实现原理 — DNS 解析流程
- K8s Service 高级配置 — externalIPs、Endpoint 手动配置
- ../network/kube-proxy 流量转发与代理模式详解 — 流量转发底层机制
- K8s 核心概念#🌐 网络与通信 — Service 概念基础
关联文档
- ../core/K8s 核心概念 — 核心概念速查
- K8s Service DNS 实现原理 — CoreDNS 域名解析
- ../../network/网络 — 网络基础模型
- K8s 组件详解#kube-proxy — kube-proxy 组件详解
关联文档