开源Gloo的两阶段Canary推广
作者: 里克·杜科特| 的GitHub | 推特
每天,我和我的同事都在与正在使用的平台所有者,架构师和工程师进行交谈。 o 作为API网关 向最终用户展示他们的应用程序。这些应用程序可能跨越旧式整体,微服务,托管云服务和Kubernetes 集群。幸运的是,使用Gloo可以轻松设置路由来管理,保护和观察应用程序流量,同时 支持灵活的部署架构,以满足用户不断变化的生产需求。
除了最初的设置外,平台所有者还经常要求我们帮助设计其组织内的操作流程: 我们如何使一个新的应用程序在线?我们如何升级应用程序?我们如何在我们之间划分责任 平台,运营和开发团队?
在本文中,我们将使用Gloo为应用程序升级设计两阶段的Canary推广工作流程:
- 在第一阶段,我们将通过将一小部分流量转移到新版本进行金丝雀测试。这使您可以安全地执行烟雾和正确性测试。
- 在第二阶段中,我们将逐步将流量转移到新版本,使我们能够监视负载下的新版本,并最终停用旧版本。
为简单起见,我们将重点放在使用 开源Gloo,我们将部署网关和 应用到Kubernetes。最后,我们将讨论一些扩展和高级主题,这些内容和后续主题可能会很有趣。
初始设置
首先,我们需要一个Kubernetes集群。此示例未利用任何特定于云的优势
功能,并且可以针对本地测试集群(例如 迷你库 .
This post assumes a basic understanding of Kubernetes 和 how to interact 与 it using kubectl
.
我们将安装最新的 开源Gloo to the gloo-system
namespace 和 deploy
version v1
of an example application to the echo
namespace. We'll expose this application outside the cluster
通过在Gloo中创建路线,最终得到这样的图片:
部署Gloo
We'll install gloo 与 the glooctl
command line tool, which we can download 和 add to the PATH
与 the following
commands:
curl -sL //run.solo.io/gloo/install | sh
export PATH=$HOME/.gloo/bin:$PATH
Now, you should be able to run glooctl version
to see that it is installed correctly:
➜ glooctl version
Client: {"version":"1.3.15"}
Server: version undefined, could not find any version of gloo running
现在,我们可以使用简单的命令将网关安装到集群中:
glooctl install gateway
控制台应指示安装成功完成:
Creating namespace gloo-system... Done.
Starting o installation...
Gloo was successfully installed!
Before long, we can see all the o pods running in the gloo-system
namespace:
➜ kubectl get pod -n gloo-system
NAME READY STATUS RESTARTS AGE
discovery-58f8856bd7-4fftg 1/1 Running 0 13s
gateway-66f86bc8b4-n5crc 1/1 Running 0 13s
gateway-proxy-5ff99b8679-tbp65 1/1 Running 0 13s
gloo-66b8dc8868-z5c6r 1/1 Running 0 13s
部署应用程序
Our echo
application is a simple container (thanks to our friends at HashiCorp) that will
响应应用程序版本,以帮助演示我们开始测试时的金丝雀工作流程,并
shifting traffic to a v2
version of the application.
Kubernetes在建模该应用程序方面给了我们很大的灵活性。我们将采用以下 conventions:
- 我们将在部署名称中包含该版本,以便我们可以运行该应用程序的两个版本 并排管理它们的生命周期。
- We'll label pods 与 an app label (
app: echo
) 和 a version label (version: v1
) to help 与 our canary rollout. - We'll deploy a single Kubernetes
Service
for the application to set up networking. Instead of updating 此操作或使用多种服务来管理到不同版本的路由,我们将使用Gloo配置管理部署。
The following is our v1
echo application:
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-v1
spec:
replicas: 1
selector:
matchLabels:
app: echo
version: v1
template:
metadata:
labels:
app: echo
version: v1
spec:
containers:
# Shout out to our friends at Hashi for this useful test server
- image: hashicorp/http-echo
args:
- "-text=version:v1"
- -listen=:8080
imagePullPolicy: Always
name: echo-v1
ports:
- containerPort: 8080
And here is the echo
Kubernetes Service
object:
apiVersion: v1
kind: Service
metadata:
name: echo
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
selector:
app: echo
为了方便起见,我们已在仓库中发布了此Yaml,以便可以使用以下命令进行部署:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/1-setup/echo.yaml
我们应该看到以下输出:
namespace/echo created
deployment.apps/echo-v1 created
service/echo created
And we should be able to see all the resources healthy in the echo
namespace:
➜ kubectl get all -n echo
NAME READY STATUS RESTARTS AGE
pod/echo-v1-66dbfffb79-287s5 1/1 Running 0 6s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/echo ClusterIP 10.55.252.216 <none> 80/TCP 6s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/echo-v1 1/1 1 1 7s
NAME DESIRED CURRENT READY AGE
replicaset.apps/echo-v1-66dbfffb79 1 1 1 7s
用Gloo暴露在集群外
现在,我们可以使用Gloo在群集之外公开此服务。首先,我们将应用程序建模为Gloo 上游,这是Gloo的抽象 对于交通目的地:
apiVersion: gloo.solo.io/v1
kind: 上游
metadata:
name: echo
namespace: gloo-system
spec:
kube:
selector:
app: echo
serviceName: echo
serviceNamespace: echo
servicePort: 8080
subsetSpec:
selectors:
- keys:
- version
Here, we're setting up subsets based on the version
label. We don't have to use this in our routes, but later
我们将开始使用它来支持我们的金丝雀工作流程。
现在,我们可以通过定义一个到Gloo的上游创建路由 虚拟服务:
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
我们可以使用以下命令来应用这些资源:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/1-setup/upstream.yaml
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/1-setup/vs.yaml
一旦应用了这两种资源,就可以开始通过Gloo将流量发送到应用程序:
➜ curl $(glooctl proxy url)/
version:v1
我们的设置已完成,并且集群现在如下所示:
两阶段推出策略
Now we have a new version v2
of the echo application that we wish to roll out. We know that when the
部署已完成,我们将最终得到以下图片:
但是,为达到此目的,我们可能需要执行几轮测试以确保应用程序的新版本 符合某些正确性和/或性能接受标准。在本文中,我们将介绍一种两阶段的方法 Gloo的金丝雀推出,可以用来满足绝大多数验收测试。
在第一阶段,我们将通过将一小部分流量路由到新版本来执行冒烟性和正确性测试
of the application. In this demo, we'll use a header stage: canary
to trigger routing to the new service, though in
实践中,可能希望根据请求的另一部分(例如,经过验证的JWT中的声明)做出此决定。
在第二阶段,我们已经确定了正确性,因此我们准备将所有流量转移到新 应用程序的版本。我们将配置加权目标,并在监控某些业务的同时转移流量 确保服务质量保持可接受水平的指标。一旦将100%的流量转移到新版本, 旧版本可以退役。
在实践中,可能希望仅使用一个阶段进行测试,在这种情况下,另一阶段可以是 skipped.
阶段1:首次推出v2的canary
In this phase, we'll deploy v2
, 和 then use a header stage: canary
to start routing a small amount of specific
traffic to the new version. We'll use this header to perform some basic smoke testing 和 make sure v2
is working the
way we'd expect:
设置子集路由
Before deploying our v2
service, we'll update our virtual service to only route to pods that have the subset label
version: v1
,使用称为的Gloo功能 子集路由.
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v1
我们可以使用以下命令将它们应用于集群:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/2-initial-subset-routing-to-v2/vs-1.yaml
该应用程序应继续像以前一样运行:
➜ curl $(glooctl proxy url)/
version:v1
部署echo v2
Now we can safely deploy v2
of the echo application:
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-v2
spec:
replicas: 1
selector:
matchLabels:
app: echo
version: v2
template:
metadata:
labels:
app: echo
version: v2
spec:
containers:
- image: hashicorp/http-echo
args:
- "-text=version:v2"
- -listen=:8080
imagePullPolicy: Always
name: echo-v2
ports:
- containerPort: 8080
我们可以使用以下命令进行部署:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/2-initial-subset-routing-to-v2/echo-v2.yaml
Since our gateway is configured to route specifically to the v1
subset, this should have no effect. However, it does enable
v2
to be routable from the gateway if the v2
subset is configured for a route.
Make sure v2
is running before moving on:
➜ kubectl get pod -n echo
NAME READY STATUS RESTARTS AGE
echo-v1-66dbfffb79-2qw86 1/1 Running 0 5m25s
echo-v2-86584fbbdb-slp44 1/1 Running 0 93s
该应用程序应继续像以前一样运行:
➜ curl $(glooctl proxy url)/
version:v1
添加到v2的路由以进行金丝雀测试
We'll route to the v2
subset when the stage: canary
header is supplied on the request. If the header isn't
provided, we'll continue to route to the v1
subset as before.
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- headers:
- name: stage
value: canary
prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v1
我们可以使用以下命令进行部署:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/2-initial-subset-routing-to-v2/vs-2.yaml
金丝雀测试
现在我们有了这条路线,我们可以进行一些测试。首先,请确保现有路由能够按预期运行:
➜ curl $(glooctl proxy url)/
version:v1
现在我们可以开始金丝雀测试我们的新应用程序版本:
➜ curl $(glooctl proxy url)/ -H "stage: canary"
version:v2
子集路由的高级用例
我们可能会决定使用用户提供的请求标头的这种方法过于开放。相反,我们可能 希望将金丝雀测试仅限于已知的授权用户。
我们已经看到的一种常见实现是,黄雀色路线需要包含以下内容的有效JWT 指明受试者已获准进行金丝雀测试的特定声明。企业Gloo已开箱 支持验证JWT,基于JWT声明更新请求标头并重新计算 基于更新的标头的路由目标。我们会将其保存在以后的文章中,以涵盖更高级的用途 金丝雀测试中的案例。
阶段2:将所有流量转移到v2并停用v1
At this point, we've deployed v2
, 和 created a route for canary testing. If we are satisfied 与 the
results of the testing, we can move on to phase 2 和 start shifting the load from v1
to v2
. We'll use
加权目的地
在Gloo中管理迁移期间的负载。
设置加权目的地
我们可以更改Gloo路线以路由到这两个目的地,并通过权重来确定应吸引的流量
go to the v1
versus the v2
subset. To start, we're going to set it up so 100% of the traffic continues to get routed to the
v1
subset, unless the stage: canary
header was provided as before.
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
# We'll keep our route from before if we want to continue testing 与 this header
- matchers:
- headers:
- name: stage
value: canary
prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
# Now we'll route the rest of the traffic to the upstream, load balanced across the two subsets.
- matchers:
- prefix: /
routeAction:
multi:
destinations:
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v1
weight: 100
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
weight: 0
我们可以使用以下命令将此虚拟服务更新应用于群集:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/3-progressive-traffic-shift-to-v2/vs-1.yaml
Now the cluster looks like this, for any request that doesn't have the stage: canary
header:
With the initial weights, we should see the gateway continue to serve v1
for all traffic.
➜ curl $(glooctl proxy url)/
version:v1
开始推出
To simulate a load test, let's shift half the traffic to v2
:
这可以通过调整权重在我们的虚拟服务上表示:
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- headers:
- name: stage
value: canary
prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
- matchers:
- prefix: /
routeAction:
multi:
destinations:
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v1
# Update the weight so 50% of the traffic hits v1
weight: 50
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
# And 50% is routed to v2
weight: 50
我们可以使用以下命令将其应用于集群:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/3-progressive-traffic-shift-to-v2/vs-2.yaml
Now when we send traffic to the gateway, we should see half of the requests return version:v1
和 the
other half return version:v2
.
➜ curl $(glooctl proxy url)/
version:v1
➜ curl $(glooctl proxy url)/
version:v2
➜ curl $(glooctl proxy url)/
version:v1
实际上,在此过程中,您可能会监视一些性能和业务指标 确保流量转移不会导致整体服务质量下降。我们甚至可以 利用像 旗手 帮助自动化这个Gloo 工作流程。 o Enterprise与您的指标后端集成,并提供开箱即用的动态, 基于上游的仪表板,可用于监视部署的运行状况。 我们将把这些主题保存起来,以备将来在Gloo的高级金丝雀测试用例上发表。
完成部署
We will continue adjusting weights until eventually, all of the traffic is now being routed to v2
:
我们的虚拟服务将如下所示:
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- headers:
- name: stage
value: canary
prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
- matchers:
- prefix: /
routeAction:
multi:
destinations:
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v1
# No traffic will be sent to v1 anymore
weight: 0
- destination:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
# Now all the traffic will be routed to v2
weight: 100
我们可以使用以下命令将其应用于集群:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/3-progressive-traffic-shift-to-v2/vs-3.yaml
Now when we send traffic to the gateway, we should see all of the requests return version:v2
.
➜ curl $(glooctl proxy url)/
version:v2
➜ curl $(glooctl proxy url)/
version:v2
➜ curl $(glooctl proxy url)/
version:v2
退役v1
至此,我们已经部署了新版本的应用程序,使用子集路由进行了正确性测试,
通过逐步将流量转移到新版本进行负载和性能测试,并完成
the rollout. The only remaining task is to clean up our v1
resources.
首先,我们将清理路线。我们将在路由上指定子集,以便为将来的升级做好准备。
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
name: echo
namespace: gloo-system
spec:
virtualHost:
domains:
- "*"
routes:
- matchers:
- prefix: /
routeAction:
single:
upstream:
name: echo
namespace: gloo-system
subset:
values:
version: v2
我们可以使用以下命令来应用此更新:
kubectl apply -f //raw.githubusercontent.com/solo-io/gloo-ref-arch/blog-30-mar-20/platform/prog-delivery/two-phased-with-os-gloo/4-decommissioning-v1/vs.yaml
And we can delete the v1
deployment, which is no longer serving any traffic.
kubectl delete deploy -n echo echo-v1
现在我们的集群如下所示:
并向网关发出的请求返回以下内容:
➜ curl $(glooctl proxy url)/
version:v2
现在,我们已经使用Gloo完成了两阶段的Canary首次发布应用程序更新!
其他高级主题
在这篇文章的过程中,我们收集了一些可以作为高级探索的良好起点的主题:
- 使用 智威汤逊 过滤器以验证JWT,将声明提取到标头上,并根据声明值路由到Canary版本。
- 看着 普罗米修斯指标 和 Grafana仪表板 由Gloo创建,用于监视部署的运行状况。
- 通过集成自动化部署 旗手 与 o .
其他一些值得进一步探讨的主题:
- 配套 自助服务 通过赋予团队所有权对其上游和路线配置进行升级
- 利用Gloo的 代表团 功能和Kubernetes RBAC 安全地分散配置管理
- 通过应用完全自动化连续交付过程 GitOps 原理和使用工具,例如 助焊剂 将配置推送到集群
- 配套 杂种 要么 非Kubernetes 通过使用其他部署模式设置Gloo来创建应用程序用例
- 利用 交通阴影 开始用实际数据测试新版本,然后再转移生产流量
参与Gloo社区
除了企业客户群以外,Gloo拥有庞大且不断增长的开源用户社区。要了解更多有关 Gloo:
如果您想与我联系(反馈总是很感激!),您可以在 单身休闲 或给我发电子邮件 rick.ducott@solo.io.