TT Lab
开始
学习 学习路径 课程

Istio 实测实验室

开两扇门,就要站两个守卫

在 TT Lab 中继续学习

目标

在真实的 Istio 网格中,用 Istio Gateway 和 Gateway API 两种方式打开入口(ingress),并通过响应和 Envoy 配置,确认 TLS 终止、权重分配和网关授权策略实际作用在哪里。

为什么重要

入口是网格的第一道关卡,所以在这里出错,外面马上就能看到。而在 Istio 与 Gateway API 并用的今天,同一个服务很容易出现两个入口,这两个入口是不同的 Pod,证书和策略也各自独立。仅凭清单“已应用”看不出这种差异,所以要用真实请求和网关 Envoy 收到的配置来确认。

步骤

  1. 用 kubectl apply -f /opt/fixtures/istlab/ingress-app.yaml 在命名空间 mall 中部署 web-v1 和 web-v2,等待两个 Deployment 就绪。然后在 /root/istlab-ingress/01-gateway.txt 中写入入口网关的 ingress_cluster_ip=(服务 istio-system/istio-ingressgateway 的 ClusterIP)和 ingress_pod=(该网关 Pod 的名称)两行。
  2. 在 /root/istlab-ingress/gateway.yaml 中写入两个资源并应用——命名空间 mall 中的 Istio Gateway mall-gw(选择器 istio: ingressgateway,端口 80 HTTP,主机 mall.example.com)和 VirtualService mall-ingress(主机 mall.example.com,网关 mall-gw,目标 web-v1.mall.svc.cluster.local 端口 80)。应用后,用 Host: mall.example.com 访问网关的 ClusterIP,应返回 v1。
  3. 用 openssl 生成 CN=mall.example.com、SAN 为 DNS:mall.example.com 的自签名证书和密钥,保存为 /root/istlab-ingress/tls/mall.crt 和 /root/istlab-ingress/tls/mall.key,并以 TLS Secret mall-cred 的形式放入 istio-system。然后在 /root/istlab-ingress/gateway.yaml 的 Gateway 中加入端口 443 的 HTTPS 服务器(主机 mall.example.com,tls.mode: SIMPLE,credentialName: mall-cred),重新应用。curl --cacert /root/istlab-ingress/tls/mall.crt --resolve mall.example.com:443:<ClusterIP> https://mall.example.com/ 应返回 v1。
  4. 在 /root/istlab-ingress/gwapi.yaml 中写入两个资源并应用——命名空间 mall 中的 ConfigMap mall-api-options(data.service 为 spec: {type: ClusterIP})和 Gateway API 的 Gateway mall-api(gatewayClassName: istio,通过 infrastructure.parametersRef 引用该 ConfigMap,监听器 http 端口 80 HTTP,主机名 api.mall.example.com,只允许同一命名空间的路由)。等待 Gateway 变为 Programmed。
  5. 在 /root/istlab-ingress/httproute.yaml 中写入并应用 HTTPRoute api-split——父资源为 Gateway mall-api,主机名 api.mall.example.com,两条规则:请求头 x-canary: yes 时转到 web-v2(端口 80),其余请求按 web-v1 80、web-v2 20 的权重分配。向 mall-api-istio 服务的 ClusterIP 发送 x-canary: yes 请求,应始终返回 v2。
  6. 在 /root/istlab-ingress/06-compare.txt 中写入四行——Istio Gateway 所选网关 Pod 的命名空间 istio_gateway_ns=,Gateway API 所创建网关 Pod 的命名空间 gwapi_gateway_ns=,其 Deployment 名称 gwapi_deployment=,其 Service 的类型 gwapi_service_type=。
  7. 在 /root/istlab-ingress/deny-admin.yaml 中写入并应用 istio-system 的 AuthorizationPolicy deny-admin——选择器 istio: ingressgateway,action: DENY,路径 /admin 和 /admin/*。应用后,在 /root/istlab-ingress/07-scope.txt 中写入 istio_gateway_admin=(mall.example.com/admin 的状态码)和 gwapi_admin=(通过 Gateway API 网关访问 api.mall.example.com/admin 的状态码)两行。
  8. 在 /root/istlab-ingress/08-report.md 中写入五行——tls_fingerprint_match=(网关在 443 上出示的证书的 SHA-256 指纹与 /root/istlab-ingress/tls/mall.crt 相同则为 yes)、gwapi_namespace=、weights=(v1/v2 权重,例如 50/50)、admin_via_istio_gateway=、admin_via_gateway_api=——并在其下写出学到的内容,至少四行。

参考

在网格中部署两套应用并找到守门人

用 kubectl apply -f /opt/fixtures/istlab/ingress-app.yaml 在命名空间 mall 中部署 web-v1 和 web-v2,等待两个 Deployment 就绪。然后在 /root/istlab-ingress/01-gateway.txt 中写入入口网关的 ingress_cluster_ip=(服务 istio-system/istio-ingressgateway 的 ClusterIP)和 ingress_pod=(该网关 Pod 的名称)两行。

default profile 会在 istiod 之外预先建好 istio-ingressgateway。这个网关是独立运行的 Envoy,而不是 sidecar,现在它没有收到任何配置,所以对任何请求都返回 404。服务类型是 LoadBalancer,k3s 会给它挂上节点地址,但实验中用不会变动的 ClusterIP 来访问。Pod 名称用 -l istio=ingressgateway 来选取。

用 Istio Gateway 和 VirtualService 打开入口

在 /root/istlab-ingress/gateway.yaml 中写入两个资源并应用——命名空间 mall 中的 Istio Gateway mall-gw(选择器 istio: ingressgateway,端口 80 HTTP,主机 mall.example.com)和 VirtualService mall-ingress(主机 mall.example.com,网关 mall-gw,目标 web-v1.mall.svc.cluster.local 端口 80)。应用后,用 Host: mall.example.com 访问网关的 ClusterIP,应返回 v1。

Istio 的 Gateway 只是用选择器选中已经在运行的网关 Pod,告诉它“用这个端口、这个主机来接收”,至于往哪里转发,它并不知道。这由在 gateways 中写了该 Gateway 名称的 VirtualService 来决定。两者缺一就是 404。生效需要几秒钟,请重复尝试直到响应发生变化。Kubernetes 中有两个名为 gateway 的资源(Istio 的和 Gateway API 的),所以查询时像 kubectl get gateways.networking.istio.io 那样连 API 组一起写会更稳妥。

在网关上终止 TLS

用 openssl 生成 CN=mall.example.com、SAN 为 DNS:mall.example.com 的自签名证书和密钥,保存为 /root/istlab-ingress/tls/mall.crt 和 /root/istlab-ingress/tls/mall.key,并以 TLS Secret mall-cred 的形式放入 istio-system。然后在 /root/istlab-ingress/gateway.yaml 的 Gateway 中加入端口 443 的 HTTPS 服务器(主机 mall.example.com,tls.mode: SIMPLE,credentialName: mall-cred),重新应用。curl --cacert /root/istlab-ingress/tls/mall.crt --resolve mall.example.com:443:<ClusterIP> https://mall.example.com/ 应返回 v1。

网关不会挂载证书文件。credentialName 指向的 Secret 由 istiod 通过 SDS 下发给网关 Envoy,修改 Secret 后无需重启网关就会使用新证书。所以 Secret 要放在与网关 Pod 相同的命名空间(istio-system)中。curl --resolve 不依赖 DNS,把名称绑定到地址上,让你同时确认 SNI 和证书验证。网关收到了什么,用 istioctl proxy-config secret -n istio-system deploy/istio-ingressgateway 查看。

Gateway API 会新建网关

在 /root/istlab-ingress/gwapi.yaml 中写入两个资源并应用——命名空间 mall 中的 ConfigMap mall-api-options(data.service 为 spec: {type: ClusterIP})和 Gateway API 的 Gateway mall-api(gatewayClassName: istio,通过 infrastructure.parametersRef 引用该 ConfigMap,监听器 http 端口 80 HTTP,主机名 api.mall.example.com,只允许同一命名空间的路由)。等待 Gateway 变为 Programmed。

与 Istio 的 Gateway 不同,Gateway API 的 Gateway 会自己创建网关的 Deployment 和 Service,名称为 <Gateway 이름>-istio(占位符为 Gateway 名称)。默认服务类型是 LoadBalancer,而这台 VM 的 k3s 已经把 80 端口让给了入口网关,所以新的 LoadBalancer 拿不到地址,一直停在 <pending>——这样 Gateway 就不会变成 Programmed(实测)。请按官方文档的方法,通过 infrastructure.parametersRef 指向 ConfigMap 来修改服务类型。状态在 kubectl -n mall get gateways.gateway.networking.k8s.io mall-api -o yaml 的 conditions 中。

用 HTTPRoute 按请求头和权重分流

在 /root/istlab-ingress/httproute.yaml 中写入并应用 HTTPRoute api-split——父资源为 Gateway mall-api,主机名 api.mall.example.com,两条规则:请求头 x-canary: yes 时转到 web-v2(端口 80),其余请求按 web-v1 80、web-v2 20 的权重分配。向 mall-api-istio 服务的 ClusterIP 发送 x-canary: yes 请求,应始终返回 v2。

HTTPRoute 通过 parentRefs 自己声明要挂到哪个 Gateway 上(与 Istio 的 VirtualService 通过 gateways 声明的方向相同)。更具体的规则(请求头条件)优先。权重靠几次请求很难确认,所以请在网关 Envoy 实际收到的路由表——istioctl proxy-config routes deploy/mall-api-istio -n mall -o json 的 weightedClusters——中确认。

两个网关各在什么位置

在 /root/istlab-ingress/06-compare.txt 中写入四行——Istio Gateway 所选网关 Pod 的命名空间 istio_gateway_ns=,Gateway API 所创建网关 Pod 的命名空间 gwapi_gateway_ns=,其 Deployment 名称 gwapi_deployment=,其 Service 的类型 gwapi_service_type=。

Istio Gateway 是选用已有的网关,Gateway API 则是在 Gateway 所在的命名空间中建立网关。所以各团队可以在自己的命名空间里设置网关,并单独扩缩。作为代价,网关增多,资源也随之增加。创建出来的对象会带上标签 gateway.networking.k8s.io/gateway-name=mall-api。

在入口前拦住 /admin——以及拦不住的那个入口

在 /root/istlab-ingress/deny-admin.yaml 中写入并应用 istio-system 的 AuthorizationPolicy deny-admin——选择器 istio: ingressgateway,action: DENY,路径 /admin 和 /admin/*。应用后,在 /root/istlab-ingress/07-scope.txt 中写入 istio_gateway_admin=(mall.example.com/admin 的状态码)和 gwapi_admin=(通过 Gateway API 网关访问 api.mall.example.com/admin 的状态码)两行。

网关也是 Envoy 工作负载,所以可以用选择器挂上授权策略,在网关上拦下,请求就不会进入网格内部。但选择器选中的只有 istio-ingressgateway Pod。Gateway API 建立的网关是另一个 Pod,这条策略对它不起作用——这意味着第二个入口一打开,第一个入口的守卫就不再守着这个入口。请把这个事实用数字记录下来。

写下两个入口时要确认什么

在 /root/istlab-ingress/08-report.md 中写入五行——tls_fingerprint_match=(网关在 443 上出示的证书的 SHA-256 指纹与 /root/istlab-ingress/tls/mall.crt 相同则为 yes)、gwapi_namespace=、weights=(v1/v2 权重,例如 50/50)、admin_via_istio_gateway=、admin_via_gateway_api=——并在其下写出学到的内容,至少四行。

指纹只要把 openssl s_client -connect <ClusterIP>:443 -servername mall.example.com 得到的证书和文件,用 openssl x509 -noout -fingerprint -sha256 并排比较即可。其余的从第 6、7 步的记录中抄过来。