回到 Istio 的微服务。 第2部分

回到 Istio 的微服务。 第2部分

笔记。 翻译。: 的第一部分 本系列致力于介绍 Istio 的功能并在实践中演示它们。 现在我们将讨论此服务网格的配置和使用的更复杂方面,特别是精细调整的路由和网络流量管理。

我们还提醒您,本文使用存储库中的配置(Kubernetes 和 Istio 的清单) istio 掌握.

交通管理

借助 Istio,集群中出现了新功能,可提供:

  • 动态请求路由:金丝雀部署、A/B 测试;
  • 负载均衡:简单且一致,基于哈希;
  • 跌倒后恢复:超时、重试、断路器;
  • 插入故障:延迟、放弃请求等。

随着本文的继续,将使用所选应用程序作为示例来说明这些功能,并在此过程中引入新概念。 第一个这样的概念将是 DestinationRules (即有关流量/请求接收者的规则 - 大约翻译),在此帮助下我们激活 A/B 测试。

A/B 测试:实践中的 DestinationRules

A/B 测试用于应用程序有两个版本(通常它们在视觉上不同)并且我们不能 100% 确定哪一个版本会改善用户体验的情况。 因此,我们同时运行两个版本并收集指标。

要部署演示 A/B 测试所需的第二个前端版本,请运行以下命令:

$ kubectl apply -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions/sa-frontend-green created

绿色版本的部署清单有两个地方不同:

  1. 该图像基于不同的标签 - istio-green,
  2. Pod 有标签 version: green.

由于两个部署都有标签 app: sa-frontend,由虚拟服务路由的请求 sa-external-services 用于服务 sa-frontend,将被重定向到其所有实例,并且负载将通过 循环算法,这会导致以下情况:

回到 Istio 的微服务。 第2部分
找不到所请求的文件

找不到这些文件,因为它们在不同版本的应用程序中的命名不同。 让我们确定一下:

$ 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提供服务的应用程序的同一版本应该为后续请求提供服务“。

我们将通过一致的基于哈希的负载平衡来实现这一目标 (一致哈希负载均衡)。 在这种情况下 来自同一客户端的请求会发送到同一后端实例,使用预定义的属性 - 例如 HTTP 标头。 使用 DestinationRules 实现。

目的地规则

虚拟服务 向所需服务发送请求,使用 DestinationRules 我们可以定义将应用于发往该服务实例的流量的策略:

回到 Istio 的微服务。 第2部分
使用 Istio 资源进行流量管理

注意:这里以易于理解的方式呈现了 Istio 资源对网络流量的影响。 准确地说,将请求发送到哪个实例是由 CRD 中配置的 Ingress Gateway 中的 Envoy 决定的。

通过目标规则,我们可以配置负载平衡以使用一致的哈希值并确保相同的服务实例响应相同的用户。 以下配置允许您实现此目的(目的地规则-sa-frontend.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: sa-frontend
spec:
  host: sa-frontend
  trafficPolicy:
    loadBalancer:
      consistentHash:
        httpHeaderName: version   # 1

1 - 将根据 HTTP 标头的内容生成哈希值 version.

使用以下命令应用配置:

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

现在运行下面的命令并确保在指定标头时获得正确的文件 version:

$ curl --silent -H "version: yogo" http://$EXTERNAL_IP/ | tr '"' 'n' | grep main

注意:要在 header 中添加不同的值并直接在浏览器中测试结果,可以使用 这个扩展 至 Chrome 有了这个 对于 Firefox - 大约。 译).

一般来说,DestinationRules 在负载平衡方面具有更多功能 - 查看详细信息 官方文档.

在进一步研究VirtualService之前,我们先通过运行以下命令删除“绿色版”应用程序以及相应的流向规则:

$ kubectl delete -f resource-manifests/kube/ab-testing/sa-frontend-green-deployment.yaml
deployment.extensions “sa-frontend-green” deleted
$ kubectl delete -f resource-manifests/istio/ab-testing/destinationrule-sa-frontend.yaml
destinationrule.networking.istio.io “sa-frontend” deleted

镜像:实践中的虚拟服务

遮蔽 (“屏蔽”) 或镜像 (“镜像”) 用于我们想要在不影响最终用户的情况下测试生产中的更改的情况:为此,我们将请求复制(“镜像”)到已进行所需更改的第二个实例,并查看结果。 简而言之,这是当你的同事选择最关键的问题并以如此巨大的污垢形式提出拉取请求时,没有人可以真正审查它。

为了测试这个场景的实际效果,让我们创建第二个带有错误的 SA-Logic 实例(buggy)通过运行以下命令:

$ kubectl apply -f resource-manifests/kube/shadowing/sa-logic-service-buggy.yaml
deployment.extensions/sa-logic-buggy created

现在让我们运行命令以确保所有实例 app=sa-logic 它们也有相应版本的标签:

$ kubectl get pods -l app=sa-logic --show-labels
NAME                              READY   LABELS
sa-logic-568498cb4d-2sjwj         2/2     app=sa-logic,version=v1
sa-logic-568498cb4d-p4f8c         2/2     app=sa-logic,version=v1
sa-logic-buggy-76dff55847-2fl66   2/2     app=sa-logic,version=v2
sa-logic-buggy-76dff55847-kx8zz   2/2     app=sa-logic,version=v2

服务 sa-logic 定位带有标签的 pod app=sa-logic,因此所有请求将分布在所有实例之间:

回到 Istio 的微服务。 第2部分

...但我们希望请求发送到 v1 实例并镜像到 v2 实例:

回到 Istio 的微服务。 第2部分

我们将通过 VirtualService 与 DestinationRule 结合来实现这一点,其中规则将确定 VirtualService 的子集以及到特定子集的路由。

在目标规则中定义子集

子集 (子集) 由以下配置决定(sa-logic-subsets-destinationrule.yaml):

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

  1. 主持人 (host) 定义该规则仅适用于路由指向服务的情况 sa-logic;
  2. 标题(name) 路由到子集实例时使用子集;
  3. 标签 (label) 定义实例必须匹配才能成为子集一部分的键值对。

使用以下命令应用配置:

$ kubectl apply -f resource-manifests/istio/shadowing/sa-logic-subsets-destinationrule.yaml
destinationrule.networking.istio.io/sa-logic created

现在已经定义了子集,我们可以继续并配置 VirtualService 以将规则应用于 sa-logic 的请求,以便它们:

  1. 路由到子集 v1,
  2. 镜像到子集 v2.

以下宣言可帮助您实现您的计划(sa-logic-subsets-shadowing-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  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.yaml
virtualservice.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 中的结果,可以看到有 bug 的版本(buggy)导致约 60% 的请求失败,但这些失败都不会影响最终用户,因为它们是由正在运行的服务响应的。

回到 Istio 的微服务。 第2部分
sa-logic服务不同版本的成功响应

这里我们首先看到 VirtualService 是如何应用到我们服务的 Envoy 上的: sa-web-app 提出请求 sa-logic,它通过 sidecar Envoy,它通过 VirtualService 配置为将请求路由到 v1 子集,并将请求镜像到服务的 v2 子集 sa-logic.

我知道,您可能已经认为虚拟服务很简单。 在下一节中,我们将对此进行扩展,说它们也确实很棒。

金丝雀推出

金丝雀部署是向少数用户推出应用程序新版本的过程。 它用于确保版本中没有问题,并且只有在对其(版本)质量充满信心之后,才将其分发给其他用户。о更多的观众。

为了演示金丝雀推出,我们将继续与一个子集合作 buggy у sa-logic.

我们不要在琐事上浪费时间,立即将 20% 的用户发送到有 bug 的版本(这将代表我们的金丝雀部署),其余 80% 的用户发送到正常服务。 为此,请使用以下 VirtualService (sa-logic-subsets-canary-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  hosts:
    - sa-logic    
  http:
  - route: 
    - destination: 
        host: sa-logic
        subset: v1
      weight: 80         # 1
    - destination: 
        host: sa-logic
        subset: v2
      weight: 20 # 1

1 是重量 (weight),它指定将定向到收件人或收件人子集的请求的百分比。

让我们更新之前的 VirtualService 配置 sa-logic 使用以下命令:

$ kubectl apply -f resource-manifests/istio/canary/sa-logic-subsets-canary-vs.yaml
virtualservice.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; done
Time: 0.153075s Status: 200
Time: 0.137581s Status: 200
Time: 0.139345s Status: 200
Time: 30.291806s Status: 500

VirtualServices 支持金丝雀部署:在这种情况下,我们已将问题的潜在影响范围缩小到了 20% 的用户群。 精彩的! 现在,在每种情况下,当我们不确定我们的代码时(换句话说 - 总是......),我们可以使用镜像和金丝雀部署。

超时和重试

但错误并不总是出现在代码中。 在列表中“关于分布式计算的 8 个误解“首先是‘网络是可靠的’这一错误信念。 现实中的网络 没有 可靠,因此我们需要超时 (超时) 并重试 (重试).

为了演示,我们将继续使用相同的问题版本 sa-logic (buggy),我们将模拟随机故障网络的不可靠性。

让我们的有 bug 的服务有 1/3 的机会响应时间过长,有 1/3 的机会以内部服务器错误结束,还有 1/3 的机会成功返回页面。

为了减轻此类问题的影响并让用户的生活更美好,我们可以:

  1. 如果服务响应时间超过 8 秒,则添加超时,
  2. 如果请求失败则重试。

为了实现,我们将使用以下资源定义(sa-logic-重试-超时-vs.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sa-logic
spec:
  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 秒,并且在失败的情况下我们将进行 XNUMX 次新的尝试来获取响应,从而增加了成功响应的机会。

使用以下命令应用更新的配置:

$ kubectl apply -f resource-manifests/istio/retries/sa-logic-retries-timeouts-vs.yaml
virtualservice.networking.istio.io/sa-logic configured

检查 Grafana 图表,成功响应的数量已增加:

回到 Istio 的微服务。 第2部分
添加超时和重试后成功响应统计数据的改进

在继续下一部分之前 (或者更确切地说,到本文的下一部分,因为在这方面将不再有实际的实验 - 大约翻译。), 删除 sa-logic-buggy 和 VirtualService 通过运行以下命令:

$ kubectl delete deployment sa-logic-buggy
deployment.extensions “sa-logic-buggy” deleted
$ kubectl delete virtualservice sa-logic
virtualservice.networking.istio.io “sa-logic” deleted

断路器和舱壁图案

我们正在讨论微服务架构中的两个重要模式,它们可以让您实现自我恢复 (自愈) 服务。

断路器 (“断路器”) 用于终止传入被认为不健康的服务实例的请求,并在客户端请求重定向到该服务的健康实例时恢复它(这会增加成功响应的百分比)。 (注:可以找到该模式的更详细描述,例如, 这里.)

舱壁 (“分割”) 隔离服务故障对整个系统的影响。 例如,服务 B 发生故障,另一个服务(服务 B 的客户端)向服务 B 发出请求,导致服务 B 耗尽其线程池并无法为其他请求提供服务(即使它们不是来自服务 B)。 (注:可以找到该模式的更详细描述,例如, 这里.)

我将省略这些模式的实现细节,因为它们很容易在 官方文档,我也很想展示身份验证和授权,这将在本文的下一部分中讨论。

译者PS

另请阅读我们的博客:

来源: habr.com

添加评论