Istio重要对象ServiceEntry详解 –《Istio权威指南》书摘

本节书摘来自华为云原生技术丛书《Istio权威指南(上):云原生服务网格Istio原理与实践》一书原理篇的第3章 流量治理的原理,3.5节ServiceEntry(服务条目),更多内容请参照原书

第3章 流量治理的原理

本章讲解Istio提供的流量治理相关内容,即Istio流量治理要解决的问题、实现原理、配置模型、配置定义和典型应用,包括负载均衡、服务熔断、故障注入、灰度发布、故障转移、入口流量和出口流量等流量管理能力的通用原理、模型,以及Istio基于服务网格形态实现的原理和机制;同时会详细解析如何通过Istio中的VirtualService、DestinationRule、Gateway、ServiceEntry、WorkloadEntry、WorkloadGroup、Sidecar、EnvoyFilter、WasmPlugin等重要的服务管理配置来实现流量治理能力。在内容安排上,每节在讲解治理能力前都会从一个最精简的入门示例入手,再详细解析配置模型和定义,并辅以典型的应用案例来呈现其使用方法和应用场景。

3.5 ServiceEntry(服务条目)

在第2章介绍架构和服务模型时提到,在Istio中管理的大部分服务都是自动注册的Kubernetes服务。但在实际应用中经常还有其他类型的服务并不能自动注册,这就需要一种服务注册机制。在Istio中提供了ServiceEntry对象进行服务注册,以这种方式注册的服务和Kubernetes服务一样被服务网格管理,可以对其配置各种流量规则。

早期的ServiceEntry主要用于服务网格外部服务注册,比如注册外部的SaaS API或中间件云服务等。当前ServiceEntry的应用范围更加广泛,包括一些非容器的内部服务,比如比较典型的虚拟机类型的服务,配合要在3.6节和3.7节介绍的WorkloadEntry和WorkloadGroup可以实现完整的服务定义和服务实例注册功能。

3.5.1 入门示例

下面通过一个入门示例了解ServiceEntry的基本用法,在该示例中通过ServiceEntry包装了一个对api.forecast.weather的服务网格外部服务的访问。通过如下配置即可把这个服务网格外部服务注册到服务网格中,并管理其访问流量:

 1apiVersion: networking.istio.io/v1beta1
 2kind: ServiceEntry
 3metadata:
 4  name: weather-external
 5spec:
 6  hosts:
 7  - api.forecast.weather
 8  ports:
 9  - number: 80
10    name: http
11    protocol: HTTP
12  resolution: DNS
13  location: MESH_EXTERNAL

3.5.2 配置模型

如图3-68所示,ServiceEntry的配置模型主要由以下两部分组成。

◎ 服务自身定义:定义服务的访问信息,主要包括服务域名hosts和端口ports等,表示服务的访问入口;还包括服务位置(location)和解析方式(resolution)。服务自身的定义类似Kubernetes上Service的功能和定义。

◎ 服务实例关联:通过workloadSelector关联到服务对应的实例。类似Kubernetes中Service的后端实例选择机制。

图3-68 ServiceEntry的配置模型

​ 图3-68 ServiceEntry的配置模型

3.5.3 配置定义

1.hosts(服务域名)

在服务发现模型中,最重要的自然是服务名和服务访问地址。Istio在通过ServiceEntry定义服务时,通过hosts来表示这个访问入口。在使用上有以下几点需要说明。

◎ 对于HTTP流量,hosts匹配HTTP头域的Host或Authority。

◎ 对于HTTPS或TLS流量,hosts匹配SNI。

◎ 对于其他协议的流量,不匹配hosts,而是使用下面的addresses和port字段。

◎ 当resolution被设置为DNS类型并且没有指定endpoints时,这个字段用作后端的域名来解析后端地址。

在Istio的流量规则被应用时,VirtualService和DestinationRule也会匹配这个hosts,来决定生效的流量规则。

2.address(虚拟IP地址)

address表示与服务关联的虚拟IP地址,可以是CIDR这种前缀表达式:

1spec:
2  hosts:
3  - recommendation # not used
4  addresses:
5  - 192.168.99.99 # VIPs

对于TCP服务,当设置了address字段时,在Enovy上会创建对应地址的监听器,并将流量转发到定义的后端服务。如果addresses为空,则只能根据目标端口来识别,在这种情况下,这个端口不能被服务网格的其他服务使用,即服务网格数据面只是作为一个TCP代理,把某个特定端口的流量转发到配置的目标后端。

如下两个ServiceEntry定义了两个相同端口的服务网格外部服务,对应两个外部域名。两个ServiceEntry都没有配置address,在Envoy生成的配置中会对每个ServiceEntry都生成一个cluster,分别是:"outbound|8099|| api.forecast.weather"和"outbound|8099||api. forecast2.weather"。但是只会生成一个监听器lister:0.0.0.0_8099,只会关联到先创建的ServiceEntry,即outbound|8099|| api.forecast.weather,也就是说,Sidecar在服务网格范围内只能通过这个端口向一个目标后端转发流量。

但是对HTTP的ServiceEntry,情况会有很大的不同。若将以上ServiceEntry协议改为HTTP,则在Envoy上也会生成两个cluster,同时会生成一个0.0.0.0_8099的监听器;在七层流量管理器上关联了一个8099的路由router;在router中对两个服务域名分别生成两个不同的虚拟主机:api.forecast.weather:8099和api.forecast2.weather:8099,根据请求头域的不同,Host或Authority会将流量分发到不同的后端服务。

3.ports(服务端口)

ports是服务定义的必选字段。ServiceEntry支持多端口形式,每个端口都可以配置服务的端口号(number)、协议(protocol)、端口名(name)和目标端口(targetPort)。

如下示例中的ports配置,表示定义了一个HTTP服务端口是8099。服务网格数据面Envoy会根据这个端口的配置生成监听器,将这个端口上的流量通过七层过滤器再关联特定的路由配置,转发到ServiceEntry定义的后端服务:

1ports:
2  - number: 8099
3    name: http
4    protocol: HTTP
4.location(服务位置)

location用于设置服务是在服务网格内部还是在服务网格外部,相应地包含以下两种模式。

◎ MESH_EXTERNAL:表示注册为服务网格外部服务,比如通过API访问的服务网格外部服务。示例中的api.forecast.weather就是这样一个服务网格外部服务。

◎ MESH_INTERNAL:表示服务网格内部服务,比如虚拟机等服务可以通过这种方式注册和管理,和被服务网格管理的Kubernetes服务具有相同的能力。

对于虚拟机等MESH_INTERNAL类型的服务网格内部服务,一般在源服务实例和目标服务实例上都会安装服务网格代理,因此具有完整的服务网格能力;但是对于MESH_EXTERNAL类型的服务网格外部服务,服务端不会安装服务网格代理,只有通过Sidecar或Egress-gateway的服务网格出流量可以被服务网格管理。流量规则被定义在目标服务上,但是大部分执行在客户端,所以服务网格对MESH_EXTERNAL类型的服务网格外部服务仍然可以应用丰富的管理手段,只有少量的mTLS等依赖服务端Sidecar的功能不适用。

5.resolution(服务解析方式)

resolution表示服务网格代理在转发流量前,通过哪种方式解析得到服务实例。如图3-69所示,服务还是根据原有的方式去发出请求,首先在集群中将域名解析到一个服务的IP地址上,类似Kubernetes中的Cluster IP;服务访问Cluster IP的出流量随后被服务网格代理拦截;服务网格代理最后根据配置获取服务实例列表,并选择一个目标服务实例发出请求。这里的resolution配置的解析方式影响的只是图上服务网格代理解析服务实例的流程,对前面的应用解析没有影响。

图3-69  ServiceEntry服务的解析流程

​ 图3-69 ServiceEntry服务的解析流程

ServiceEntry主要支持如下几种解析方式。

1)STATIC(静态解析)

表示服务网格代理决定接收流量的服务实例。一般在服务网格中注册的服务都使用STATIC方式,类似Kubernetes里普通ClusterIP的Service。在如下示例中,当服务网格内部服务访问ServiceEntry定义的recommendation时,服务网格会做服务发现并得到10.118.12.12和10.118.12.13两个实例地址,在两个实例上做负载均衡。实际上Envoy会为这种STATIC解析方式的ServiceEntry创建一个EDS类型的cluster。

 1spec:
 2  hosts:
 3  - recommendation # not used
 4  addresses:
 5  - 192.168.99.99 # VIPs
 6  ports:
 7  - number: 8099
 8    name: tcp
 9    protocol: TCP
10  location: MESH_INTERNAL
11  resolution: STATIC
12  endpoints:
13  - address: 10.118.12.12
14  - address: 10.118.12.13

注意:ServicEntry中的STATIC解析方式和Envoy服务发现中的STATIC解析方式略有不同,前者指服务网格代理基于EDS进行服务发现,后者指服务的后端地址在Envoy中已被静态配置。

2)DNS(域名解析)

表示用查询环境下的DNS进行解析。如果没有设置endpoints,代理就会使用在hosts中指定的域名进行DNS解析,要求在hosts中未使用通配符;如果设置了endpoints,则使用endpoints中的DNS地址解析出目标IP地址。示例如下。

◎ 不配置后端:比如在本节入门示例中使用域名api.forecast.weather配置hosts,定义了一个服务网格外部服务,当服务网格的服务通过域名访问这个服务网格外部服务时,服务网格会查询DNS,返回这个域名对应的IP地址并进行访问,这也是ServiceEntry定义服务网格外部服务的常见做法。

◎ 配置后端:更一般的做法是按照如下方式定义一个服务网格外部服务,包含多个实例,每个实例都通过域名表达。这样当应用通过服务网格访问api.forecast.weather服务时,服务网格会把流量分发到两个后端weatherdb1.com和weatherdb2.com,对这两个后端的访问会基于DNS进行解析。

其实观察ServiceEntry在Envoy中生成的配置会发现,二者都是在Envoy上生成了STRICT_DNS类型的Cluster。区别在于,前者只有一个后端实例,实例地址就是这个服务域名的地址api.forecast.weather;后者的实例地址是配置的两个地址weatherdb1.com和weatherdb2.com。

 1spec:
 2  hosts:
 3  - api.forecast.weather
 4  ports:
 5  - number: 8099
 6    name: http
 7    protocol: HTTP
 8  resolution: DNS
 9  location: MESH_EXTERNAL
10  endpoints:
11  - address: weatherdb1.com
12  - address: weatherdb2.com

在配置这种解析策略时,代理的DNS解析是异步的,不会阻塞服务请求。另外,DNS解析的每个IP地址都会作为目标实例地址,当一个域名解析出多个IP地址时,会在这几个IP地址上都分配流量。

3)DNS_ROUND_ROBIN(轮转域名解析)

和DNS解析类似,不同之处在于,DNS_ROUND_ROBIN仅在建立新连接时使用返回的第1个IP地址。观察Envoy的配置会发现,在该类型的ServiceEntry上生成了一个Logical DNS的Cluster。

4)NONE:(无须解析)

代理直接转发流量到请求的IP地址,在这种方式下连通的已经是一个具体的可访问地址了,不需要再进行解析。比如典型的微服务框架的服务发现和内部负载均衡已经选到一个服务端的实例地址;或者在流量到达服务网格代理前已经通过iptables或eBPF转到了明确的后端地址。ServiceEntry的NONE模式类似于Kubernetes的Headless Service的处理模式。

6.endpoints(后端实例)

表示ServiceEntry关联的服务实例,在Istio 1.6之前的版本中是如下Endpoints类型的结构嵌套在ServiceEntry中,在Istio 1.6中通过一个独立的资源对象WorkloadEntry定义后端服务实例。关于WorkloadEntry,请参照3.6节的详细介绍。当在ServiceEntry的endpoints中配置后端时,一般只配置IP地址等基础信息。

1  endpoints:
2  - address: 10.118.12.12
3  - address: 10.118.12.13
7.workloadSelector(负载选择器)

除了基于endpoints配置后端实例,更推荐的做法是基于workloadSelector关联服务实例。通过workloadSelector可以动态选择ServiceEntry的服务后端进行服务注册,既可以选择Kubernetes的Pod,也可以选择WorkloadEntry描述的服务实例。如图3-70所示,一个ServiceEntry描述的服务可以同时选择这两种类型的实例,这样即可在不改变客户端调用方式的前提下在虚拟机和Kubernetes间进行服务实例的迁移。

图3-70  ServiceEntry基于标签的负载选择

​ 图3-70 ServiceEntry基于标签的负载选择

对于一个ServiceEntry,可通过endpoints或workloadSelector方式任选其一配置服务实例。

8.subjectAltNames(主题备用名,SAN)

表示这个服务负载的主题备用名即SAN列表。在Istio安全相关配置的多个地方被用到。当被配置时,代理将验证服务证书的主题备用名是否匹配。

9.exportTo(导出)

控制ServiceEntry的可见性,控制在一个命名空间下定义的资源对象是否可以被其他命名空间下的Sidecar、Gateway和VirtualService使用。

3.5.4 典型应用

1. 服务网格外部服务注册

ServiceEntry的解析方式resolution很灵活,再加上location表示的服务网格内部服务和服务网格外部服务的组合,经常让初学者困惑。下面结合典型的外部访问示例来解析其用法。

(1)解析方式:DNS。这是ServiceEntry早期的基本用法,在服务网格中注册一个外部服务,配置DNS解析到服务地址。如图3-71所示,当frontend访问外部目标服务api.forecast.weather时,根据配置的解析方式DNS,Sidecar会执行DNS的域名解析,解析到139.xx.xx.29,并发起访问。

图3-71  解析方式:DNS

​ 图3-71 解析方式:DNS

1spec:
2  hosts:
3  - api.forecast.weather
4  ports:
5  - number: 9999
6    name: http
7    protocol: HTTP
8  resolution: DNS
9  location: MESH_EXTERNAL

(2)解析方式:NONE。设置为NONE方式时,Sidecar不执行解析,采用直通的方式访问。以这种方式访问会存在潜在的安全隐患。如图3-72所示,frontend访问服务网格外部服务,在请求头域Host上设置正确的目标服务域名api.forecast.weather,但连接的可以不是这个域名对应的地址139.xx.xx.39,而是另一个地址139.xx.xx.29。这样即使请求匹配了Sidecar的对外访问检查,仍然可能访问到一个不安全或者不允许的外部地址。

图3-72  解析方式:NONE

​ 图3-72 解析方式:NONE

(3)解析方式:STATIC。如图3-73所示,通过如下方式配置STATIC的解析时,使用Istio的EDS机制进行服务发现,将对目标地址api.forecast.weather的访问分发到配置的后端139.xx.xx.39。在实际应用中,这种解析方式被更多地用于后面要介绍的服务网格内部服务定义,比如虚拟机类型的服务在服务网格中的管理。

图3-73  解析方式:STATIC

​ 图3-73 解析方式:STATIC

 1spec:
 2  addresses:
 3  - 192.168.99.99
 4  endpoints:
 5  - address: 139.xx.xx.39 
 6  hosts:
 7  - api.forecast.weather
 8  location: MESH_EXTERNAL
 9  ports:
10  - name: http
11    number: 9999           
12    protocol: http
13  resolution: STATIC
2. 服务网格内部服务注册

除了在服务网格中注册一个可以管理的外部服务,当前的另一类广泛应用是使用ServiceEntry注册各种服务网格内部服务。如下配置可以在服务网格内部注册一个虚拟机类型的服务:

◎ 将locattion设置为MESH_INTERNAL,表示内部类型;

◎ 将resolution设置为STATIC,表示使用静态服务发现方式;

◎ 标签选择器app选择WorkloadEntry定义的负载forecast。

这种模式的使用方式、工作机制都和Kubernetes服务在Istio中的基本相同,也是服务网格管理内部服务的典型方式:

 1spec:
 2  hosts:
 3  - forecast
 4  location: MESH_INTERNAL
 5  ports:
 6  - number: 8080
 7    name: http
 8    protocol: HTTP
 9    targetPort: 8090
10  resolution: STATIC
11  workloadSelector:
12    labels:
13      app: forecast

关于本应用的完整示例和模型,请参照3.6.4节WorkloadEntry的第1个示例。

3. 治理ServiceEntry注册的服务

通过ServiceEntry注册到服务网格的服务,可以像其他服务网格内部服务一样使用本章前面介绍的VirtualService和DestinationRule定义服务的流量路由、访问策略。比如通过如下VirtualService可以对3.5.1节示例中的服务网格外部服务访问api.forecast.weather配置2秒的访问超时,这样服务网格内部服务在访问这个服务网格外部服务时,在源服务的Sidecar上会执行对应的超时规则,代理应用程序在超过2秒后自动取消请求。对于服务网格内部服务,也可以使用类似的方式进行管理,和对Kubernetes服务配置流量规则没有区别。

1spec:
2  hosts:
3    - api.forecast.weather
4  http:
5  - timeout: 2s
6    route:
7      - destination:
8          host: api.forecast.weather
4. 用Egress访问ServiceEntry服务

可以看到,不管配置哪种解析方式,最终都是访问服务网格外部服务的应用的Sidecar拦截了对外流量,执行ServiceEntry中resolution配置的服务发现,并执行配置的流量规则。在生产环境下更多地是使用出口网关Egress-gateway来统一管理对外的流量,即通过ServiceEntry注册服务网格外部服务,再配置出口网关对服务网格外部服务进行访问。如3.4.4节的典型应用所示,出口网关可以参照应用5通过HTTP发起外部访问,也可以参照应用6通过TLS协议访问。