默认情况下,服务网格中的服务无法访问外部的服务。要想访问外部服务,可以创建ServiceEntry对象,把外部服务注册到服务网格的注册中心,这样,网格中的服务就可以访问外部服务了。同时,可以配合VirtualService更好地控制网格内服务访问外部服务的行为。本节介绍通过ServiceEntry来导入外部服务,让服务网格中的服务也可以访问外部的服务的方法 [1] 。也可以使用Egress gateway配合ServiceEntry统一管理集群的出口流量;此外,还可以通过修改Istio的部署配置文件,直接放开所有对外部服务的访问流量。
1.使用ServiceEntry导入网格外部服务
(1)配置访问HTTP协议的外部服务
导入httpbin.org服务示例:
1 apiVersion: networking.istio.io/v1alpha3 2 kind: ServiceEntry 3 metadata: 4 name: httpbin-ext 5 spec: 6 hosts: 7 - httpbin.org 8 ports: 9 - number: 80 10 name: http 11 protocol: HTTP 12 resolution: DNS 13 location: MESH_EXTERNAL 14 --- 15 apiVersion: networking.istio.io/v1alpha3 16 kind: VirtualService 17 metadata: 18 name: httpbin-ext 19 spec: 20 hosts: 21 - httpbin.org 22 http: 23 - timeout: 3s 24 route: 25 - destination: 26 host: httpbin.org
第1~13行定义了名为httpbin-ext的ServiceEntry。
第6~7行定义的hosts字段指定了要访问的外部服务的DNS域名,可以使用泛域名匹配,如*.httpbin.org。如果是非HTTP协议的流量,会使用ports和addresses字段的定义来区分目标地址。
第8~9行定义了要访问的外部服务的端口和协议。
第12行定义的resolution字段定义了外部服务的解析方式,取值包括DNS、STATIC、NONE,一般情况下使用DNS方式,设置为STATIC时需要在endpoints中配置IP地址,当应用程序自己负责解析时,使用NONE方式。
第13行定义的location字段表示服务所处的位置,取值可以为MESH_EXTERNAL和MESH_INTERNAL两种,MESH_EXTERNAL一般用于导入外部服务,并不作为网格的一部分,通常服务以API的方式提供使用。MESH_INTERNAL一般用于把网格外的服务作为网络中的一部分,这个配置会影响Istio对服务的mTLS认证及策略的实施等。当网格内的服务与网格外的服务通信时,mTLS认证会被关闭,策略的实施也会由客户端实施,而不是由服务端实施。
第15~26行定义了同名的VirtualService,进一步配合ServiceEntry实现更好地控制网格内服务访问外部服务的行为。hosts字段中的值httpbin.org表示网格内服务访问时使用的域名,route里的host字段应设置为在ServiceEntry定义中hosts字段指定的域名。
(2)配置访问HTTPS协议的外部服务
导入baidu.com服务示例:
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: baidu-ext spec: hosts: - baidu.com ports: - number: 443 name: https protocol: HTTPS resolution: DNS location: MESH_EXTERNAL
与上述导入HTTP协议的外部服务定义基本一致,只是协议字段变成HTTPS协议,并且减少了对应的VirtualService的定义。HTTPS协议的服务也可以配置VirtualService进行更细致的流量管理,与HTTP协议的使用方法基本一致。
2.使用Egress gateway统一管理访问外部服务的流量
配置Egress gateway示例:
1 apiVersion: networking.istio.io/v1alpha3 2 kind: Gateway 3 metadata: 4 name: istio-egressgateway 5 spec: 6 selector: 7 istio: egressgateway 8 servers: 9 - port: 10 number: 80 11 name: http 12 protocol: HTTP 13 hosts: 14 - "*" 15 - port: 16 number: 443 17 name: tls 18 protocol: TLS 19 tls: 20 mode: PASSTHROUGH 21 hosts: 22 - "*"
第6~7行选择Istio部署时默认创建的egressgateway作为网格访问外部服务的流量出口。
第9~14行定义了一个支持HTTP协议的网关出口。
第15~22行定义了一个支持HTTPS协议的网关出口。tls字段中的mode模式指定为PASSTHROUGH,表示不进行TLS终止,也就是说,服务通过Egress gateway直接访问HTTPS协议的外部服务。
(1)配置通过Egress gateway访问外部HTTP协议服务
导入httpbin.org服务示例:
1 apiVersion: networking.istio.io/v1alpha3 2 kind: ServiceEntry 3 metadata: 4 name: external-svc-httpbin 5 spec: 6 hosts: 7 - httpbin.org 8 location: MESH_EXTERNAL 9 ports: 10 - number: 80 11 name: http 12 protocol: HTTP 13 resolution: DNS 14 --- 15 apiVersion: networking.istio.io/v1alpha3 16 kind: VirtualService 17 metadata: 18 name: gateway-routing-http-httpbin 19 spec: 20 hosts: 21 - httpbin.org 22 gateways: 23 - mesh 24 - istio-egressgateway 25 http: 26 - match: 27 - port: 80 28 gateways: 29 - mesh 30 route: 31 - destination: 32 host: istio-egressgateway.istio-system.svc.cluster.local 33 - match: 34 - port: 80 35 gateways: 36 - istio-egressgateway 37 route: 38 - destination: 39 host: httpbin.org
第26~32行的路由规则定义表明,对于网格内部请求httpbin.org的流量,全部转发到istio-egressgateway外部网关服务上。
第33~39行的路由规则定义表明,对于不是网格内部访问httpbin.org的流量,也就是说,对于从istio-egressgateway外部网关中请求httpbin.org的流量,全部转发到ServiceEntry中定义的httpbin.org地址上。
(2)配置通过Egress gateway访问外部HTTPS协议服务
导入baidu.com服务示例:
apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: external-svc-baidu spec: hosts: - www.baidu.com location: MESH_EXTERNAL ports: - number: 443 name: tls protocol: TLS resolution: DNS --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: gateway-routing-https-baidu spec: hosts: - www.baidu.com gateways: - mesh - istio-egressgateway tls: - match: - port: 443 gateways: - mesh sni_hosts: - www.baidu.com route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local - match: - port: 443 gateways: - istio-egressgateway sni_hosts: - www.baidu.com route: - destination: host: www.baidu.com
路由规则的配置方式与配置HTTP协议的路由规则基本一致,只是换成了tls的相关配置。
3.网格内服务直接调用外部所有服务
在Istio的部署配置中,可以指定服务网格只拦截指定IP段的流量,这样,当服务访问网格内的服务时,流量经过Envoy代理;而访问网格外部的服务时,Envoy代理将不再拦截流量,完全由调用方自己来处理流量。如果使用这种方式来开放网格内服务调用外部服务,你将不再能享受到服务网格带来的服务路由相关的功能,比如重试、超时、熔断等。所以不到万不得已的时候,请不要使用这种方式。下面展示了本书实验部署的Istio集群开启此功能的步骤 [2] 。
1)修改Istio部署文件。
找到/usr/local/istio/install/kubernetes/istio-demo.yaml文件的10503行左右的配置行或者istio-mini.yaml文件的326行,修改成如下的形式:
- "[[ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges` "10.244.0.0/16,10.96.0.0/12" ]]"
10.244.0.0/16是kube-controller-manager中配置的cluster-cidr的参数值,也就是Pod的IP地址段。10.96.0.0/12是kube-apiserver中配置的service-cluster-ip-range的参数值,也就是Service的IP地址段。
如上配置表明,网格只拦截10.244.0.0/16和10.96.0.0/12网段的流量,其他网段的流量全部放行,由应用自己处理。
2)应用配置文件:
$ kubectl apply -f /usr/local/istio/install/kubernetes/istio-demo.yaml
3)部署用于测试的Pod,新配置的规则只对新创建的Pod生效:
$ kubectl apply -f kubernetes/dns-test.yaml
4)访问服务:
$ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/user-agent { "user-agent": "curl/7.35.0" } $ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/ip { "origin": "116.226.24.128" } $ kubectl exec dns-test -c dns-test -- curl -s -I https://www.baidu.com/ HTTP/1.1 200 OK Accept-Ranges: bytes Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform Connection: Keep-Alive Content-Length: 277 Content-Type: text/html Date: Mon, 14 Jan 2019 15:01:31 GMT Etag: "575e1f8a-115" Last-Modified: Mon, 13 Jun 2016 02:50:50 GMT Pragma: no-cache Server: bfe/1.0.8.18
5)清理:
$ kubectl delete -f kubernetes/dns-test.yaml
【实验一】
以ServiceEntry方式访问外部服务。
1)创建测试Pod:
$ kubectl create -f kubernetes/dns-test.yaml
2)测试访问:
$ kubectl exec dns-test -c dns-test -- curl -I -s http://httpbin.org/user-agent HTTP/1.1 404 Not Found date: Mon, 14 Jan 2019 14:35:51 GMT server: envoy transfer-encoding: chunked $ kubectl exec dns-test -c dns-test -- curl -s -I https://www.baidu.com/ command terminated with exit code 35
3)创建httpbin和baidu的ServiceEntry:
$ kubectl apply -f istio/route/service-entry-httpbin.yaml $ kubectl apply -f istio/route/service-entry-baidu.yaml
4)测试访问:
$ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/user-agent { "user-agent": "curl/7.35.0" } $ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/ip { "origin": "116.226.24.128" } $ kubectl exec dns-test -c dns-test -- curl -s -I https://www.baidu.com/ HTTP/1.1 200 OK Accept-Ranges: bytes Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform Connection: Keep-Alive Content-Length: 277 Content-Type: text/html Date: Mon, 14 Jan 2019 14:40:52 GMT Etag: "575e1f8a-115" Last-Modified: Mon, 13 Jun 2016 02:50:50 GMT Pragma: no-cache Server: bfe/1.0.8.18
5)清理:
$ kubectl delete -f istio/route/service-entry-baidu.yaml $ kubectl delete -f istio/route/service-entry-httpbin.yaml $ kubectl delete -f kubernetes/dns-test.yaml
【实验二】
通过Egress gateway方式访问外部服务。
1)创建测试Pod:
$ kubectl create -f kubernetes/dns-test.yaml
2)测试访问:
$ kubectl exec dns-test -c dns-test -- curl -I -s http://httpbin.org/user-agent HTTP/1.1 404 Not Found date: Mon, 14 Jan 2019 14:55:51 GMT server: envoy transfer-encoding: chunked $ kubectl exec dns-test -c dns-test -- curl -s -I https://www.baidu.com/ command terminated with exit code 35
3)创建Egress gateway:
$ kubectl create -f istio/route/gateway-egress.yaml
4)创建httpbin和baidu的路由规则:
$ kubectl create -f istio/route/service-entry-egress-httpbin.yaml $ kubectl create -f istio/route/service-entry-egress-baidu.yaml
5)测试访问:
$ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/user-agent { "user-agent": "curl/7.35.0" } $ kubectl exec dns-test -c dns-test -- curl -s http://httpbin.org/ip { "origin": "10.244.1.43, 116.226.24.128" } $ kubectl exec dns-test -c dns-test -- curl -s -I https://www.baidu.com/ HTTP/1.1 200 OK Accept-Ranges: bytes Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform Connection: Keep-Alive Content-Length: 277 Content-Type: text/html Date: Mon, 14 Jan 2019 14:49:14 GMT Etag: "575e1f8a-115" Last-Modified: Mon, 13 Jun 2016 02:50:50 GMT Pragma: no-cache Server: bfe/1.0.8.18
6)清理:
$ kubectl delete -f istio/route/service-entry-egress-baidu.yaml $ kubectl delete -f istio/route/service-entry-egress-httpbin.yaml $ kubectl delete -f istio/route/gateway-egress.yaml $ kubectl delete -f kubernetes/dns-test.yaml
【总结】
默认情况下,网格内服务是不能访问任何外部服务的,当前可以使用如下三种方式来满足网格内服务调用外部服务的需求:
1)使用ServiceEntry,让每一个Envoy代理都可以访问特定外部服务。
2)使用Egress gateway管理出口流量,统一管理对外部服务的调用出口。
3)配置Istio直接开放对任何外部服务的调用,调用外部服务的流量由网格内服务自行管理。
第一种方式适用于集群中每台服务器都能和外部互通的网络环境。第二种方式适用于集群中存在边界服务器,或者集群中只有部分节点能与外部网络互通,所有外部流量都经过此服务器的情况。当然使用第二种方式也可以只是为了使用Egress gateway对网格的出口流量进行统一管理。第三种方式是最直接、暴力的方式,也是最不推荐的方式,因为使用这种方式的流量不经过网格,所有流量全部由网格内的服务自行处理,此时出口流量就不能享受到网格带来的路由管理等功能了,而且这也会降低服务网格的安全性。
[1] 详细文档可参考官方文档https://istio.io/docs/reference/config/istio.networking.v1alpha3/#ServiceEntry。
[2] 具体配置方式可参考官方文档https://istio.io/docs/tasks/traffic-management/egress/#calling-external-services-directly。