Ingress Gateway

    本任务描述了如何配置 Istio,以使用 Istio Gateway 来将服务暴露至服务网格之外。

    • 遵照安装指南中的文档说明,安装 Istio。

    • 确定当前目录路径为 istio 目录。

    • 启动 样例程序。

      如果您启用了 Sidecar 自动注入,通过以下命令部署 httpbin 服务:

      否则,您必须在部署 httpbin 应用程序前进行手动注入,部署命令如下:

      Zip

      1. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@)
    • 根据下文描述,确定 Ingress IP 和端口。

    执行如下指令,确定您的 Kubernetes 集群是否运行在支持外部负载均衡的环境中:

    1. $ kubectl get svc istio-ingressgateway -n istio-system
    2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    3. istio-ingressgateway LoadBalancer 172.21.109.129 130.211.10.121 ... 17h

    如果 EXTERNAL-IP 值已设置,说明环境正在使用外部负载均衡,可以用其为 Ingress Gateway 提供服务。如果 EXTERNAL-IP 值为 <none> (或持续显示 <pending>),说明环境没有为 Ingress Gateway 提供外部负载均衡,无法使用 Ingress Gateway。在这种情况下,您可以使用服务的 访问网关。

    选择符合自身环境的指令执行:

    external load balancer node port

    若已确定您的环境使用了外部负载均衡器,执行如下指令。

    1. $ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    2. $ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
    3. $ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
    4. $ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')

    在特定的环境下,可能会使用主机名来暴露负载均衡器,而不是 IP 地址。此时,Ingress Gateway 的 EXTERNAL-IP 值将不再是 IP 地址,而是主机名。前文设置 INGRESS_HOST 环境变量的命令将执行失败。使用下面的命令更正 INGRESS_HOST 值:

    1. $ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

    若您的环境未使用外部负载均衡器,需要通过 Node Port 访问。执行如下命令。

    设置 Ingress 端口:

    1. $ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
    2. $ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
    3. $ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')

    基于集群供应商,设置 Ingress IP:

    1. GKE:

      1. $ export INGRESS_HOST=worker-node-address

      需要创建防火墙规则,允许 TCP 流量通过 ingressgateway 服务的端口。执行下面的命令,设置允许流量通过 HTTP 端口、HTTPS 安全端口,或均可:

    2. IBM Cloud Kubernetes Service:

      1. $ ibmcloud ks workers --cluster cluster-name-or-id
      2. $ export INGRESS_HOST=public-IP-of-one-of-the-worker-nodes
    3. Minikube:

      1. $ export INGRESS_HOST=$(minikube ip)
    4. Docker For Desktop:

      1. $ export INGRESS_HOST=127.0.0.1
    5. 其他环境(如:IBM Cloud Private 等):

      1. $ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

    使用 Istio Gateway 配置 Ingress

    Ingress 描述运行在网格边界的负载均衡器,负责接收传入的 HTTP/TCP 连接。其中配置了对外暴露的端口、协议等。但是,与Kubernetes Ingress 资源不同,Ingress Gateway 不包含任何流量路由配置。Ingress 流量的路由使用 Istio 路由规则来配置,和内部服务请求完全一样。

    让我们一起来看如何在 80 端口为 HTTP 流量配置一个 Gateway

    1. 创建 Istio Gateway

      1. $ kubectl apply -f - <<EOF
      2. apiVersion: networking.istio.io/v1alpha3
      3. kind: Gateway
      4. metadata:
      5. name: httpbin-gateway
      6. spec:
      7. istio: ingressgateway # use Istio default gateway implementation
      8. servers:
      9. - port:
      10. number: 80
      11. name: http
      12. protocol: HTTP
      13. - "httpbin.example.com"
      14. EOF
    2. 为通过 Gateway 的入口流量配置路由:

      1. $ kubectl apply -f - <<EOF
      2. apiVersion: networking.istio.io/v1alpha3
      3. kind: VirtualService
      4. metadata:
      5. name: httpbin
      6. spec:
      7. hosts:
      8. - "httpbin.example.com"
      9. gateways:
      10. - httpbin-gateway
      11. http:
      12. - match:
      13. - uri:
      14. prefix: /status
      15. - uri:
      16. prefix: /delay
      17. route:
      18. - destination:
      19. port:
      20. number: 8000
      21. host: httpbin
      22. EOF

      列表规约了哪些请求允许通 httpbin-gateway 网关。 所有其他外部请求均被拒绝并返回 404 响应。

      来自网格内部其他服务的内部请求无需遵循这些规则,而是默认遵守轮询调度路由规则。您可以为 gateways 列表添加特定的 mesh 值,将这些规则同时应用到内部请求。由于服务的内部主机名可能与外部主机名不一致(譬如:httpbin.default.svc.cluster.local),您需要同时将内部主机名添加到 hosts 列表中。 详情请参考操作指南

    3. 使用 curl 访问 httpbin 服务:

      注意上文命令使用 -H 标识将 HTTP 头部参数 Host 设置为 “httpbin.example.com”。该操作为必须操作,因为 Ingress Gateway 已被配置用来处理 “httpbin.example.com” 的服务请求,而在测试环境中并没有为该主机绑定 DNS,而是简单直接地向 Ingress IP 发送请求。

    4. 访问其他没有被显式暴露的 URL 时,将看到 HTTP 404 错误:

      1. $ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/headers"
      2. HTTP/1.1 404 Not Found
      3. ...

    在浏览器中输入 httpbin 服务的 URL 不能获得有效的响应,因为无法像 curl 那样,将请求头部参数 Host 传给浏览器。在现实场景中,这并不是问题,因为您需要合理配置被请求的主机及可解析的 DNS,从而在 URL 中使用主机的域名,譬如:https://httpbin.example.com/status/200

    为了在简单的测试和演示中绕过这个问题,请在 GatewayVirtualService 配置中使用通配符 *。譬如,修改 Ingress 配置如下:

    1. $ kubectl apply -f - <<EOF
    2. kind: Gateway
    3. metadata:
    4. spec:
    5. selector:
    6. istio: ingressgateway # use Istio default gateway implementation
    7. servers:
    8. - port:
    9. number: 80
    10. name: http
    11. protocol: HTTP
    12. hosts:
    13. - "*"
    14. ---
    15. apiVersion: networking.istio.io/v1alpha3
    16. kind: VirtualService
    17. metadata:
    18. name: httpbin
    19. spec:
    20. hosts:
    21. - "*"
    22. gateways:
    23. - httpbin-gateway
    24. http:
    25. - match:
    26. - uri:
    27. prefix: /headers
    28. route:
    29. - destination:
    30. port:
    31. number: 8000
    32. host: httpbin
    33. EOF

    此时,便可以在浏览器中输入包含 $INGRESS_HOST:$INGRESS_PORT 的 URL。譬如,输入http://$INGRESS_HOST:$INGRESS_PORT/headers,将显示浏览器发送的所有 Header 信息。

    理解原理

    Gateway 配置资源允许外部流量进入 Istio 服务网格,并对边界服务实施流量管理和 Istio 可用的策略特性。

    在前面的步骤中,在服务网格中创建一个服务并向外部流量暴露该服务的 HTTP 端点。

    1. 检查环境变量 INGRESS_HOST and INGRESS_PORT。确保环境变量的值有效,命令如下:

      1. $ kubectl get svc -n istio-system
      2. $ echo "INGRESS_HOST=$INGRESS_HOST, INGRESS_PORT=$INGRESS_PORT"
    2. 检查没有在相同的端口上定义其它 Istio Ingress Gateways:

      1. $ kubectl get gateway --all-namespaces
    3. 检查没有在相同的 IP 和端口上定义 Kubernetes Ingress 资源:

      1. $ kubectl get ingress --all-namespaces
    4. 如果使用了外部负载均衡器,该外部负载均衡器无法正常工作,尝试通过 Node Port 访问 Gateway

    清除

    Zip

    1. $ kubectl delete gateway httpbin-gateway
    2. $ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@