淘先锋技术网

首页 1 2 3 4 5 6 7

前言

部署在 Kubernetes 集群中的应用,在升级发布时可能会存在的问题:

1,由于 Kuberneter 底层 Pod 容器生命周期与网络组件生命周期是异步管理的,在升级时如果没有处理好应用优雅退出的问题,就很容易导致 http 访问请求 5xx

2,原生 Deployment 应用的滚动发布功能是一把梭的全量发布模式,没有灰度和分批控制发布的概念,一旦出现问题,故障影响范围就会迅速扩大

这也是为什么需要灰度发布,蓝绿发布,彩虹发布,金丝雀发布、A/B Test等多样化形式发布的重要原因,核心目标只有一个,就是为了确保服务的稳定性,减少或避免因变更带来的不稳定因素

今天我们主要来聊下,如何在不引入第三方插件的情况下,来实现简单的灰度发布和蓝绿发布功能

通过 Service 实现灰度发布和蓝绿发布

在 Kubernetes 里面 Pod 的网络通信都是借助 Service 实现的,Service 的底层是依赖 Iptables 或者 eBPF 加上 dns 技术实现的,具体细节感兴趣可自行探索,这里不在展开,那么我们来看下如何借助 Service 实现灰度发布和蓝绿发布。

灰度发布

依赖资源:1 个 Service 对象,两个 Deployment 对象,一般称为 blue 和 green

原理:每次升级发布都会额外拉起一个 Deployment ,然后通过 Service 的 selector 来绑定 Deployment,通过在旧的 Deployment上缩容一个副本,新的 Deployment 上扩容一个副本类似这样的一缩一扩的方式实现灰度发布。

假设:blue_deployment.yaml 是 v1 版本,注意我们设置了 selector label

apiVersion: apps/v1
kind: Deployment
metadata:
  name: py-hello-blue
spec:
  selector:
    matchLabels:
      app: hello
      color: blue
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        color: blue
    spec:
      terminationGracePeriodSeconds: 30
      containers:
      - name: hello
        imagePullPolicy: Always
        image: localhost:5001/py-http:1
        ports:
        - containerPort: 8888
        resources:
          requests:
            memory: "50Mi"
          limits:
            memory: "200Mi"
        lifecycle:
          preStop:
            exec:                                                                      
              command: ["sleep", "5"]
        # command: ["/usr/bin/tini", "--", "bash", "-c"]
        command: ["sh", "-c"]
        args:
        - |
          python app.py

现在要灰度升级,我们新建一个 green_deployment.yaml 是 v2 版本,注意也设置了 label 标签:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: py-hello-green
spec:
  selector:
    matchLabels:
      app: hello
      color: green
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        color: green
    spec:
      terminationGracePeriodSeconds: 30
      containers:
      - name: hello
        imagePullPolicy: Always
        image: localhost:5001/py-http:2
        ports:
        - containerPort: 8888
        resources:
          requests:
            memory: "50Mi"
          limits:
            memory: "200Mi"
        lifecycle:
          preStop:
            exec:                                                                      
              command: ["sleep", "5"]
        # command: ["/usr/bin/tini", "--", "bash", "-c"]
        command: ["sh", "-c"]
        args:
        - |
          python app.py

ok,现在我们通过 Service 来绑定流量:

apiVersion: v1
metadata:
  name: py-hello-service
kind: Service
spec:
  selector:
    app: hello
    #color: green
    #color: blue
  ports:
    - name: web
      port: 8888
      protocol: TCP
      targetPort: 8888
  type: ClusterIP

在这个 Service 中,我们如果设置 color 标签,则可以把流量全部导入v1 或者 v2,如果我们不设置 color 标签,那么默认会把流量请求在两个 Deployment 中轮询,为了实现灰度发布,我们需要通过缩扩副本来实现:我们例子都只有一个副本,所以灰度起来也很简单:

# blue 旧服务
kubectl scale deployment/py-hello-blue --replicas=1

# green 新服务扩容 1,就相当于 50% 流量进来,如果想要 10% 流量,则可以把 bluee 扩容到 9
kubectl scale deployment/py-hello-green --replicas=1

注意这里面的流量调控并不是精准的,只能实现简单的灰度发布

蓝绿发布

接着灰度发布的例子,我们蓝布发布的实现就非常简单了,直接在 Service 里面增加 color 标签为 green 就可以把全部流量切换到新拉起的服务中,当然如果测试失败了,也可以快速进行回滚,同理将 color 改回为 blue 即可

apiVersion: v1
metadata:
  name: py-hello-service
kind: Service
spec:
  selector:
    app: hello
    color: green #如果蓝布切换后,测试失败,可以快速把 color 改为 blue 进行回滚
  ports:
    - name: web
      port: 8888
      protocol: TCP
      targetPort: 8888
  type: ClusterIP

总结

通过 service 进行灰度发布和蓝绿发布虽然功能比较简单,但是对于大部分普通的发布业务也足够用了,这种方案可以结合发布平台稍做封装,比如一键创建新克隆版本,切换流量,扩缩容等,有了这些基础功能后,使用起来会更加流畅