K8s Service 与 Istio 流量管理
| 技术Kubernetes (K8s) 的 Service 是修路并挂牌子,而 Istio 的路由则是专业的交警和智能导航。虽然它们最终都能帮你找到对应的 Pod,但处理流量的方式完全不在一个维度。
形象类比:去医院看病
我们可以把后端服务想象成医院里的"诊室"。
K8s Service:医院的导视牌
K8s Service 就像是医院大厅里的固定导视牌。
- 功能:牌子上写着"内科在 3 楼"。它给一组 Pod 提供了一个固定的名字(域名)和 VIP(虚拟 IP)
- 局限性:导视牌是死板的。只要内科开门,它就让你往 3 楼走。它不管 3 楼现在是不是人满为患,也不管你是普通病号还是急诊 VIP,它只负责最基础的指路(轮询转发)
Istio 路由:智能导诊系统 + 专职护士
Istio 在导视牌的基础上,派了一个"专职护士"(Sidecar 代理)跟着每一位病人。
- 功能:护士会根据你的病历信息(HTTP Header)、你的身份(Cookie)或者当前的科室忙碌程度(负载情况),决定带你去哪个诊室
超能力:
- 灰度发布:让 10% 的病人去试用"新诊室",90% 留在"旧诊室"
- 熔断:如果 3 楼内科突然停电了,护士会直接告诉你"别去了",而不是让你白跑一趟
- 安全:护士会检查每个科室之间的通行证(mTLS 加密)
技术层面的核心区别
| 特性 | K8s Service (默认) | Istio 路由 (VirtualService) |
|---|---|---|
| 工作层次 | L4 (传输层),基于 IP 和端口 | L7 (应用层),能看懂 HTTP 路径、Header、Cookie |
| 负载均衡策略 | 单一,随机或轮询 | 丰富,最少连接、一致性哈希、按权重分配 |
| 流量切分 | 很难做,只能控制 Pod 数量比例 | 简单,精确到 5% 的流量去 v2 版本 |
| 弹性能力 | 基本没有,Pod 挂了就重试,易引发雪崩 | 强悍,超时控制、重试策略、故障注入、熔断 |
| 可见性 | 只知道流量进来了 | 知道每秒多少请求、延迟多少、成功率多少 |
为什么有了 K8s 还需要 Istio?
K8s 的 Service 解决了**“怎么找到你”**的问题,是基础设施。
但当你面临以下场景时,K8s 原生的 Service 就显得力不从心:
- 金丝雀发布:我只想让北京地区的老用户先体验新版 App
- 故障模拟:我想人为制造 1s 的延迟,看看系统会不会崩溃
- 全链路加密:我不希望服务之间的通信被明文抓包
总结:K8s Service 负责连通性,让服务"能通";Istio 负责治理,让流量"听话"。
网络模型的差异:L4 vs L7
这是两者最根本的技术分界点:
K8s Service (L4 - 传输层)
它主要通过 iptables 或 IPVS 运行在操作系统的内核态。它只认识 IP 地址、协议(TCP/UDP)和端口号。
- 局限:无法判断一个 HTTP 请求的路径是
/api/v1还是/api/v2,也无法识别 Header 中的 User-Agent
Istio (L7 - 应用层)
它通过在 Pod 旁边部署 Envoy 代理(Sidecar),拦截所有的网络流量。Envoy 运行在用户态,能够解析 HTTP、gRPC、TLS 等应用层协议。
- 优势:可以根据 URL 路径、Cookie、Header(如
user-type: gold)、甚至方法(GET/POST)来做路由决策
流量切分的实现方式
K8s Service:基于 Label Selector 的"全量匹配"
K8s Service 通过标签(Label)关联 Pod。如果你想做灰度发布:
- 必须同时运行两个版本的 Pod,并让它们拥有相同的 Label
- 流量分配比例取决于 Pod 的数量比例
- 这种方式极度不灵活,且无法针对特定用户进行切分
Istio:基于 VirtualService 的"逻辑定义"
Istio 将"服务定义"与"流量规则"解耦:
- 所有版本的 Pod 都可以属于同一个 K8s Service
- 通过 VirtualService 定义权重,例如:
weight: 99指向 v1,weight: 1指向 v2 - 这种切分是精确的、逻辑上的,与 Pod 的物理数量无关
K8s 原生不支持 L7 的原因
这是一个深刻的设计哲学问题。K8s 之所以在默认的 Service 实现中不直接支持 L7,主要基于以下考量:
1. 核心设计的"原子性"与"通用性"
K8s 的设计理念是提供最小化、最通用的基础设施原子能力。
- 网络中立性:K8s 旨在支持任何类型的应用,不仅仅是 HTTP。如果默认 Service 强制包含 L7 逻辑,对于运行数据库(MySQL/Redis)、消息队列(Kafka)或自定义 TCP/UDP 协议的用户来说,这些逻辑就是多余的负担
- 简单性原则:L4 只处理 IP 和端口,逻辑极其简单稳定。这符合 Kubernetes “核心组件保持简单,复杂逻辑交给插件"的 Unix 哲学
2. 内核态性能 vs 用户态灵活性
K8s Service 的底层实现经历了从 Userspace 到 Iptables 再到 IPVS 的演进:
- Iptables/IPVS 的优势:它们运行在系统的内核态,处理数据包时不需要在内核和用户空间之间来回拷贝数据,转发效率极高,延迟极低
- L7 的代价:要实现 L7 路由,必须将数据包解包并读入用户态内存,这种上下文切换会消耗大量 CPU 资源,并显著增加网络延迟
3. 动态变化的复杂性
L7 路由规则极其复杂且变化频繁。如果 Service 支持 L7,那么 kube-proxy 的配置项将呈爆炸式增长,整个集群的网络响应速度会变慢。
4. 责任分离
K8s 社区通过引入其他抽象对象解决 L7 需求:
- Ingress:K8s 为 L7 流量设计的"大门”,允许定义域名、路径
- Service Mesh(Istio/Linkerd):通过 Sidecar 代理实现极其精细的 L7 治理
总结
- K8s Service:基础设施层提供的服务发现机制,解决"这个名字对应哪些 IP"的问题
- Istio 路由:在服务发现之上构建的流量管理层,解决"在复杂的逻辑下,这封报文到底该给谁"的问题
K8s Service 默认不支持 L7,是为了在极致性能和协议通用性之间取得平衡:
- Service 负责基础的、高性能的"连通性"(L4)
- Ingress / Service Mesh 负责复杂的、业务感知的"策略控制"(L7)