当SRS遇到K8s:如何实现高可用、回滚与灰度发布?
Photo by Luis Quintero from Pexels
本文来自流媒体直播集群SRS的官方wiki(https://github.com/ossrs/srs/wiki/v4_CN_K8s),由SRS的创始作者杨成立授权发布。
文 / 杨成立
相关文章:
当SRS遇到K8s:快速构建高并发直播集群
当SRS遇到K8s:如何构建海量推流源站?
服务的更新、回滚和灰度,是个简单的问题,如果加上一个条件"不中断服务的前提下",那么就是一个难题,如果再加上"大规模",那么就是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的连接。
下面我们会分几个部分,看发布中遇到的问题:
SRS Cluster Rolling Update: 在平滑退出基础上的滚动更新,集群更新的基础机制。
SRS Cluster Rolling Back: 在平滑退出基础上的发布回滚,发布遇到问题首先考虑回滚。
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对外提供服务:
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:
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,就能调整流量的比例了,比如我们增加新版本比重,只留一台老的:
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了,系统全部变成新的版本了,如下图所示:
亲,爽吗?干净利落,谈笑间,强撸灰飞湮灭啦。
LiveVideoStackCon 2020
上海/北京/旧金山 讲师招募
2020年LiveVideoStackCon将持续迭代,LiveVideoStackCon将分别在上海(6月13-14日),北京(9月11-12日)和旧金山(11月)举行。欢迎将你的技术实践、踩坑与填坑经历、技术与商业创业的思考分享出来,独乐不如众乐。请将个人资料和话题信息邮件到 speaker@livevideostack.com 或点击【阅读原文】了解成为LiveVideoStackCon讲师的权益与义务,我们会在48小时内回复。
当SRS遇到K8s:如何实现高可用、回滚与灰度发布?相关推荐
- 【kubernetes】k8s v1.20高可用多master节点部署
一,安装环境 1,硬件要求 内存:2GB或更多RAM CPU: 2核CPU或更多CPU 硬盘: 30GB或更多 2,本次环境说明: 操作系统:CentOS 7.9 内核版本:3.10.0-1160 虚 ...
- 进阶之路:从零到一在k8s上部署高可用prometheus —— thanos receive、thanos query
目录 导航 前言 相关yaml文件 thanos receive 配置相关 thanos-objectstorage.yaml thanos-receiver-hashring.yaml 服务相关 t ...
- 二进制搭建kubernetes多master集群【三、配置k8s master及高可用】
前面两篇文章已经配置好了etcd和flannel的网络,现在开始配置k8s master集群. etcd集群配置参考:二进制搭建kubernetes多master集群[一.使用TLS证书搭建etcd集 ...
- k8s.4-kubeadm部署高可用kubernetes集群 1.21
kubeadm部署高可用kubernetes集群 1.21 一.kubernetes 1.21发布 1.1 介绍 2021年04月,Kubernetes 1.21正式与大家见面,这是我们 2021 ...
- RKE安装k8s及部署高可用rancher
此博客,是根据 Rancher 官网文档,使用 RKE 测试部署最新发布版 Rancher v2.5.9 高可用集群的总结文档. 一 了解 Rancher Rancher 是为使用容器的公司打造的容器 ...
- 【kubernetes】k8s集群高可用部署安装和概念详细说明【含离线部署】,客户端连接haproxy访问高可用流程
文章目录 说明 高可用原理 K8S多master节点架构图 测试环境说明 部署高可用 安装包准备[可选] 高可用架构说明 配置haproxy 说明[必看] 安装haproxy 编辑配置文件 配置etc ...
- PostgreSQL 10 高可用 本地SSD盘 版本发布
信息摘要: 相比原 9.4 版本有多项功能更新,但由于架构限制当前不支持 PostgreSQL 9.4 高可用版 及 10 基础版 直接升级 适用客户: 所有 PostgreSQL 数据库用户 版本/ ...
- ASP.NET Core on K8s学习之旅(14)Ingress灰度发布
[云原生]| 作者/Edison Zhou 这是恰童鞋骚年的第236篇原创文章 上一篇介绍了Ingress的基本概念和Nginx Ingress的基本配置和使用,然后我还录了一个快速分享小视频介绍了一 ...
- 当SRS遇到K8S,快速高效运营直播流媒体集群
K8S 流媒体服务和流媒体服务器的关键差异是什么?高效的运维能力是其中极其关键的差异之一,云计算+Docker+K8S让开源项目也能拥有这种能力,让每个人都能具备互联网流媒体服务能力,正如:旧时王谢堂 ...
最新文章
- UNITY_MATRIX_IT_MV[Matrix]
- vue读取终端硬件信息_[提示]在macOS终端上查看硬件信息
- 检查本地服务器是否配置成功
- 程序员找工作那些事(一)幸存者偏差
- Chrome插件开发之一: 搭建基本结构
- golang——strconv包常用函数
- cdr 表格自动填充文字_PS那些好用到哭的新手小技巧(1)——如何快速去除文字图片的水印或背景文字?...
- 怎样查一个文件被复制了几次_复制拷贝文件不怕再出错,一个超级好用的小工具,支持多线程工作...
- datagrid底部显示水平滚动_CSS flex 布局,头部和底部固定,中间出现滚动条
- ad18修改过孔和走线间距_PCB设计之“过孔”
- android 今日头条布局,Android今日头条UI适配完善版
- Linux系统如何查看内存
- 【圆梦名企第三季】4月12日软件业“人才留湘 引才入湘”专项行动
- 转发文章【我们是怎样一步步的走向平庸的】
- 大厂软件测试流程完整版
- 15、RDA8910(4GCAT1)CSDK二次开发:通过OneWire驱动库获取DS18B20/DHT11的数据
- 读书笔记-《像高手一样发言》
- OC加强(三)之protocol(协议)/代理
- 人工智能的未来:趋势和对软件工程师的启示
- Python写违章扣分程序
热门文章
- 【208天】黑马程序员27天视频学习笔记【Day21-中】
- 《UML面向对象设计基础》—第1章1.5节消息
- Discovering versions from the identity service failed when creating the password plugin.
- Bootstrap~大叔封装的弹层
- C# 该行已经属于还有一个表 的解决方法
- [转]深入理解G1垃圾收集器
- CodeIgniter的快速操作
- 什么是 Visual VM?
- 牛客 - 牛牛的mex(主席树/思维)
- HDU - 4738 Caocao's Bridges(边双缩点)