写点什么

通过 Istio 重新实现微服务(四):跨服务跟踪和流量管理

  • 2019-05-27
  • 本文字数:6980 字

    阅读完需:约 23 分钟

通过 Istio 重新实现微服务(四):跨服务跟踪和流量管理

内置的特性

通过拦截所有的网络通信,Istio 能够得到一些指标和数据,这些指标和数据能够用来实现整个应用的可观察性。Kiali是一个开源的项目,它能够利用这些数据回答这样的问题:微服务是如何成为 Istio 服务网格的一部分的,它们是如何连接在一起的?

Kiali——可观察性

在安装 Istio 到我们的集群中之前,我们创建了一个用于 kiali 的 secret(还有另外一个用于 grafana),其中,我们将 user 和 password 都设置为 admin。要访问 Kiali 的 Admin UI,需要执行如下的命令:


$ kubectl port-forward \  $(kubectl get pod -n istio-system -l app=kiali \   -o jsonpath='{.items[0].metadata.name}') \  -n istio-system 20001
复制代码


打开http://localhost:20001/并使用“admin”(不含引号)作为 user 和 password 进行登录。这里很有多有用的特性,比如检查 Istio 组件的配置、拦截网络请求和响应所收集的信息构建的可视化服务,比如“谁调用了谁?”,“哪个版本的服务出现了故障?”等等。在进入下一步章节学习使用 Grafana 可视化指标之前,请花一些时间检验 Kiali 的功能。



图 10 Kiali——服务可观察性

Grafana——指标可视化

Istio 收集到的指标被放到了 Prometheus 中,并使用 Grafana 进行可视化。要访问 Grafana 的 Admin UI,请执行如下的命令并打开http://localhost:3000


$ kubectl -n istio-system port-forward \  $(kubectl -n istio-system get pod -l app=grafana \   -o jsonpath='{.items[0].metadata.name}') 3000

复制代码


在左上角点击 Home 菜单,并选择 Istio Service Dashboard,然后选择 sa-web-app 打头的服务,这样我们就会看到收集到的指标,如下图所示:



图 11 Grafana 指标的可视化


这是一个空的视图,一点也无法调动我们的兴致,管理层是不允许这样的。通过如下的命令,我们可以造一些负载出来:


$ while true; do \  curl -i http://$EXTERNAL_IP/sentiment -H "Content-type: application/json" \   -d '{"sentence": "I love yogobella"}' \  sleep .8; done
复制代码


现在,我们的图表漂亮多了,除此之外,我们还有一些非常棒的工具,Prometheus 能够用来监控,Grafana 能用来对指标进行可视化,有了它们之后,我们就能知道随着时间的推移服务的性能、健康状况以及是否有升级和降级。


最后,我们将会研究一下如何跨服务进行跟踪。

Jaeger——跟踪

我们需要跟踪系统的原因在于服务越多就越难以定位失败的原因。以下面图中这个简单的场景为例:



图 12 一个常见的请求失败


请求调用进来了,但是出现了失败,失败的原因是什么呢?是第一个服务失败还是第二个服务失败?两者都发生了异常,我们需要探查它们的日志。我们有多少次重复这样的经历了?在一定程度上讲,我们更像软件侦探,而不是开发人员。


这是微服务中一个非常普遍的问题,借助分布式的跟踪系统可以解决该问题,这种跟踪系统会在服务间传递一个唯一的 header 信息,然后这个信息会发送至分布式跟踪系统中,这样请求的跟踪信息就能汇总在一起了。样例如图 13 所示:



图 13 用于识别请求 span 的 TraceId


Istio 使用了 Jaeger Tracer,后者实现了 OpenTracing API,这是一个独立于供应商的框架。要访问 Jaegers UI,请执行如下的命令:


$ kubectl port-forward -n istio-system \  $(kubectl get pod -n istio-system -l app=jaeger \   -o jsonpath='{.items[0].metadata.name}') 16686
复制代码


然后通过http://localhost:16686打开 UI,选择 sa-web-app 服务,如果服务没有出现在下拉列表中的话,那么在页面进行一些活动,并点击刷新。随后点击 Find Traces 按钮,它只会展现最近的 trace,选择任意一条记录,所有的跟踪信息将会分别展示,如图 14 所示:



图 14 Jaeger-一个请求的跟踪信息


trace 展示了如下所述的信息:


  1. 传入 istio-ingressgateway 的请求(这是第一次与某个服务产生关联,所以会生成 Trace ID),随后网关将请求转发至 sa-web-app 服务;

  2. sa-web-app 服务中,请求会被 Envoy 容器捕获并生成一个 span child(这就是我们能够在 trace 中看到它的原因),然后会被转发至 sa-web-app 应用。

  3. 在这里,sentimentAnalysis 方法会处理请求。这些 trace 是由应用生成的,这意味着需要对代码进行一些修改;

  4. 在此之后,会发起针对 sa-logic 的 POST 请求。sa-web-app 需要将 Trace ID 传播下去;

  5. ……


注意:在第 4 步,我们的应用需要获取 Istio 生成的 header 信息,并将其向下传递给下一个请求,如下面的图片所示。



图 15 (A)Istio 传播 header 信息,(B)服务传播 header 信息


Istio 承担了主要的工作,它会为传入的请求生成 header 信息、为每个 sidecar 创建新的 span 并传递 span,但是,如果我们的服务不同时传播这些 header 信息的话,那么我们就会丢失请求的完整跟踪信息。


要传播的 header 信息如下所示:


x-request-idx-b3-traceidx-b3-spanidx-b3-parentspanidx-b3-sampledx-b3-flagsx-ot-span-context
复制代码


尽管这是一个很简单的任务,但依然有很多的库在简化这个过程,例如,在 sa-web-app 服务中,RestTemplate 客户端就进行了 instrument 操作,这样的话,我们只需要在依赖中添加 Jaeger 和 OpenTracing 就能传播 header 信息了。


在研究了开箱即用(以及部分开箱即用)的功能之后,我们看一下这里的主要话题,也就是细粒度路由、管理网络流量以及安全性等等。

流量管理

借助 Envoy,Istio 能够提供为集群带来很多新功能:


  • 动态请求路由:金丝雀发布、A/B 测试

  • 负载均衡:简单一致的 Hash 均衡

  • 故障恢复:超时、重试、断路器

  • 故障注入:延时、请求中断等。


在本文后面的内容中,我们将会在自己的应用中展示这些功能并在这个过程中介绍一些新的概念。我们要介绍的第一个概念就是 DestinationRules,并使用它们来实现 A/B 测试。

A/B 测试——Destination Rule 实践

A/B 测试适用于应用有两个版本的场景中(通常这些版本在视觉上有所差异),我们无法 100%确定哪个版本能够增加用户的交互,所以我们同时尝试这两个版本并收集指标。


为了阐述这种场景,我们部署前端的第二个版本(使用绿色按钮替换白色按钮)。执行如下的命令:


$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green- deployment.yamldeployment.extensions/sa-frontend-green created
复制代码


绿色版本的部署 manifest 有两点差异:


  1. 镜像基于不同的标签:istio-green

  2. pod 添加了version: green标记。


因为两个 deployment 都有app: sa-frontend标记,所以 virtual service sa-external-services在将请求路由至sa-frontend服务时,会转发到这两者上面,并且会使用 round robin 算法进行负载均衡,这样会导致图 16 所示的问题。



图 16 请求的文件未找到


这里有文件没有找到的错误,这是因为在应用的不同版本中它们的名称不相同。我们验证一下:


$ curl --silent http://$EXTERNAL_IP/ | tr '"' '\n' | grep main/static/css/main.c7071b22.css /static/js/main.059f8e9c.js$ curl --silent http://$EXTERNAL_IP/ | tr '"' '\n' | grep main/static/css/main.f87cd8c9.css/static/js/main.f7659dbb.js
复制代码


也就是说,index.html在请求某个版本的静态文件时,被负载均衡到了另外一个版本的 pod 上了,这样的话,就会理解为其他文件不存在。这意味着,对我们的应用来说,如果想要正常运行的话,就要引入一种限制“为 index.html 提供服务的应用版本,必须也要为后续的请求提供服务”。


我们会使用一致性 Hash 负载均衡(Consistent Hash Loadbalancing)来达成这种效果,这个过程会将同一个客户端的请求转发至相同的后端实例中,在实现时,会使用一个预先定义的属性,如 HTTP header 信息。使用 DestionatioRules 就能够让这一切变成现实。

DestinationRules

在 VirtualService 将请求路由至正确的服务后,借助 DestinationRules 我们能够为面向该服务实例的流量指定策略,如图 17 所示。



图 17 借助 Istio 资源进行流量管理


注意: 图 17 用非常易于理解的方式可视化地展现了 Istio 资源是如何影响网络流量的。但是,精确决定请求要发送至哪个实例是 Ingress Gateway 的 Envoy 完成的,它需要使用 CRD 来进行配置。


借助 Destination Rules 我们可以配置负载均衡使用一致哈希算法,从而确保相同的用户会由同一个服务实例提供响应。我们可以通过如下的配置实现这一点:


apiVersion: networking.istio.io/v1alpha3 kind: DestinationRulemetadata:  name: sa-frontendspec:  host: sa-frontend  trafficPolicy:      loadBalancer:        consistentHash:            httpHeaderName: version   # 1
复制代码


1.根据“version”头信息的内容生成 consistentHash


执行如下的命令应用该配置:


 $ kubectl apply -f resource-manifests/istio/ab-testing/destinationrule-sa- frontend.yaml destinationrule.networking.istio.io/sa-frontend created
复制代码


执行如下命令并校验在指定 version header 信息时会得到相同的文件:


$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' '\n' | grep main
复制代码


注意:在浏览器中,你可以使用chrome扩展为 version 设置不同的值。


DestinationRules 有很多其他负载均衡的功能,关于其细节,请参考官方文档


在更详细地探索 VirtualService 之前,通过如下的命令移除应用的 green 版本和 DestinationRules:


$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green- deployment.yamldeployment.extensions "sa-frontend-green" deleted$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa- frontend.yamldestinationrule.networking.istio.io "sa-frontend" deleted
复制代码

Shadowing———Virtual Services 服务实践

当我们想要在生产环境测试某项变更,但是不想影响终端用户的时候,可以使用影子(Shadowing)或镜像(Mirroring)技术,这样的话,我们能够将请求镜像至具有变更的第二个实例并对其进行评估。或者说更简单的场景,你的同事解决了一个最重要的缺陷,并提交了包含大量内容的 Pull Request,没人能够对其进行真正的审查。


为了测试这项特性,我们通过如下的命令创建 SA-Logic 的第二个实例(它是有缺陷的):


$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service.buggy.yaml
复制代码


执行下面的命令校验所有的版本除了app=sa-logic之外都带有对应版本的标记:


$ kubectl get pods -l app=sa-logic --show-labelsNAME                              READY   LABELSsa-logic-568498cb4d-2sjwj         2/2    app=sa-logic,version=v1sa-logic-568498cb4d-p4f8c         2/2    app=sa-logic,version=v1sa-logic-buggy-76dff55847-2fl66   2/2    app=sa-logic,version=v2sa-logic-buggy-76dff55847-kx8zz   2/2    app=sa-logic,version=v2
复制代码


因为sa-logic服务的目标是带有app=sa-logic标记的 pod,所以传入的请求会在所有的实例间进行负载均衡,如图 18 所示:



图 18 Round Robin 负载均衡


但是,我们想要将请求路由至 version v1 的实例并镜像至 version v2,如图 19 所示:



图 19 路由至 v1 并镜像至 v2


这可以通过组合使用 VirtualService 和 DestinationRule 来实现,Destination Rule 声明子集,而 VirtualService 路由至特定的子集。

通过 Destination Rule 声明子集

我们通过如下的配置定义子集:


apiVersion: networking.istio.io/v1alpha3 kind: DestinationRulemetadata:  name: sa-logicspec:  host: sa-logic   # 1  subsets:  - name: v1   # 2      labels:        version: v1  # 3  - name: v2      labels:      version: v2

复制代码


  1. host 定义这个规则只适用于路由至 sa-logic 服务的请求;

  2. 当路由至子集实例时所使用子集名称;

  3. 以键值对形式定义的标记,将实例定义为子集的一部分。


通过执行如下的命令应用该配置:


$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets- destinationrule.yamldestinationrule.networking.istio.io/sa-logic created
复制代码


在子集定义完之后,我们可以继续配置 VirtualService,让针对 sa-logic 的请求遵循如下的规则:


  1. 路由至名为 v1 的子集;

  2. 镜像至名为 v2 的子集。


这可以通过如下的 manifest 实现:


apiVersion: networking.istio.io/v1alpha3 kind: VirtualServicemetadata:  name: sa-logicspec:  hosts:    - sa-logic  http:  - route:    - destination:        host: sa-logic        subset: v1    mirror:      host: sa-logic      subset: v2

复制代码


所有的配置都很具有表述性,我们不再赘述,接下来看一下它的实际效果:


$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-shadowing- vs.yamlvirtualservice.networking.istio.io/sa-logic created
复制代码


通过执行如下命令生成一些负载:


$ while true; do curl -v http://$EXTERNAL_IP/sentiment \   -H "Content-type: application/json" \  -d '{"sentence": "I love yogobella"}'; \  sleep .8; done
复制代码


在 Grafana 检查结果,你会发现有缺陷的版本会出现 60%的请求失败,但是这些失败都不会影响终端用户,因为他们是由当前活跃的服务来提供响应的。



图 20 sa-logic 服务的成功率


在本节中,我们第一次看到如何将 VirtualService 用到服务的 envoy 上,当 sa-web-app 发起对 sa-logic 的请求时,会经过 sidecar envoy,借助我们配置的 VirtualService,将会路由至 sa-logic 的 v1 子集并镜像至 v2 子集。

金丝雀部署

金丝雀(Canary)部署指的是让少量用户使用应用新版本的一个过程,借助这一过程能够验证新版本是否存在问题,然后能够确保以更高的质量发布给更多的受众。


我们继续使用sa-logic带有缺陷的子集来阐述金丝雀发布。


首先,我们大胆地将 20%的用户发送至带有缺陷的版本(这就是金丝雀发布),并将 80%的用户发送至正常的服务,这可以通过如下的 VirtualService 来实现:


apiVersion: networking.istio.io/v1alpha3 kind: VirtualServicemetadata:  name: sa-logicspec:  hosts:    - sa-logic  http:  - route:    - destination:    host: sa-logic    subset: v1    weight: 80         # 1  - destination:    host: sa-logic    subset: v2      weight: 20         # 1
复制代码


1.权重声明了请求要转发到目的地或目的地子集的百分比。


通过下面的命令,更新上述的sa-logic virtual service:


$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yamlvirtualservice.networking.istio.io/sa-logic configured
复制代码


我们马上可以看到有些请求失败了:


$ while true; do \  curl -i http://$EXTERNAL_IP/sentiment -H "Content-type: application/json" \   -d '{"sentence": "I love yogobella"}' \  --silent -w "Time: %{time_total}s \t Status: %{http_code}\n" -o /dev/null; \   sleep .1; doneTime: 0.153075s      Status: 200Time: 0.137581s      Status: 200Time: 0.139345s      Status: 200Time: 30.291806s    Status: 500
复制代码


VirtualServices 实现了金丝雀发布,借助这种方法,我们将潜在的损失降低到了用户群的 20%。非常好!现在,当我们对代码没有确切把握的时候,就可以使用 Shadowing 技术和金丝雀发布。

超时和重试

代码不仅仅会有缺陷,在“分布式计算的8个谬误”中,排名第一的就是“网络是可靠的”。网络实际上是不可靠的,这也是我们需要超时和重试的原因。


为了便于阐述,我们将会继续使用有缺陷版本的sa-logic,其中随机的失败模拟了网络的不可靠性。


带有缺陷的服务版本会有三分之一的概率在生成响应时耗费过长的时间,三分之一的概率遇到服务器内部错误,其余的请求均能正常完成。


为了降低这些缺陷的影响并给用户带来更好的用户体验,我们会采取如下的措施:


  • 如果服务耗时超过了 8 秒钟,将会超时;

  • 对于失败的请求进行重试。


这可以通过如下的资源定义来实现:


apiVersion: networking.istio.io/v1alpha3 kind: VirtualServicemetadata:  name: sa-logicspec:  hosts:    - sa-logic  http:  - route:    - destination:        host: sa-logic        subset: v1      weight: 50    - destination:        host: sa-logic        subset: v2      weight: 50  timeout: 8s  # 1  retries:    attempts: 3  # 2    perTryTimeout: 3s  # 3

复制代码


  1. 请求有 8 秒钟的超时;

  2. 它会尝试 3 次;

  3. 如果耗时超过 3 秒钟就将本次尝试标记为失败。


这是一种优化:用户的等待不会超过 8 秒钟,如果失败的话,我们将会重试三次,这样的话增加了获取成功响应的概率。


通过如下命令应用更新后的配置:


$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts- vs.yamlvirtualservice.networking.istio.io/sa-logic configured
复制代码


查阅 Grafana 中的图表,看成功率是否有所提升(参见图 21)。



图 21 在使用超时和重试功能之后,带来了成功率的提升


在进入下一节之前,通过如下命令移除掉sa-logic-buggy和 VirtualService:


$ kubectl delete deployment sa-logic-buggydeployment.extensions "sa-logic-buggy" deleted$ kubectl delete virtualservice sa-logicvirtualservice.networking.istio.io "sa-logic" deleted
复制代码




系列回顾


通过 Istio 重新实现微服务 (一):认识 Istio


通过 Istio 重新实现微服务 (二):Istio 实践


通过 Istio 重新实现微服务 (三):使用 Istio 代理运行应用


2019-05-27 10:235939

评论

发布
暂无评论
发现更多内容

开源一款监控数据采集器,啥都能监控

巴辉特

Nightingale #Prometheus

大数据培训Spark 高频面试考点分享

@零度

spark 大数据开发

flutter系列之:flutter中的offstage

程序那些事

flutter 程序那些事 6月月更

Prometheus PushGateway 碎碎念

耳东@Erdong

Prometheus PushGateway 6月月更

一个使用 Shell 脚本实现的 Docker

ScratchLab

4个不可不知的“安全左移”的理由

SEAL安全

安全左移 shift left

应用升级SpringCloud版本时的注意事项(Dalston升级到Edgware)

程序员欣宸

spring Spring Cloud 6月月更

Linux开发_BMP图片编程(翻转、添加水印)

DS小龙哥

6月月更

一文讲透研发效能!您关心的问题都在 ‍

思码逸研发效能

研发效能

「开源摘星计划之操作系统100人」发布,共建操作系统开源人才生态

腾源会

如何在物联网低代码平台中进行任务管理?

AIRIOT

低代码 物联网

钛星数安加入龙蜥社区,共同打造网络安全生态

OpenAnolis小助手

开源 龙蜥社区 合作 CLA 钛星数安

居家办公更要高效-自动化办公完美提升摸鱼时间 | 社区征文

迷彩

Python AI 6月月更 初夏征文 Python自动化办公

事件

Jason199

js 事件 6月月更

Vue2/3 自定义组件的 v-model 到底怎么写?💎

德育处主任

JavaScript Vue model Javascript框架 6月月更

保健品一物一码防窜货营销软件开发

开源直播系统源码

软件开发 app源码

初中级开发如何有效减少自身的工作量?

百家饭隐私计算平台创业者

开发 书籍推荐

详解kubernetes备份恢复利器 Velero | 深入了解Carina系列第三期

BoCloud博云

Kubernetes 云原生 数据备份

《梦华录》要大结局了,看超前点映不如先来学学它!

博文视点Broadview

AS深圳站来了!参与讨论领取官方限量周边!

InfoQ写作社区官方

热门活动 ArchSummit

敏捷之道 | 敏捷开发真的过时了么?

LigaAI

敏捷 敏捷开发 敏捷软件开发 敏捷宣言 LigaAI

快手实时数仓保障体系研发实践

Apache Flink

大数据 flink 编程 流计算 实时计算

龙蜥开发者说:首次触电,原来你是这样的龙蜥社区? | 第 8 期

OpenAnolis小助手

Linux 开源 故事 龙蜥社区 开发者说

web前端培训怎么修改 node_modules 中的文件

@零度

node.js 前端开发

java技术培训在 MySQL 中使用枚举需要注意什么

@零度

JAVA开发 枚举

初识云原生安全:云时代的最佳保障

SEAL安全

云原生安全

保险APP适老化服务评测分析2022第06期

易观分析

保险APP

LRU 原理与算法实现

Ayue、

LRU

【直播回顾】战码先锋第七期:三方应用开发者如何为开源做贡献

OpenHarmony开发者

OpenHarmony

青藤入选工信部网安中心“2021年数字技术融合创新应用典型解决方案”

青藤云安全

网络安全 工信部 主机安全

知识经济时代,教会你做好知识管理

小炮

通过 Istio 重新实现微服务(四):跨服务跟踪和流量管理_文化 & 方法_Rinor Maloku_InfoQ精选文章