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

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

NOTE

此方法适用于基于 sidecar 的部署:您已在使用 Istio Gateway 和 VirtualService 资源,并且不计划迁移到 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. 定义 hosts,VirtualService 路由规则将应用于该对象。指定的 hosts 必须由 Istio Gateway 资源公开,而此 VirtualService 将附加到该资源。
    2. 将 VirtualService 附加到上一步中的 Istio Gateway 资源,方法是将 Gateway 的名称添加到 gateways 列表中。
    3. 定义 destination,指定 httpbin Service 的 host 和 port,将匹配的流量定向到之前部署的 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 Gateway 和 VirtualService 中指定的主机保持一致。执行以下 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