通过 Istio Gateway 和 VirtualService 资源公开服务

本指南演示如何使用 Istio GatewayVirtualService 资源,为通过 gateway injection 部署的 gateway 配置路由。这些资源用于设置 gateway,使其将 mesh 内的服务公开给来自外部的流量。随后,将 gateway 的 Service 更改为 LoadBalancer 类型,即可将 gateway 公开给集群外部的流量。

NOTE

此方法适用于基于 sidecar 的部署:您已在使用 Istio GatewayVirtualService 资源,并且不计划迁移到 ambient mode。对于 ambient mode,请改用 Kubernetes Gateway API。

前提条件

  • 已使用 gateway injection 安装 Istio gateway。
  • 您的 Kubernetes 集群支持外部负载均衡器(即类型为 LoadBalancer 的 Service)。

操作步骤

  1. 执行以下命令,创建名为 httpbin 的新 namespace:

    kubectl create namespace httpbin
    kubectl label namespace httpbin pod-security.kubernetes.io/enforce=restricted --overwrite
  2. 为该 namespace 启用 sidecar injection。如果您的设置使用 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.30.4   47h
    2. 使用 revision 名称为 namespace 添加标签,以启用 sidecar injection:

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

    kubectl apply -n httpbin -f https://raw.githubusercontent.com/alauda-mesh/istio/refs/heads/istio-1.30/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 template 中定义的唯一标签。默认情况下,Istio Gateway 配置会应用于所有 namespace 中匹配的 gateway pod。
    2. hosts 字段中列出客户端可用于访问对应端口上的 mesh 服务的地址。
  5. 使用以下命令应用 YAML 文件:

    kubectl apply -f httpbin-gw.yaml
  6. 创建另一个名为 httpbin-vs.yaml 的 YAML 文件,用于定义 VirtualService。此 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. 定义 hostsVirtualService 路由规则将应用于该对象。指定的 hosts 必须由 Istio Gateway 资源公开,而此 VirtualService 将附加到该资源。
    2. VirtualService 附加到上一步中的 Istio Gateway 资源,方法是将 Gateway 的名称添加到 gateways 列表中。
    3. 定义 destination,指定 httpbin Servicehostport,将匹配的流量定向到之前部署的 httpbin 服务。
  7. 使用以下命令应用 YAML 文件:

    kubectl apply -f httpbin-vs.yaml

验证

  1. 执行以下命令,为 curl 客户端创建 namespace:

    kubectl create namespace curl
    kubectl label namespace curl pod-security.kubernetes.io/enforce=restricted --overwrite
  2. 使用以下命令部署 curl 客户端:

    kubectl apply -n curl -f https://raw.githubusercontent.com/alauda-mesh/istio/refs/heads/istio-1.30/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 endpoint 发送请求。将 Host header 设置为 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 前缀匹配的 endpoint 发送另一个请求:

    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 endpoint 在 httpbin VirtualService 中没有定义 URI 前缀匹配。

    示例输出

    HTTP/1.1 404 Not Found
    ...
    server: istio-envoy
    ...
  7. 将 gateway proxy 的 Service 类型更改为 LoadBalancer,使 gateway proxy 对集群外部的流量开放:

    kubectl patch service <gateway_name> -n <gateway_namespace> -p '{"spec": {"type": "LoadBalancer"}}'
  8. 使用 gateway Service 的外部 hostname 或 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. 运行以下命令,使用 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