服务网格

服务网格(Service Mesh),由轻量级的网络代理实现服务间通讯和服务治理相关功能,是云原生微服务的运行时平台。

主要的好处:

  • 减轻服务重量,服务治理功能独立到服务网格中,可单独升级
  • 跨语言,服务网格和应用开发语言无关,有利于小众编程语言
  • 流量控制,安全认证,监控指标等功能齐全

可以降低开发门槛,让开发人员专注业务逻辑;并提高运维对应用/服务的统一控制和监控。

Istio目前是服务网格中最广泛采用的一个实现方案,本篇主要是对Istio的资料整理,以及输出一个SpringBoot2程序在Istio环境下的实践Example。

Istio

Google,IBM,Lyft 联合开源的服务网格,提供连接、保护、控制和观测服务的功能。

Istio 的主要特性包括:

  • 流量管理:服务治理功能更加强大
  • 安全:服务通信的认证、授权和加密
  • 可观察性:策略控制和遥测收集,提供后端抽象和中介
  • 平台支持:Kubernetes,Consul
  • 集成和定制:策略执行组件可以扩展和定制,以便与现有的 ACL、日志、监控、配额、审计等方案集成

架构模式:

Istio 服务网格逻辑上分为数据平面和控制平面。

  • 数据平面由一组以 sidecar 方式部署的智能代理(Envoy)组成。这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。
  • 控制平面负责管理和配置代理来路由流量。此外控制平面配置 Mixer 以实施策略和收集遥测数据。

  • Envoy:sidecar / proxy,Istio 使用 Envoy 代理的扩展版本,调解服务网格中所有服务的入站和出站流量。它支持动态服务发现、负载均衡、TLS 、HTTP/2 和 gPRC 代理、熔断、健康检查、故障注入和性能测量等丰富的功能。
  • Mixer:访问控制、执行策略并从 Envoy 代理中收集遥测数据。Mixer 支持灵活的插件模型,方便扩展(支持 GCP、AWS、Prometheus、Heapster 等多种后端)。
  • Pilot:动态管理 Envoy 实例的生命周期,提供服务发现、智能路由(例如 A/B 测试、金丝雀部署等)和弹性流量管理(如超时、重试、熔断器等)等功能。它将流量管理策略转化为 Envoy 数据平面配置,并传播到 sidecar 中。Pilot 将服务发现机制抽象为符合 Envoy 数据平面 API 的标准格式,以便支持在多种环境下运行并保持流量管理的相同操作接口。
  • Citadel 通过内置身份和凭证管理提供服务间和最终用户的身份认证。支持基于角色的访问控制、基于服务标识的策略执行等。

设计目标

  • 最大化透明度:自动Sidecar注入与流量劫持
  • 增量:扩展策略系统,集成其他策略和控制来源
  • 可移植性:最少的代价运行在任何云或预置环境中
  • 策略一致性:策略系统作为独特的服务来维护

流量管理

Pilot 和 Envoy

Istio 流量管理的核心组件是 Pilot,它管理和配置部署在特定 Istio 服务网格中的所有 Envoy 代理实例。它允许您指定在 Envoy 代理之间使用什么样的路由流量规则,并配置故障恢复功能,如超时、重试和熔断器。它还维护了网格中所有服务的规范模型,并使用这个模型通过发现服务让 Envoy 了解网格中的其他实例。

每个 Envoy 实例都会维护负载均衡信息,负载均衡信息是基于从 Pilot 获得的信息,以及其负载均衡池中的其他实例的定期健康检查。从而允许其在目标实例之间智能分配流量,同时遵循其指定的路由规则。

流量管理的好处

使用 Istio 的流量管理模型,本质上是将流量与基础设施扩容解耦,通过 Pilot 指定特定服务的 5% 流量可以转到金丝雀版本,或根据请求的内容将流量发送到特定版本。


将流量从基础设施扩展中解耦,这样就可以让 Istio 提供各种流量管理功能,这些功能在应用程序代码之外。
除了 A/B 测试的动态请求路由,逐步推出和金丝雀发布之外,它还使用超时、重试和熔断器处理故障恢复,最后还可以通过故障注入来测试服务之间故障恢复策略的兼容性。

upload successful
Pilot 维护了网格中的服务的规范表示,这个表示是独立于底层平台的并做特定适配。然后公开了用于服务发现 、负载均衡池和路由表的动态更新的 API。这些 API 将 Envoy 从平台特有的细微差别中解脱出来,简化了设计并提升了跨平台的可移植性。

服务之间的通讯

Istio 的流量路由规则可以根据服务版本来对服务之间流量进行附加控制。这些版本不一定是不同的 API 版本:它们可能是部署在不同环境(prod、staging 或者 dev 等)中的同一服务的不同迭代。

upload successful

使用 Pilot 指定路由规则,Envoy 根据这些规则动态地确定其服务版本的实际选择。

服务的客户端不知道服务不同版本间的差异。Envoy sidecar/代理拦截并转发客户端和服务器之间的所有请求和响应。

路由规则让 Envoy 能够根据诸如 header、与源/目的地相关联的标签和/或分配给每个版本的权重等标准来进行版本选择。

网关与路由

  1. istio-ingressgateway: 入口 IP 和端口配置 , 使用外部负载均衡或NodePort,将外部流量导入到gateway

  2. Gateway: 对标kubernetes ingress, 用于为 HTTP / TCP 流量配置负载均衡器,并不管该负载均衡器将在哪里运行, 只用于配置L4-L6功能(例如,对外公开的端口,TLS 配置),通过标签查找对应的L7 VirtualService 路由配置

  3. VirtualService: L7层路由规则,描述可寻址目标到网格内实际工作负载之间的映射。还可以配置超时,重试

  4. DestinationRule: 请求被 VirtualService 路由之后,DestinationRule 配置的一系列策略就生效了,由服务提供者撰写,用于描述断路器,负载均衡设置,TLS 设置等。

  5. ServiceEntry: 对访问网格外部依赖的流量进行建模,例如访问 Web 上的 API 或遗留基础设施中的服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#Gateway 配置
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- bookinfo.com
tls:
mode: SIMPLE
serverCertificate: /tmp/tls.crt
privateKey: /tmp/tls.key

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

#DestinationRule 配置
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
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
在服务之间分拆流量

VirtualService不仅定义路由规则,还可以分拆流量
例如下面会把 25% 的 reviews 服务流量分配给 v2 标签;其余的 75% 流量分配给 v1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 75
- destination:
host: reviews
subset: v2
weight: 25
金丝雀部署

利用负载均衡配置实现金丝雀部署
upload successful
执行bookinfo.yaml : 此时,三个版本的 reviews 服务以负载均衡的方式轮询。

#创建默认路由,全部请求转发到 v1
$ istioctl create -f samples/bookinfo/routing/route-rule-all-v1.yaml
istioctl get virtualservices -o yaml

服务发现和负载均衡

服务注册:Istio 对接诸如 Kubernetes、Mesos 等平台已经为基于容器的应用程序提供了这样的功能。

服务发现:Pilot 使用来自服务注册的信息,并提供与平台无关的服务发现接口。网格中的 Envoy 实例执行服务发现,并相应地动态更新其负载均衡池。
upload successful
网格中的服务使用其 DNS 名称访问彼此。服务的所有 HTTP 流量都会通过 Envoy 自动重新路由。Envoy 在负载均衡池中的实例之间分发流量。

目标规则-负载均衡策略

在请求被 VirtualService 路由之后,DestinationRule 配置的一系列策略就生效了。
下面是 reviews 服务的 DestinationRule 配置策略以及子集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
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

注意在单个 DestinationRule 配置中可以包含多条策略(比如 default 和 v2)。

故障处理

Envoy 提供了一套开箱即用,可选的的故障恢复功能,对应用中的服务大有裨益。这些功能包括:

  1. 超时
  2. 具备超时预算,并能够在重试之间进行可变抖动(间隔)的有限重试功能
  3. 并发连接数和上游服务请求数限制
  4. 对负载均衡池中的每个成员进行主动(定期)运行健康检查
  5. 细粒度熔断器(被动健康检查)- 适用于负载均衡池中的每个实例
请求超时

缺省情况下,HTTP 请求的超时设置为 15 秒,可以使用路由规则来覆盖这个限制:

1
2
3
4
5
kind: VirtualService
spec:
http:
- route:
timeout: 1s

重试

设置最大重试次数,或者在规定时间内一直重试,时间长度同样可以进行覆盖:

1
2
3
4
5
6
7
kind: VirtualService
spec:
http:
- route:
retries:
attempts: 3
perTryTimeout: 2s

目标规则-熔断
  • 并发连接数和上游服务请求数限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    kind: DestinationRule
    subsets:
    - name: v1
    trafficPolicy:
    connectionPool:
    tcp:
    maxConnections: 1
    http:
    http1MaxPendingRequests: 1
    maxRequestsPerConnection: 1
  • 调用下游服务

    1
    2
    3
    4
    5
    6
    7
    8
    kind: DestinationRule
    spec:
    trafficPolicy:
    outlierDetection:
    consecutiveErrors: 1
    interval: 1s
    baseEjectionTime: 3m
    maxEjectionPercent: 100
微调

Istio 的流量管理规则允许运维人员为每个服务/版本设置故障恢复的全局默认值。然而,服务的消费者也可以通过特殊的 HTTP 头提供的请求级别值覆盖超时和重试的默认值。在 Envoy 代理的实现中,对应的 Header 分别是 x-envoy-upstream-rq-timeout-ms 和 x-envoy-max-retries。

故障注入

可以为符合特定条件的请求配置故障,还可以进一步限制遭受故障的请求的百分比。可以注入两种类型的故障:延迟和中断。延迟是计时故障,模拟网络延迟上升或上游服务超载的情况。中断是模拟上游服务的崩溃故障。中断通常以 HTTP 错误代码或 TCP 连接失败的形式表现。

对其中的 10% 注入 5 秒钟的延迟。

1
2
3
4
5
6
7
8
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
spec:
http:
- fault:
delay:
percent: 10
fixedDelay: 5s

对其中的 10% 注入 HTTP 400 错误

1
2
3
4
5
http:
- fault:
abort:
percent: 10
httpStatus: 400

策略与遥测

Istio 提供灵活的模型来执行授权策略,并为网格中的服务收集遥测数据。

基础设施后端旨在提供用于构建服务的支持功能。它们包括访问控制系统,遥测捕获系统,配额执行系统,计费系统等等。服务传统上直接与这些后端系统集成,创建硬耦合,还有沾染特定的语义和使用选项。Istio 提供统一抽象,使得 Istio 可以与一组开放式基础设施后端进行交互。

Mixer 是负责提供策略控制和遥测收集的 Istio 组件:

upload successful

在每次请求执行先决条件检查之前以及在每次报告遥测请求之后,Envoy sidecar 在逻辑上调用 Mixer。

该 sidecar 具有本地缓存,从而可以在缓存中执行相对较大比例的前提条件检查。此外,sidecar 缓冲出站遥测,使其实际上不需要经常调用 Mixer。

  • 适配器

Mixer 是高度模块化和可扩展的组件。他的一个关键功能就是把不同后端的策略和遥测收集系统的细节抽象出来,使得 Istio 的其余部分对这些后端不知情。

Mixer 处理不同基础设施后端的灵活性是通过使用通用插件模型实现的。每个插件都被称为 Adapter,Mixer通过它们与不同的基础设施后端连接,这些后端可提供核心功能,例如日志、监控、配额、ACL 检查等。通过配置能够决定在运行时使用的确切的适配器套件,并且可以轻松扩展到新的或定制的基础设施后端。

策略

速率限制(限流)

为实现速率限制,需要配置 memquota、quota、rule、QuotaSpec 以及 QuotaSpecBinding 五个对象:

$ istioctl create -f samples/bookinfo/policy/mixer-rule-ratings-ratelimit.yaml

  • memquota:速率限制
  • quota:应用范围表达式
  • rule:使用 quota 模板将 dimensions 数据映射给 memquota 进行处理
  • QuotaSpec:为创建的 quota 实例设置 charge 值
  • QuotaSpecBinding:QuotaSpec 绑定到需要应用限流的服务上
Denier以及黑白名单

在 Istio 环境里,可以使用 Mixer 中的任何属性来对服务进行访问控制。这是一种简易的访问控制,使用 Mixer 选择器来有条件的拒绝请求。

Denier

运行下列命令设置一条拒绝规则,其中包含了一个 handler 以及一个 instance。

$ istioctl create -f samples/bookinfo/policy/mixer-rule-deny-label.yaml

黑白名单

配置和前面的 Denier 配置是等价的,配置元素包括:listchecker,listentry,rule

遥测

分布式跟踪

istio-demo.yaml 或者 istio-demo-auth.yaml 模板中都包含了跟踪支持,或者还可以使用 Helm chart 的方式进行部署,需要设置 –set tracing.enabled=true 选项。

Jaeger dashboard 的访问:

$ kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath=’{.items[0].metadata.name}’) 16686:16686 &

打开浏览器访问 http://localhost:16686 来访问 Jaeger 的 Dashboard。

收集指标和日志

Mixer 使用的是缺省配置(–configDefaultNamespace=istio-system)。如果使用的是不同的值,需要根据实际情况对文中提及的的配置和命令进行变更。

  1. 新建一个 YAML 文件,用来配置新的指标以及数据流,Istio 将会进行自动生成和收集的工作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    # 指标 instance 的配置
    apiVersion: "config.istio.io/v1alpha2"
    kind: metric
    metadata:
    name: doublerequestcount
    namespace: istio-system
    spec:
    value: "2" # 每个请求计数两次
    dimensions:
    source: source.service | "unknown"
    destination: destination.service | "unknown"
    message: '"twice the fun!"'
    monitored_resource_type: '"UNSPECIFIED"'
    ---
    # prometheus handler 的配置
    apiVersion: "config.istio.io/v1alpha2"
    kind: prometheus
    metadata:
    name: doublehandler
    namespace: istio-system
    spec:
    metrics:
    - name: double_request_count # Prometheus 指标名称
    instance_name: doublerequestcount.metric.istio-system # Mixer Instance 名称(全限定名称)
    kind: COUNTER
    label_names:
    - source
    - destination
    - message
    ---
    # 将指标 Instance 发送给 prometheus handler 的 rule 对象
    apiVersion: "config.istio.io/v1alpha2"
    kind: rule
    metadata:
    name: doubleprom
    namespace: istio-system
    spec:
    actions:
    - handler: doublehandler.prometheus
    instances:
    - doublerequestcount.metric
    ---
    # logentry(日志条目)的 instance 配置
    apiVersion: "config.istio.io/v1alpha2"
    kind: logentry
    metadata:
    name: newlog
    namespace: istio-system
    spec:
    severity: '"warning"'
    timestamp: request.time
    variables:
    source: source.labels["app"] | source.service | "unknown"
    user: source.user | "unknown"
    destination: destination.labels["app"] | destination.service | "unknown"
    responseCode: response.code | 0
    responseSize: response.size | 0
    latency: response.duration | "0ms"
    monitored_resource_type: '"UNSPECIFIED"'
    ---
    # stdio(标准输入输出)handler 的配置
    apiVersion: "config.istio.io/v1alpha2"
    kind: stdio
    metadata:
    name: newhandler
    namespace: istio-system
    spec:
    severity_levels:
    warning: 1 # Params.Level.WARNING
    outputAsJson: true
    ---
    # 将 logentry instance 发送到 stdio 的 rule 对象配置
    apiVersion: "config.istio.io/v1alpha2"
    kind: rule
    metadata:
    name: newlogstdio
    namespace: istio-system
    spec:
    match: "true" # 匹配所有请求
    actions:
    - handler: newhandler.stdio
    instances:
    - newlog.logentry
  2. 把新配置推送给集群。
    $ istioctl create -f new_telemetry.yaml

  3. 向示例应用发送流量。
    在浏览器中打开 Bookinfo 应用的 product 页面:http://$GATEWAY_URL/productpage,或者使用如下的等价命令:
    $ curl http://$GATEWAY_URL/productpage

  4. 复查新指标的生成和收集情况。
    在 Kubernetes 环境中,使用下面的命令为 Prometheus 设置端口转发:
    $ kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=prometheus -o jsonpath=’{.items[0].metadata.name}’) 9090:9090 &
    使用 Prometheus 界面查看新的指标。

  5. 检查请求过程中生成和处理的日志流。
    在 Kubernetes 环境中,像这样在 istio-telemetry pod 中搜索日志:
    $ kubectl -n istio-system logs $(kubectl -n istio-system get pods -l istio-mixer-type=telemetry -o jsonpath=’{.items[0].metadata.name}’) -c mixer | grep \”instance\”:\”newlog.logentry.istio-system\”

更多还包括安全,性能与可伸缩性等,请参考官方文档。

总结

容器抽象统一了应用的分发和运行的标准;
kubernetes成了资源管理和应用调度编排的标准;
istio也在向应用/微服务通用的运行时平台前进。

最后,输出一个SpringBoot2程序在Istio环境下的实践Example:
https://github.com/masenmiao/acloud-istio-example

参考:

Istio是什么
https://istio.io/zh/docs/concepts/what-is-istio/
流量管理
https://istio.io/zh/docs/concepts/traffic-management/
控制 Ingress 流量
https://istio.io/zh/docs/tasks/traffic-management/ingress/
https://istio.io/zh/blog/2018/v1alpha3-routing/