侧边栏壁纸
博主头像
Wyatt博主等级

Done is better than perfect!

  • 累计撰写 103 篇文章
  • 累计创建 31 个标签
  • 累计收到 7 条评论

灰度发布

Wyatt
2021-05-18 / 0 评论 / 0 点赞 / 602 阅读 / 7,096 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-06-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 灰度发布

灰度发布又名金丝雀发布。很久很久以前矿工进入矿底开采作业之前,为了测试矿的底部 氧气是否稀薄以及是否有毒气,先放一只金世雀飞进矿底,如果金丝雀可以活着飞回来,则说明矿中的环境满足矿工们进入挖矿开采的条件。金世雀发布应用于互联网生产系统的系统版本发布中,新版本接收少部分的流量 好比进入飞往矿底的金世雀,如果新版本的用户体验及功能性满足预期目标,则逐渐替换老版本的服务接收用户的请求。
灰色是介于黑与白之间,即有黑色又有白色,黑白并存的颜色。恢复发布也就是新版本系统和老版本系统一段时间并存。新版本的系统就是金世雀,金世雀活着回来,新版本的系统用户体验及功能性需求满足预期目标。则新版本系统的占比逐渐大于老版本系统。
发布前
发布前

先发布一台金丝雀
先发布一台金丝雀

发布完
发布完成

1.1 系统灰度发布

灰度发布通常从网关域名处通过负载均衡的权重比例分配流量。
运营后台前端服务目前云主机单节点部署,如果云主机可以部署2台运营后台的前端服务,通过负载均衡权重比例进行灰度;后续也可以考虑运营后台前端K8S pod部署,这样可以通过K8S里面的ingress-nginx的权重比例进行灰度。
后端接口网关域名如何灰度?通过K8S不同命名空间如canary命名空间部署一套完成的服务,然后在zuul网关上面通过负载均衡的权重比例分配少部分流量到灰度名称空间canary的zuul网关服务。这时zuul网关注册到不同命名空间的注册中心,微服务之间调用不同命名空间的服务,互不干扰。
灰度发布的哪些流量分配到新版本,哪些流量仍然分配到老版本呢?这是一个值得思考的问题。目前K8S的ingress控制器ingress-nginx主要使用的是基于百分比权重分流、基于header请求头的匹配分流。
基于百分比权重分流。比如10%的流量分配到新版本,90%的流量分配到老版本。问题是让哪些用户的流量分配到新版本,哪些用户的流量分配到老版本?

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: zuul-ingress-canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"	#10%的流量分配灰度版本。如果100代表全部流量分配到灰度版本
  namespace: canary
spec:
  tls:
  - hosts:
    - api.fat.teax.shop
    secretName: zuul-ingress-secret
  rules:
  - host: api.fat.teax.shop
    http:
      paths:
      - path: /
        backend:
          serviceName: teax-zuul
          servicePort: 80

基于header头的分流。比如K8S ingress-nginx域名网关处定义一个header头和value为 canary:new-version,当用户请求网关域名时携带header头 canary:new-version 则流量被分配到新版本。否则流量分配到老版本。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: zuul-ingress-canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "canary"
    nginx.ingress.kubernetes.io/canary-by-header-value: "new-version"
  namespace: uat
spec:
  tls:
  - hosts:
    - api.fat.teax.shop
    secretName: zuul-ingress-secret
  rules:
  - host: api.fat.teax.shop
    http:
      paths:
      - path: /
        backend:
          serviceName: teax-zuul
          servicePort: 80
curl -H 'canary: new-version' https://api.fat.teax.shop  #流量分配到新版本

基于header头的分流。是否可以通过正则表达式匹配header头的value呢?ingress-nginx提供了一个正则表达式匹配header value的参数
nginx.ingress.kubernetes.io/canary-by-header-pattern: 630100000001611|222222|333333
我们生产环境也是使用的如下方案:header为labelList,value为用户ID,用户请求时header头labelList的值匹配到任何一个值流量都会进入灰度环境。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: zuul-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: labelList	#header头的名称是labelList
    #如下参数的意思是header的值包含其中任何一个则流量都转发到灰度环境,value为用户的ID标识,匹配拥有如下用户ID的用户 流量到灰度环境便于内部用户提前介入体验新版本
    nginx.ingress.kubernetes.io/canary-by-header-pattern: 630100000001611|222222|333333	
  namespace: canary
spec:
  tls:
  - hosts: 
    - api.teax.shop 
    secretName: teax.shop
  rules:
  - host: api.teax.shop
    http:
      paths:
      - path: /
        backend:
          serviceName: teax-zuul
          servicePort: 5001
curl -H 'labelList: 630100000001611' https://api.teax.shop  #流量分配到新版本

1.2 MySQL灰度发布

1.2.1 新增字段

灰度版本和生产版本使用同一个MySQL数据库。如果灰度发布时涉及到表结构新增字段,先执行sql修改表结构,然后再发布新版本的相关服务。
开发代码中insert数据时,开发人员必须明确指明插入什么value到哪些列,目的防止发布新版本新增字段,新服务insert数据正常,老版本服务错误。

1.2.2 删除字段

通常不会也不允许删除字段,如果删除字段 则老版本服务运行会报错。

1.2.3 修改字段

修改字段的数据类型长度,字段描述等,通常不影响老版本程序正常运行。

1.2.4 新增表

发布前先执行创建表的sql语句,则老版本读写不涉及新表,新版本涉及新表,因此互补影响

1.2.5 其他问题

有待持续添加。

1.3 redis

redis存储key value的非关系型键值对,通常情况影响不大。但是具体灰度发布的影响还需以存储数据的业务使用场景进行分析。

1.4 rabbitmq

新版本中如涉及新增消息队列,则老版本代码仍然消费历史代码中的消息队列。新版本的灰度代码消费新增消费队列,互不影响。
如果不涉及新增消息队列,则正常生产消费消息队列的消息。如果老版本的服务生产了消息至队列,灰度的服务消费了消息队列的消息会有怎样的影响?老版本生产的消息老版本的服务未消费之前,被灰度版本的服务消费了该消息?(需要继续查证并寻找对应的解决方案)

1.5 mongodb

mongodb介于关系型与非关系型之间的数据库。表结构会不会发生变动?如果表结构发生了变动 增加了key value的键值对,那么老版本仍然消费mongodb同一个表的数据,是否有影响,需要结合业务代码的实际情况。是否和MySQL表新增加字段 insert数据时候一样。往mongodb指定表的key value的键值对写数据,读取数据?(需要开发人员配套解答)
现有mongodb数据库carts_info表的一条数据示例如下,表里面每条数据是一个json,每个json有13个key value,可以称为13个列名或者字段名。

0:{13 items
"_id":"ObjectId("5c32095fb7722900014499c5")"
"_class":"com.xuanwei.app.entity.CartsInfo"
"cartsId":"6003005607541546783071107743944"
"userId":"NumberLong(610100000000446)"
"goodsId":"NumberLong(620100000000108)"
"goodsCnt":1
"cupType":1
"tasteList":[...]2 items
"unitPrice":"20.0"
"goodsCash":"20.00"
"createTime":"ISODate("2019-01-06T13:57:51.116Z")"
"updateTime":"ISODate("2019-01-06T13:57:51.116Z")"
"storeId":1000
}

1.6 xxl-job

xxl-job如何处理?灰度发布或者蓝绿发布 canary名称空间下面是否也部署定时任务器 执行调度任务?

2. 蓝绿发布

蓝绿发布仅适用于双服务器组发布。V1版本称为蓝,V2版本称为绿。V1老版本,V2新版本。发布时通过负载均衡把流量一次性切换为V2版本。出问题回退直接流量一次性切换V1版本。发布完成后V1蓝版本一般不直接回收,留一个观察期,确认V2版本没问题之后 再回收V1版本。

蓝绿发布

2.1 系统蓝绿发布

可以在同一个K8S集群里面的不同命名空间分别部署1套系统来进行蓝绿发布,不同命名空间进行隔离。比如我们正常生产业务系统部署在default命名空间下面,在canary命名空间部署一套新版的服务。
蓝绿流量如何切换?更换金世雀发布的流量权重比为100,则所有的流量都转发到canary名称空间的服务。canary命名空间部署了相同的zuul、eureka、order等等的相同服务。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: zuul-ingress-canary
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "100"	#如果100代表全部流量分配到绿版本
  namespace: canary
spec:
  tls:
  - hosts:
    - api.fat.teax.shop
    secretName: zuul-ingress-secret
  rules:
  - host: api.fat.teax.shop
    http:
      paths:
      - path: /
        backend:
          serviceName: teax-zuul
          servicePort: 80

2.2 数据库问题

数据库的处理思路和灰度发布一样。

2.3 存在问题

与灰度发布存在的问题一样。

建议:
假设生产环境有2个ip地址,发布服务后,ip地址A对应老环境,ip地址B对应新环境。将生产环境的域名地址指向ip地址A,域名不可跳转到ip地址B。生产发布后,测试人员将电脑内ipconfig配置域名指向ip地址B。测试通过后,再放开网关限制。

3. K8S下容器滚动升级

K8S支持部署pod容器的时候支持滚动升级。可以根据参数设置滚动升级的策略。如果一个服务的pod副本数较多的时候,可以根据百分比滚动升级新版本的pod,然后删除旧版本的pod。也可以根据数量升级几个新版本的pod,然后删除几个旧版本的pod。另外关于先升级新版本的pod 还是先删除老版本的pod 也可以进行设置。

测试环境:

#如下为测试环境滚动升级的策略参数设置。
#前提条件admin服务正常运行的pod副本数为1。发布的过程中先创建一个新版本的pod副本,由该产生决定maxSurge: 1,
然后就删除1个老版本的pod副本由该参数决定 maxUnavailable: 1  ,因为老版本的pod副本删除了,而新版本的
pod副本还未启动完成,因此1个pod副本不可用。这种情况是为了解决测试环境中 服务器资源不足的问题,避免升级过程中
同时1个服务同时占用2个pod资源。
spec:
  replicas: 1
  selector:
    matchLabels:
      app: teax-admin
  strategy:
    rollingUpdate:
      maxSurge: 1					#滚动升级过程中先升级1个新版本的pod
      maxUnavailable: 1		#滚动升级的过程中最大不可用的pod副本数

生产环境:

#如下为生产环境滚动升级的策略参数设置。
#前提条件admin服务正常运行的pod副本数为1。发布过程中先创建一个新版本的pod副本,由该参数决定maxSurge: 1,
当新版本的pod副本启动完成 就绪行健康检测通过之后,新版本的pod状态达到Running状态之后,删除老版本的pod副本,
因此升级过程中最大不可用的pod副本数为0,由该参数决定 maxUnavailable: 0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: teax-admin
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

3.1 存在问题

一个服务滚动升级完成,其他服务未发布的时候,服务之间相互调用的问题。

0

评论区