首页 > 解决方案 > 如何处理服务网格中的重大更改

问题描述

我正在使用 Kubernetes 构建一个示例微服务应用程序,以找出未来项目的最佳实践和一些模式。我使用 Istio 作为服务网格来处理东西向流量,并且我对这些概念(VirtualServices、DestinationRules,...)有基本的了解。服务网格允许我轻松推出新版本的微服务并将流量重定向到新实例(例如使用加权分布)。当考虑到语义版本控制时,这对更新非常有效PatchMinor因为理论上它们不会改变现有的合同,因此可以作为现有服务的直接替代品。现在我想知道如何正确处理服务的重大更改,所以是Major版本更新。

很难找到这方面的信息,但由于我得到的信息有限,我现在正在考虑两种方法:

  1. 服务的每个主要版本(例如user-service)都有自己的VirtualService,以便客户端可以正确地处理它(通过不同的服务名称,例如user-service-v1)。然后使用 Istio 将主要版本(例如1.*)的流量正确路由到不同的可用服务(例如user-service v1.3.1user-service v1.4.0)。

  2. 我将一个整体VirtualService用于特定的微服务(例如user-service)。这VirtualService包含许多路由定义以使用例如客户端发送的标头(例如x-major-version=1)将请求匹配到目的地。

总的来说,两种方法之间没有太大的区别。客户端显然需要通过设置标头或通过解析不同的服务名称来指定他想要谈论的主要版本。所描述的方法是否有任何限制,使一种优于另一种?或者还有其他我完全想念的选择吗?非常感谢任何帮助和指点!

标签: kubernetesversioningistiosemantic-versioningservicemesh

解决方案


TLDR

除了我在评论中提到的之外,在对该主题进行更详细的检查后,我会选择方法 2 ,为特定微服务提供一个整体虚拟服务,并带有金丝雀部署镜像

方法一

文档中所述

在不方便在单个 VirtualService 或 DestinationRule 资源中为特定主机定义完整的路由规则或策略集的情况下,最好在多个资源中逐步指定主机的配置。如果绑定到网关,Pilot 将合并此类目标规则并合并此类虚拟服务。

所以理论上你可以使用方法 1,但我会说有太多的配置,这样做有更好的主意。

假设您有 name 的旧应用程序和 namev1.3.1的新应用程序v1.4.0,因此适当的虚拟服务如下所示。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-vervice1
spec:
  hosts:
  - '*'
  http:
  - name: "v1.3.1"
    route:
    - destination:
        host: service1.namespace.svc.cluster.local

---

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-service2
spec:
  hosts:
  - '*'
  http:
  - name: "v1.4.0"
    route:
    - destination:
        host: service2.namespace.svc.cluster.local

方法二

实践中,我会使用方法 2,例如,您可以创建应用程序的 2 个版本,在下面的示例中oldnew然后为它配置虚拟服务和目标规则。

这里的问题是,为什么?因为它更容易管理,至少对我来说是这样,而且在这里使用金丝雀部署和镜像很容易,下面会详细介绍。

假设您部署了新应用程序,您不想在这里发送 1% 的传入流量,另外您可以使用镜像,因此每个发往旧服务的请求都将被镜像到新服务以进行测试。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: vs-vervice
spec:
  hosts:
  - '*'
  http:
  - name: "old"
    route:
    - destination:
        host: service.namespace.svc.cluster.local
        subset: v1
      weight: 99
    mirror:
      host: service.namespace.svc.cluster.local
      subset: v2
    mirror_percent: 100
  - name: "new"
    route:
    - destination:
        host: service.namespace.svc.cluster.local
        subset: v2
      weight: 1

---


apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews-destination
spec:
  host: service.namespace.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1  <--- label on old pod
  - name: v2
    labels:
      version: v2  <--- label on new pod

测试新应用程序

客户端显然需要通过设置标头或通过解析不同的服务名称来指定他想要谈论的主要版本。

实际上,这取决于配置,如果您将上述选项与new版本一起使用old,那么这就是金丝雀部署(例如加权分布)的用途。您可以指定应发送到应用程序新版本的流量百分比。当然,您可以在虚拟服务中指定标头或前缀,以便用户可以使用您的应用程序的旧版本或新版本。

金丝雀部署

正如这里提到的

Istio 项目的好处之一是它提供了部署金丝雀服务所需的控制。金丝雀部署(或推出)背后的想法是通过首先使用一小部分用户流量对其进行测试来引入新版本的服务,然后如果一切顺利,可能会逐渐增加百分比,同时逐步淘汰旧版本。如果在此过程中出现任何问题,我们将中止并回滚到以前的版本。在最简单的形式中,发送到金丝雀版本的流量是随机选择的请求百分比,但在更复杂的方案中,它可以基于请求的区域、用户或其他属性。

根据您在该领域的专业水平,您可能想知道为什么甚至需要 Istio 对金丝雀部署的支持,因为像 Kubernetes 这样的平台已经提供了一种进行版本发布和金丝雀部署的方法。问题解决了,对吧?嗯,不完全是。尽管以这种方式进行部署在简单的情况下有效,但它非常有限,尤其是在接收大量(尤其是变化量)流量的大型云环境中,需要自动缩放。

istio

使用 Istio,流量路由和副本部署是两个完全独立的功能。实现服务的 Pod 数量可以根据流量负载自由伸缩,与版本流量路由的控制完全正交。这使得在存在自动缩放的情况下管理金丝雀版本成为一个更简单的问题。事实上,自动缩放器可能会响应流量路由变化导致的负载变化,但它们仍然独立运行,与负载因其他原因发生变化时没有什么不同。

Istio 的路由规则还提供其他重要优势;您可以轻松控制细粒度的流量百分比(例如,路由 1% 的流量而不需要 100 个 Pod),并且您可以使用其他标准控制流量(例如,将特定用户的流量路由到金丝雀版本)。为了说明这一点,让我们看一下部署 helloworld 服务,看看问题变得多么简单。

有一个例子

镜像

经常用于测试新版本应用程序的第二件事是流量镜像。

正如这里提到的

使用 Istio,您可以使用流量镜像将流量复制到另一个服务。您可以将流量镜像规则合并为 Canary 部署管道的一部分,允许您在向其发送实时流量之前分析服务的行为。

如果您正在寻找最佳实践,我建议您从本教程开始,因为它在这里得到了很好的解释。

流量镜像的工作原理

流量镜像使用以下步骤工作:

  • 您部署新版本的应用程序并打开流量镜像。

  • 旧版本像以前一样响应请求,但也会向新版本发送异步副本。

  • 新版本处理流量但不响应用户。

  • 运营团队监控新版本并向开发团队报告任何问题。

在此处输入图像描述

当应用程序处理实时流量时,它可以帮助团队发现他们在预生产环境中通常不会发现的问题。您可以使用 Prometheus 和 Grafana 等监控工具来记录和监控您的测试结果。

此外,还有一个 nginx 示例完美地展示了它应该如何工作。

值得一提的是,如果你使用写API,比如订单或支付,那么镜像流量将意味着多次写API,比如订单。Christian Posta在此处详细描述了该主题。


让我知道您是否还有其他要讨论的内容。


推荐阅读