两扇门就要两个守卫
一句话总结
进入网格的入口(入口网关,ingress gateway)是单独运行的 Envoy,而不是 sidecar。在 Istio 中打开这个入口有两种方法——选中一个已经在运行的网关并在其上叠加配置的 Istio Gateway,以及用一个 Gateway 资源新建网关的 Kubernetes Gateway API。
为什么需要它
sidecar 处理的是 Pod 内部出入的流量。来自集群外用户的请求,必须在一个不属于任何 Pod 的 sidecar 的地方接收,并在那里解开 TLS、按主机名分流、拦住可疑的路径。Kubernetes 的 Ingress 资源要做这些事,表达能力不足——请求头条件、权重、TLS 方式,全都是各控制器各自不同的注解。
Istio 最初用自己的 API(Gateway + VirtualService)解决了这个问题,Kubernetes 汇集了这些经验,做出了厂商中立的 Gateway API。Istio 文档表示,今后会把 Gateway API 作为默认的流量管理 API。所以现在两种方式并行使用,同一个网格里出现两个入口是常有的事。
工作原理
Istio Gateway——选定网关,并规定要接收什么。
Gateway mall-gw selector: istio=ingressgateway ← 이미 떠 있는 파드를 고른다
servers: 80 HTTP mall.example.com
443 HTTPS mall.example.com credentialName: mall-cred
VirtualService mall-ingress gateways: [mall-gw] ← 어디로 보낼지는 여기서
只有 Gateway 的话,网关虽然会接收,但不知道往哪里转发,会返回 404。VirtualService 要在 gateways 中写上那个名称,两者才会连接起来。
TLS 通过 SDS 获取。credentialName 不是文件路径,而是 Secret 的名称。istiod 读取该 Secret,并通过 SDS 下发给网关 Envoy,所以更换证书时不需要重启网关。作为代价,Secret 必须与网关 Pod 位于同一个命名空间。
Gateway API——由 Gateway 来建立网关。
| Istio Gateway | Gateway API Gateway | |
|---|---|---|
| 网关 Pod | 用选择器选取预先安装好的网关 | 为每个 Gateway 创建 <이름>-istio(占位符为名称)的 Deployment 和 Service |
| 所在位置 | 通常是 istio-system | Gateway 所在的命名空间 |
| 路由 | VirtualService(gateways:) |
HTTPRoute(parentRefs:) |
| 细节调整 | IstioOperator、Helm 值 | infrastructure.parametersRef 指向的 ConfigMap |
自己建立网关,意味着各团队可以单独扩缩自己的入口。相应地 Pod 会增多,而且最重要的是每个入口都要单独设置策略。
授权策略同样挂在工作负载上。网关是 Envoy 工作负载,所以可以用选择器给它挂上 AuthorizationPolicy,在那里被拦下的请求不会进入网格内部。但如果选择器是 istio: ingressgateway,就不会作用于 Gateway API 新建的网关。
在现场相遇的样子
“Gateway 已创建,但 Programmed 没有成立”——自动部署出来的服务拿不到 LoadBalancer 地址时就会这样。在云上,常见原因是负载均衡器配额;在本地部署中,常见原因是没有 LB 实现。在这台 VM 的 k3s 上,80 端口已被另一个网关占用,新服务一直停在 <pending>。
“证书换了,但看到的还是旧证书”——Secret 创建在了别的命名空间,或者名称与 credentialName 不一致。用 istioctl proxy-config secret 查看网关实际收到的证书,马上就能分清。
“管理路径明明拦住了,从新网关却进得来”——新开了入口,却没有把守卫也一并布置过去。这就是添加网关时要一并检查授权策略的选择器(或 targetRefs)的原因。
官方文档:Ingress Gateways、Secure Gateways、Kubernetes Gateway API、Authorization on ingress gateways
下一项实验要做什么
在 VM 内真实的网格(k3s + Istio 1.31.0)中部署两套应用,用 Istio Gateway 打开 HTTP 和 HTTPS 入口,再用 Gateway API 建立第二个入口,按请求头和权重做分流。比较两个网关各自所处的位置,并用状态码确认:加在第一个入口上的 /admin 拦截,并不会作用于第二个入口。