通过 Istio Gateway 和 VirtualService 资源暴露服务

本指南演示如何使用 Istio GatewayVirtualService 资源来配置通过 gateway 注入部署的 gateway。这些资源会将 gateway 设置为向来自网格外部的流量暴露网格内部的服务。之后,你可以通过将 gateway 的 Service 类型更改为 LoadBalancer,将该 gateway 暴露给集群外部流量。

NOTE

对于基于 sidecar 的部署,如果你已经在使用 Istio GatewayVirtualService 资源,并且不打算迁移到 ambient 模式,请使用此方法。对于 ambient 模式,请改用 Kubernetes Gateway API。

先决条件

  • 已通过 gateway 注入 安装 Istio gateway。
  • 你的 Kubernetes 集群支持外部负载均衡器(即类型为 LoadBalancer 的 Service)。

操作步骤

  1. 通过执行以下命令创建一个名为 httpbin 的新命名空间:

    kubectl create namespace httpbin
  2. 为该命名空间启用 sidecar 注入。如果你的环境使用 InPlace 升级策略,请运行以下命令:

    kubectl label namespace httpbin istio-injection=enabled
    NOTE

    如果你使用的是 RevisionBased 升级策略,请执行以下命令:

    1. 要获取 <revision-name>,请运行以下命令:

      kubectl get istiorevisions.sailoperator.io

      示例输出:

      NAME      NAMESPACE      PROFILE   READY   STATUS    IN USE   VERSION   AGE
      default   istio-system             True    Healthy   True     v1.28.6   47h
    2. 使用 revision 名称为命名空间添加标签,以启用 sidecar 注入:

      kubectl label namespace httpbin istio.io/rev=default
  3. 运行以下命令部署 httpbin 示例服务:

    kubectl apply -n httpbin -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/httpbin/httpbin.yaml
  4. 创建一个名为 httpbin-gw.yaml 的文件,其中包含 Istio Gateway 资源定义。此资源会配置 gateway proxy 为主机 httpbin.example.com 打开 80 端口(HTTP)。

    apiVersion: networking.istio.io/v1
    kind: Gateway
    metadata:
      name: httpbin-gateway
      namespace: httpbin
    spec:
      selector:
        istio: <gateway_name>
      servers:
        - port:
            number: 80
            name: http
            protocol: HTTP
          hosts:
            - httpbin.example.com
    1. selector 设置为与 gateway proxy Deployment 的 pod 模板中定义的唯一标签匹配。默认情况下,Istio Gateway 配置会应用于所有命名空间中匹配的 gateway pod。
    2. hosts 字段中,列出客户端可用于访问对应端口上的网格服务的地址。
  5. 使用以下命令应用该 YAML 文件:

    kubectl apply -f httpbin-gw.yaml
  6. VirtualService 创建另一个名为 httpbin-vs.yaml 的 YAML 文件。该 VirtualService 将定义将流量从 gateway proxy 路由到 httpbin 服务的规则。

    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: httpbin
      namespace: httpbin
    spec:
      hosts:
        - httpbin.example.com
      gateways:
        - httpbin-gateway
      http:
        - match:
            - uri:
                prefix: /status
            - uri:
                prefix: /headers
          route:
            - destination:
                port:
                  number: 8000
                host: httpbin
    1. 定义 VirtualService 路由规则将应用于哪些 hosts。指定的 hosts 必须由该 VirtualService 所附加到的 Istio Gateway 资源公开。
    2. 通过将 Gateway 的名称添加到 gateways 列表中,将 VirtualService 附加到上一步中的 Istio Gateway 资源。
    3. 通过定义一个 destination,其中指定 httpbin Servicehostport,将匹配的流量转发到先前部署的 httpbin 服务。
  7. 使用以下命令应用该 YAML 文件:

    kubectl apply -f httpbin-vs.yaml

验证

  1. 通过执行以下命令为 curl 客户端创建一个命名空间:

    kubectl create namespace curl
  2. 使用以下命令部署 curl 客户端:

    kubectl apply -n curl -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/curl/curl.yaml
  3. 通过运行以下命令将 curl pod 的名称存储到 CURL_POD 变量中:

    CURL_POD=$(kubectl get pods -n curl -l app=curl -o jsonpath='{.items[*].metadata.name}')
  4. curl 客户端,通过入口 gateway Servicehttpbin 应用的 /headers 端点发送请求。将 Host 标头设置为 httpbin.example.com,以与 Istio GatewayVirtualService 中指定的主机保持一致。执行以下 curl 命令:

    kubectl exec $CURL_POD -n curl -- \
      curl -sS -I \
        -H Host:httpbin.example.com \
        <gateway_name>.<gateway_namespace>.svc.cluster.local/headers
  5. 响应应显示 200 OK HTTP 状态,这表明请求成功。

    示例输出

    HTTP/1.1 200 OK
    ...
    server: istio-envoy
    ...
  6. 再向一个没有在 httpbin VirtualService 中对应 URI 前缀匹配的端点发送请求,运行以下命令:

    kubectl exec $CURL_POD -n curl -- \
      curl -sS -I \
        -H Host:httpbin.example.com \
        <gateway_name>.<gateway_namespace>.svc.cluster.local/get

    响应应为 404 Not Found 状态。这是预期结果,因为 /get 端点在 httpbin VirtualService 中没有定义 URI 前缀匹配。

    示例输出

    HTTP/1.1 404 Not Found
    ...
    server: istio-envoy
    ...
  7. 通过将 gateway proxy 的 Service 类型更改为 LoadBalancer,使其能够接收来自集群外部的流量:

    kubectl patch service <gateway_name> -n <gateway_namespace> -p '{"spec": {"type": "LoadBalancer"}}'
  8. 使用 gateway Service 的外部主机名或 IP 地址,确认可以从集群外部访问 httpbin 服务。请确保为集群环境正确设置 INGRESS_HOST 变量。

    a. 使用以下命令设置 INGRESS_HOST 变量:

    INGRESS_HOST=$(kubectl get service <gateway_name> -n <gateway_namespace> -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo "INGRESS_HOST=$INGRESS_HOST"

    在某些环境中,负载均衡器可能通过主机名而不是 IP 地址暴露。在这种情况下,入口 gateway 的 EXTERNAL-IP 值不会是 IP 地址,而是主机名,并且上述命令将无法成功设置 INGRESS_HOST 环境变量。请使用以下命令修正 INGRESS_HOST 的值:

    INGRESS_HOST=$(kubectl get service <gateway_name> -n <gateway_namespace> -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    echo "INGRESS_HOST=$INGRESS_HOST"

    b. 使用 gateway 的主机向 httpbin 服务发送 curl 请求,运行以下命令:

    INFO

    如果 $INGRESS_HOST 是 IPv6 地址,在构造 URL 时请将其放在方括号中。例如:

    curl -sS -g -I -H Host:httpbin.example.com http://[$INGRESS_HOST]/headers
    curl -sS -g -I -H Host:httpbin.example.com http://$INGRESS_HOST/headers
  9. 检查响应是否包含 HTTP/1.1 200 OK 状态,这表明请求成功。

清理

删除在此操作步骤中创建的资源:

kubectl delete namespace curl
kubectl delete namespace httpbin