一、Istio基本概念

Istio 是一个开源服务网格,它透明地分层到现有的分布式应用程序上。 Istio 强大的特性提供了一种统一和更有效的方式来保护、连接和监视服务。 Istio 是实现负载平衡、服务到服务身份验证和监视的路径——只需要很少或不需要更改服务代码。它强大的控制平面带来了重要的特点,包括:

  • 使用 TLS 加密、强身份认证和授权的集群内服务到服务的安全通信
  • 自动负载均衡的 HTTP, gRPC, WebSocket,和 TCP 流量
  • 通过丰富的路由规则、重试、故障转移和故障注入对流量行为进行细粒度控制
  • 一个可插入的策略层和配置 API,支持访问控制、速率限制和配额
  • 对集群内的所有流量(包括集群入口和出口)进行自动度量、日志和跟踪

Istio 是为可扩展性而设计的,可以处理不同范围的部署需求。Istio 的控制平面运行在 Kubernetes 上,您可以将部署在该集群中的应用程序添加到您的网格中,将网格扩展到其他集群,甚至连接 VM 或运行在 Kubernetes 之外的其他端点。

说明:目前版本为1.18.2

二、Istio架构

Istio 由两个部分组成:控制平面和数据平面。

  • 数据平面:由一组Sidecar方式部署的智能代理(Envoy)组成,这些代理可以调节和控制微服务及Mixer之间所有的网络通信,还可以收集和报告网格流量的遥测数据,在Kubernetes中和应用的容器处于同一个Pod
  • 控制平面:负责管理和配置代理来路由流量,由管理员创建的Istio资源会解析成相关的配置下发到数据平面

Istio架构图

三、Istio组件

Istio的架构主要由两部分组成,数据平面Envoy和控制平面Istiod。

3.1 Envoy

Istio的数据平面使用的是Envoy,Envoy是以C++开发的高性能代理,用于调解服务网格中所有服务的所有入站和出站流量。Envoy代理是唯一一个与数据平面流量交互的Istio组件。

Envoy通过Sidecar的方式和业务应用部署在同一个Pod内,在逻辑上为服务增加了许多内置特性,例如:

  • 动态服务发现
  • 负载均衡
  • TLS终止
  • HTTP/2 & gRPC代理
  • 熔断器
  • 健康检查、基于百分比流量拆分的灰度发布
  • 故障注入
  • 丰富的度量指标

使用Sidecar的方式将Envoy注入业务Pod,可以让Istio执行策略决策,并提取丰富的遥测数据,然后将这些数据发送到监控系统,以提供整个网格的监控面板。同时,Sidecar模式还允许向现有的环境中添加Istio功能,并且不需要重新设计架构或重写代码。

由Envoy代理启用的一些Istio功能和任务包括:

  • 流量控制功能:通过丰富的HTTP、gRPC、WebSocket和TCP流量路由规则来执行细粒度的流量控制。
  • 网络弹性和测试功能:超时、重试、故障转移、熔断器和故障注入功能
  • 安全性和身份认证功能:执行安全策略,通过配置强制实行API的访问控制和速率限制
  • 遥测:基于WebAssembly的可插拔扩展模型,允许通过自定义策略执行和生成网格流量的遥测

3.2 Istiod

Istiod为Istio的控制平面,提供服务发现、配置、证书管理、加密通信和认证功能。早期的Istio控制平台并没有Istiod这个容器,而是由Mixer(新版本已废弃)、Pilot、Citadel共同组成的,后来为了简化Istio的架构,将其合并为Istiod,所以对于新版本的Istio(v1.5+),部署后仅能看到Istiod一类Pod。

Istiod将控制流量行为的高级路由规则转换为Envoy特定的配置,并在运行时将其传播给Sidecar。可以使用Istio流量管理API让Istiod重新构造Envoy的配置,以便对服务网格中的流量进行更精细的流量控制。

Istiod由Pilot、Citadel和Galley组成,三者分别实现的功能如下:

  • Pilot:为Envoy Sidecar提供服务发现的功能,为智能路由(例如A/B测试、金丝雀部署等)和弹性(超时、重试、熔断器等)提供流量管理功能。Pilot将控制流量行为的高级路由规则转换为特定于Envoy的配置,并在运行时将它们传播到Sidecar。Pilot将平台特定的服务发现机制抽象化,并将其合成为符合Envoy数据平面API的任何Sidecar都可以使用的标准格式,这种松耦合使得Istio能够在多种环境下运行(如Kubernetes、Consul、Nomad),同时保持用于流量管理的相同操作界面。
  • Citadel:通过内置身份和凭证管理可以提供强大的服务与服务之间的最终用户身份验证,可用于升级服务网格中未加密的流量,并为运维人员提供基于服务标识而不是网络控制的强制执行策略的能力。从0.5版本开始,Istio支持基于角色的访问控制,以控制服务访问
  • Galley:负责配置管理的组件,用于验证配置信息的格式和正确性。Galley使用网格配置协议(Mesh Configuration Protocol)和其他组件进行配置的交互

四、Istio核心资源

和Kubernetes资源一致,Istio的配置也是通过声明式自定义资源配置来加载的。常用的核心资源有VirtualService、DestinationRule、Gateway、ServiceEntry、Sidecar等。

4.1 VirtualService

VirtualService(虚拟服务)和Kubernetes的Service类似,但是两种并不是对等的资源类型。VirtualService基于Istio和对应平台提供的基本连通性和服务发现能力,将请求路由到对应的目标。每一个VirtualService包含一组路由规则,Istio将每个请求根据路由匹配到指定的目标地址。

和Kubernetes的Service不同的是,Kubernetes的Service只提供了最简单的服务发现和负载均衡的能力,如果想要实现更加细粒度的流量分发,比如灰度、蓝绿等流量管理,Kubernetes的Service显得比较吃力或者无法实现,而VirtualService在流量管理方面有着比较好的灵活性和有效性,可以在代码零侵入的情况下实现更加丰富的流量管理,比如灰度等。

一个典型的用例是将流量发送到被指定服务的不同版本,比如80%的流量发送给v1版本,20%的流量发送给新版本。或者将某个登录用户指定到新版本,其他用户指定到旧版本,可以实现AB测试等功能。

接下来看一个VirtualService的配置示例,根据特定用户将流量分发至不同版本:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v3

上面参数说明:

  • apiVersion:对应的API版本
  • kind:创建的资源类型,和Kubernetes的Service类似
  • metadata:元数据,和Kubernetes资源类似,可以定义annotations、labels、name等
  • metadata.name:VirtualService的名称
  • spec:关于VirtualService的定义
  • spec.hosts:VirtualService的主机,即用户指定的目标或路由规则的目标,客户端向服务端发送请求时使用的一个或多个地址,可以是IP地址、DNS名称,或者是依赖于底层平台的一个简称(比如Kubernetes的Service短名称),隐式或显式地指向一个完全限定域名(FQDN),当然也可以是一个通配符"*"
  • spec.http:路由规则配置,用来指定流量的路由行为,通过该处的配置将HTTP/1.1、HTTP2和gRPC等流量发送到hosts字段指定的目标
  • spec.http[].match:路由规则的条件,可以根据一些条件制定更加细粒度的路由。比如当前示例的headers,表示匹配headers里面的end-user字段,并且值为jason的请求
  • route:路由规则,destination字段指定了符合此条件的流量的实际目标地址。比如该示例的请求,如果请求头包含end-user=jason字段,则该请求会被路由到reviews的v2版本。如果没有匹配到该请求头,则流量会被路由到v3版本(版本由DestinationRule划分)

注意:VirtualService路由规则按照从上往下的顺序进行匹配,第一个规则有最高的优先级,如果不满足第一个路由规则,则流量会选择下一个规则。

除了上述的路由匹配外,VirtualService也支持域名+路径的方式进行路由。比如后端有两个服务,一个是reviews,通过bookinfo.com/reviews访问;另一个是ratings,通过bookinfo.com/ratings访问。此时可以配置VirtualService如下:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - bookinfo.com
  http:
  - match:
    - uri:
       prefix: /reviews
    route:
    - destination:
        host: reviews
  - match:
    - uri:
       prefix: /ratings
    route:
    - destination:
        host: ratings

4.2 DestinationRule

将VirtualService理解为Kubernetes Service层面,DestinationRule理解为Service后端真实的目标地址,即VirtualService用于Service层面的路由管控,DestinationRule用于对后端真实的服务再做进一步的划分。比如存在一个Service名为paycenter,指向后端多个paycenter的Pod(该Pod可能是不同的Deployment创建的),而DestinationRule可以对后端的多个Pod区分新旧版本,划分成不同的subnet,之后VirtualService可以针对不同的版本进行流量管控。

在下面的示例中,目标规则为 my-svc 目标服务配置了 3 个具有不同负载均衡策略的子集:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: my-destination-rule
spec:
  host: my-svc
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
  - name: v3
    labels:
      version: v3

上面参数说明:

  • trafficPolicy:这是一个用于定义流量策略的部分。在这个例子中,定义了三个子集(subsets)的流量策略
  • loadBalancer:用于指定负载均衡算法的配置
  • subsets:这里定义了三个子集(v1、v2、v3),每个子集通过 labels 来选择对应版本的服务实例。第一个子集 v1 对应版本为 v1 的服务实例,并使用 simple: RANDOM 负载均衡策略。这意味着请求将随机路由到 v1 子集中的服务实例;第二个子集 v2 对应版本为 v2 的服务实例,并使用 simple: ROUND_ROBIN 负载均衡策略。这意味着请求将以循环方式(Round Robin)路由到 v2 子集中的服务实例;第三个子集 v3 对应版本为 v3 的服务实例。在这里没有指定负载均衡策略,因此将使用默认的负载均衡策略。

每个子集都是基于一个或多个 labels 定义的,在 Kubernetes 中它是附加到像 Pod 这种对象上的键/值对。这些标签应用于 Kubernetes 服务的 Deployment 并作为 metadata 来识别不同的版本。

除了定义子集之外,此目标规则对于所有子集都有默认的流量策略,而对于该子集, 则有特定于子集的策略覆盖它。定义在 subsets 上的默认策略,为 v1v3 子集设置了一个简单的随机负载均衡器。在 v2 策略中,轮询负载均衡器被指定在相应的子集字段上

默认情况下,Istio 使用轮询的负载均衡策略,实例池中的每个实例依次获取请求。Istio 同时支持如下的负载均衡模型, 可以在 DestinationRule 中为流向某个特定服务或服务子集的流量指定这些模型。

  • 随机:请求以随机的方式转发到池中的实例
  • 权重:请求根据指定的百分比转发到池中的实例
  • 最少请求:请求被转发到最少被访问的实例

4.3 Gateway

Istio同样支持网关功能,可以使用Gateway在网格最外层接收HTTP/TCP流量,并将流量转发到网格内的某个服务。

在安装Istio时,可以在istio-system命名空间下安装ingressgateway的Pod,用来充当Ingress Gateway。其中Ingress Gateway为入口网关,可以将网格内的服务“暴露”出去,一般和VirtualService配置使用,并配置一个可以被外部服务访问的域名,从而外部服务可以通过该域名访问网格内的服务。

配置Gateway和Istio其他资源类似,kind指定为Gateway即可。比如配置一个VirtualService和Gateway实现对网格内的某个服务进行发布,首先创建一个Gateway,代码如下:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "httpbin.example.com"

上面参数说明:

  • selector:必选字段。选择由哪个ingressgateway的Pod发布服务,默认为istio-system命名空间下具有istio=ingressgateway标签的Pod
  • servers:必选字段。表示发布的服务列表,用于描述发布服务的属性,比如代理监听的端口、协议和端口的名称等
  • hosts:必选字段。Gateway发布的服务地址,也就是允许用户访问的域名,可以配置为“*”,表示任何域名都可以被代理,本示例为httpbin.example.com可被路由

之后配置一个VirtualService与之匹配,即可通过该域名访问VirtualService配置的服务。比如将httpbin.example.com的/status和/delay代理到httpbin服务的8000端口:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
       prefix: /status
    - uri:
       prefix: /delay
    route:
    - destination:
        host: httpbin
        port:
          number: 8000

之后将域名解析至ingressgateway Pod的Service上即可访问该域名。