☝点击上方蓝字,关注我们!
本文字数:3657字
预计阅读时间:15分钟
导 读
在过去的几年中,微服务技术迅猛发展。随着容器相关技术的普及,服务间的通信管理越来越受到人们的重视。回忆服务间的通信史,从原始的网线直连,到网络层的出现,从应用程序自我控制,到专用的服务发现应用(库)和断路器。技术在不断地快速地进行着更迭。到了 2017 年,以 Spring Cloud 为代表的传统侵入式开发框架,牢牢占据着微服务市场,它甚至一度成为微服务的代名词。天下苦侵入式久矣。就是在这样的大环境下,2017 年以 istio 和 linkerd 为代表的服务网格横空出世,人们才惊觉微服务原来还有这种玩法。那么所谓服务网格和 istio 到底讲了些什么呢,让我们跟随下面的文章来一探究竟吧。
基本概念
服务网格是什么
服务网格(Service Mesh):构成应用程序的微服务网络以及应用之间的交互。
服务网络的需求包括:服务发现、负载均衡、故障恢复、指标收集和监控、运维需求(例如 A/B 测试、金丝雀发布、限流、访问控制和端到端认证等)。
服务网格的特点:
-
应用程序间通讯的中间层;
-
轻量级网络代理;
-
应用程序无感知;
-
解耦应用程序的重试/超时、监控、追踪和服务发现。
istio是什么
Istio 提供一种简单的方式来为已部署的服务建立网络,该网络具有负载均衡、服务间认证、监控等功能,而不需要对服务的代码做任何改动。
简单的说:Istio 提供了一个完整的解决方案来实现服务网格。
istio 具有以下特征:
-
istio 适用于容器或虚拟机环境(特别是k8s),兼容异构架构;
-
istio 使用 sidecard(边车模式)代理服务的网络,不需要对业务代码本身做任何的改动;
-
HTTP、gRPC、WebSocket 和 TCP 流量的自动负载均衡;
-
istio 通过丰富的路由规则、重试、故障转移和故障注入,可以对流量行为进行细粒度控制,支持访问控制、速率限制和配额;
-
istio 对出入集群入口和出口中所有流量的自动度量指标、日志记录和跟踪。
istio架构
istio 构架如图,由数据平面和控制平面组成,具体如下:
数据平面(data plane)
-
数据平面:由一组以 sidecar 方式部署的智能代理(Envoy1)组成;
-
作用:调节和控制微服务之间所有的网络通信,以及与 Mixer (控制平面控制器)之间的通信。
sidecar(边车模式):将应用程序的功能划分为单独的进程,在 k8s 中既在同一 pod 中起多个不同功能划分的 container。
Envoy代理
-
Envoy 被部署为 sidecar,和对应服务在同一个 Kubernetes pod 中;
-
调解服务网格中所有服务的所有入站和出站流量。
控制平面(control plane)
-
管理和配置代理。
Mixer
-
负责在服务网格上执行访问控制和使用策略;
-
从 Envoy 代理和其他服务收集遥测数据。
Pilot
-
为 Envoy sidecar 提供服务发现功能;
-
为智能路由(例如 A/B 测试、金丝雀部署等)和弹性(超时、重试、熔断器等)提供流量管理功能;
-
将控制流量行为的高级路由规则转换为特定于 Envoy 的配置。
Citadel
-
传输身份验证(服务到服务身份验证);
-
来源身份认证(最终用户身份验证)。
Galley
-
其他的 Istio 控制平面组件;
-
验证用户编写的 Istio API 配置;
-
将其他的 Istio 组件与从底层平台(例如 Kubernetes)获取用户配置的细节中隔离开来。
功能
Istio 最令人感到激动的地方就是它丰富的功能,主要分为:流量管理、安全与访问控制、监控(遥测)三个方面。
流量管理
-
http 路由:根据 http 内容进行路由,比如根据 http header 中的用户信息进行权限控制;
-
tcp 路由:TCP 流量控制;
-
基于权重的路由:按比例控制流量,支持 http、tcp;
-
故障注入:可以在服务之间注入延迟、终止等,在测试服务时会有帮助。
-
超时设置:可以给 http 请求设置超时。istio 的超时和应用本身的超时设置结合起来可以使语义更清晰,控制更精细;
-
ingress 控制:利用 istio gateway,可以将监控、路由规则应用于进入集群的流量;
-
egress 控制:通过 ServiceEntry 可以控制(监控、路由规则)集群调用外部服务;
-
熔断:可以自定义配置熔断规则,包括:并发限制、开放时间等;
-
镜像流量:可以将实时流量的副本发送到镜像服务,镜像的请求是“即发即弃”(镜像请求引发的响应会被丢弃)。
安全与访问控制
-
TLS、CA;
-
service role:namespace 级访问控制、服务集(service)访问控制;
-
身份验证(JWT):基于组(用户组)、基于列表。
监控(遥测)
Prometheus
从 envoy 收集日志和指标,存储,提供查询接口。
主要的指标有:
-
Request Count:随 Istio 代理处理的每个请求递增;
-
Request Duration:它测量请求的持续时间;
-
Request Size:它测量 HTTP 请求的 body 大小;
-
Response Size:它测量 HTTP 响应 body 的大小;
-
Tcp Byte Sent:它测量在 TCP 连接场景下响应期间发送的总字节数,由服务端代理测量;
-
Tcp Byte Received:它测量在 TCP 连接场景下请求期间接收的总字节数,由服务端代理测量。
Granfana
日志指标的可视化展示。
Jeager
分布式追踪,通过往 http 请求中加入特定 header 实现,要求应用必须转发传播该 header(异构项目兼容)。
示例
为了更好了理解 istio 强大的功能,下面以官网 book-info 为例,简单看一下流量控制和遥测等功能。
首先部署一个简短书评网站,构架如图:
网站包括四个微服务:
-
productpage :本服务会调用 details 和 reviews 两个微服务,用来生成页面;
-
details :这个微服务包含了书籍的信息;
-
reviews :这个微服务包含了书籍相关的评论,它还会调用 ratings 微服务;
-
ratings :ratings 微服务中包含了由书籍评价组成的评级信息。
reviews 微服务有 3 个版本:
-
v1 版本不会调用 ratings 服务;
-
v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息;
-
v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。
访问效果如下:
由于 Bookinfo 示例部署了三个版本的 reviews 微服务,当我们多次访问应用程序时,可以看到有时输出包含星级评分,有时又没有。
智能路由
可以通过路由规则对流量进行细粒度的控制。
自定义路由
首先将所有流量导入 v1 版本的 reviews,使用如下配置,并提交给 k8s :
1apiVersion: networking.istio.io/v1alpha3 2kind: VirtualService 3metadata: 4 name: reviews 5spec: 6 hosts: 7 - reviews 8 http: 9 - route:10 - destination:11 host: reviews12 subset: v1
稍等几秒钟就可以看到变化:
可以看到现在所有流量都走 v1 版 review。
根据header内容分发流量
istio 可以基于内容分发流量,在这里我们让普通用户全部访问 v1 版,而特殊用户(jason)访问 v2 版,使用如下配置,并提交给 k8s:
1apiVersion: networking.istio.io/v1alpha3 2kind: VirtualService 3metadata: 4 name: reviews 5spec: 6 hosts: 7 - reviews 8 http: 9 - match:10 - headers:11 end-user:12 exact: jason13 route:14 - destination:15 host: reviews16 subset: v217 - route:18 - destination:19 host: reviews20 subset: v1
可以看到普通用户仍然访问 v1 版 review。
而以 jason 身份登录后会访问 v2 版 review(黑色五角星)。
除此以外,istio 可以在服务之间注入延迟、断开等故障,也可以做到按比例迁移等需求。
监控
在 Istio 中,可以让 Mixer 自动为所有的网格内流量生成和报告新的指标以及新的日志流。下面以 book-info 应用为例,展示分布式追踪。
Prometheus
用作指标采集与查询。
分布式追踪
虽然 Istio 代理能够自动发送 Span 信息,但还是需要一些辅助手段来把整个跟踪过程统一起来。应用程序应该自行传播跟踪相关的 HTTP Header,这样在代理发送 Span 信息的时候,才能正确的把同一个跟踪过程统一起来。
productpage 服务访问总览:
调用链追踪:
服务拓扑图:
grafana
使用 grafana 对 istio 本身和服务网格进行监控。
服务大盘:
原理分析
sidecar
sidecar(边车模式):将应用程序的功能划分为单独的进程,在 k8s 中既在同一 pod 中起多个不同功能划分的 container。
istio 手工注入 sidecar 会修改 deployment,增加两个容器:
-
Init 容器 istio-init:用于给 Sidecar 容器即 Envoy 代理做初始化,设置 iptables 端口转发;
-
Envoy sidecar 容器 istio-proxy:运行 Envoy 代理。
一个 istio 注入的 sidecar deployment 模板示例:
1containers: 2- name: istio-proxy 3 image: istio.io/proxy:0.5.0 4 args: 5 - proxy 6 - sidecar 7 - --configPath 8 - {{ .ProxyConfig.ConfigPath }} 9 - --binaryPath10 - {{ .ProxyConfig.BinaryPath }}11 - --serviceCluster12 {{ if ne "" (index .ObjectMeta.Labels "app") -}}13 - {{ index .ObjectMeta.Labels "app" }}14 {{ else -}}15 - "istio-proxy"16 {{ end -}}
sidecar 的流量劫持是通过 iptables 转发实现的,详细的流量劫持过程可以参考这篇文章[1]。
Pilot与Envoy通信
-
discovery service(pilot-discovery二进制):从Kubernetes apiserver list/watch service、endpoint、pod、node等资源信息,监听istio控制平面配置信息(如VirtualService、DestinationRule等), 翻译为 Envoy 可以直接理解的配置格式;
-
proxy(Envoy二进制):也就是 Envoy,直接连接 discovery service 获取配置(相当于间接地从 Kubernetes 等服务注册中心获取集群中微服务的注册情况);
-
agent(pilot-agent二进制):生成 Envoy 配置文件,管理 Envoy 生命周期;
-
service A/B:使用了 istio 的应用,如 Service A/B的进出网络流量会被 proxy 接管。
性能测试
官方性能测试
在官方给定的标准测试[2]下(1000 services、2000 sidecars、70,000/s 网格内调用),大概性能数据如下:
-
Envoy 代理每 1000 qps 消耗 0.6 vCPU 和 50 MB 内存;
-
遥测服务每 1000 qps (网格内调用)消耗 0.6 vCPU;
-
Pilot 消耗 1 vCPU 和 1.5 GB 内存;
-
Envoy 代理使得 90% 的调用都增加 8ms 的延迟。
有几点需要注意一下:
-
proxy 的策略越复杂,延时增加越多;
-
遥测数据异步发送,不影响当前请求的响应时间,但是会造成网络拥堵,间接增加延时。
测试方案说明
本次测试基于 istio 1.2 版本,单机版 k8s(v1.14.3)。
-
fortio 是一个端到端负载测试工具,在本例中调用 Service A;
-
Service A 与 Service B 之间有调用关系,可以控制他们的调用深度(即 Service A 返回给 fortio 之前经过 几次 A 和 B 之间调用);
-
fortio 与两个 Service 在同一 namespace 下;
-
测试结果 1 ~ 3 没有包含了基本遥测配置。
测试结果
1. istio性能损耗
在 500 QPS、调用深度为 3(即 Service A 返回给 fortio 之前经过 3 次 A 和 B 之间调用)的情况下,有 istio 代理和没有 istio 代理的性能差异:
可以看到,istio 的 P90 = 6.63 ms(90% 的调用都小于 6.63 ms),没有 istio 的 P90 = 3.70 ms 。调用延时增加了 3ms。
2. 调用深度对延迟的影响
在 500 QPS、调用深度分别为 0、3、5、8、10、12 的情况下,有 istio 代理的调用延迟分析图:
无 istio 代理的调用延迟分析图:
选取调用深度分别为 5、8、10、12的测试,进行对比:
红线左侧是深度分别为 5、8、10、12 时有 istio 代理的调用延迟,红线右侧是深度分别为 5、8、10、12 时无 istio 代理的调用延迟。
可以看到随着调用深度的增加,istio 代理所增加的延时相应增加,但增加幅度放缓。8 次以上的调用深度,有 istio 代理的 P90 平均比无 istio 代理的 P90 高 10 ms。
3. QPS对延迟的影响
在调用深度为 3、QPS 分别为 300、 500、 1000 的情况下,有 istio 代理的调用延迟分析图:
无 istio 代理的调用延迟分析图:
可以看到,QPS 的增加并不会增加调用延时。
性能测试小结
-
调用延时的增加主要和调用深度(经过 istio proxy 的层数)正相关;
-
调用层数每增加一层,有 istio 的 P90 延时比无 istio 的 P90 延时平均增加 1 ms;
-
QPS 的增加并不会增加调用延时。
备注:我在测试时,服务间调用 QPS 最高压到过 45k,以上结论仍然成立。对于 istio proxy 的 QPS 极限在哪里,需要后面做出更详细的测试。
go-client操作istio资源
我们可以利用 k8s 提供的 go-client 来操作 istio crd 资源。
步骤如下:
-
在 pkg/apis/{API Group}/{version} 下编写 crd 定义;
-
增加合适的代码生成标签,参考[3];
-
利用 code-generator[4] 生成 clientset、informer 等代码。
如果是需要操作别人已经预先定义好的 crd,可以直接在定义 crd 时进行引用。以 istio 的 virtual service 为例,只需引入 istio.io/api/networking/v1alpha3/VirtualService 即可。
我已经把生成好的 api 代码,放到了 github 上,有需要的同学可以自行取用[5]。
Istio crd代码生成脚本
1# 代码生成的工作目录,也就是我们的项目路径2ROOT_PACKAGE="github.com/RuiWang14/k8s-istio-client"34# 安装 k8s.io/code-generator5go get -u k8s.io/code-generator/...6cd $GOPATH/src/k8s.io/code-generator78# 执行代码自动生成,其中 pkg/client 是生成目标目录,pkg/apis 是类型定义目录9./generate-groups.sh all "$ROOT_PACKAGE/pkg/client" "$ROOT_PACKAGE/pkg/apis" "authentication:v1alpha1 networking:v1alpha3"
参考:
[1].https://jimmysong.io/istio-handbook/concepts/sidecar-injection-deep-dive.html
[2].https://istio.io/docs/concepts/performance-and-scalability/
[3].https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/
[4].https://github.com/kubernetes/code-generator
[5].https://github.com/RuiWang14/k8s-istio-client
也许你还想看
(▼点击文章标题或封面查看)
2018-08-30
2019-04-18
2018-08-16
加入搜狐技术作者天团
千元稿费等你来!
戳这里!☛