Ingress 网关

除了支持 Kubernetes Ingress, Istio 还允许使用 Istio GatewayKubernetes Gateway 资源来配置 Ingress 流量。 与 Ingress 相比,Gateway 提供了更广泛的自定义和灵活性,并允许将 Istio 功能(例如监控和路由规则)应用于进入集群的流量。

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

开始之前

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

  • 启动 httpbin 示例,用作 Ingress 流量的目标服务:

    Zip
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@
    

    请注意,本文旨在展示如何使用网关来控制到 “Kubernetes 集群"中的 Ingress 流量, 无论是否启用 Sidecar 注入,您都可以启动 httpbin 服务 (即目标服务可以在 Istio 网格内,也可以在 Istio 网格外)。

使用网关配置 Ingress

Ingress Gateway 描述在网格边界运作的、用于接收传入的 HTTP/TCP 连接的负载均衡器。 此负载均衡器会配置暴露的端口、协议等,但与 Kubernetes Ingress 资源不同, 它不会包括任何流量路由配置。转而使用路由规则来配置 Ingress 流量的流量路由, 这与内部服务请求所用的方式相同。

现在看看如何为 HTTP 流量在 80 端口上配置 Gateway

创建 Istio Gateway

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  # selector 应与 Ingress Gateway Pod 标签相匹配。
  # 如果您参照标准文档使用 Helm 安装了 Istio,
  # 此字段应设置为 "istio=ingress"
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "httpbin.example.com"
EOF

通过 Gateway 为进入的流量配置路由:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

已为 httpbin 服务创建了虚拟服务配置, 包含两个路由规则,允许流量流向路径 /status/delay

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

确定 Ingress IP 和端口

每个 Gateway类型为 LoadBalancer 的 Service 支撑,该 Service 的外部负载均衡器 IP 和端口用于访问 Gateway。 大多数云平台上运行的集群默认支持类型为 LoadBalancer 的 Kubernetes Service, 但在某些环境(例如测试环境)中,您可能需要执行如下操作:

  • minikube - 在另一个终端中运行以下命令,启动一个外部负载均衡器:

    $ minikube tunnel
    
  • kind - 按照指南使 LoadBalancer 类型的 Service 正常工作。

  • 其他平台 - 您可以使用 MetalLB 获取 LoadBalancer Service 的 EXTERNAL-IP

为了方便演示,我们将 Ingress IP 和端口存储到环境变量中,在后续的教程中使用。 根据以下指示说明来设置 INGRESS_HOSTINGRESS_PORT 环境变量:

将以下环境变量设置到您集群中 Istio Ingress Gateway 的名称及其所在的命名空间:

$ export INGRESS_NAME=istio-ingressgateway
$ export INGRESS_NS=istio-system

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

$ kubectl get svc "$INGRESS_NAME" -n "$INGRESS_NS"
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)   AGE
istio-ingressgateway   LoadBalancer   172.21.109.129   130.211.10.121   ...       17h

如果 EXTERNAL-IP 值已被设置,说明您的环境正在使用外部负载均衡器,您可以用其为 Ingress Gateway 提供服务。 如果 EXTERNAL-IP 值为 <none>(或持续显示 <pending>),说明您的环境没有为 Ingress Gateway 提供外部负载均衡器。

如果您的环境不支持外部负载均衡器, 您可以尝试使用 Node Port 访问 Ingress Gateway。 否则,使用以下命令设置 Ingress IP 和端口:

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

访问 Ingress 服务

  1. 使用 curl 访问 httpbin 服务:

    $ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
    ...
    HTTP/1.1 200 OK
    ...
    server: istio-envoy
    ...
    

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

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

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

通过浏览器访问 Ingress 服务

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

您可以在简单的测试和演示中按下述方法绕过这个问题:

GatewayVirtualService 配置中使用通配符 *。例如如下修改 Ingress 配置:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  # selector 应与 Ingress Gateway Pod 标签相匹配。
  # 如果您参照标准文档使用 Helm 安装了 Istio,
  # 此字段应设置为 "istio=ingress"
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /headers
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

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

理解原理

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

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

使用 Ingress Gateway 服务的 Node Port

如果您的环境不支持外部负载均衡器,则您仍然可以使用 istio-ingressgateway Service 的 Node Port 来实验某些 Istio 特性。

设置 Ingress 端口:

$ export INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
$ export TCP_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')

根据集群提供商的要求来设置 Ingress IP:

  1. GKE:

    $ export INGRESS_HOST=worker-node-address
    

    您需要创建防火墙规则以允许 TCP 流量到达 ingressgateway Service 的端口。 运行以下命令以允许到 HTTP 和/或 HTTPS 端口的流量:

    $ gcloud compute firewall-rules create allow-gateway-http --allow "tcp:$INGRESS_PORT"
    $ gcloud compute firewall-rules create allow-gateway-https --allow "tcp:$SECURE_INGRESS_PORT"
    
  2. IBM Cloud Kubernetes Service:

    $ ibmcloud ks workers --cluster cluster-name-or-id
    $ export INGRESS_HOST=public-IP-of-one-of-the-worker-nodes
    
  3. Docker For Desktop:

    $ export INGRESS_HOST=127.0.0.1
    
  4. 其他环境:

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

问题排查

  1. 检查环境变量 INGRESS_HOST and INGRESS_PORT 的值。确保这些是合法的值,命令如下:

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

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

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

清理

删除 GatewayVirtualService 配置, 并关闭 httpbin 服务:

Zip
$ kubectl delete gateway httpbin-gateway
$ kubectl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
这些信息有用吗?
您是否有更多建议和改进意见?

感谢您的反馈!