7.6 流量镜像

上一章

7.8 实现服务A/B测试

下一章

更多图书

7.7 管理集群的出口流量

默认情况下,服务网格中的服务无法访问外部的服务。要想访问外部服务,可以创建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。