控制 Egress 流量
缺省情况下,Istio 服务网格内的 Pod,由于其 iptables 将所有外发流量都透明的转发给了 Sidecar,所以这些集群内的服务无法访问集群之外的 URL,而只能处理集群内部的目标。
本文的任务描述了如何将外部服务暴露给 Istio 集群中的客户端。你将会学到如何通过定义 ServiceEntry
来调用外部服务;或者简单的对 Istio 进行配置,要求其直接放行对特定 IP 范围的访问。
开始之前
根据安装指南的内容,部署 Istio。
启动 sleep 示例应用,我们将会使用这一应用来完成对外部服务的调用过程。
如果启用了 Sidecar 的自动注入功能,运行:
$ kubectl apply -f @samples/sleep/sleep.yaml@
否则在部署
sleep
应用之前,就需要手工注入 Sidecar:$ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@)
将
SOURCE_POD
环境变量设置为已部署的sleep
pod:$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
Envoy 配置外部服务
Istio 有一个安装选项 global.outboundTrafficPolicy.mode
,配置了 Sidecar 对外部服务的处理方式,所谓外部服务就是没有在 Istio 内部服务注册表中进行注册的服务。
如果此选项设置为 ALLOW_ANY
,则 Istio 代理允许调用未知服务。
如果该选项设置为 REGISTRY_ONLY
,则 Istio 代理会阻止任何没有 HTTP 服务的主机或网格中定义的服务条目。
ALLOW_ANY
是默认值,允许您快速开始评估 Istio,而无需控制对外部服务的访问。
然后后面你可以决定配置对外部服务的访问 .
要查看此方法,您需要确保将
global.outboundTrafficPolicy.mode
选项设置为ALLOW_ANY
来配置 Istio 安装。除非您在安装 Istio 时将其明确设置为REGISTRY_ONLY
模式,否则默认情况下可能会启用它。运行以下命令以确认配置正确:
$ kubectl get configmap istio -n istio-system -o yaml | grep -o "mode: ALLOW_ANY" mode: ALLOW_ANY
如果启用了
mode: ALLOW_ANY
,它应出现在输出中。从
SOURCE_POD
向外部 HTTPS 服务发出几个请求以确认成功的200
响应:$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.google.com | grep "HTTP/"; kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://edition.cnn.com | grep "HTTP/" HTTP/2 200 HTTP/2 200
恭喜!您已成功从网格中发送出口流量。
这种访问外部服务的简单方法的缺点是您失去了对外部服务流量的监控和控制;例如,调用外部服务不会出现在 Mixer 日志中。 下一节将向您展示如何监视和控制网格对外部服务的访问。
控制外部服务
使用 Istio ServiceEntry
配置,您可以从 Istio 集群中访问任何可公开访问的服务。本节介绍如何配置对外部 HTTP 服务的访问,
httpbin.org, 以及外部 HTTPS 服务,
www.google.com 并且已久有 Istio 的流量监控和控制功能。
修改为默认阻止策略
要演示启用对外部服务的访问的受控方式,您需要将 global.outboundTrafficPolicy.mode
选项从 ALLOW_ANY
模式更改为 REGISTRY_ONLY
模式。
运行以下命令将
global.outboundTrafficPolicy.mode
选项改为REGISTRY_ONLY
:$ kubectl get configmap istio -n istio-system -o yaml | sed 's/mode: ALLOW_ANY/mode: REGISTRY_ONLY/g' | kubectl replace -n istio-system -f - configmap "istio" replaced
从
SOURCE_POD
向外部 HTTPS 服务发出一些请求,以验证它们现在是否被阻止:$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.google.com | grep "HTTP/"; kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://edition.cnn.com | grep "HTTP/" command terminated with exit code 35 command terminated with exit code 35
访问外部 HTTP 服务
创建一个
ServiceEntry
对象,放行对一个外部 HTTP 服务的访问:$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: httpbin-ext spec: hosts: - httpbin.org ports: - number: 80 name: http protocol: HTTP resolution: DNS location: MESH_EXTERNAL EOF
从
SOURCE_POD
向外部 HTTP 服务发出请求:$ kubectl exec -it $SOURCE_POD -c sleep -- curl http://httpbin.org/headers { "headers": { "Accept": "*/*", "Connection": "close", "Host": "httpbin.org", "User-Agent": "curl/7.60.0", ... "X-Envoy-Decorator-Operation": "httpbin.org:80/*", } }
注意由 Istio sidecar 代理添加的标题:
X-Envoy-Decorator-Operation
。检查
SOURCE_POD
的 sidecar 代理的日志:$ kubectl logs $SOURCE_POD -c istio-proxy | tail [2019-01-24T12:17:11.640Z] "GET /headers HTTP/1.1" 200 - 0 599 214 214 "-" "curl/7.60.0" "17fde8f7-fa62-9b39-8999-302324e6def2" "httpbin.org" "35.173.6.94:80" outbound|80||httpbin.org - 35.173.6.94:80 172.30.109.82:55314 -
请注意与您对
httpbin.org/headers
的 HTTP 请求相关的条目。检查 Mixer 日志。如果 Istio 部署在
istio-system
命名空间中,则打印日志的命令是:$ kubectl -n istio-system logs -l istio-mixer-type=telemetry -c mixer | grep 'httpbin.org' {"level":"info","time":"2019-01-24T12:17:11.855496Z","instance":"accesslog.logentry.istio-system","apiClaims":"","apiKey":"","clientTraceId":"","connection_security_policy":"unknown","destinationApp":"","destinationIp":"I60GXg==","destinationName":"unknown","destinationNamespace":"default","destinationOwner":"unknown","destinationPrincipal":"","destinationServiceHost":"httpbin.org","destinationWorkload":"unknown","grpcMessage":"","grpcStatus":"","httpAuthority":"httpbin.org","latency":"214.661667ms","method":"GET","permissiveResponseCode":"none","permissiveResponsePolicyID":"none","protocol":"http","receivedBytes":270,"referer":"","reporter":"source","requestId":"17fde8f7-fa62-9b39-8999-302324e6def2","requestSize":0,"requestedServerName":"","responseCode":200,"responseSize":599,"responseTimestamp":"2019-01-24T12:17:11.855521Z","sentBytes":806,"sourceApp":"sleep","sourceIp":"AAAAAAAAAAAAAP//rB5tUg==","sourceName":"sleep-88ddbcfdd-rgk77","sourceNamespace":"default","sourceOwner":"kubernetes://apis/apps/v1/namespaces/default/deployments/sleep","sourcePrincipal":"","sourceWorkload":"sleep","url":"/headers","userAgent":"curl/7.60.0","xForwardedFor":"0.0.0.0"}
请注意,
destinationServiceHost
属性等于httpbin.org
。 还要注意 HTTP 相关的属性:method
,url
,responseCode
等。使用 Istio 出口流量控制,您可以监控对外部 HTTP 服务的访问,包括每次访问的 HTTP 相关信息。
配置外部 HTTPS 服务
创建一个
ServiceEntry
以允许访问外部 HTTPS 服务。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: google spec: hosts: - www.google.com ports: - number: 443 name: https protocol: HTTPS resolution: DNS location: MESH_EXTERNAL EOF
从
SOURCE_POD
向外部 HTTPS 服务发出请求:$ kubectl exec -it $SOURCE_POD -c sleep -- curl -I https://www.google.com | grep "HTTP/" HTTP/2 200
检查
SOURCE_POD
的 sidecar 代理的日志:$ kubectl logs $SOURCE_POD -c istio-proxy | tail [2019-01-24T12:48:54.977Z] "- - -" 0 - 601 17766 1289 - "-" "-" "-" "-" "172.217.161.36:443" outbound|443||www.google.com 172.30.109.82:59480 172.217.161.36:443 172.30.109.82:59478 www.google.com
请注意与您对
www.google.com
的 HTTPS 请求相关的条目。检查 Mixer 日志。如果 Istio 部署在
istio-system
命名空间中,则打印日志的命令是:$ kubectl -n istio-system logs -l istio-mixer-type=telemetry -c mixer | grep 'www.google.com' {"level":"info","time":"2019-01-24T12:48:56.266553Z","instance":"tcpaccesslog.logentry.istio-system","connectionDuration":"1.289085134s","connectionEvent":"close","connection_security_policy":"unknown","destinationApp":"","destinationIp":"rNmhJA==","destinationName":"unknown","destinationNamespace":"default","destinationOwner":"unknown","destinationPrincipal":"","destinationServiceHost":"www.google.com","destinationWorkload":"unknown","protocol":"tcp","receivedBytes":601,"reporter":"source","requestedServerName":"www.google.com","sentBytes":17766,"sourceApp":"sleep","sourceIp":"rB5tUg==","sourceName":"sleep-88ddbcfdd-rgk77","sourceNamespace":"default","sourceOwner":"kubernetes://apis/apps/v1/namespaces/default/deployments/sleep","sourcePrincipal":"","sourceWorkload":"sleep","totalReceivedBytes":601,"totalSentBytes":17766}
请注意,
requestedServerName
属性等于www.google.com
。 使用 Istio 出口流量控制,您可以监控对外部 HTTPS 服务的访问,特别是 SNI 以及发送和接收的字节数。 请注意,在 HTTPS 中,所有与 HTTP 相关的信息(如方法,URL 路径,响应代码)都已加密,因此 Istio 无法查看,也无法监控 HTTPS 的信息。 如果您需要在访问外部时监视与 HTTP 相关的信息 HTTPS 服务,您可能希望让您的应用程序发出 HTTP 请求 配置 Istio 以执行 TLS。
管理外部服务的流量
通过 ServiceEntry
访问外部服务的流量,和网格内流量类似,都可以进行 Istio 路由规则 的配置。下面我们使用 istioctl
为 httpbin.org 服务设置一个超时规则。
在测试 Pod 内部,使用
curl
调用 httpbin.org 这一外部服务的/delay
端点:$ kubectl exec -it $SOURCE_POD -c sleep sh $ time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5 200 real 0m5.024s user 0m0.003s sys 0m0.003s
这个请求会在大概五秒钟左右返回一个内容为
200 (OK)
的响应。退出测试 Pod,使用
kubectl
为 httpbin.org 外部服务的访问设置一个 3 秒钟的超时:$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin-ext spec: hosts: - httpbin.org http: - timeout: 3s route: - destination: host: httpbin.org weight: 100 EOF
等待几秒钟之后,再次发起 curl 请求:
$ kubectl exec -it $SOURCE_POD -c sleep sh $ time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5 504 real 0m3.149s user 0m0.004s sys 0m0.004s
这一次会在 3 秒钟之后收到一个内容为
504 (Gateway Timeout)
的响应。虽然 httpbin.org 还在等待他的 5 秒钟,Istio 却在 3 秒钟的时候切断了请求。
清除对外部服务的受控访问
$ kubectl delete serviceentry httpbin-ext google
$ kubectl delete virtualservice httpbin-ext --ignore-not-found=true
直接调用外部服务
如果想要跳过 Istio,直接访问某个 IP 范围内的外部服务,就需要对 Envoy sidecar 进行配置,阻止 Envoy 对外部请求的劫持。可以在 Helm 中设置 global.proxy.includeIPRanges
变量,然后使用 kubectl apply
命令来更新名为 istio-sidecar-injector
的 Configmap
。在 istio-sidecar-injector
更新之后,global.proxy.includeIPRanges
会在所有未来部署的 Pod 中生效。
排除所有外部 IP 重定向到 sidecar 代理的一种简单方法是将 global.proxy.includeIPRanges
配置选项设置为用于内部集群服务的 IP 范围。这些 IP 范围值取决于群集运行的平台。
确定平台的内部 IP 范围
根据集群部署情况为 global.proxy.includeIPRanges
赋值。
IBM Cloud Private
从 IBM Cloud Private 配置文件(
cluster/config.yaml
)中获取service_cluster_ip_range
。$ cat cluster/config.yaml | grep service_cluster_ip_range
会输出类似内容:
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
命令来获取范围的具体定义,例如:
$ gcloud container clusters describe XXXXXXX --zone=XXXXXX | grep -e clusterIpv4Cidr -e servicesIpv4Cidr
clusterIpv4Cidr: 10.4.0.0/14
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, Docker For Desktop, Bare Metal
它没有固定值,但默认值为 10.96.0.0/12 。要确定您的实际值:
$ kubectl describe pod kube-apiserver -n kube-system | grep 'service-cluster-ip-range'
--service-cluster-ip-range=10.96.0.0/12
使用 --set global.proxy.includeIPRanges="10.96.0.0/12"
配置代理绕过
使用特定于您平台的 IP 范围更新 istio-sidecar-injector
configmap。
例如,假设范围是 10.0.0.1/24,请使用以下命令:
$ helm template install/kubernetes/helm/istio <the flags you used to install Istio> --set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml | kubectl apply -f -
使用您以前使用的相同 Helm 命令安装 Istio,
特别,确保为 --namespace
标志使用相同的值
添加这些标志: --set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml
。
访问外部服务
由于旁路配置仅影响新部署,因此需要按照开始之前部分中的说明重新部署 sleep
应用程序。
更新了 ConfigMap
istio-sidecar-injector
并且重新部署了 sleep
应用之后,Istio sidecar 就应该只劫持和管理集群内部的请求了。任意的外部请求都会简单的绕过 Sidecar,直接访问目的地址。
$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec -it $SOURCE_POD -c sleep curl http://httpbin.org/headers
{
"headers": {
"Accept": "*/*",
"Connection": "close",
"Host": "httpbin.org",
"User-Agent": "curl/7.60.0"
}
}
与通过 HTTP 或 HTTPS 访问外部服务不同,您看不到与 Istio sidecar 相关的任何 header ,并且发送到外部服务的请求既不出现在 sidecar 的日志中,也不出现在 Mixer 日志中。 绕过 Istio sidecar 意味着您无法再监控对外部服务的访问。
清除对外部服务的直接访问
更新 istio-sidecar-injector.configmap.yaml
configmap 以将所有出站流量重定向到 sidecar 代理:
$ helm template install/kubernetes/helm/istio <the flags you used to install Istio> -x templates/sidecar-injector-configmap.yaml | kubectl apply -f -
理解原理
这个任务中,我们使用两种方式从 Istio 服务网格内部来完成对外部服务的调用:
使用
ServiceEntry
(推荐方式)配置 Istio sidecar,从它的重定向 IP 表中排除外部服务的 IP 范围
第一种方式(ServiceEntry
)中,网格内部的服务不论是访问内部还是外部的服务,都可以使用同样的 Istio 服务网格的特性。我们通过为外部服务访问设置超时规则的例子,来证实了这一优势。
第二种方式越过了 Istio sidecar proxy,让服务直接访问到对应的外部地址。然而要进行这种配置,需要了解云供应商特定的知识和配置。
安全说明
要以更安全的方式实施出口流量控制,您必须通过出口网关指示出口流量,并查看本节中描述的安全性附加安全性考虑因素。
清理
停止 sleep 服务:
$ kubectl delete -f @samples/sleep/sleep.yaml@