如何使用Istio和Kubernetes进行Canary部署
介绍
在引入服务的新版本时,通常希望在逐步淘汰旧版本的过程中将受控百分比的用户流量转移到新版本的服务。 这种技术称为 金丝雀部署 。
Kubernetes 集群操作员可以使用 labels 和 Deployments 本地编排 canary 部署。 然而,这种技术有一定的局限性:流量分布和副本计数是耦合的,这实际上意味着必须手动控制副本比率才能将流量限制到金丝雀版本。 换句话说,要将 10% of 流量引导到 Canary 部署,您需要有一个由 10 个 Pod 组成的池,其中一个 Pod 接收 10% of 用户流量,另外九个接收其余的。
使用 Istio 服务网格部署可以通过明确区分副本计数和流量管理来解决此问题。 Istio 网格允许细粒度的流量控制,将流量分配和管理与副本扩展分离。 您可以定义流量百分比和目标,而不是手动控制副本比率,其余部分由 Istio 管理。
在本教程中,您将使用 Istio 和 Kubernetes 创建一个金丝雀部署。 您将部署两个版本的演示 Node.js 应用程序,并使用 Virtual Service 和 Destination Rule 资源配置到新旧版本的流量路由. 这将是使用 Istio 构建未来金丝雀部署的良好起点。
先决条件
- 启用了基于角色的访问控制 (RBAC) 的 Kubernetes 1.10+ 集群。 此设置将使用具有三个节点的 DigitalOcean Kubernetes 集群,但您可以自由地 使用另一种方法 创建集群。
<$>[注] 笔记: 对于此设置,我们强烈建议使用至少 8GB 可用内存和 4vCPU 的集群。 本教程将使用三个 DigitalOcean 的标准 4GB/2vCPU Droplet 作为节点。 <$>
kubectl
命令行工具安装在开发服务器上并配置为连接到您的集群。 您可以在官方文档中阅读更多关于安装kubectl
的信息。- Docker 安装在您的开发服务器上。 如果您使用的是 Ubuntu 18.04,请按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2; 否则,请按照 官方文档 了解有关在其他操作系统上安装的信息。 确保将您的非 root 用户添加到
docker
组,如链接教程的第 2 步所述。 - 一个 Docker Hub 帐户。 有关如何设置的概述,请参阅 this Introduction to Docker Hub。
- 按照 How To Install and Use Istio With Kubernetes 中的说明安装和配置 Istio。 您还应该启用 Grafana 遥测插件并将其配置为外部访问。
第 1 步 — 打包应用程序
在先决条件教程如何通过Kubernetes安装和使用Istio中,您创建了一个node-demo Docker镜像来运行一个鲨鱼信息应用程序,并将这个镜像推送到Docker Hub。 在此步骤中,您将创建另一个映像:将用于金丝雀部署的较新版本的应用程序。
我们最初的演示应用程序在其 Shark Info 页面上强调了一些关于鲨鱼的友好事实:
但我们决定在新的金丝雀版本中强调一些更可怕的事实:
我们的第一步是将应用程序的第二个版本的代码克隆到一个名为 node_image
的目录中。 使用以下命令,从 DigitalOcean 社区 GitHub 帐户 克隆 nodejs-canary-app 存储库。 这个存储库包含我们应用程序的第二个更可怕的版本的代码:
git clone https://github.com/do-community/nodejs-canary-app.git node_image
导航到 node_image
目录:
cd node_image
该目录包含我们的鲨鱼信息应用程序的新版本的文件和文件夹,该应用程序为用户提供有关鲨鱼的信息,与原始应用程序一样,但强调更可怕的事实。 除了应用程序文件之外,该目录还包含一个 Dockerfile,其中包含使用应用程序代码构建 Docker 映像的说明。 有关 Dockerfile 中的说明的更多信息,请参阅 如何使用 Docker 构建 Node.js 应用程序的第 3 步。
要测试应用程序代码和 Dockerfile 是否按预期工作,您可以使用 docker build 命令构建和标记映像,然后使用该映像运行演示容器。 将 -t
标志与 docker build
一起使用将允许您使用 Docker Hub 用户名标记图像,以便在测试后将其推送到 Docker Hub。
使用以下命令构建映像:
docker build -t your_dockerhub_username/node-demo-v2 .
命令中的 .
指定构建上下文是当前目录。 我们将镜像命名为 node-demo-v2
,以引用我们在 How To Install and Use Istio With Kubernetes 中创建的 node-demo
镜像 。
构建过程完成后,您可以使用 docker images 列出您的图像:
docker images
您将看到以下输出确认映像构建:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/node-demo-v2 latest 37f1c2939dbf 5 seconds ago 77.6MB node 10-alpine 9dfa73010b19 2 days ago 75.3MB
接下来,您将使用 docker run
基于此图像创建一个容器。 我们将在此命令中包含三个标志:
-p
:这会发布容器上的端口并将其映射到我们主机上的端口。 我们将在主机上使用端口80
,但如果您有另一个进程在该端口上运行,您可以根据需要随意修改此端口。 有关其工作原理的更多信息,请参阅 Docker 文档中关于 端口绑定 的讨论。-d
:这会在后台运行容器。--name
:这允许我们给容器一个自定义的名称。
运行以下命令来构建容器:
docker run --name node-demo-v2 -p 80:8080 -d your_dockerhub_username/node-demo-v2
使用 docker ps 检查正在运行的容器:
docker ps
您将看到确认您的应用程序容器正在运行的输出:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 49a67bafc325 your_dockerhub_username/node-demo-v2 "docker-entrypoint.s…" 8 seconds ago Up 6 seconds 0.0.0.0:80->8080/tcp node-demo-v2
您现在可以在浏览器中访问您的服务器 IP 以测试您的设置:http://your_server_ip
。 您的应用程序将显示以下登录页面:
点击【X13X】获取鲨鱼信息【X31X】按钮获取更恐怖的鲨鱼信息:
现在您已经测试了应用程序,您可以停止正在运行的容器。 再次使用 docker ps
得到你的 CONTAINER ID
:
docker ps
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 49a67bafc325 your_dockerhub_username/node-demo-v2 "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:80->8080/tcp node-demo-v2
使用 docker stop 停止容器。 请务必将此处列出的 CONTAINER ID
替换为您自己的应用程序 CONTAINER ID
:
docker stop 49a67bafc325
现在您已经测试了映像,您可以将其推送到 Docker Hub。 首先,登录到您在先决条件中创建的 Docker Hub 帐户:
docker login -u your_dockerhub_username
出现提示时,输入您的 Docker Hub 帐户密码。 以这种方式登录将使用您的 Docker Hub 凭据在非 root 用户的主目录中创建一个 ~/.docker/config.json
文件。
使用 docker push 命令 将应用程序镜像推送到 Docker Hub。 请记住将 your_dockerhub_username
替换为您自己的 Docker Hub 用户名:
docker push your_dockerhub_username/node-demo-v2
您现在有两个应用程序映像保存到 Docker Hub:node-demo
映像和 node-demo-v2
。 我们现在将修改您在先决条件教程 How To Install and Use Istio With Kubernetes 中创建的清单,以将流量引导到应用程序的金丝雀版本。
第 2 步 — 修改应用程序部署
在 How To Install and Use Istio With Kubernetes 中,您为应用程序 Service 和 Deployment 创建了一个具有 规范 的应用程序 manifest 对象。 这些规范描述了每个对象的期望状态。 在此步骤中,您将为应用程序的第二个版本添加一个 Deployment,以及使 Istio 能够管理这些资源的版本标签。
当您按照先决条件教程中的设置说明进行操作时,您创建了一个名为 istio_project
的目录和两个 yaml
清单:node-app.yaml
,其中包含服务和部署对象的规范,和 node-istio.yaml
,其中包含 Istio 虚拟服务和网关资源的规范。
现在导航到 istio_project
目录:
cd cd istio_project
使用 nano
或您喜欢的编辑器打开 node-app.yaml
以更改您的应用程序清单:
nano node-app.yaml
目前,该文件如下所示:
~/istio_project/node-app.yaml
apiVersion: v1 kind: Service metadata: name: nodejs labels: app: nodejs spec: selector: app: nodejs ports: - name: http port: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: nodejs labels: version: v1 spec: replicas: 1 selector: matchLabels: app: nodejs template: metadata: labels: app: nodejs version: v1 spec: containers: - name: nodejs image: your_dockerhub_username/node-demo ports: - containerPort: 8080
有关此文件内容的完整说明,请参阅 如何在 Kubernetes 中安装和使用 Istio 的 Step 3。
根据 Istio 对 Pod 和服务 的建议,我们已经在 Deployment metadata
和 template
字段中包含了版本标签。 现在我们可以为第二个 Deployment 对象添加规范,这将代表我们应用程序的第二个版本,并对我们第一个 Deployment 对象的 name
进行快速修改。
首先,将现有 Deployment 对象的名称更改为 nodejs-v1
:
~/istio_project/node-app.yaml
... apiVersion: apps/v1 kind: Deployment metadata: name: nodejs-v1 labels: version: v1 ...
接下来,在此部署的规范下方,添加您的第二个部署的规范。 记得在 image
字段中添加您自己的图像名称:
~/istio_project/node-app.yaml
... --- apiVersion: apps/v1 kind: Deployment metadata: name: nodejs-v2 labels: version: v2 spec: replicas: 1 selector: matchLabels: app: nodejs template: metadata: labels: app: nodejs version: v2 spec: containers: - name: nodejs image: your_dockerhub_username/node-demo-v2 ports: - containerPort: 8080
和第一个 Deployment 一样,这个 Deployment 使用一个 version
标签来指定这个 Deployment 对应的应用程序的版本。 在这种情况下,v2
将与此 Deployment 关联的应用程序版本与我们的第一个 Deployment 对应的 v1
区分开来。
我们还确保由 v2
部署管理的 Pods 将运行我们在上一步中构建的 node-demo-v2
金丝雀镜像。
完成编辑后保存并关闭文件。
修改应用程序清单后,您可以继续更改 node-istio.yaml
文件。
第 3 步 — 使用虚拟服务加权流量并添加目标规则
在 How To Install and Use Istio With Kubernetes 中,您创建了网关和虚拟服务对象以允许外部流量进入 Istio 网格并将其路由到您的应用程序服务。 在这里,您将修改您的虚拟服务配置以包括路由到您的应用程序服务子集 - v1
和 v2
。 您还将添加一个 Destination Rule 来为您应用到 nodejs
应用程序服务的路由规则定义额外的、基于版本的策略。
打开node-istio.yaml
文件:
nano node-istio.yaml
目前,该文件如下所示:
~/istio_project/node-istio.yaml
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: nodejs-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: nodejs spec: hosts: - "*" gateways: - nodejs-gateway http: - route: - destination: host: nodejs
有关此清单中规范的完整说明,请参阅 如何通过 Kubernetes 安装和使用 Istio 的 Step 4。
我们的第一个修改将是虚拟服务。 目前,此资源通过我们的 nodejs-gateway
将进入网格的流量路由到我们的 nodejs
应用服务。 我们想做的是配置一个路由规则,将 80% 的流量发送到我们的原始应用程序,并将 20% 的流量发送到较新的版本。 一旦我们对金丝雀的性能感到满意,我们就可以重新配置我们的流量规则,以逐步将所有流量发送到较新的应用程序版本。
不像我们在原始清单中那样路由到单个 destination
,我们将为我们的两个应用程序子集添加 destination
字段:原始版本 (v1
) 和金丝雀(v2
)。
对虚拟服务进行以下添加以创建此路由规则:
~/istio_project/node-istio.yaml
... apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: nodejs spec: hosts: - "*" gateways: - nodejs-gateway http: - route: - destination: host: nodejs subset: v1 weight: 80 - destination: host: nodejs subset: v2 weight: 20
我们添加的策略包括两个目标:我们的 nodejs
服务的 subset
运行我们的应用程序的原始版本 v1
和 [X176X ] 正在运行金丝雀,v2
。 子集 1 将接收 80% 的传入流量,而金丝雀将接收 20%。
接下来,我们将添加一个目标规则,该规则将在该流量被路由到适当的服务后将规则应用于传入流量。 在我们的例子中,我们将配置 subset
字段以将流量发送到具有适当版本标签的 Pod。
在您的虚拟服务定义下方添加以下代码:
~/istio_project/node-istio.yaml
... --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: nodejs spec: host: nodejs subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2
我们的规则确保到我们的服务子集 v1
和 v2
的流量到达带有适当标签的 Pod:version: v1
和 version: v2
。 这些是我们包含在应用程序部署规范中的标签。
但是,如果我们愿意,我们还可以在子集级别应用特定的流量策略,从而在我们的金丝雀部署中实现进一步的特异性。 有关在此级别定义流量策略的更多信息,请参阅 官方 Istio 文档 。
完成编辑后保存并关闭文件。
修改应用程序清单后,您就可以应用配置更改并使用 Grafana 遥测插件检查应用程序流量数据。
第 4 步 — 应用配置更改和访问流量数据
应用程序清单已更新,但我们仍需要将这些更改应用到 Kubernetes 集群。 我们将使用 kubectl apply 命令应用我们的更改,而不会完全覆盖现有配置。 完成此操作后,您将能够向您的应用程序生成一些请求,并在 Istio Grafana 仪表板中查看相关数据。
将您的配置应用到您的应用程序服务和部署对象:
kubectl apply -f node-app.yaml
您将看到以下输出:
Outputservice/nodejs unchanged deployment.apps/nodejs-v1 created deployment.apps/nodejs-v2 created
接下来,应用您对 node-istio.yaml
所做的配置更新,其中包括对虚拟服务和新目标规则的更改:
kubectl apply -f node-istio.yaml
您将看到以下输出:
Outputgateway.networking.istio.io/nodejs-gateway unchanged virtualservice.networking.istio.io/nodejs configured destinationrule.networking.istio.io/nodejs created
您现在已准备好为您的应用程序生成流量。 但是,在此之前,首先检查以确保您正在运行 grafana
服务:
kubectl get svc -n istio-system | grep grafana
Outputgrafana ClusterIP 10.245.233.51 <none> 3000/TCP 4d2h
还要检查关联的 Pod:
kubectl get svc -n istio-system | grep grafana
Outputgrafana-67c69bb567-jpf6h 1/1 Running 0 4d2h
最后,检查 grafana-gateway
网关和 grafana-vs
虚拟服务:
kubectl get gateway -n istio-system | grep grafana
Outputgrafana-gateway 3d5h
kubectl get virtualservice -n istio-system | grep grafana
Outputgrafana-vs [grafana-gateway] [*] 4d2h
如果您没有看到这些命令的输出,请查看 How To Install and Use Istio With Kubernetes 的 Steps 2 和 5,其中讨论了如何启用安装 Istio 时的 Grafana 遥测插件以及如何启用对 Grafana 服务的 HTTP 访问。
您现在可以在浏览器中访问您的应用程序。 为此,您需要与 istio-ingressgateway
服务关联的外部 IP,它是 LoadBalancer 服务类型 。 在 How To Install and Use Istio With Kubernetes 中编写网关清单时,我们将 nodejs-gateway
网关与此控制器匹配。 有关网关清单的更多详细信息,请参阅该教程的 Step 4。
使用以下命令获取 istio-ingressgateway
服务的外部 IP:
kubectl get svc -n istio-system
您将看到如下输出:
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE grafana ClusterIP 10.245.85.162 <none> 3000/TCP 42m istio-citadel ClusterIP 10.245.135.45 <none> 8060/TCP,15014/TCP 42m istio-galley ClusterIP 10.245.46.245 <none> 443/TCP,15014/TCP,9901/TCP 42m istio-ingressgateway LoadBalancer 10.245.171.39 ingressgateway_ip 15020:30707/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:30285/TCP,15030:31668/TCP,15031:32297/TCP,15032:30853/TCP,15443:30406/TCP 42m istio-pilot ClusterIP 10.245.56.97 <none> 15010/TCP,15011/TCP,8080/TCP,15014/TCP 42m istio-policy ClusterIP 10.245.206.189 <none> 9091/TCP,15004/TCP,15014/TCP 42m istio-sidecar-injector ClusterIP 10.245.223.99 <none> 443/TCP 42m istio-telemetry ClusterIP 10.245.5.215 <none> 9091/TCP,15004/TCP,15014/TCP,42422/TCP 42m prometheus ClusterIP 10.245.100.132 <none> 9090/TCP 42m
istio-ingressgateway
应该是唯一具有 TYPE
LoadBalancer
的 Service,并且是唯一具有外部 IP 的 Service。
在浏览器中导航到此外部 IP:http://ingressgateway_ip
。
您应该看到以下登录页面:
单击获取鲨鱼信息按钮。 您将看到两个鲨鱼信息页面之一:
在此页面上单击刷新几次。 您应该比更可怕的版本更频繁地看到更友好的鲨鱼信息页面。
通过刷新五六次产生一些负载后,您可以前往 Grafana 仪表板。
在浏览器中,再次使用 istio-ingressgateway
外部 IP 和 Grafana 网关清单中定义的端口导航到以下地址:http://ingressgateway_ip:15031
。
您将看到以下登录页面:
点击页面顶部的 Home 将带您进入包含 istio 文件夹的页面。 要获取下拉选项列表,请单击 istio 文件夹图标:
从此选项列表中,单击 Istio Service Dashboard。
这将带您进入带有另一个下拉菜单的登录页面:
从可用选项列表中选择 nodejs.default.svc.cluster.local
。
如果您向下导航到页面的 Service Workloads 部分,您将能够查看 Incoming Requests by Destination And Response Code:
在这里,您将看到 200 和 304 HTTP 响应代码的组合,表示成功的 OK
和 Not Modified
响应。 标记为 nodejs-v1
的响应应该超过标记为 nodejs-v2
的响应,这表明传入流量正在按照我们在清单中定义的参数路由到我们的应用程序子集。
结论
在本教程中,您使用 Istio 和 Kubernetes 部署了一个演示 Node.js 应用程序的金丝雀版本。 您创建了虚拟服务和目标规则资源,它们共同允许您将 80% 的流量发送到原始应用程序服务,并将 20% 的流量发送到较新的版本。 一旦您对较新的应用程序版本的性能感到满意,您就可以根据需要更新您的配置设置。
有关 Istio 中流量管理的更多信息,请参阅文档中相关的 高级概述 ,以及使用 Istio 的 bookinfo 和 helloworld 示例的具体示例应用程序。