原生Ingress灰度发布能力不够?咱们是这么干的

灰度发布是一种常见的服务滚动升级或 A/B 测试策略。在新版本服务正式发布前,能够部署少许的新版本服务和上个版本共存,用部分生产流量测试新版本的功能和特性。若是新版本反馈良好,则能够渐进地提升新版本的比例或者所有替换成新版本,若是有问题也可以及时撤回,不至于形成太大范围的影响。nginx

目前,原生容器发布基本都是使用 deployment,经过给 deployment 和 service 灵活配置 labels ,能够实现一种基于服务版本的灰度发布。正则表达式

因为原生 Ingress 对象描述能力的限制,一些常见 Ingress controller 的灰度发布功能也大打折扣,很难知足用户灰度发布的实际需求。api

博云基于原生Ingress,作了大量加强,基于请求特征的灰度发布是其中一个重要特性。app

使用 deployment 实施灰度发布负载均衡

经过配置 pod labels 和 service label selector,Kubernetes 原生支持灰度发布。假设咱们部署了 echo-demo 服务的两个版本的 deployment:dom

echo-demo-v1.yamlcurl

name: echo-demo-v1

 replicas: 3

 ...

 labels:

    app: echo-demo

    track: stable

 ...

 image: deploy.bocloud/ingress/echo-demo:1.0.0

echo-demo-v2.yamlide

name: echo-demo-v2

 replicas: 2

 ...

 labels:

    app: echo-demo

    track: canary

 ...

 image: deploy.bocloud/ingress/echo-demo:2.0.0

以及一个 echo-demo service:测试

apiVersion: v1url

kind: Service

metadata:

name: echo-demo

spec:

ports:

- port: 80

  protocol: TCP

  targetPort: 8080

selector:

app: echo-demo

上述配置中,echo-demo service 聚合了 echo-demo-v1 和 echo-demo-v2 两个版本的服务,两个版本分别有 3 个和 2 个实例。此时咱们访问 echo-demo service,请求将根据实例数量的占比按 3:2 的比例分布到 v1 和 v2 两个服务中。这样就实现了基于权重的灰度发布。

然而这种灰度发布却自有其限制和问题。首先,若是咱们给服务加上自动水平伸缩(HPA),那么两个版本的服务将彻底根据各自的负载状况独立调整 pod 实例数量,这样一来两个版本的实例数量和比例就会发生变化,打破咱们一开始设置的灰度比例。其次,若是咱们只想配置很小比例的请求到灰度版本,好比 100:1,那么在最少提供一个灰度版本 pod 的前提下,须要配置最少 100 个旧版本实例,很不灵活。第三,除了按比例的灰度策略,有时可能还须要根据请求特征来辨别用户并分配灰度服务到其中的一小部分。因为deployment有着上述的缺陷,致使其不多被当作灰度使用的缘由。因此在实际应用当中,灰度发布基本上 由ingress来作。而这几个问题,均可以经过使用 Ingress 灰度发布方案来解决。

Ingress 能描述灰度发布吗?

Ingress 是 Kubernetes 平台的一个原生 API,定义了 HTTP 路由到 Kubernetes service 的映射。Ingress Controller 依据 Ingress 对象的声明,动态生成负载均衡器的配置文件,由负载均衡器将 k8s 内部服务暴露出去,Nginx Ingress Controller 是使用最普遍的一个 Ingress 控制器。一个典型的 Ingress 描述文件以下:

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: echo-demo

labels:

app: echo-demo

annotations:

kubernetes.io/ingress.class: nginx

namespace: default

spec:

rules:

- host: test.domain.com

  http:

    paths:

      - backend:

          serviceName: echo-demo-v1

          servicePort: 80

        path: /echo

      - backend:

          serviceName: hello-world

          servicePort: 8080

        path: /hello

由 Ingress API 能够看到,一个域名下能够定义多个访问路径,然而一个路径却只能定义一个 service。因而,在使用 Ingress 对象来描述灰度发布的情形下,要实现一个访问端点与多个服务版本的映射,仍然只能采用上述一个 kubernetes service 聚合多个版本的 deployment 的方式,这种方案的种种问题上文已经分析过了。社区版 Nginx Ingress Controller 受限于 Ingress API 的描述能力,对灰度发布的支持彻底是不可用的状态。有没有一种办法,既兼容 Ingress API,又能作到一个访问端点映射到多个 service 呢?

博云基于 Nginx Ingress Controller 开发的 Ingress 控制器 BeyondELB,设计了一种 Ingress 组合模型,在兼容 Ingress API 的基础上,用 labels 给 Ingress 对象分类,并将多个不一样类别的 Ingress 对象组合成一个逻辑 Ingress,从而实现了一个访问端点到多个 service 的映射。这种组合模型为实现另外一种灰度发布方案提供了可能。

使用 BeyondELB

实施基于权重的灰度发布

上面能够看到,使用 deployment labels 实现的基于权重的灰度发布,其灰度比例彻底依赖于服务的实例数量,而引入 HPA 以后服务实例数量可能会发生倾斜从而打破灰度比例。而由 Ingress 组合模型实现的灰度方案中,一个访问端点可以配置多个 service,每一个 service 有本身的灰度权重和同一版本的服务实例,灰度权重再也不和服务实例数量相绑定。经过将灰度权重和服务实例数量解耦,权重能够随时按需调整,服务的实例数量则能够根据负载状况自行伸缩,不会影响到设定好的灰度比例。
原生Ingress灰度发布能力不够?咱们是这么干的

采用了 Ingress 组合模型的 BeyondELB 支持上述权重灰度发布策略。能够为某个 Ingress 访问路径定义一个或多个服务版本,而后为不一样服务版本设定灰度权重,请求将按照灰度权重分配到不一样版本的服务中。

上图中,选择开启基于权重的灰度发布,定义了一个灰度服务并设定 20 的权重,若是主版本服务的权重设定为 80(图中未给出主服务版本定义),则请求将按照 4:1 的比例分配到主版本服务和灰度版本服务。下面对配置了 80/20 权重的服务连续请求 100 次,可见流量按设定的比例分配到 v1 和 v2 服务中。

$ for i in seq 1 100; do curl -s http://test.domain.com/weight; done | jq .version | sort | uniq -c

78 "1.0.0"

22 "2.0.0"

使用 BeyondELB

实施基于请求特征的版本级灰度发布

在某些场景下,可能会须要对灰度选择有更好的控制,好比对于 HTTP 服务来讲,能够经过请求特征甄别用户,容许将具备特定请求 Header 或 Cookie 的用户发送到灰度版本。

原生Ingress灰度发布能力不够?咱们是这么干的

上图中,咱们添加了一个灰度服务版本,而且设定灰度策略为“基于请求特征”。当请求附有名为 "canary" 值为 "true" 的请求 Header 时,将由该灰度服务版本响应;而其它未匹配该灰度条件的请求则由主服务版本响应(图中未给出主服务版本定义)。

咱们经过如下两个脚本测试基于 Header 的灰度效果。

请求携带名为 canary 且其值为 "true" 的 Header:

$ for i in seq 1 100; do curl -s -H "canary: true" http://test.domain.com/header; done | jq .version | sort | uniq -c

100 "1.0.0"

请求不携带名为 "canary" 的 Header:

$ for i in `seq 1 100`; do curl -s http://test.domain.com/header; done | jq .version | sort | uniq -c

100 "2.0.0"

除了基于请求 Header 的灰度匹配策略,博云的 Ingress Controller 还支持基于请求 Cookie 的匹配策略,以及多个 Header 或 Cookie 灰度条件组合的匹配策略。咱们还能够用正则表达式匹配 operator 来实现更具体化的灰度方案,好比以下匹配表达式能够将 User ID 以 3 结尾的用户发送到灰度服务版本。

(header("x-userid" regex "^[0-9]+3$")

总结

经过使用 Kubernetes pod labels 和 service,能够初步实现基于权重的灰度发布。但这种灰度发布依赖于服务版本的实例数量,不只不灵活并且在引入 HPA 时会形成服务比例倾斜。博云自研的 Ingress Controller 将灰度权重和服务实例数量解耦,服务可依据负载和 HPA 规则自行伸缩实例,不影响灰度比例。而基于 Header 或 Cookie 的灰度匹配策略,为实现更可控的灰度方案提供了支持。除了加强的灰度发布能力,博云商用版本的 Ingress Controller 还支持租户级负载、热更新等特性,后续将会逐步介绍。

相关文章
相关标签/搜索