用Kubernetes和Istio进行Canary部署教程

296 阅读5分钟

Istio是一个服务网,它可以透明地给你的分布式微服务集合增加各种能力,如可观察性、流量管理和安全性。它具有各种功能,如断路、细化流量路由、mTLS管理、认证和授权政策,以及进行混乱测试的能力等。

在这篇文章中,我们将探讨如何使用Istio对我们的应用程序进行金丝雀部署。

什么是金丝雀部署

使用金丝雀部署策略,你将你的应用程序的一个新版本发布到生产流量的一小部分。然后你监控你的应用程序,并逐渐扩大其在生产流量中的比例。

为了使金丝雀部署成功,你需要有良好的监控到位。根据你的具体使用情况,你可能想检查各种指标,如性能、用户体验或跳出率

前提条件

这篇文章假设以下组件已经被配置或安装:

  • Kubernetes集群
  • Istio
  • cert-manager:(可选,如果你想提供TLS证书,则需要)。
  • Kiali (可选)

Istio的概念

对于这个具体的部署,我们将使用Istio流量管理能力的三个具体功能:

  • 虚拟服务。虚拟服务描述了流量如何流向一组目的地。使用虚拟服务,你可以配置如何将请求路由到网格内的服务。它包含一堆路由规则,这些规则被评估,然后决定将传入的请求路由到哪里(如果没有路由匹配,甚至拒绝)。
  • 网关。网关用于管理你的入站和出站流量。它们允许你指定虚拟主机和它们相关的端口,这些端口需要被打开以允许流量进入集群。
  • 目的地规则。这是用来配置网格中的客户端如何与你的服务互动。它用于配置你的挎包的TLS设置,将你的服务分割成子集,为你的客户提供负载平衡策略等。

在进行金丝雀部署时,目标规则起着重要的作用,因为我们将用它来把服务分成子集,并相应地路由流量。

应用部署

对于我们的金丝雀部署,我们将使用以下版本的应用程序。

  • httpbin.org。这将是我们应用程序的第一个版本(v1)。这是已经部署好的应用程序,你的目的是用较新版本的应用程序部分取代它。
  • websocket应用程序。这将是应用程序的第二版(v2),必须逐步引入。

请注意,在真正的现实世界中,两个应用程序将共享相同的代码。对于我们的例子,我们只是采取两个任意的应用程序,以使测试更容易。

我们的假设是,我们已经部署了我们的应用程序的第一个版本。因此,让我们首先部署这个版本。我们将为它编写我们常用的Kubernetes资源。第一版应用程序的部署清单。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
  namespace: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80

让我们为它创建一个相应的服务:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: httpbin
  name: httpbin
  namespace: canary
spec:
  ports:
  - name: httpbin
    port: 8000
    targetPort: 80
  - name: tornado
    port: 8001
    targetPort: 8888
  selector:
    app: httpbin
  type: ClusterIP

应用程序的SSL证书,将使用cert-manager。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: httpbin-ingress-cert
  namespace: istio-system
spec:
  secretName: httpbin-ingress-cert
  issuerRef:
    name: letsencrypt-dns-prod
    kind: ClusterIssuer
  dnsNames:
  - canary.33test.dev-sandbox.fpcomplete.com

还有应用程序的Istio资源。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
  namespace: canary
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - canary.33test.dev-sandbox.fpcomplete.com
    port:
      name: https-httpbin
      number: 443
      protocol: HTTPS
    tls:
      credentialName: httpbin-ingress-cert
      mode: SIMPLE
  - hosts:
    - canary.33test.dev-sandbox.fpcomplete.com
    port:
      name: http-httpbin
      number: 80
      protocol: HTTP
    tls:
      httpsRedirect: true
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
  namespace: canary
spec:
  gateways:
  - httpbin-gateway
  hosts:
  - canary.33test.dev-sandbox.fpcomplete.com
  http:
  - route:
    - destination:
        host: httpbin.canary.svc.cluster.local
        port:
          number: 8000

上述资源定义了网关和虚拟服务。你可以看到,我们在这里使用TLS,并将HTTP重定向到HTTPS。

我们还必须确保命名空间已启用istio注入。

apiVersion: v1
kind: Namespace
metadata:
  labels:
    app.kubernetes.io/component: httpbin
    istio-injection: enabled
  name: canary

我有上述一组通过kustomize管理的k8s资源。让我们把它们部署起来,得到初始环境,其中只有v1(httpbin)应用程序。

❯ kustomize build overlays/istio_canary > istio.yaml
❯ kubectl apply -f istio.yaml
namespace/canary created
service/httpbin created
deployment.apps/httpbin created
gateway.networking.istio.io/httpbin-gateway created
virtualservice.networking.istio.io/httpbin created
❯ kubectl apply -f overlays/istio_canary/certificate.yaml
certificate.cert-manager.io/httpbin-ingress-cert created

现在我可以在我的浏览器中验证我的应用程序是否真的启动和运行。

httpbin: Version 1 application

现在,有趣的部分来了。我们必须部署我们的应用程序的第二版,并确保大约20%的流量流向它。让我们为它编写部署清单。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin-v2
  namespace: canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v2
  template:
    metadata:
      labels:
        app: httpbin
        version: v2
    spec:
      containers:
      - image: psibi/tornado-websocket:v0.3
        imagePullPolicy: IfNotPresent
        name: tornado
        ports:
        - containerPort: 8888

现在是目标规则,以分割服务。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
  namespace: canary
spec:
  host: httpbin.canary.svc.cluster.local
  subsets:
  - labels:
      version: v1
    name: v1
  - labels:
      version: v2
    name: v2

最后,让我们修改虚拟服务,将20%的流量分配给新版本。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
  namespace: canary
spec:
  gateways:
  - httpbin-gateway
  hosts:
  - canary.33test.dev-sandbox.fpcomplete.com
  http:
  - route:
    - destination:
        host: httpbin.canary.svc.cluster.local
        port:
          number: 8000
        subset: v1
      weight: 80
    - destination:
        host: httpbin.canary.svc.cluster.local
        port:
          number: 8001
        subset: v2
      weight: 20

现在,如果你再次进入浏览器并刷新若干次(注意,我们只将20%的流量路由到新的部署),你最终会看到新的应用程序。

websocket: Version 2 application

测试部署

让我们对我们的端点做大约10个curl请求,看看流量是如何被路由的。

❯ seq 10 | xargs -Iz curl -s https://canary.33test.dev-sandbox.fpcomplete.com | rg "<title>"
    <title>httpbin.org</title>
    <title>httpbin.org</title>
    <title>httpbin.org</title>
<title>tornado WebSocket example</title>
    <title>httpbin.org</title>
    <title>httpbin.org</title>
    <title>httpbin.org</title>
    <title>httpbin.org</title>
    <title>httpbin.org</title>
<title>tornado WebSocket example</title>

你可以确认在这10个请求中,有2个请求被路由到websocket(v2)应用程序。如果你部署了Kiali,你甚至可以将上述流量可视化。

Kiali visualization

这就是我们关于如何使用Istio实现金丝雀部署的帖子的总结。虽然这篇文章展示了一个基本的例子,但流量引导和路由是Istio的核心功能之一,它提供了各种方法来配置它的路由决策。你可以在官方文档中找到更多关于它的细节。你还可以使用像Argo Rollouts这样的控制器与Istio一起执行金丝雀部署,并使用分析实验等附加功能。