灰度发布介绍

灰度发布,又名金丝雀发布(canary release、canary launch 或 canary deployment ),是指在黑与白之间,能够平滑过渡的一种发布方式。是指在发布新版本的服务时,将部分流量引入新版本,如果新版本没有问题,再逐步将流量引入新版本,直至全部流量引入新版本。A/B 测试:灰度发布方法中的一种,将用户分为 AB 两组,然后收集他们的反馈效果,在 istio 中可以很方便的实现 A/B 测试。

部署测试应用

本地使用的 k3s 安装的 kubernetes 集群,使用 istioctl 安装的 istio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@dev:~/istio-1.17.2# istioctl install --set profile=demo -y
root@dev:~/istio-1.17.2# kubectl label namespace default istio-injection=enabled
root@dev:~/istio-1.17.2# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
// reviews 服务有三个版本
root@dev:~/istio-1.17.2# kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
ratings-v1-b8f8fcf49-wkkx8       2/2     Running   0          96s
productpage-v1-d4f8dfd97-k5798   2/2     Running   0          96s
details-v1-6997d94bb9-5w7ns      2/2     Running   0          96s
reviews-v1-5896f547f5-ngqr4      2/2     Running   0          96s
reviews-v2-5d99885bc9-ncw2j      2/2     Running   0          96s
reviews-v3-589cb4d56c-59v4h      2/2     Running   0          96s
root@dev:~/istio-1.17.2# kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
root@dev:~/istio-1.17.2# kubectl get gateway
NAME               AGE
bookinfo-gateway   4m8s

// 获取 host
root@dev:~# kubectl get po -l istio=ingressgateway -n istio-system  -o jsonpath='{.items[0].status.hostIP}'
172.19.52.61
// 获取 nodeport
root@dev:~# kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
30871

通过测试访问“http://172.19.52.61:30871/productpage”可以看到服务正常运行了,并且多次刷新时,可以看到“Book Reviews”下面有不同的显示:

  • v1 版本不会调用 ratings 服务。
  • v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。
  • v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。

为 reviews 服务定义不同的版本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3

路由到版本 1

要仅路由到 v1 版本的 review 服务,需要设置如下的 VirtualService:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1

此时访问页面,多次刷新可以看到“Book Reviews”只有 v1 版本的显示,查看 envoy 相关配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// gateway svc 中将 80 端口请求转发到 gateway pod 的 8080 端口,然后会转发到 productpage 服务 9080 端口
root@dev:~# istioctl pc route istio-ingressgateway-85c59bdcd9-wfb68.istio-system
NAME          DOMAINS     MATCH                  VIRTUAL SERVICE
http.8080     *           /productpage           bookinfo.default

// productpage 监听 9080
root@dev:~# istioctl pc listener productpage-v1-d4f8dfd97-k5798  --port 9080
ADDRESS PORT MATCH                                DESTINATION
0.0.0.0 9080 Trans: raw_buffer; App: http/1.1,h2c Route: 9080

// productpage 路由到 "outbound|9080|v1|reviews.default.svc.cluster.local"
root@dev:~# istioctl pc route productpage-v1-d4f8dfd97-k5798 --name 9080
NAME     DOMAINS                                          MATCH     VIRTUAL SERVICE
9080     reviews, reviews.default + 1 more...             /*        reviews.default

// cluster 对应的 endpoints "10.42.0.14"即为 v1 版本的 review 服务的 pods
root@dev:~# istioctl pc endpoints productpage-v1-d4f8dfd97-k5798 --cluster "outbound|9080|v1|reviews.default.svc.cluster.local"
ENDPOINT            STATUS      OUTLIER CHECK     CLUSTER
10.42.0.14:9080     HEALTHY     OK                outbound|9080|v1|reviews.default.svc.cluster.local

基于用户身份的路由

路由配置中,可以将特定用户的所有流量路由到特定服务版本。下面的例子,将来自名为 Jason 的用户的流量将被路由到服务 reviews:v2。 因为 productpage 在到 reviews 服务的 HTTP 请求中都增加了一个 end-user 请求头,因此以用户 Jason 登录时,将会展示黑色星形图标的评分信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v1

流量转移

在 VirtualService 通过设置不同 subset 的权重大小,可以控制流量路由比例,实现流量从微服务的一个版本逐步迁移到另一个版本。下面例子中,会把 50% 的流量发送到 reviews:v1,50% 的流量发送到 reviews:v3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 50
    - destination:
        host: reviews
        subset: v3
      weight: 50

灰度发布配置说明

主要涉及到 VirtualService 中的 HTTPRoute、HTTPMatchRequest、HTTPRouteDestination 字段,HTTPRoute 中重点字段说明:

字段 类型 说明 必填
name string 路由名称,用于调试
match HTTPMatchRequest[] 匹配的条件,单个匹配块里面是与的关系,多个匹配块列表是或的关系
route HTTPRouteDestination[] 要么返回直接响应,要么返回重定向。转发到服务的某个版本,权重决定了流量的比例

HTTPMatchRequest 重点字段说明如下,其中 uri、scheme、method、authority 都是大小写敏感,格式可以是全路径、前缀、正则方式:

字段 类型 说明 必填
uri StringMatch 匹配的 url,
scheme StringMatch 匹配的 url scheme
method StringMatch 匹配的 url method
authority StringMatch 匹配的 url,说明见 彻底了解 URL
headers map<string, StringMatch> 必须小写,用连字符作为分隔符,如果只指定了头的名称,但值为空,那么将检查头是否存在
port uint32 指定被寻址的主机上的端口
queryParams map<string, StringMatch> 匹配的查询参数
withoutHeaders map<string, StringMatch> 跟 headers 相反,匹配没有某些 header

HTTPRouteDestination 字段说明如下:

字段 类型 说明 必填
destination Destination 转发的目的服务
weight int32 转发流量权重
headers StringMatch 匹配的 url,
headers Headers header 处理规则,用于请求和响应时增加或者删除某个 header 字段

参考