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

本指南演示如何使用 Istio 的 GatewayVirtualService 资源来配置通过网关注入部署的网关。这些资源将配置网关,使其能够将网格内的服务暴露给外部流量。随后,通过将网关的 Service 类型更改为 LoadBalancer,将网关暴露给集群外部的流量。

前提条件

  • 已使用网关注入安装 Istio 网关。
  • 你的 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.3   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 资源定义。该资源配置网关代理打开端口 80(HTTP),并为主机 httpbin.example.com 提供服务。

    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 字段中列出客户端可用于访问对应端口网格服务的地址。
  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. 定义 hosts,指定 VirtualService 路由规则适用的主机。所指定的 hosts 必须由该 VirtualService 所附加的 Istio Gateway 资源暴露。
    2. 通过将网关名称添加到 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 Service 发送请求,访问 httpbin 应用的 /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. 通过将网关代理的 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 状态,确认请求成功。