• 控制 Egress 流量
    • 开始之前
    • 在 Istio 中配置外部服务
      • 配置外部服务
      • 配置外部 HTTPS 服务
      • 为外部服务设置路由规则
    • 直接调用外部服务
      • 确定 global.proxy.includeIPRanges 的值
        • IBM Cloud Private
        • IBM Cloud Kubernetes Service
        • Google Container Engine (GKE)
        • Azure Container Service(ACS)
        • Minikube
        • Docker For Desktop
        • Bare Metal
      • 访问外部服务
    • 理解原理
    • 清理

    控制 Egress 流量

    缺省情况下,Istio 服务网格内的 Pod,由于其 iptables 将所有外发流量都透明的转发给了 Sidecar,所以这些集群内的服务无法访问集群之外的 URL,而只能处理集群内部的目标。

    本文的任务描述了如何将外部服务暴露给 Istio 集群中的客户端。你将会学到如何通过定义 ServiceEntry 来调用外部服务;或者简单的对 Istio 进行配置,要求其直接放行对特定 IP 范围的访问。

    开始之前

    • 根据安装指南的内容,部署 Istio。

    • 启动 sleep 示例应用,我们将会使用这一应用来完成对外部服务的调用过程。

    如果启用了 Sidecar 的自动注入功能,运行:

    Zip

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

    否则在部署 sleep 应用之前,就需要手工注入 Sidecar:

    Zip

    1. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@)

    实际上任何可以 execcurl 的 Pod 都可以用来完成这一任务。

    • SOURCE_POD 环境变量设置为已部署的 sleep pod:
    1. $ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})

    在 Istio 中配置外部服务

    通过配置 Istio ServiceEntry,可以从 Istio 集群中访问任何可公开访问的服务。这里我们会使用 httpbin.org 以及 www.google.com 进行试验。

    配置外部服务

    • 创建一个 ServiceEntry 对象,放行对一个外部 HTTP 服务的访问:
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: ServiceEntry
    4. metadata:
    5. name: httpbin-ext
    6. spec:
    7. hosts:
    8. - httpbin.org
    9. ports:
    10. - number: 80
    11. name: http
    12. protocol: HTTP
    13. resolution: DNS
    14. location: MESH_EXTERNAL
    15. EOF
    • 创建一个 ServiceEntry 以及 VirtualService,允许访问外部 HTTPS 服务。注意:包括 HTTPS 在内的 TLS 协议,在 ServiceEntry 之外,还需要创建 TLS VirtualService
    1. $ kubectl exec -it $SOURCE_POD -c sleep sh
    • 向外部 HTTP 服务发出请求:
    1. $ curl http://httpbin.org/headers

    配置外部 HTTPS 服务

    • 创建一个 ServiceEntry 以允许访问外部 HTTPS 服务。对于 TLS 协议(包括 HTTPS),除了 ServiceEntry 之外,还需要 VirtualService。 没有 VirtualService, ServiceEntry 所暴露的服务将不被定义。VirtualService 必须在 match 子句中包含 tls 规则和 sni_hosts 以启用 SNI 路由。
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: ServiceEntry
    4. metadata:
    5. name: google
    6. spec:
    7. hosts:
    8. - www.google.com
    9. ports:
    10. - number: 443
    11. name: https
    12. protocol: HTTPS
    13. resolution: DNS
    14. location: MESH_EXTERNAL
    15. ---
    16. apiVersion: networking.istio.io/v1alpha3
    17. kind: VirtualService
    18. metadata:
    19. name: google
    20. spec:
    21. hosts:
    22. - www.google.com
    23. tls:
    24. - match:
    25. - port: 443
    26. sni_hosts:
    27. - www.google.com
    28. route:
    29. - destination:
    30. host: www.google.com
    31. port:
    32. number: 443
    33. weight: 100
    34. EOF
    • 执行 sleep service 源 pod:
    1. $ kubectl exec -it $SOURCE_POD -c sleep sh
    • 向外部 HTTPS 服务发出请求:
    1. $ curl https://www.google.com

    为外部服务设置路由规则

    通过 ServiceEntry 访问外部服务的流量,和网格内流量类似,都可以进行 Istio 路由规则 的配置。下面我们使用 istioctl 为 httpbin.org 服务设置一个超时规则。

    • 在测试 Pod 内部,使用 curl 调用 httpbin.org 这一外部服务的 /delay 端点:
    1. $ kubectl exec -it $SOURCE_POD -c sleep sh
    2. $ time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
    3. 200
    4. real 0m5.024s
    5. user 0m0.003s
    6. sys 0m0.003s

    这个请求会在大概五秒钟左右返回一个内容为 200 (OK) 的响应。

    • 退出测试 Pod,使用 kubectl 为 httpbin.org 外部服务的访问设置一个 3 秒钟的超时:
    1. $ kubectl apply -f - <<EOF
    2. apiVersion: networking.istio.io/v1alpha3
    3. kind: VirtualService
    4. metadata:
    5. name: httpbin-ext
    6. spec:
    7. hosts:
    8. - httpbin.org
    9. http:
    10. - timeout: 3s
    11. route:
    12. - destination:
    13. host: httpbin.org
    14. weight: 100
    15. EOF
    • 等待几秒钟之后,再次发起 curl 请求:
    1. $ kubectl exec -it $SOURCE_POD -c sleep sh
    2. $ time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
    3. 504
    4. real 0m3.149s
    5. user 0m0.004s
    6. sys 0m0.004s

    这一次会在 3 秒钟之后收到一个内容为 504 (Gateway Timeout) 的响应。虽然 httpbin.org 还在等待他的 5 秒钟,Istio 却在 3 秒钟的时候切断了请求。

    直接调用外部服务

    如果想要跳过 Istio,直接访问某个 IP 范围内的外部服务,就需要对 Envoy sidecar 进行配置,阻止 Envoy 对外部请求的劫持。可以在 Helm 中设置 global.proxy.includeIPRanges 变量,然后使用 kubectl apply 命令来更新名为 istio-sidecar-injectorConfigmap。在 istio-sidecar-injector 更新之后,global.proxy.includeIPRanges 会在所有未来部署的 Pod 中生效。

    使用 global.proxy.includeIPRanges 变量的最简单方式就是把内部服务的 IP 地址范围传递给它,这样就在 Sidecar proxy 的重定向列表中排除掉了外部服务的地址了。

    内部服务的 IP 范围取决于集群的部署情况。例如 Minikube 中这一范围是 10.0.0.1/24,这个配置中,就应该这样更新 istio-sidecar-injector

    1. $ helm template install/kubernetes/helm/istio <安装 Istio 时所使用的参数> --set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml | kubectl apply -f -

    注意这里应该使用和之前部署 Istio 的时候同样的 Helm 命令,尤其是 —namespace 参数。在安装 Istio 原有命令的基础之上,加入 —set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml 即可。

    和前面一样,重新部署 sleep 应用。




    确保已删除之前部署的 ServiceEntryVirtualService

    确定 global.proxy.includeIPRanges 的值

    根据集群部署情况为 global.proxy.includeIPRanges 赋值。

    IBM Cloud Private

    • 从 IBM Cloud Private 配置文件(cluster/config.yaml)中获取 service_cluster_ip_range
    1. $ cat cluster/config.yaml | grep service_cluster_ip_range

    会输出类似内容:

    1. service_cluster_ip_range: 10.0.0.1/24
    • 使用 —set global.proxy.includeIPRanges="10.0.0.1/24"

    IBM Cloud Kubernetes Service

    使用 —set global.proxy.includeIPRanges="172.30.0.0/16\,172.21.0.0/16\,10.10.10.0/24"

    Google Container Engine (GKE)

    这个范围是不确定的,所以需要运行 gcloud container clusters describe 命令来获取范围的具体定义,例如:

    1. $ gcloud container clusters describe XXXXXXX --zone=XXXXXX | grep -e clusterIpv4Cidr -e servicesIpv4Cidr
    2. clusterIpv4Cidr: 10.4.0.0/14
    3. servicesIpv4Cidr: 10.7.240.0/20

    使用 —set global.proxy.includeIPRanges="10.4.0.0/14\,10.7.240.0/20"

    Azure Container Service(ACS)

    使用 —set global.proxy.includeIPRanges="10.244.0.0/16\,10.240.0.0/16

    Minikube

    使用 —set global.proxy.includeIPRanges="10.0.0.1/24"

    Docker For Desktop

    使用 —set global.proxy.includeIPRanges="10.96.0.0/12"

    Bare Metal

    使用 service-cluster-ip-range 的值。它没有固定值,但默认值为 10.96.0.0/12 。要确定您的实际值:

    1. $ kubectl describe pod kube-apiserver -n kube-system | grep 'service-cluster-ip-range'
    2. --service-cluster-ip-range=10.96.0.0/12

    访问外部服务

    更新了 ConfigMap istio-sidecar-injector 并且重新部署了 sleep 应用之后,Istio sidecar 就应该只劫持和管理集群内部的请求了。任意的外部请求都会简单的绕过 Sidecar,直接访问目的地址。

    1. $ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
    2. $ kubectl exec -it $SOURCE_POD -c sleep curl http://httpbin.org/headers

    理解原理

    这个任务中,我们使用两种方式从 Istio 服务网格内部来完成对外部服务的调用:

    • 使用 ServiceEntry (推荐方式)

    • 配置 Istio sidecar,从它的重定向 IP 表中排除外部服务的 IP 范围

    第一种方式(ServiceEntry)中,网格内部的服务不论是访问内部还是外部的服务,都可以使用同样的 Istio 服务网格的特性。我们通过为外部服务访问设置超时规则的例子,来证实了这一优势。

    第二种方式越过了 Istio sidecar proxy,让服务直接访问到对应的外部地址。然而要进行这种配置,需要了解云供应商特定的知识和配置。

    清理

    • 删除规则:
    1. $ kubectl delete serviceentry httpbin-ext google
    2. $ kubectl delete virtualservice httpbin-ext google
    • 停止 sleep 服务:

    Zip

    1. $ kubectl delete -f @samples/sleep/sleep.yaml@
    • 更新 ConfigMap istio-sidecar-injector,要求 Sidecar 转发所有外发流量:
    1. $ helm template install/kubernetes/helm/istio <安装 Istio 时所使用的参数> -x templates/sidecar-injector-configmap.yaml | kubectl apply -f -