本系列的第二部分中(使用Istio简化微服务系列二:如何通过HTTPS与外部服务进行通信?),我们学会了使用 Istio egress rules 来控制服务网格之外的服务的访问。

在这一部分中,我们将看到如何才能做“金丝雀部署”(Canary Deployments),并通过 Istio 增加流量。

背景:

在过去的文章中,我详细解释了我们如何使用 Kubernetes 进行蓝/绿部署。这是一种部署技术,在此技术中,我们部署了与应用程序当前版本和新版本相同的生产环境。这项技术使我们能够执行零停机时间部署(Zero Downtime Deployments, 简称:ZDD),以确保我们的用户在切换到新版本时不受影响。有了这两个版本(当前版本和新版本)也使我们能够在新版本出现任何问题时进行回滚。

我们还需要的是能够将流量增加(或下降)到我们的应用程序的新版本,并监控它以确保没有负面影响。实现这一点的一种方法是使用“金丝雀部署”或“金丝雀”发行版。

一个不太有趣的事实:当矿工们带着金丝雀进入矿场时,任何有毒气体都会首先杀死金丝雀,并以此警告矿工们离开矿井。

同样地,在应用程序部署世界中,使用“金丝雀部署”,我们可以将应用程序的新版本部署到生产中,并只向这个新部署发送一小部分流量。这个新版本将与当前版本并行运行,并在我们将所有流量切换到新版本之前提醒我们注意任何问题。

例如:我们应用程序的 v1 可以占到90%的流量,而 v2 可以得到另外的10%。如果一切看起来都很好,我们可以将 v2 流量增加到25%,50%,最终达到100%。Istio Canary 部署的另一个优势是,我们可以根据请求中的自定义标头来增加流量。例如,在我们的应用程序的v2中设置了一个特定 cookie 头值的流量的10%。

注意:虽然“金丝雀部署”“可以”与 A/B 测试一起使用,以查看用户如何从业务度量的角度对新版本做出反应,但其背后的真正动机是确保应用程序从功能角度上满意地执行。此外,企业所有者可能希望在更长的时间内(例如:许多天甚至几周)进行 A/B 测试,而不是金丝雀码头可能采取的措施。因此,把它们分开是明智的。

Let’s see it in action

从第一部分我们知道,我们的 PetService 与 PetDetailsService(v1) 和 PetMedicalHistoryService(v1) 进行了会谈。对 PetService的调用的输出如下:

$ curl http://108.59.82.93/pet/123
{
  "petDetails": {
    "petName": "Maximus",
    "petAge": 5,
    "petOwner": "Nithin Mallya",
    "petBreed": "Dog"
  },
  "petMedicalHistory": {
    "vaccinationList": [
      "Bordetella, Leptospirosis, Rabies, Lyme Disease"
    ]
  }
}

在上面的回复中,你会注意到 petBreed 说“狗”。然而,Maximus 是德国牧羊犬,这时候我们需要修改 PetDetailsService,以便正确返回品种。

因此,我们创建了 PetDetailsService 的 v2,它将返回“德国牧羊犬”。但是,我们希望确保在将所有流量驱动到 v2 之前,我们可以使用一小部分用户来测试这个服务的 v2。

在下面的图1中,我们看到流量被配置成这样,50%的请求将被定向到 v1 和50%到 v2,我们的 “金丝雀部署”。(它可以是任意数字,取决于变化的大小,并尽量减少负面影响)。

步骤

1、创建 PetDetails Service 的 v2 版本并像以前一样部署它。 (请参阅 petdetailservice / kube 文件夹下的 petinfo.yaml)

$ kubectl get pods
NAME                                                     READY     STATUS    RESTARTS      AGE
petdetailsservice-v1-2831216563-qnl10        2/2        Running      0                19h
petdetailsservice-v2-2943472296-nhdxt       2/2       Running       0              2h
petmedicalhistoryservice-v1-28468096-hd7ld   2/2       Running      0              19h
petservice-v1-1652684438-3l112                      2/2       Running      0             19h

2、创建一个RouteRule,将流量分成 petdetailsservice 的50%(v1)和50%(v2),如下所示:

cat <<EOF | istioctl create -f -
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: petdetailsservice-default
spec:
  destination:
    name: petdetailsservice
  route:
  - labels:
      version: v1
    weight: 50
  - labels:
      version: v2
    weight: 50
EOF
$ istioctl get routerule
NAME    KIND     NAMESPACE
petdetailsservice-default RouteRule.v1alpha2.config.istio.io default

3、现在,如果你访问 PetService,你应该看到替代请求分别返回“Dog”和“German Shepherd Dog”,如下所示:

$ curl http://108.59.82.93/pet/123
{
  "petDetails": {
    "petName": "Maximus",
    "petAge": 5,
    "petOwner": "Nithin Mallya",
    "petBreed": "Dog"
  },
  "petMedicalHistory": {
    "vaccinationList": [
      "Bordetella, Leptospirosis, Rabies, Lyme Disease"
    ]
  }
}
$ curl http://108.59.82.93/pet/123
{
  "petDetails": {
    "petName": "Maximus",
    "petAge": 5,
    "petOwner": "Nithin Mallya",
    "petBreed": "German Shepherd Dog"
  },
  "petMedicalHistory": {
    "vaccinationList": [
      "Bordetella, Leptospirosis, Rabies, Lyme Disease"
    ]
  }
}

It works!

这引出了一个问题:我们不能用 Kubernetes Canary Deployments 来做到这一点吗?简短的答案是肯定的。

然而,这样做的步骤更复杂,也有局限性:

  • 你仍然需要创建 PetDetailsService (v1和v2)的两个部署,但是你需要手动限制在部署过程中 v2 副本的数量,以维护 v1:v2 比。例如:你可以使用10个副本部署 v1,并将v2 部署到2个副本,以获得10:2的负载平衡,等等。
  • 由于所有的 pod 无论版本是否相同,Kubernetes 集群中的流量负载平衡仍然受到随机性的影响。
  • 基于业务量的自动伸缩 pods 也是有问题的,因为我们需要单独的 autoscale 的2个部署,它可以根据每个服务的流量负载分配来做出不同的行为。
  • 如果我们想根据某些标准(如 request headers)仅允许某些用户使用/限制流量,则Kubernetes Canary Deployments可能无法实现此目标。

结论

你刚刚看到了创建一个“金丝雀部署”和增加 Istio 的流量是多么容易。

参考链接:

  1. DevOxx Istio presentation by Ray Tsang: https://www.youtube.com/watch?v=AGztKw580yQ&t=231s
  2. Github link to this example: https://github.com/nmallya/istiodemo