控制Egress流量

缺省情况下,启用了Istio的服务是无法访问外部URL的,这是因为Pod中的iptables把所有外发传输都转向到了Sidecar代理,而这一代理只处理集群内的访问目标。

本节内容会描述如何把外部服务提供给启用了Istio的客户端服务使用,你会学到如何使用Egress规则访问外部服务,或者如何简单的让特定IP范围穿透Istio代理。

开始之前

  • 遵循安装指南设置Istio
  • 启动sleep示例,用于测试外部访问。

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

    注意所有可以在其中执行execcurl的pod都可以这样做。

使用Istio的Egress规则

使用Egress规则,就可以从Istio集群内访问任何公开服务。本任务中我们会使用httpbin.org以及www.google.com作为范例。

配置外部服务

  1. 创建一个Egress规则,来允许访问外部HTTP服务:

    1. cat <<EOF | istioctl create -f -
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: EgressRule
    4. metadata:
    5. name: httpbin-egress-rule
    6. spec:
    7. destination:
    8. service: httpbin.org
    9. ports:
    10. - port: 80
    11. protocol: http
    12. EOF
  2. 创建一个Egress规则,容许访问外部HTTPS服务:

    1. cat <<EOF | istioctl create -f -
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: EgressRule
    4. metadata:
    5. name: google-egress-rule
    6. spec:
    7. destination:
    8. service: www.google.com
    9. ports:
    10. - port: 443
    11. protocol: https
    12. EOF

发出对外请求

  1. 使用exec进入作为测试源使用的pod。例如,如果你正在使用sleep服务,运行下列命令:

    1. export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
    2. kubectl exec -it $SOURCE_POD -c sleep bash
  2. 发出请求到外部HTTP服务:

    1. curl http://httpbin.org/headers
  3. 发送请求到外部HTTPS服务

    外部的HTTPS服务必须可以通过HTTP访问,在请求中指定端口:

    1. curl http://www.google.com:443

为外部服务设置路由规则

和集群内请求类似,Istio的路由规则也可以用于设置使用了Egress规则的外部服务。为了进行演示,我们为httpbin.org服务设置一个超时。

  1. 在作为测试源使用的Pod内,调用外部服务httpbin.org的/delay端点:

    1. kubectl exec -it $SOURCE_POD -c sleep bash
    2. time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
    1. 200
    2. real 0m5.024s
    3. user 0m0.003s
    4. sys 0m0.003s

    请求应该在大约五秒钟之后返回200(OK)。

  2. 退出源Pod,再使用istioctl来给外部服务httpbin.org设置3秒钟的调用超时:

    1. cat <<EOF | istioctl create -f -
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: RouteRule
    4. metadata:
    5. name: httpbin-timeout-rule
    6. spec:
    7. destination:
    8. service: httpbin.org
    9. http_req_timeout:
    10. simple_timeout:
    11. timeout: 3s
    12. EOF
  3. 等待几秒钟之后,再次发起curl调用:

    1. kubectl exec -it $SOURCE_POD -c sleep bash
    2. time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
    1. 504
    2. real 0m3.149s
    3. user 0m0.004s
    4. sys 0m0.004s

    这次在大概3秒钟之后返回504(网关超时)。虽然httpbin.org在等待5秒钟,Istio在3秒钟时就切断了请求。

直接调用外部服务

目前Istio的Egress规则只提供对HTTP/HTTPS请求的支持。如果想要访问其他协议的外部服务(例如mongodb://host/database),或者让指定IP范围直接穿透Istio,就需对源服务的Envoy Sidecar进行配置,阻止其对外部请求的拦截。可以在使用istio kube-inject时启用--includeIPRanges参数来满足这一要求。

最简单的--includeIPRanges用法就是提交所有的内部服务的IP范围,然后让所有外部服务地址都绕过Sidecar代理。内部IP范围跟你所运行的集群有关,例如Minikube中的范围是10.0.0.1/24,所以应该这样启动sleep服务:

  1. kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml --includeIPRanges=10.0.0.1/24)

On IBM Cloud Private, use:

  1. cluster/config.yaml下的IBM Cloud Private配置文件中获取service_cluster_ip_range

    1. cat cluster/config.yaml | grep service_cluster_ip_range

    下面是输出示例:

    1. service_cluster_ip_range: 10.0.0.1/24
  2. 通过--includeIPRanges,注入service_cluster_ip_range到应用的profile,来限制Isito的流量拦截到service cluster IP range。

    1. kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml --includeIPRanges=10.0.0.1/24)

在IBM Cloud Container Service上,使用:

  1. kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml --includeIPRanges=172.30.0.0/16,172.20.0.0/16,10.10.10.0/24)

在Google容器引擎(GKE)上的IP范围不是固定的,所以需要运行命令gcloud container clusters describe来获取要使用的IP范围。例如:

  1. gcloud container clusters describe XXXXXXX --zone=XXXXXX | grep -e clusterIpv4Cidr -e servicesIpv4Cidr
  1. clusterIpv4Cidr: 10.4.0.0/14
  2. servicesIpv4Cidr: 10.7.240.0/20
  1. kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml --includeIPRanges=10.4.0.0/14,10.7.240.0/20)

而在Azure容器服务(ACS)上就应该执行:

  1. kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml --includeIPRanges=10.244.0.0/16,10.240.0.0/16)

以这种方式启动服务之后,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集群中使用两种方式来访问外部服务:

  1. 使用Egress规则(推荐)。
  2. 配置Istio Sidecar,在他的iptables中排除对外部IP的控制。

第一种方式目前只支持HTTP(S)请求,但是可以为外部服务提供和内部服务一样的路由支持。我们使用了一个路由规则来展示了这一能力。

第二种方式绕过了Sidecar代理,让服务可以直接访问任何外部资源,只是这样的设置需要一点对集群配置的了解。

清理

  1. 删除规则:

    1. istioctl delete egressrule httpbin-egress-rule google-egress-rule
    2. istioctl delete routerule httpbin-timeout-rule
  2. 关闭sleep服务:

    1. kubectl delete -f samples/sleep/sleep.yaml

延伸阅读