使用外部 Web 服务
HTTPS 流量的出口规则
在许多情况下,在 service mesh 中的微服务序并不是应用程序的全部,有时, 网格内部的微服务需要使用在服务网格外部的遗留系统提供的功能,虽然我们希望逐步将这些系统迁移到服务网格中。 但是在迁移这些系统之前,必须让服务网格内的应用程序能访问它们。还有其他情况, 应用程序使用外部组织提供的 Web 服务,通常是通过万维网提供的服务。
在这篇博客文章中,我修改了 Istio Bookinfo 示例应用程序让它可以 从外部 Web 服务(Google Books APIs )获取图书详细信息。 我将展示如何使用 mesh-external service entries 在 Istio 中启用外部 HTTPS 流量。最后, 我解释了当前与 Istio 出口流量控制相关的问题。
初始设定
为了演示使用外部 Web 服务的场景,我首先使用安装了 Istio 的 Kubernetes 集群, 然后我部署 Istio Bookinfo 示例应用程序, 此应用程序使用 details 微服务来获取书籍详细信息,例如页数和发布者, 原始 details 微服务提供书籍 详细信息,无需咨询任何外部服务。
此博客文章中的示例命令适用于 Istio 1.0+,无论启用或不启用双向 TLS。
Bookinfo 配置文件位于 Istio 发行存档的 samples/bookinfo
目录中。
以下是原始 Bookinfo 示例应用程序中应用程序端到端体系结构的副本。
执行部署应用程序、确认应用正在运行,以及 应用默认目标规则中的步骤部分。
Bookinfo 使用 HTTPS 访问 Google 图书网络服务
让我们添加一个新版本的 details 微服务,_v2_,从 Google Books APIs 中获取图书详细信息。
它设定了服务容器的 DO_NOT_ENCRYPT
环境变量为 false
。此设置将指示已部署服务使用 HTTPS(而不是 HTTP )来访问外部服务。
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@ --dry-run -o yaml | kubectl set env --local -f - 'DO_NOT_ENCRYPT=false' -o yaml | kubectl apply -f -
现在,应用程序的更新架构如下所示:
请注意,Google Book 服务位于 Istio 服务网格之外,其边界由虚线标记。
现在让我们将指向 details 微服务的所有流量定向到 _details v2_:
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
请注意,VirtualService
依赖于您在应用默认目标规则部分中创建的目标规则。
在确定 ingress 的 IP 和端口之后, 让我们访问应用程序的网页。
糟糕… 页面显示 _Error fetching product details_,而不是书籍详细信息:
好消息是我们的应用程序没有崩溃, 通过良好的微服务设计,我们没有让故障扩散。在我们的例子中,
失败的 details 微服务不会导致 productpage
微服务失败, 尽管 details 微服务失败,
仍然提供了应用程序的大多数功能, 我们有优雅的服务降级:正如您所看到的,评论和评级正确显示,
应用程序仍然有用。
那可能出了什么问题?啊…… 答案是我忘了启用从网格内部到外部服务的流量,在本例中是 Google Book Web 服务。 默认情况下,Istio sidecar 代理(Envoy proxies) 阻止到集群外目的地的所有流量, 要启用此类流量,我们必须定义 mesh-external service entry。
启用对 Google Books 网络服务的 HTTPS 访问
不用担心,让我们定义网格外部 ServiceEntry
并修复我们的应用程序。您还必须定义 virtual
service 使用 SNI 对外部服务执行路由。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: googleapis
spec:
hosts:
- www.googleapis.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: googleapis
spec:
hosts:
- www.googleapis.com
tls:
- match:
- port: 443
sni_hosts:
- www.googleapis.com
route:
- destination:
host: www.googleapis.com
port:
number: 443
weight: 100
EOF
现在访问应用程序的网页会显示书籍详细信息而不会出现错误:
您可以查询您的 ServiceEntry
:
$ kubectl get serviceentries
NAME AGE
googleapis 8m
您可以删除您的 ServiceEntry
:
$ kubectl delete serviceentry googleapis
serviceentry "googleapis" deleted
并在输出中看到删除了 ServiceEntry
。
删除 ServiceEntry
后访问网页会产生我们之前遇到的相同错误,即 Error fetching product details,
正如我们所看到的,与许多其他 Istio 配置一样,ServiceEntry
是动态定义的 , Istio 运算符可以动态决定
它们允许微服务访问哪些域, 他们可以动态启用和禁用外部域的流量,而无需重新部署微服务。
清除对 Google 图书网络服务的 HTTPS 访问权限
$ kubectl delete serviceentry googleapis
$ kubectl delete virtualservice googleapis
$ kubectl delete -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
$ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@
由 Istio 发起的 TLS
这个故事有一个警告。假设您要监视您的微服务使用 Google API 的哪个特定集
(书籍,日历,任务等)
假设您要强制执行仅允许使用图书 API 的策略。
假设您要监控您的微服务访问的标识符。对于这些监视和策略任务,您需要知道 URL 路径。
考虑例如 URL www.googleapis.com/books/v1/volumes?q=isbn:0486424618
。
在该网址中,路径段指定了图书 API
/books
和路径段的 ISBN 代码
/volumes?q=isbn:0486424618
。但是,在 HTTPS 中,所有 HTTP 详细信息(主机名,路径,标头等)都是加密的
sidecar 代理的这种监督和策略执行是无法实现的。Istio 只能通过 SNI(_Server Name Indication_)得知加密请求中的主机名称,在这里就是 www.googleapis.com
。
为了允许 Istio 基于域执行出口请求的过滤,微服务必须发出 HTTP 请求, 然后,Istio 打开到目标的 HTTPS 连接(执行 TLS 发起), 根据微服务是在 Istio 服务网格内部还是外部运行, 微服务的代码必须以不同方式编写或以不同方式配置, 这与最大化透明度 的 Istio 设计目标相矛盾, 有时我们需要妥协……
下图显示了如何执行外部服务的 HTTPS 流量, 在顶部,Istio 服务网格外部的微服务发送常规 HTTPS 请求, 端到端加密, 在底部,Istio 服务网格内的相同微服务必须在 pod 内发送未加密的 HTTP 请求, 这些请求被 sidecar Envoy 代理拦截 , sidecar 代理执行 TLS 发起,因此 pod 和外部服务之间的流量被加密。
以下是我们如何在 Bookinfo 的 details 微服务代码 中使用 Ruby net/http 模块:
uri = URI.parse('https://www.googleapis.com/books/v1/volumes?q=isbn:' + isbn)
http = Net::HTTP.new(uri.host, ENV['DO_NOT_ENCRYPT'] === 'true' ? 80:443)
...
unless ENV['DO_NOT_ENCRYPT'] === 'true' then
http.use_ssl = true
end
当定义 WITH_ISTIO
环境变量时,在没有 SSL(普通 HTTP )的情况下请求会通过 80 端口执行。
我们将 details v2 的部署配置文件 的环境变量 DO_NOT_ENCRYPT
设置为 _“true”_。
container
部分:
env:
- name: DO_NOT_ENCRYPT
value: "true"
在下一节中,您将配置 TLS 发起以访问外部 Web 服务。
具有 TLS 的 Bookinfo 起源于 Google Books 网络服务
部署 details v2 版本,将 HTTP 请求发送到 Google Books API。 在
bookinfo-details-v2.yaml
中,DO_NOT_ENCRYPT
变量设置为 true。$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@
将指向 details 微服务的流量定向到 _details v2_。
$ kubectl apply -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
为
www.google.apis
创建网格外部ServiceEntry
,virtual service 将目标端口从 80 重写为 443,并执行 TLS 的destination rule
。$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: googleapis spec: hosts: - www.googleapis.com ports: - number: 80 name: http protocol: HTTP - number: 443 name: https protocol: HTTPS resolution: DNS --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: rewrite-port-for-googleapis spec: hosts: - www.googleapis.com http: - match: - port: 80 route: - destination: host: www.googleapis.com port: number: 443 --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: originate-tls-for-googleapis spec: host: www.googleapis.com trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 443 tls: mode: SIMPLE # 访问 edition.cnn.com 时启动 HTTPS EOF
访问应用程序的网页,并验证显示的书籍详细信息没有错误。
检查 details v2 的 sidecar 代理的日志,并查看 HTTP 请求。
$ kubectl logs $(kubectl get pods -l app=details -l version=v2 -o jsonpath='{.items[0].metadata.name}') istio-proxy | grep googleapis [2018-08-09T11:32:58.171Z] "GET /books/v1/volumes?q=isbn:0486424618 HTTP/1.1" 200 - 0 1050 264 264 "-" "Ruby" "b993bae7-4288-9241-81a5-4cde93b2e3a6" "www.googleapis.com:80" "172.217.20.74:80" EOF
请注意日志中的 URL 路径,可以监视路径并根据它来应用访问策略。要了解有关 HTTP 出口流量的监控和访问策略 的更多信息,请查看归档博客之出口流量监控之日志。
清除 TLS 原始数据到 Google Books 网络服务
$ kubectl delete serviceentry googleapis
$ kubectl delete virtualservice rewrite-port-for-googleapis
$ kubectl delete destinationrule originate-tls-for-googleapis
$ kubectl delete -f @samples/bookinfo/networking/virtual-service-details-v2.yaml@
$ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-details-v2.yaml@
Istio 双向 TLS 的关系
请注意,在这种情况下,TLS 的源与 Istio 应用的双向 TLS 无关, 无论 Istio 双向 TLS 是否启用,外部服务的 TLS 源都将起作用 , 保证服务网内的服务到服务通信, 并为每个服务提供强大的身份认证, 在此博客文章中的 外部服务的情况下,我们有单向 TLS, 这是用于保护 Web 浏览器和 Web 服务器之间通信的相同机制 , TLS 应用于与外部服务的通信, 以验证外部服务器的身份并加密流量。
结论
在这篇博文中,我演示了 Istio 服务网格中的微服务如何通过 HTTPS 使用外部 Web 服务, 默认情况下,
Istio 会阻止集群外主机的所有流量, 要启用此类流量,请使用 mesh-external, 必须为服务网格创建 ServiceEntry
,
可以通过 HTTPS 访问外部站点,当微服务发出 HTTPS 请求时,流量是端到端加密的,但是 Istio 无法监视 HTTP 详细信息,
例如请求的 URL 路径。当微服务发出 HTTP 请求时,Istio 可以监视请求的 HTTP 详细信息并强制执行基于 HTTP 的访问策略。
但是,在这种情况下,微服务和 sidecar 代理之间的流量是未加密的。在具有非常严格的安全要求的组织中,
可以禁止未加密的部分流量。