在理解VirtualService的完整功能之前,先看下如下简单示例:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: forecast
spec:
hosts:
- forecast
http:
- match:
- headers:
location:
exact: north
route:
- destination:
host: forecast
subset: v2
route:
- destination:
host: forecast
subset: v1
Istio 的配置都是通过Kubernetes 的CRD方式表达的,与传统的键值对配置相比,语法描述性更强。因此,我们很容易理解以上规则的意思:对于forecast 服务访问,如果在请求的Header中location取值是north,则将请求转发到服务的v2版本上,其余请求都转发到服务的v1版本上。
VirtualService 定义了对特定目标服务的一组流量规则。如其名所示,VirtualService在形式上表示一个虚拟服务,将满足条件的流量都转发到对应的服务后端,这个服务后端可以是一个服务,也可以是在DestinationRule中定义的服务的子集。
VirtualService 是在Istio V1alpha3版本的API 中引入的信路由定义。不同于V1alpha1 版本中RouteRule使用一组零散的流量规则的组合,并通过优先级表达规则的覆盖关系,VirtualService 描述了一个具体服务对象,在服务对象内包含了对流量的各种处理,其主体是一个服务而不是一组规则,更易于理解。
VirtualService 中的一些术语如下:
通过VirtualService 的配置,应用在访问目标服务时,只需要指定目标服务的地址即可,不需要额外指定其他目标资源的信息。在实际请求中到底将流量路由到哪种特征的后端上,则基于在VirtualService中配置的路由规则执行。
VirtualService的完整规则定义很复杂,后面将展开来讲。我们先来看看第一级的定义。除了host、gateway 等通用字段,规则的主体是http、tcp和tls,都是复合字段,分别对应HTTPRout、TCPRoute和TLSRoute,表示Istio支持的HTTP、TCP很热TLS协议的流量规则。
非复合字段hosts和gateway是每种协议都要用到的的公众字段,体现了VirtualService的设计思想。
1)hosts:是一个重要必选的字段,表示流量发送的目标。可以将其理解为VirtualService 定义路由规则的的标识,用于匹配访问地址,可以是一个DNS域名或IP地址。DNS名称可以使用通配符前缀,也可以只使用短域名,也就是说若不用全限定域名FQDN,则一般的运行平台会把短域名解析成FQDN。
注意:VirtualService的hosts的短域名解析到完整域名时,补齐的是NamespaceVirtualService的Namespace,而不是Service的Namespace。所以这里可能会造成一些误解。建议在规则中最好是写完整域名,避免由此产生的问题,这样的问题一般不好定位。
hosts一般建议用字母的域名而不是IP地址,IP地址等多用在以Gateway方式发布一个服务的场景,这时hosts匹配Gateway的外部访问地址,当没有做外部域名解析时,可以是外部的IP地址。
2)gateway:表示应用这些流量规则的Gateway。VirtualService描述的规则则可以用到网格里的Sidecar和入口处的Gateway,表示将路由规则应用于网格内的访问还是网格外经过Gateway的访问。其使用方式有点绕,需要注意以下场景
服务只是在网格内访问的,这是主要的场景。gateway字段可以省略,实际上在VirtualService的定义中都不会出现这个字段。一切都很正常,定义的规则作用于网格内的Sidecar。
服务只是在网格外访问的。配置要关联的Gateway,表示对于Gateway进来的流量执行在这个VirtualService上定义的流量规则。
在服务网格内和网格外都需要访问。这里要给这个数组字段至少写两个元素,一个是外部访问的Gateway,另一个是保留关键字“mesh”。使用中的常见错误就是忘了配置“mesh”这个常量而导致错误。我们很容易认为场景3是场景1和场景2的叠加,只需在内部访问的基础上添加一个可用于外部访问的Gateway。
注:在VirtualService中定义的服务需要同时对网格内外部访问时,gateway字段也要包含两个元素,一个匹配发布成外部访问的Gateway名,另外一个是“mesh”这个关键字。
3)http:是一个与HTTPRoute类似的路由集合,用于处理HTTP的流量,是Istio中内容最丰富的一种流量规则。
4)tls:是一个TLSRoute类型的路由集合,用于处理非中街的TLS和HTTPS的流量。
5)tcp:是一个TCPRoute类型的路由集合,用于处理TCP流量,应用于所有非HTTP和TLS端口的流量。如果在VirtualService中对HTTPS和TLS没有定义对于的TLSRoute,则所有流量都会被当成TCP流量来处理,都会走到TCP路由集合上。
6)exportTo:是Istio 1.1在VirtualService上增加的一个重要字段,用于控制VirtualService跨命名空间的可见性,这样就可以控制在一个命名空间下定义的VirtualService是否可以被其他命名空间下的Sidecar和Gateway使用了。如果未赋值,则默认全局可见,“.”表示仅应用在当前命名空间,“”表示应用到所有的命名空间。在Istio 1.1 中支持“.”和“”这两种配置。
HTTP是是当前最通用、内容最丰富的协议,控制也最多,是Istio上支持最完整的一种协议。通过它除了可以根据协议的内容进行路由,还可以进行其他的操作。
VirtualService中的http是一个HTTPRoute类型的路由集合,用于处理HTTP的流量。
HTTPRoute规则的功能是:满足HTTPMatchRequest条件的流量都被路由到HTTPRouteDestination,执行重定向(HTTPRedirect)、重写(HTTPRewrite)、重试(HTTPRetry)、故障注入(HTTPFaultInjection)、跨站(CorsPolicy)策略等。HTTPRoute不仅可以做路由匹配,还可以做一些写操作来修改请求本身。
在HTTPRoute中最重要的字段是条件字段Match,为一个HTTPMatchRequest类型的数组,表示HTTP请求满足条件,支持将HTTP属性如uri、scheme、method、authority、port等作为条件来匹配请求。一个URL的完整格式是:URI=scheme:[//authority]path[?query][#fragment]
authority 的定义与host的定义容易混淆,都是类似于“weather.com”这样的服务主机名,两者的差别如下。实际上authority的标准定义是:“authority=[userinfo@]host[:port]”。
看下authority标准定义中的字段:
(1)uri、scheme、method、authority:4个字段都是StringMatch类型,在匹配请求时都支持exact、prefix和regex三种模式的匹配。
(2)headers:匹配请求中的Header,是一个map类型。map的key是字符串类型,value是StringMatch类型。即对于每一个Header的值,都可以使用精确、前缀和正则三种方式进行匹配。如下所示为自定义Header中source的取值为“north”,并且ur以“/advertisment" 开头的请求:
- match:
- headers:
source:
exact: north
uri:
prefix: "/advertisment"
(3)port:表示请求服务的端口。大部分服务只开放一个端口,这也是在微服务中推荐的做法,在这种场景下可以不指定port;
(4)sourceLabels:是一个map类型的键值对,表示请求来源负载匹配标签。这在很多情况有用,可以对一组服务都打一个相同的标签,然后使用sourceLabels字段对这些服务实施相同的流量规则。在Kubernetes平台上,这里的Label就是Pod上的标签。如下所示表示请求来源是testA服务的v2版本的负载:
http:
- match:
- sourceLabels:
app: testA
version: v2
(5)gateway:表示规则应用的Gateway名称,语义同VirtualService 上面的gateway定义,是一个更细的Match条件,会覆盖在VirtualService上配置的gateway。
注:在HTTPRoute的匹配条件中,每个HTTPMatchRequest中的诸多属性是“与”逻辑,几个元素之间的关系是“或”逻辑。
需要注意的是,在VirtualService中match字段都是数组类型。HTTPMatchRequest中的诸多属性如uri、header、method等都是“与”逻辑,而数组中几个元素间关系是“或”逻辑。
在下面的例子中,match包含两个HTTPMatchRequest元素,其条件的语义是:headers中的source取值为“north”,且uri以“/advertisment”开头的请求,或者uri以“/forecast”开头的请求。
- match:
- heraders:
source:
exact: north
uri:
prefix: "/advertisment"
- uri:
prefix: "/forecast"
HTTPRoute上的route字段是一个HTTPRouteDestination类型的数组,表示满足条件的流量目标。在HTTPRouteDestination中主要由三个字段:destination(请求目标)、weight(权重)、和headers(HTTP头操作),destination、weight是必选字段。
(1)destination
核心字段destination吧小时请求的目标。在Virtualservice 上执行一组规则,最终的流量要被送到这个目标上。这个字段是一个Destination类型结构,通过host、subset和port三个属性来描述。
host是Destination必选字段,表示在Istio中注册的服务名,不但包括网格内的服务,也包括通过ServiceEntry方式注册的外部服务。在kubernetes平台上如果使用短域名,Istio就会根据规则的命名空间解析服务名,而不是根据Service的命名空间来解析。所以在使用上建议写全域名,这和VirtualService的hosts用法类似。
与host配合来表示流量路由后端的是另一个重要字段subset,它表示在host上定义的一个子集。例如,在灰度发布中将版本定义为subset,配置路由策略会将流量转发到不同版本的subset上。
(2)weight
除了destination,HTTPRouteDestination上的另一个必选字段是weight,表示流量分配的比例,在一个route下多个destination的weight总和要求是100。
在下面示例,从原有的v1版本中切分20%的流量到v2版本,这也是灰度发布常用的一个流量策略,即不区分内容,平等的从总流量中切出一部分流量给新版本。
.....
spec:
hosts:
- forecast
http:
- route:
- destination:
host: forecast
subset: v2
weight: 20
- destination:
host: forecast
subset: v1
weight: 80
如果一个route只有一个destination,那么可以省略weight,默认就是100.
(3)headers
headers字段提供了对HTTP header的一种操作机制,可以修改一次HTTP请求中的Request或Response的值,包含request和response两个字段。
以上分别介绍了HTTP请求匹配条件的定义和HTTP目标路由的定义,这也是HTTPRoute的主要功能。Istio对于HTTP除了做流量路由,还可以做适当的其他操作,很多原来需要在代码中进行的HTTP操作,在使用Istio后通过这些配置都可以达到同样的效果,下面分别进行讲解。
我们通过HTTPRedirect可以发送一个301重定向的应答给服务调用方,简单讲就是从一个URL到另外一个URL的永久重定向。用户输入一个URL,通过HTTPRedirect可以将其跳转到另一个URL。比较常见的场景:有一个在线网站,网址变了,通过这样的重定向,可以在用户输入老地址时跳转到新地址。
可以看下过程:
HTTPRedirect包括两个重要的字段来表示重定向的目标。
需要注意的是,这里使用HTTPRedirect的uri的配置会替换原请求中的完整Path,而不是匹配条件上的uri部分。如下所示,对forecast服务所有前缀是“/advertisement”的请求都会被重定向到new-forecast的“/recommendation/activity”地址:
apiVersion:networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: forecast
namespace:weather
spec:
hosts:
- forecast
http:
- match:
- uri:
prefix: /advertisement
redirect:
uri: /recommendation/activity
authority: new-forecast
通过HTTP重写可以在将请求转发给目标服务前修改HTTP请求中指定部分的内容,不同于重定向,用户是可见,HTTP重写对用户是不可见的,因为是在服务端进行的。
和重定向配置类似,重写HTTP也包括uri和authority这两个字段。
和HTTPRedirect规则稍有不同的是,HTTPRedirect的uri只能替换全部的Path,HTTPRewrite的uri是可以重写前缀的,即如果原来匹配条件是前缀匹配,则修改后只修改匹配到的前缀。
如下所示,前缀匹配“/advertisement”的请求,其请求uri中的这部分前缀会被“/recommendation/activity”替换:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: forecast
namespace: weather
spec:
hosts:
- forecast
http:
- match:
- uri:
prefix: /advertisement
rewrite:
uri: /recommendation/activity
route:
- destination:
host: forecast
HTTP重试是解决很多请求异常最直接、简单的方法,尤其是在工作环境比较复杂的场景下,克提高总体的服务质量。但是重试使用不当也会有问题,最糟糕的情况是重试一直不成功,反而增加延迟和性能开销。所以,根据系统运行环境、服务自身特点,配置适当的重试规则显得尤为重要。
HTTPRetry可以定义请求失败时的重试策略,重试策略包括重试次数、超时、重试条件等,这里分别描述相应的三个字段。
其中重试条件retryOn的取值可以包括以下几种。
如下所示,配置了一个重试策略,在“5xx,connect-failure”条件下进行最多5次重试,每次重试超时为3秒:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
medata:
name: forecast
namespace: weather
spec:
hosts:
- forecast
http:
- route:
- destination:
host: forecast
retries:
attempts: 5
perTryTimeout: 3s
retryOn: 5xx,connect-failure
HTTP流量镜像指的是将流量转发到原目标地址的同时将流量给另外一个目标地址镜像一份。把生产环境中的实际流量镜像一份到另外一个系统上,完全不会对生产系统产生影响,这里只是镜像了一份流量,数据面代理只需要关注原来转发的流量就可以,不用等待镜像目标地址的返回。
如下所示:
apiVersion: networking.istio.io:v1alpha3
kind: VirtualService
metadata:
name: forecast
namespace: weather
spec:
hosts:
- forecast
http:
- route:
- destination:
host: forecast
subset: v1
mirror:
host: forecast
subset: v2
在场景上,与其说是HTTP支持Istio的故障注入,不如说是Istio的故障注入功能支持HTTP。在Istio中为了支持非侵入的故障注入,要构造一定的业务故障,最好在HTTP这个通用的应用协议上有所支持。
实现上,在HTTP上做故障注入和前面的Redirect、Rewrite、Retry没有太大的差别,都是修改HTTP请求或者应答的内容。需要注意的是,在使用故障注入时,不能启用超时和重试。
HTTPFaultInjection通过delay和abort两个字段设置延时和中止两种故障,分别表示Proxy延迟转发HTTP请求和中止HTTP请求。
(1)延迟故障注入
HTTPFaultInjection中的延迟故障使用HTTPFaultInjection.Delay 类型描述延时故障,表示在发送请求前进行一段延时,模拟网络、远端服务负载均衡等各种原因导致的失败,主要是如下两个字段:
如下所示,表示forecast服务v1版本上的1.5%的请求产生10秒的延时:
......
route:
- destination:
host: forecast
subset: v1
fault:
delay:
percenttage:
value: 1.5
fixedDelay: 10s
(2)请求中止故障注入
HTTPFaultInjection 使用HTTPFaultInjection.Abort 描述中止故障,模拟服务端异常,给调用的客户端返回预先定义的错误状态码,主要有以下两个字段。
httpStatus:是一个必选字段,表示中止的HTTP状态码
percentage:配置中止故障作用在多少比例的请求上,通过这种方式可以只让部分请求发生故障,用法通延迟故障
如下所示,让forecast服务v1版本上1.5%的请求返回“500”代码
......
route:
- destination:
host: forecast
subset: v1
fault:
abort:
percnetage:
value: 1.5
httpStatus: 500
当一个资源向该资源所在服务器的不同的域发起请求时,就会产生一个跨域的HTTP请求。出于安全考虑,浏览器会限制从脚本发起的跨域HTTP请求。通过跨域资源共享CORS(Cross Origin Resource Sharing)机制克允许web应用服务器进行跨域访问控制,使跨域数据传递安全进行。在实现上是在HTTP Header中追加一些额外的信息来通知浏览器准许以上访问。
在 VirtualService 中可以对满足条件的请求配置跨域资源共享。有allowOrigin、allowMethods、allowHeader、exposeHeader、maxAge、allowCredentials,其实都是被转化为 Access-Control-* 的Header。 如下所示,允许源自news.com的GET方法的请求的访问。
......
http:
- route:
- destination:
host: forecast
corsPolicy:
allowOrigin:
- news.com
allowMethods:
- GET
maxAge: "2d"