入口网关

    本任务描述了如何配置 Istio,以使用 Istio Gateway 来将服务暴露至服务网格之外。

    • 遵照中的文档说明,安装 Istio。

      如果准备使用 Gateway API 指令,您可以使用 minimal 配置文件来安装 Istio,因为您不再需要默认以其他方式安装的 istio-ingressgateway

    • 启动 httpbin 样例,用作入口流量的目标服务:

      1. $ kubectl apply -f @samples/httpbin/httpbin.yaml@

      请注意本文旨在展示如何使用网关控制到“Kubernetes 集群”中的入口流量,无论是否启用 Sidecar 注入都可以启动 httpbin 服务(即目标服务可以在 Istio 网格内,也可以在 Istio 网格外)。

    使用网关配置 Ingress

    Ingress Gateway 描述在网格边界运作的负载均衡器,用于接收传入的 HTTP/TCP 连接。 它会配置暴露的端口、协议等,但与 不同,不会包括任何流量路由配置。 转而使用路由规则来配置入口流量的流量路由,这与内部服务请求所用的方式相同。

    现在看看如何为 HTTP 流量在 80 端口上配置 Gateway

    创建 Istio Gateway

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: httpbin-gateway
    6. spec:
    7. selector:
    8. istio: ingressgateway # use Istio default gateway implementation
    9. servers:
    10. - port:
    11. number: 80
    12. name: http
    13. protocol: HTTP
    14. hosts:
    15. - "httpbin.example.com"
    16. EOF

    通过 Gateway 为进入的流量配置路由:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: httpbin
    6. spec:
    7. hosts:
    8. - "httpbin.example.com"
    9. gateways:
    10. - httpbin-gateway
    11. http:
    12. - match:
    13. - uri:
    14. prefix: /status
    15. - uri:
    16. prefix: /delay
    17. route:
    18. - destination:
    19. port:
    20. number: 8000
    21. host: httpbin
    22. EOF

    已为 httpbin 服务创建了配置,包含两个路由规则,允许流量流向路径 /status/delay

    Gateways 列表指定了哪些请求允许通 httpbin-gateway 网关。 所有其他外部请求均被拒绝并返回 404 响应。

    来自网格内部其他服务的内部请求无需遵循这些规则,而是默认遵守轮询调度路由规则。您可以为 gateways 列表添加特定的 mesh 值,将这些规则同时应用到内部请求。由于服务的内部主机名可能与外部主机名不一致(譬如:httpbin.default.svc.cluster.local),您需要同时将内部主机名添加到 hosts 列表中。 详情请参考。

    创建 Kubernetes Gateway

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: gateway.networking.k8s.io/v1beta1
    3. kind: Gateway
    4. metadata:
    5. name: httpbin-gateway
    6. spec:
    7. gatewayClassName: istio
    8. listeners:
    9. - name: http
    10. hostname: "httpbin.example.com"
    11. port: 80
    12. protocol: HTTP
    13. allowedRoutes:
    14. namespaces:
    15. from: Same
    16. EOF

    在生产环境中,Gateway 及其对应的路由通常由具有不同角色的用户在独立的命名空间中进行创建。 这种情况下,将配置 Gateway 中的 allowedRoutes 字段以指定应创建路由的命名空间。 在此例中,预期这些路由应处于与 Gateway 相同的命名空间中。

    因为创建 Kubernetes Gateway 资源也将,运行以下命令等待 Gateway 就绪:

    1. $ kubectl wait --for=condition=ready gtw httpbin-gateway

    通过 Gateway 为进入的流量配置路由:

    1. $ kubectl apply -f - <<EOF
    2. kind: HTTPRoute
    3. name: httpbin
    4. spec:
    5. parentRefs:
    6. - name: httpbin-gateway
    7. hostnames: ["httpbin.example.com"]
    8. rules:
    9. - matches:
    10. - path:
    11. type: PathPrefix
    12. value: /status
    13. - path:
    14. type: PathPrefix
    15. value: /delay
    16. backendRefs:
    17. - name: httpbin
    18. port: 8000
    19. EOF

    您现在已为 httpbin 服务创建了HTTP 路由配置,包含两个路由规则,允许流量流向路径 /status/delay

    • minikube - 在另一个终端中运行以下命令,启动一个外部负载均衡器:

      1. $ minikube tunnel
    • kind - 遵循 MetalLB 设置指南使得类型为 LoadBalancer 的服务能够工作。

    • 其他平台 - 您可以使用 获取 EXTERNAL-IP 用于 LoadBalancer 服务。

    为了方便演示,我们将 Ingress IP 和端口存储到环境变量中,在后续的教程中使用。 根据以下指示设置 INGRESS_HOSTINGRESS_PORT 环境变量:

    将以下环境变量设置到您集群中 Istio Ingress Gateway 所用的名称及其所在的命名空间:

    1. $ export INGRESS_NAME=istio-ingressgateway
    2. $ export INGRESS_NS=istio-system

    如果您使用 Helm 安装 Istio,则 Ingress Gateway 名称和命名空间都是 istio-ingress

    1. $ export INGRESS_NAME=istio-ingress
    2. $ export INGRESS_NS=istio-ingress

    执行如下指令,确定您的 Kubernetes 集群是否运行在支持外部负载均衡器的环境中:

    如果 EXTERNAL-IP 值已设置,说明环境正在使用外部负载均衡器,可以用其为 Ingress Gateway 提供服务。 如果 EXTERNAL-IP 值为 <none> (或持续显示 <pending>),说明环境没有为 Ingress Gateway 提供外部负载均衡器,无法使用 Ingress Gateway。

    如果您的环境不支持外部负载均衡器,您可以尝试使用 Node Port 访问 Ingress Gateway。 否则,使用以下命令设置 Ingress IP 和端口:

    1. $ export INGRESS_HOST=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    2. $ export INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
    3. $ export SECURE_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
    4. $ export TCP_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')

    在特定的环境下,可能会使用主机名来暴露负载均衡器,而不是 IP 地址。 此时,Ingress Gateway 的 EXTERNAL-IP 值将不再是 IP 地址,而是主机名。 前文设置 INGRESS_HOST 环境变量的命令将执行失败。使用下面的命令更正 INGRESS_HOST 值:

    1. $ export INGRESS_HOST=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

    从 httpbin 网关资源获取网关地址和端口:

    1. $ export INGRESS_HOST=$(kubectl get gtw httpbin-gateway -o jsonpath='{.status.addresses[*].value}')
    2. $ export INGRESS_PORT=$(kubectl get gtw httpbin-gateway -o jsonpath='{.spec.listeners[?(@.name=="http")].port}')

    您可以使用类似的命令找到任何网关上的其他端口。 例如在名为 my-gateway 的网关上访问名为 https 的安全 HTTP 端口:

    1. $ export INGRESS_HOST=$(kubectl get gtw my-gateway -o jsonpath='{.status.addresses[*].value}')
    2. $ export SECURE_INGRESS_PORT=$(kubectl get gtw my-gateway -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')
    1. 使用 curl 访问 httpbin 服务:

      1. $ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
      2. HTTP/1.1 200 OK
      3. server: istio-envoy
      4. ...

      注意上文命令使用 -H 标识将 HTTP 头部参数 Host 设置为 “httpbin.example.com”。 该操作是必需的,因为 Ingress Gateway 已被配置用来处理 “httpbin.example.com” 的服务请求, 而在测试环境中并没有为该主机绑定 DNS,而是简单直接地向 Ingress IP 发送请求。

    2. 访问其他没有被显式暴露的 URL 时,将看到 HTTP 404 错误:

      1. $ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/headers"
      2. HTTP/1.1 404 Not Found
      3. ...

    通过浏览器访问 Ingress 服务

    在浏览器中输入 httpbin 服务的 URL 不能获得有效的响应,因为无法像 curl 那样,将请求头部参数 Host 传给浏览器。 在现实场景中,这并不是问题,因为您需要合理配置被请求的主机及可解析的 DNS,从而在 URL 中使用主机的域名, 例如 https://httpbin.example.com/status/200

    您可以在简单的测试和演示中按下述方法绕过这个问题:

    GatewayVirtualService 配置中使用通配符 *。例如如下修改 Ingress 配置:

    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: Gateway
    4. metadata:
    5. name: httpbin-gateway
    6. spec:
    7. selector:
    8. istio: ingressgateway # use Istio default gateway implementation
    9. servers:
    10. - port:
    11. number: 80
    12. name: http
    13. protocol: HTTP
    14. hosts:
    15. ---
    16. apiVersion: networking.istio.io/v1alpha3
    17. kind: VirtualService
    18. metadata:
    19. name: httpbin
    20. spec:
    21. hosts:
    22. - "*"
    23. - httpbin-gateway
    24. http:
    25. - match:
    26. - uri:
    27. prefix: /headers
    28. route:
    29. - destination:
    30. port:
    31. number: 8000
    32. host: httpbin
    33. EOF
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: gateway.networking.k8s.io/v1beta1
    3. kind: Gateway
    4. metadata:
    5. name: httpbin-gateway
    6. spec:
    7. gatewayClassName: istio
    8. listeners:
    9. - name: http
    10. port: 80
    11. protocol: HTTP
    12. allowedRoutes:
    13. namespaces:
    14. from: Same
    15. ---
    16. apiVersion: gateway.networking.k8s.io/v1beta1
    17. kind: HTTPRoute
    18. metadata:
    19. name: httpbin
    20. spec:
    21. parentRefs:
    22. - name: httpbin-gateway
    23. rules:
    24. - matches:
    25. - path:
    26. type: PathPrefix
    27. value: /headers
    28. backendRefs:
    29. - name: httpbin
    30. port: 8000
    31. EOF

    此时,便可以在浏览器中输入包含 $INGRESS_HOST:$INGRESS_PORT 的 URL。 譬如,输入 http://$INGRESS_HOST:$INGRESS_PORT/headers,将显示浏览器发送的所有 Header 信息。

    Gateway 配置资源允许外部流量进入 Istio 服务网格,并对边界服务实施流量管理和 Istio 可用的策略特性。

    在前面的步骤中,在服务网格中创建一个服务并向外部流量暴露该服务的 HTTP 端点。

    使用 Ingress Gateway 服务的 Node Port

    如果您的 Kubernetes 环境有支持 的外部负载均衡器,您不应使用这些指示步骤。

    如果您的环境不支持外部负载均衡器,则您仍然可以使用 istio-ingressgateway 服务的 Node Port来实验某些 Istio 特性。

    设置 Ingress 端口:

    1. $ export INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
    2. $ export SECURE_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
    3. $ export TCP_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')

    根据集群提供商来设置 Ingress IP:

    1. GKE:

      您需要创建防火墙规则以允许 TCP 流量到达 ingressgateway 服务的端口。 运行以下命令以允许到 HTTP 和/或 HTTPS 端口的流量:

      1. $ gcloud compute firewall-rules create allow-gateway-http --allow "tcp:$INGRESS_PORT"
      2. $ gcloud compute firewall-rules create allow-gateway-https --allow "tcp:$SECURE_INGRESS_PORT"
    2. IBM Cloud Kubernetes Service:

      1. $ ibmcloud ks workers --cluster cluster-name-or-id
      2. $ export INGRESS_HOST=public-IP-of-one-of-the-worker-nodes
    3. Docker For Desktop:

      1. $ export INGRESS_HOST=127.0.0.1
    4. 其他环境:

      1. $ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n "${INGRESS_NS}" -o jsonpath='{.items[0].status.hostIP}')
    1. 检查环境变量 INGRESS_HOST and INGRESS_PORT。确保环境变量的值有效,命令如下:

      1. $ kubectl get svc -n istio-system
      2. $ echo "INGRESS_HOST=$INGRESS_HOST, INGRESS_PORT=$INGRESS_PORT"
    2. 检查没有在相同的端口上定义其它 Istio Ingress Gateway:

      1. $ kubectl get gateway --all-namespaces
    3. 检查没有在相同的 IP 和端口上定义 Kubernetes Ingress 资源:

      1. $ kubectl get ingress --all-namespaces
    4. 如果使用了外部负载均衡器,该外部负载均衡器无法正常工作,尝试通过 Node Port 访问 Gateway

    清除

    删除 GatewayVirtualService 配置,并关闭 httpbin 服务:

    1. $ kubectl delete gateway httpbin-gateway
    2. $ kubectl delete virtualservice httpbin
    3. $ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@

    Zip

    1. $ kubectl delete gtw httpbin-gateway
    2. $ kubectl delete httproute httpbin