首页 > 其他分享 >当SRS遇到K8s:如何实现高可用、回滚与灰度发布?

当SRS遇到K8s:如何实现高可用、回滚与灰度发布?

时间:2023-06-14 12:33:02浏览次数:41  
标签:回滚 4.0 deploy server SRS edge 灰度 version srs


当SRS遇到K8s:如何实现高可用、回滚与灰度发布?_回滚

Photo by Luis Quintero from Pexels


文 / 杨成立


服务的更新、回滚和灰度,是个简单的问题,如果加上一个条件"不中断服务的前提下",那么就是一个难题,如果再加上"大规模",那么就是K8S要解决的核心问题之一。坏消息是这个难搞的问题还真是流媒体服务的核心的、关键的、不可忽视的关键能力之一,好消息是K8S和云计算让这个难题稍微好一点点了。

我们在什么场景下会遇到更新、回滚和灰度的问题:

  • SRS需要升级新版本,如何知道升级后对现有业务没有影响?如果选择业务量小升级,那一般常态会是半夜三更、凌晨三四点,还要不要头发了呢?
  • 改进了新的功能或优化,根据业务定制了新的东西(完全直接使用SRS也得有自己的业务服务器),如何只在一部分机器发布,看看效果有没有达到预期?
  • 更新新版本后,如果发现有问题,影响了用户服务,如何在最短时间内回滚到之前的版本?问题出现时首先是要确认问题后(若由升级引起则)回滚,而不是很费时间的找Bug。

在这个场景下,对比K8S和传统部署方式的差异:

对比项

ECS

K8S

说明

部署

安装包

镜像

Docker镜像可回滚,开发和生产环境一致,可Cache,高效率和高密度,高可移植性,资源隔离可预测程序性能

看门狗

手动

自动

SRS异常退出由看门狗重新拉起,非K8S需要手动安装,K8S自动管理和拉起服务

更新

手动

自动

传统方式用脚本下载和更新二进制,人工分批更新,K8S自动Rolling Update,自动下载镜像和分批更新

灰度

手动

自动

传统方式手动操作SLB决定切量比例,K8S通过Replicas控制比例,自动切量

回滚

手动

自动

传统方式手动回滚,K8S有版本管理和回滚机制

Note:平滑更新的关键是平滑退出,重点是边缘集群的更新,对于源站集群我们可以选择直接重启,因为一般会有边缘集群作为代理,源站断开后边缘会重试,不影响用户,参考#1579(https://github.com/ossrs/srs/issues/1579#issuecomment-587233844)。

我们重点关注边缘集群的平滑退出,SRS边缘属于长连接无状态服务。和Nginx一样,SRS使用SIGQUIT作为信号,同时配置force_grace_quit认为SIGTERM也是平滑退出,收到SIGQUIT信号后,会等待grace_start_wait指定的时间,然后关闭Listeners新的连接不会分配到这个服务器,然后开始清理并等待现有连接退出,所有连接退出后还会等待grace_final_wait指定的时间,才会退出。

以之前部署的SRS源站和边缘集群为例,参考SRS Origin Cluster for a Large Number of Streams,SRS边缘的Pod的配置,需要指定平滑退出的参数,例如:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
 name: srs-edge-config
data:
 srs.conf: |-
   listen              1935;
   max_connections     1000;
   daemon              off;
   grace_start_wait    700;
   grace_final_wait    800;
   force_grace_quit    on;
   http_api {
       enabled         on;
       listen          1985;
    }
   http_server {
       enabled         on;
       listen          8080;
    }
   vhost __defaultVhost__ {
       cluster {
           mode            remote;
           origin          srs-origin-0.socssrs-origin-1.socs srs-origin2.socs;
       }
       http_remux {
           enabled     on;
       }
    }
EOF

Remark:一定要开启force_grace_quit,不开启(默认)将使用暴力更新,直接断开现有的连接,参考#1579(https://github.com/ossrs/srs/issues/1579#issuecomment-587233844)。

Note:在K8S中开始删除Pod时,会快速从Service删除Pod,所以我们将grace_start_wait和grace_final_wait设置时间短一些,只需要几百毫秒就足够了。

SRS边缘的配置,也需要在lifecycle.preStop事件时启动平滑退出,并设置terminationGracePeriodSeconds等待时间,例如:

cat <<EOF | kubectl apply --record -f -
apiVersion: apps/v1
kind: Deployment
metadata:
 name: srs-edge-deploy
 labels:
   app: srs-edge
spec:
 replicas: 2
 revisionHistoryLimit: 10
 selector:
   matchLabels:
     app: srs-edge
 template:
   metadata:
     labels:
       app: srs-edge
   spec:
     volumes:
     - name: config-volume
       configMap:
         name: srs-edge-config
     containers:
     - name: srs
       image: ossrs/srs:v4.0.5
       imagePullPolicy: IfNotPresent
       ports:
       - containerPort: 1935
       - containerPort: 1985
       - containerPort: 8080
       volumeMounts:
       - name: config-volume
         mountPath: /usr/local/srs/conf
       lifecycle:
         preStop:
           exec:
              command:["/usr/local/srs/etc/init.d/srs", "grace"]
     terminationGracePeriodSeconds: 120
EOF

Note: kubectl apply增加了一个参数--record,后面回滚会用到。

Note: terminationGracePeriodSeconds等待退出时间我们设置2分钟,线上服务可以设置更长,比如12小时。

Remark:为了更好体现平滑更新的逻辑,我们设置Replicas=2可以更容易演示。

Remark:我们使用SRS4演示,例如v4.0.5,实际上SRS3也可以的比如v3.0-b1等。

我们停掉了之前srs-demo-deploy推的两个DEMO流,采用手动推流到Edge,方便演示升级时有长连接需要服务的情况:

ffmpeg -re -i ./doc/source.200kbps.768x320.flv-c copy \

    -fflv rtmp://28.170.32.118/live/livestream

Note:请将上面的EIP换成你自己的,可用命令kubectlget svc/srs-edge-service查看你的EIP。

咱们可以看到目前启动了2个Edge,可以看下它的版本,是通过Pod(z9gbm)推流:

kubectl get po|grep edge
srs-edge-deploy-58d9999b7c-pnr2f       1/1    Running   0          16s
srs-edge-deploy-58d9999b7c-z9gbm       1/1    Running   0          16s
 
kubectl exec srs-edge-deploy-58d9999b7c-pnr2f-- ./objs/srs -v
4.0.5
kubectl exec srs-edge-deploy-58d9999b7c-pnr2f-- yum install -y net-tools
kubectl exec srs-edge-deploy-58d9999b7c-pnr2f-- netstat -anp|grep 1935
tcp       0      0 0.0.0.0:1935            0.0.0.0:*               LISTEN      1/./objs/srs
 
kubectl exec srs-edge-deploy-58d9999b7c-z9gbm-- ./objs/srs -v
4.0.5
kubectl exec srs-edge-deploy-58d9999b7c-z9gbm-- yum install -y net-tools
kubectl exec srs-edge-deploy-58d9999b7c-z9gbm-- netstat -anp|grep 1935
tcp       0      0 0.0.0.0:1935            0.0.0.0:*               LISTEN      1/./objs/srs
tcp       0      0 172.20.0.62:46482       172.20.0.41:1935        ESTABLISHED 1/./objs/srs
tcp       0      0 172.20.0.62:1935        172.20.0.1:12066        ESTABLISHED 1/./objs/srs

Note:我们只推流一个流,会有两个连接,一个是客户端到Edge的连接,一个是Edge回源到Origin的连接。

下面我们会分几个部分,看发布中遇到的问题:

  1. SRS Cluster Rolling Update: 在平滑退出基础上的滚动更新,集群更新的基础机制。
  2. SRS Cluster Rolling Back: 在平滑退出基础上的发布回滚,发布遇到问题首先考虑回滚。
  3. SRS Cluster Canary Release: 金丝雀升级,可精确控制的流量控制和回滚。

SRS Cluster Rolling Update

K8S的更新是Rolling Update,也就是修改和更新Pods时,会分批次执行。比如,上面的例子中SRS边缘的版本是v4.0.5,若我们现在需要更新到4.0.6,镜像已经打好了ossrs/srs:v4.0.6,那么我们可以用命令更新:

kubectl set image deploy/srs-edge-deploy srs=ossrs/srs:v4.0.6 --record

可以看这两个Pod的日志,没有连接的Pod很快就退出了,而有连接的Pod经过了一定的时间才退出(若客户端连接主动断开会更快退出):

kubectl exec srs-edge-deploy-58d9999b7c-pnr2f -- tail -f objs/srs.log
[2020-02-19 11:07:20.818][Trace][1][937]sig=3, user start gracefully quit
[2020-02-19 11:07:20.960][Trace][1][937]force gracefully quit, signo=15
[2020-02-19 11:07:21.772][Trace][1][932]cleanup for quitsignal fast=0, grace=1
[2020-02-19 11:07:21.772][Warn][1][932][11]main cycle terminated, system quit normally.
commandterminated with exit code137
 
kubectl exec srs-edge-deploy-58d9999b7c-z9gbm -- tail -f objs/srs.log
[2020-02-19 11:07:23.095][Trace][1][1009]sig=3, user start gracefully quit
[2020-02-19 11:07:23.316][Trace][1][1009]force gracefully quit, signo=15
[2020-02-19 11:07:23.784][Trace][1][1004]cleanup for quitsignal fast=0, grace=1
[2020-02-1911:07:23.784][Warn][1][1004][11] main cycle terminated, system quit normally.
[2020-02-19 11:07:24.784][Trace][1][1004] waitfor 1 conns to quit
[2020-02-19 11:07:26.968][Trace][1][1010] <- CPB time=120041497, okbps=0,0,0,ikbps=252,277,0, mr=0/350, p1stpt=20000, pnt=5000
[2020-02-19 11:08:26.791][Trace][1][1004] waitfor 1 conns to quit
[2020-02-19 11:08:52.602][Trace][1][1010]edge change from 200 to state 0 (init).
[2020-02-19 11:08:52.792][Trace][1][1004] waitfor 0 conns to quit
commandterminated with exit code137
 
kubectl get po |grep edge
NAME                                   READY   STATUS       RESTARTS   AGE
srs-edge-deploy-58d9999b7c-z9gbm       0/1    Terminating   0          3m52s
srs-edge-deploy-76fcbfb848-z5rmn       1/1    Running       0          104s
srs-edge-deploy-76fcbfb848-zt4wv       1/1    Running       0          106s
Remark:注意我们现在是有一个Pod有客户端在推流的。同样,我们指定了参数--record,会在后面回滚时用得着。
若RollingUpdate期间,我们需要暂停更新,可以用kubectl rollout暂停和恢复:
kubectl rollout pausedeploy/srs-edge-deploy
kubectl rollout resumedeploy/srs-edge-deploy

SRS Cluster Rolling Back

每次发布K8S都会记录一个Revision,若我们传递了--record参数(正如前面我们做的),则会记录更详细的CHANGE-CAUSE,比如:

kubectl rollout history deploy/srs-edge-deploy
REVISION CHANGE-CAUSE
1        kubectl apply --record=true --filename=-
2        kubectl set imagedeploy/srs-edge-deploy srs=ossrs/srs:v4.0.6 --record=true

Note:默认ACK只保留10个Revision,可以通过设置revisionHistoryLimit增加可回滚的版本。

若出现异常,可以回滚到之前的版本,例如:

kubectl rollout undo deploy/srs-edge-deploy--to-revision=1

实际上回滚的过程也是Rolling Update的过程,只是不用指定修改什么配置,而是指定的哪个历史版本的配置。回滚后,新增了一个版本3,和1是一样的:

REVISION CHANGE-CAUSE

1        kubectl apply --record=true --filename=-
2        kubectl set image deploy/srs-edge-deploy srs=ossrs/srs:v4.0.6--record=true
3        kubectl apply --record=true --filename=-

Note:可以在阿里云控制台来选择回滚到哪个版本。

SRS Cluster Canary Release

Canary是金丝雀发布,指试探性的发布一些版本,没有问题就继续扩大比例。由于涉及到具体的发布比例,所以我们要在RollingUpdate基础上,能控制新老Pods的数目,这就需要使用SLB了,参考Kubernetes集群中使用阿里云 SLB 实现四层金丝雀发布。

Note:关于金丝雀发布,最初发布的版本就好比金丝雀,在以前煤矿中会把金丝雀先送下去,如果缺氧雀儿就挂了。

以上面的Edge集群为例,假设目前版本是v4.0.5,有三个Edge Pod在运行,通过SLB对外提供服务:

当SRS遇到K8s:如何实现高可用、回滚与灰度发布?_Pod_02

cat <<EOF | kubectl apply --record -f -
apiVersion: apps/v1
kind: Deployment
metadata:
 name: srs-edge-r5-deploy
 labels:
   run: srs-edge-r5
spec:
 replicas: 3
 selector:
   matchLabels:
     run: srs-edge-r5
 template:
   metadata:
     labels:
       run: srs-edge-r5
       app: srs-edge
   spec:
     volumes:
     - name: config-volume
       configMap:
         name: srs-edge-config
     containers:
     - name: srs
       image: ossrs/srs:v4.0.5
       imagePullPolicy: IfNotPresent
       ports:
       - containerPort: 1935
       - containerPort: 1985
       - containerPort: 8080
       volumeMounts:
       - name: config-volume
         mountPath: /usr/local/srs/conf
       lifecycle:
         preStop:
           exec:
              command:["/usr/local/srs/etc/init.d/srs", "grace"]
     terminationGracePeriodSeconds: 120
EOF

Remark:注意Pod的labels有两个,一个是run:srs-edge-r5是这个应用所使用的,另外一个是app: srs-edge是Service用的,新老的SRS都有这个标签这样Service就可以都转发了。

执行命令后,可以看到三个Pod在运行:

kubectl get po
NAME                                   READY   STATUS   RESTARTS   AGE
srs-edge-r5-deploy-6c84cdc77b-q2j97    1/1    Running   0          3m15s
srs-edge-r5-deploy-6c84cdc77b-s6pzh    1/1    Running   0          3m15s
srs-edge-r5-deploy-6c84cdc77b-wjdtl    1/1    Running   0          3m15s

如果我们要升级到v4.0.6,但是只想先升级一台,这台就是金丝雀了。我们可以创建另外一个Deployment,他们的name不一样,但使用同样的Service:

当SRS遇到K8s:如何实现高可用、回滚与灰度发布?_Deployment_03

cat <<EOF | kubectl apply --record -f -
apiVersion: apps/v1
kind: Deployment
metadata:
 name: srs-edge-r6-deploy
 labels:
   run: srs-edge-r6
spec:
 replicas: 1
 selector:
   matchLabels:
     run: srs-edge-r6
 template:
   metadata:
     labels:
       run: srs-edge-r6
       app: srs-edge
   spec:
     volumes:
     - name: config-volume
       configMap:
         name: srs-edge-config
     containers:
     - name: srs
       image: ossrs/srs:v4.0.6
       imagePullPolicy: IfNotPresent
       ports:
       - containerPort: 1935
       - containerPort: 1985
       - containerPort: 8080
       volumeMounts:
       - name: config-volume
         mountPath: /usr/local/srs/conf
       lifecycle:
         preStop:
           exec:
              command:["/usr/local/srs/etc/init.d/srs", "grace"]
     terminationGracePeriodSeconds: 120
EOF

Remark:注意Pod的labels有两个,一个是run:srs-edge-r6是这个应用所使用的,另外一个是app: srs-edge是Service用的,和之前的老版本是一样的,这样Service就可以都转发了。

执行命令后,可以看到四个Pod在运行,三个老的,一个新的,这样就灰度了25%的流量到了新版本:

kubectl get po
NAME                                   READY   STATUS   RESTARTS   AGE
srs-edge-r5-deploy-6c84cdc77b-q2j97    1/1    Running   0          3m30s
srs-edge-r5-deploy-6c84cdc77b-s6pzh    1/1    Running   0          3m30s
srs-edge-r5-deploy-6c84cdc77b-wjdtl    1/1    Running   0          3m30s
srs-edge-r6-deploy-598f4698d-kkfnb     1/1    Running   0          6s
 
whiletrue;do ffmpeg -f flv -irtmp://r.ossrs.net/live/livestream 2>&1|grep server_version; sleep 1;done
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.6 # 这是新版本
   server_version  : 4.0.5
   server_version  : 4.0.5
   server_version  : 4.0.6 # 这是新版本

那么接下来,只需要调整新老的Deployment的Replicas,就能调整流量的比例了,比如我们增加新版本比重,只留一台老的:

当SRS遇到K8s:如何实现高可用、回滚与灰度发布?_Deployment_04

kubectl scale --replicas=3deploy/srs-edge-r6-deploy

kubectl scale --replicas=1deploy/srs-edge-r5-deploy

可以看到经过Gracefully Quit平滑升级和退出,最终变成了我们声明的那个样子,对业务不影响:

kubectl get po
NAME                                   READY   STATUS   RESTARTS   AGE
nginx-origin-deploy-85f4695685-gn2df   3/3    Running   0          5h31m
srs-edge-r5-deploy-6c84cdc77b-s6pzh    1/1    Running   0          25m
srs-edge-r6-deploy-f6b59c6c6-ddgxw     1/1    Running   0          2m59s
srs-edge-r6-deploy-f6b59c6c6-gvnd8     1/1    Running   0          2m54s
srs-edge-r6-deploy-f6b59c6c6-j46b5     1/1    Running   0          2m58s
 
whiletrue;do ffmpeg -f flv -irtmp://r.ossrs.net/live/livestream 2>&1|grep server_version; sleep 1;done
    server_version : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.5 # 这是老版本
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.6
   server_version  : 4.0.5 # 这是老版本
   server_version  : 4.0.6
   server_version  : 4.0.6

最终我们只要把老的Replicas设为0,然后就可以删除老的应用srs-edge-r5-deploy了,系统全部变成新的版本了,如下图所示:

当SRS遇到K8s:如何实现高可用、回滚与灰度发布?_Pod_05

亲,爽吗?干净利落,谈笑间,强撸灰飞湮灭啦。


标签:回滚,4.0,deploy,server,SRS,edge,灰度,version,srs
From: https://blog.51cto.com/u_13530535/6476687

相关文章

  • 在 MySQL 中,可以通过将插入操作放在事务中并使用 ROLLBACK 语句实现出现异常时全部回
    在MySQL中,可以通过将插入操作放在事务中并使用ROLLBACK语句实现出现异常时全部回滚。示例如下:STARTTRANSACTION;--开始事务INSERTINTOtable_name(column1,column2,...)VALUES(value1,value2,...);INSERTINTOtable_name(column1,column2,...)VALUES(val......
  • Jenkins自动部署与回滚
    Deploy发布的思路:用户无需填写Version 直接选择对应的项目和Deploy发布即可,会自动生成一个项目名+构建时间的备份文件多人使用Jenkins构建的时候会不知道Version要填什么 而且本人不喜欢使用Version这个变量Rollback回退的思路:选择Rollback 选择对应的回退版本......
  • opencv彩色图转灰度图的理解
    opencv彩色图转灰度图的理解 OpenCV中将彩色图像转换为灰度图像的实现原理是基于人眼对于彩色的感知。人眼能够感知的颜色分为三个类别:红色、绿色和蓝色。这三种颜色的波长不同,人眼对它们的感知也不同。在彩色图像中,不同颜色的像素值被连接在一起表示整个图像,但人眼对这种连接......
  • 【服务治理】基于SpringCloudAlibaba微服务组件的灰度发布设计(二)
    一.背景在上文中,灰度发布遇到了些问题,例如:1.动态修改Nacos配置中心的元数据信息,如何同步到Nacos注册中心对应服务的列表中2.管理后台业务调用其它服务灰度实例时的路由规则二.解决方案//TODO ......
  • 音视频开发--摄像头推流SRS6.0
    SRS帮助文档http://ossrs.io/lts/zh-cn/about https://avmedia.0voice.com/?id=31 音视频开发中文网 下载:SRS(SimpleRealtimeServer)高效的实时视频服务器v6.0.36   erwa.cn  二娃制作2023-5-20......
  • gitee代码回滚
    首先在终端上输入  gitlog查看已经提交的版本 然后输入gitreset--hard1cfd1634e3c82de62b8edd84315f89fc7de935bc(你要回退的版本id)然后强制推送到远程仓库gitpush--force ......
  • 【BZOJ4241】【回滚莫队模板题】历史研究
    Description给定一个序列,每次询问区间[l,r][l,r]内,所有权值与其出现次数的乘积的最大值。Solution回滚莫队模板题。将询问以左端点所在块为第一关键字,右端点为第......
  • git回滚代码
    1、未提交未提交有以下两种情况:1)已经在工作区修改了文件,但还未执行gitadd提交到暂存区。2)已经执行了gitadd提交到暂存作,但还未执行gitcommit提交本地仓库。这时候回退:gitreset--hard这样等于清空了暂存区和工作区,本地仓库回退到了最新的提交状态。2、已提交未推送......
  • errorCode: SYSTEM_EXCEPTION(UnexpectedRollbackException), message: 系统出现异常,请联系
    该异常为A方法加上@Transactional注解后,在方法内某段代码加上trycatch捕获且调用外部A方法也加上了异常捕获;原因是事务回滚是一旦它在方法内发现了exception,就会向上回滚,此时你将异常包裹,先行处理掉异常后事务自然回滚不了。解决方法是,直接try去掉,然后解决异常即可。......
  • m基于图像灰度共生矩阵纹理提取和GRNN神经网络的人口密度检测算法matlab仿真
    1.算法仿真效果matlab2013b仿真结果如下:  2.算法涉及理论知识概要       灰度共生矩阵,指的是一种通过研究灰度的空间相关特性来描述纹理的常用方法。[1] 1973年Haralick等人提出了用灰度共生矩阵来描述纹理特征。由于纹理是由灰度分布在空间位置上反复出现而形......