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
现在我可以在我的浏览器中验证我的应用程序是否真的启动和运行。

现在,有趣的部分来了。我们必须部署我们的应用程序的第二版,并确保大约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%的流量路由到新的部署),你最终会看到新的应用程序。

测试部署
让我们对我们的端点做大约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,你甚至可以将上述流量可视化。

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