AICon上海|与字节、阿里、腾讯等企业共同探索Agent 时代的落地应用 了解详情
写点什么

通过 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:235986

评论

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

洞悉MySQL底层架构:一举拿下腾讯美团滴滴offer,持续更新中

欢喜学安卓

android 程序员 面试 移动开发

分布式消息中间件(1):Rabbitmq入门到高可用实战!学会了这个还怕被B站面试官看不起?

北游学Java

Java 分布式 RabbitMQ 中间件

开发也可以如此简单!华为云发布两款开发工具

华为云开发者联盟

云原生 HDC2021 编码工具 SmartAssist

IT 专业的高校大学生编程技能及就业问卷调研

Yano

问卷调查

教育是限制吗?

箭上有毒

4月日更

剖析MySQL黑盒——MySQL架构设计

学Java关注我

Java 编程 架构 程序人生 计算机

腾讯云发布“创新成长快线”,首期向创业者赠送10亿分钟实时音视频时长

腾讯云音视频

音视频

合作伙伴眼中的鸿蒙,专访海雀科技研发总监李尹

Geek_283163

linux高性能服务器编程--高性能服务器程序框架

赖猫

Linux 服务器开发 高性能服务器 C/C++后端

java单元测试代码自动生成

范晓刚

自动化 单元测试

chia奇亚挖矿软件开发|chia奇亚挖矿APP系统开发

系统开发

聪明人的训练(二十六)

Changing Lin

4月日更

云原生开发者须具备的1+N技能,开启第二曲线

华为云开发者联盟

DevOps 云原生 HDC2021 华为云DevCloud 企业数字化

【死磕JVM】什么是JVM调优?

牧小农

JVM jvm调优 JVM基础

2021团体程序设计天梯赛-部分题解

玄兴梦影

算法 比赛 算法解析

金三银四 Java 架构面试指南上线, 1000 余道大厂面试真题,送你上岸

Java 编程 程序员 架构 面试

系统高可用之健康检查和健康度量那些事

vivo互联网技术

高可用 服务器

分布式消息中间件(2):Kafka系统学习—集群搭建与使用、副本机制和实时日志统计流程

北游学Java

Java kafka 分布式 中间件

JVM类加载机制笔记

风翱

4月日更 JVM类加载

为工地装上大脑,落实企业安全管理责任

百度大脑

百度大脑 工地

数据驱动的元数据组件

鲸品堂

方法论 架构设计原则 架构设计实战 数据分析方法论

Android 关于CPU类型的so文件兼容问题(ABI)

寻找生命中的美好

android native so库 abi so文件

Substrate 合约书之合约语言框架

Patract

rust Substrate polkadot Patract Wasm

chia奇亚分币软件开发|chia奇亚分币APP系统开发

系统开发

聚力边缘计算 共建数字中国丨浪潮边缘云ICP Edge 2.0 全新发布

容器 & 服务: 扩容(二)

程序员架构进阶

容器 k8s 28天写作 弹性扩容 4月日更

2021金三银四最新拼多多 +蚂蚁金服 +头条(已拿offer),面试真题分享!

Java 编程 程序员 架构 面试

刹车失灵,数据的刹车是否也会失灵?

BinTools图尔兹

数据库 运维 dba 数据库管理工具

xch挖矿APP开发|xch挖矿系统软件开发

系统开发

怎么理解组织?

石云升

团队建设 28天写作 职场经验 管理经验 4月日更

阿里P8独家揭秘:短期内升职加薪的方法,到底是什么?

Java架构师迁哥

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