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

本指南演示如何使用 Istio GatewayVirtualService 资源来配置通过 gateway injection 部署的网关。这些资源会将网关设置为对外暴露 mesh 内部的服务。随后,你可以通过将网关的 Service 更改为 LoadBalancer 类型,使该网关能够接收来自集群外部的流量。

前提条件

  • 已通过 gateway injection 安装 Istio 网关。
  • 你的 Kubernetes 集群支持外部负载均衡器(即,类型为 LoadBalancer 的 Services)。

操作步骤

  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 资源定义。该资源将网关代理配置为为主机 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 设置为与网关代理 Deployment 的 Pod 模板中定义的唯一标签或标签组匹配。默认情况下,Istio Gateway 配置会应用于所有命名空间中匹配的网关 Pod。
    2. hosts 字段中,列出客户端可用于通过对应端口访问 mesh 服务的地址。
  5. 使用以下命令应用 YAML 文件:

    kubectl apply -f httpbin-gw.yaml
  6. 再创建一个名为 httpbin-vs.yaml 的 YAML 文件,用于定义 VirtualService。该 VirtualService 将定义从网关代理路由流量到 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. 通过定义一个指定 httpbin Servicehostportdestination,将匹配到的流量直接路由到之前部署的 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 客户端通过 ingress 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 prefix 匹配的端点发送另一条请求:

    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 prefix 匹配。

    示例输出

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

    kubectl patch service <gateway_name> -n <gateway_namespace> -p '{"spec": {"type": "LoadBalancer"}}'
  8. 确认可以使用网关 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 地址进行暴露。在这种情况下,ingress 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. 使用网关的主机向 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 状态,这表示请求已成功。