SRS: Cloud Native改进知多少

Posted SRS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SRS: Cloud Native改进知多少相关的知识,希望对你有一定的参考价值。

前段时间推送了,现在SRS3已经提供了完整的K8S+Docker支持,SRS正式走进Could Native时代,这意味着更便捷的部署、更高的弹性、更快的扩容和缩容、无中断服务的发布和灰度能力。


这篇文章,让我们一起看看SRS,以及一个应用,要达到弹性能力需要做出多少关键的改变吧(更详细的说明可以点阅读原文链接哦)。


SRS: Cloud Native改进知多少

Daemon


Daemon就是后台启动服务的意思,一般使用安装包和二进制部署时,都要求程序实现daemon启动的功能,这样可以防止退出terminal时进程也退出。比如nginx和SRS都实现了这个功能,是在配置文件中指定为daemon启动:

# whether start as daemon# @remark: do not support reload.# default: ondaemon on;


这个功能是通过两次fork,这样grandpa(当前进程)创建father,father创建son进程,然后让father和grandpa退出,son就成为孤儿进程被init(1)接管,成为了后台进程:

int pid = fork();// grandpaif(pid > 0) {    int status = 0; waitpid(pid, &status, 0); srs_trace("grandpa process exit."); exit(0);}
// fatherpid = fork();if(pid > 0) { srs_trace("father process exit"); exit(0);}
// sonsrs_trace("son(daemon) process running.");


这个功能在linux服务器上一直work很好,但是在K8S中却总是出现问题,总有朋友反馈说docker启动SRS会失败(参考#1594)。原因就是docker启动时是不能daemon启动,它就是一个进程,它接管了进程的生命周期;所以用docker启动SRS时,需要将daemon改成off,否则docker认为容器退出了,K8S就会不断的拉起SRS的pod。


有没有更简单的办法呢?比如SRS发现是docker就自动设置为daemon,SRS就这么实现了。不过做得更好,达到了同样的效果,同时也可以关闭这个功能,避免在linux server直接启动时出现问题:

# whether start as daemon# @remark: do not support reload.# default: ondaemon on;# Whether disable daemon for docker.# If on, it will set daemon to off in docker, even daemon is on.# default: ondisable_daemon_for_docker on;


这两个配置的意图如下:

  • 如果daemon设置为on,则准备进入daemon后台服务模式。

  • 如果disable_daemon_for_docker设置为on,当检测到SRS在docker中运行,则自动将daemon设置为off。


可以看出,用户可以完全不用配置这两个参数,默认在docker和linux server上都可以运行得很好。SRS: Cloud Native改进知多少


SRS如何检测docker呢,通过读取文件/proc/1/cgroup的内容,如果有docker则认为是在docker中运行:

[root@05181e679dc0 trunk]# cat /proc/1/cgroup14:name=systemd:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef13:rdma:/12:pids:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef11:hugetlb:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef10:net_prio:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef9:perf_event:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef8:net_cls:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef7:freezer:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef6:devices:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef5:memory:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef4:blkio:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef3:cpuacct:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef2:cpu:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef1:cpuset:/docker/05181e679dc0edc0ac42c19b18f2a0c96175901c5b68f6fce00cf7a5beac78ef


而在真实的linux server上运行时,这个文件的内容如下:

[winlin@SRS ~]$ cat /proc/1/cgroup8:blkio:/7:net_cls:/6:freezer:/5:devices:/4:memory:/3:cpuacct:/2:cpu:/1:cpuset:/


这样就解决了daemon在docker和linux server上的支持问题,简单易用。


SRS: Cloud Native改进知多少

Service Discovery


SRS: Cloud Native改进知多少


在K8S中,源站是通过Service提供的服务,也就是域名提供的服务,边缘可以通过配置源站的内部域名(Service名称)来实现服务发现。源站集群内部,则需要使用StatefulSet或者每个Service一个Origin的方式实现服务发现。如下图所示:

SRS: Cloud Native改进知多少



源站集群的改进方案,是通过OCM(Origin Cluster Manager)将流的信息无状态化存储,同时通过内部转发实现边缘访问的无状态化,这个是在超大规模的源站集群中(比如100万路流)才需要用到,目前还未实现。


SRS: Cloud Native改进知多少

Docker Images


很早以前,SRS部署是通过安装包和脚本安装,进程和服务管理是通过linux service方式。当然这种方式现在也是支持的,比如我们可以用docker打包:

echo "Package SRS 3.0 for CentOS7" &&git clone https://github.com/ossrs/srs.git && cd srs && git checkout 3.0release &&docker run -it -v `pwd`:/tmp/srs -w /tmp/srs/trunk ossrs/srs:dev ./scripts/package.sh --x86-x64


下载安装包后,解压和执行安装脚本:

echo "For CentOS7" &&unzip -q SRS-CentOS7-x86_64-*.zip && cd SRS-CentOS7-x86_64-* && sudo bash INSTALL &&sudo /etc/init.d/srs start


就可以启动服务了:

sudo /etc/init.d/srs start # CentOS 6sudo systemctl start srs # CentOS 7


上面的方式对于CentOS6或CentOS7比较容易实现,但对于其他linux或windows,就比较困难了。在K8S中部署,我们当然也不能用二进制方式,而要提供官方的docker镜像。


因此,SRS创建了官方的镜像项目srs-docker,并且提供了多个docker镜像:

  • ossrs/srs ,SRS的最新稳定版本,目前是SRS3的最新稳定版本。

  • ossrs/srs:3 ,SRS3.0的最新稳定版本,目前稳定版是SRS3.0-beta2。

  • ossrs/srs:v3.0-b2 ,SRS3.0每次发布的稳定版本,可以在releases中看发布了哪些版本,每个版本都会打一个镜像。

  • registry.cn-hangzhou.aliyuncs.com/ossrs/srs:v3.0-b2 ,杭州阿里云的docker镜像站,有上面所有的镜像,国内访问的速度更快,推荐用这个镜像。

  • ossrs/srs:2 ,SRS2.0最新的稳定版镜像。

  • ossrs/srs:v4.0.8 ,SRS4.0的开发版镜像,只有某些具体的版本,可以参考github上的release,或者使用git tag查看可用的版本。

  • ossrs/srs:dev ,开发的镜像,CentOS7环境,有比较完善的工具。

  • ossrs/srs:srt ,支持SRT的镜像,CentOS7环境,有编译好的SRT库。


Note: 上面所有的这些版本,可以在github官网上查看releases,或者通过git tag查看可用的版本


SRS: Cloud Native改进知多少

Console & Demos


默认安装好SRS后,会将srs-console,还有一些静态文件比如crossdomain.xml,以及index.html等页面,都安装到html根目录。而在K8S部署源站集群时,我们需要将html目录挂载成volume,和nginx共享HTTP文件,这样可以用nginx分发HLS或DASH切片。


当我们将html根目录挂载成volume时,会将SRS自带的console等文件清空,导致flash播放器无法访问crossdomain.xml,导致播放失败。这也很不方便,没有srs-console和必要的静态文件,无法查看SRS的状态。


在K8S中,我们通过一个sidecar容器,将SRS的默认文件拷贝到挂载的volume中,如下图所示:

SRS: Cloud Native改进知多少


这个Sidecar容器很容易实现,就是将文件拷贝后等待就好了:

cat <<EOF | kubectl apply -f -apiVersion: apps/v1kind: Deploymentmetadata: name: srs-deploy labels: app: srsspec: replicas: 1 selector: matchLabels: app: srs template: metadata: labels: app: srs spec: volumes: - name: cache-volume emptyDir: {} containers: - name: srs image: ossrs/srs:3 imagePullPolicy: IfNotPresent ports: - containerPort: 1935 - containerPort: 1985 - containerPort: 8080 volumeMounts: - name: cache-volume mountPath: /usr/local/srs/objs/nginx/html readOnly: false - name: nginx image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 volumeMounts: - name: cache-volume mountPath: /usr/share/nginx/html readOnly: true - name: srs-cp-files image: ossrs/srs:3 imagePullPolicy: IfNotPresent volumeMounts: - name: cache-volume mountPath: /tmp/html readOnly: false command: ["/bin/sh"] args: - "-c" - > if [[ ! -f /tmp/html/index.html ]]; then cp -R ./objs/nginx/html/* /tmp/html fi && sleep infinityEOF


这个Pod运行了三个容器,SRS主容器,还有两个Sidecar:

  • srs,提供流媒体源站服务,生成HLS切片到共享volume。

  • nginx,提供HTTP文件分发服务,从共享volume读取HLS切片。

  • srs-cp-files,拷贝SRS静态文件到共享volume,它没有挂载volume到html根目录,所以能将SRS的html根目录文件拷贝到共享volume。


这种方式也不需要太多改动,是解决这个问题的最简单的办法,更多信息参考#1603的描述。


SRS: Cloud Native改进知多少

Storage & ConfigMap


上面的例子中,我们使用emptyDir方式在Pod之间共享文件,但是对于源站集群,需要在多个Pod之间共享文件,比如源站集群将文件写到共享的存储,统一对外提供HLS服务,这就需要用到pv持久化存储了。如下图所示:

SRS: Cloud Native改进知多少

Note: 上图中的NAS,就是阿里云提供的一种PV持久化存储,可以在Pod之间共享存储,多个Pod写入HLS文件。


另外,对于配置文件,也是需要在多个Pod中共享的,在K8S中可以通过ConfigMap保存配置,然后挂载到Pod中。例如下面的方式:

cat <<EOF | kubectl apply -f -apiVersion: v1kind: ConfigMapmetadata: name: srs-configdata: srs.conf: |- listen 1935; max_connections 1000; daemon off; http_api { enabled on; listen 1985; } http_server { enabled on; listen 8080; } vhost __defaultVhost__ { http_remux { enabled on; } hls { enabled on; } }
---
apiVersion: apps/v1kind: Deploymentmetadata: name: srs-deploy labels: app: srsspec: replicas: 1 selector: matchLabels: app: srs template: metadata: labels: app: srs spec: volumes: - name: config-volume configMap: name: srs-config containers: - name: srs image: ossrs/srs:3 imagePullPolicy: IfNotPresent ports: - containerPort: 1935 - containerPort: 1985 - containerPort: 8080 volumeMounts: - name: config-volume mountPath: /usr/local/srs/confEOF


我们可以修改ConfigMap,这样文件会自动更新。之前SRS可以通过reload命令,或者给SRS发送SIGHUP实现reload加载配置文件,在K8S中无法发送命令,SRS会自动侦听配置文件的修改,自动实现reload,我们新增了两个配置:

# Whether auto reload by watching the config file by inotify.# default: offinotify_auto_reload off;# Whether enable inotify_auto_reload for docker.# If on, it will set inotify_auto_reload to on in docker, even it's off.# default: onauto_reload_for_docker on;


这两个配置项的意图如下:

  • inotify_auto_reload默认为off,也就是不会自动侦听配置文件的更新和自动reload。

  • auto_reload_for_docker默认为on,也就是在docker下面会自动开启上面的配置。


这样用户使用默认配置,在linux server中部署,还是在docker中部署,都可以达到同样的效果。同样这个方案也很简单,更多信息参考#1635的描述。


SRS: Cloud Native改进知多少

Canary & Gracefully Quit


K8S关键可以实现服务的无中断升级和灰度(Canary)发布,之前SRS无法实现这个功能,需要靠用户的运维平台才能实现。实现Canary发布的关键,是需要SRS实现Gracefully Quit(平滑退出),也就是收到信号SIGQUIT后会关闭侦听不再服务新的连接,当有连接时,SRS要等待一定时间才退出。


下面是两个SRS,没有连接的SRS会很快退出,有连接的SRS会等待一定时间后才会退出,或者服务完客户端后退出:

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 quit signal fast=0, grace=1[2020-02-19 11:07:21.772][Warn][1][932][11] main cycle terminated, system quit normally.command terminated with exit code 137
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 quit signal fast=0, grace=1[2020-02-19 11:07:23.784][Warn][1][1004][11] main cycle terminated, system quit normally.[2020-02-19 11:07:24.784][Trace][1][1004] wait for 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] wait for 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] wait for 0 conns to quitcommand terminated with exit code 137
kubectl get po |grep edgeNAME READY STATUS RESTARTS AGEsrs-edge-deploy-58d9999b7c-z9gbm 0/1 Terminating 0 3m52ssrs-edge-deploy-76fcbfb848-z5rmn 1/1 Running 0 104ssrs-edge-deploy-76fcbfb848-zt4wv 1/1 Running 0 106s

Note: K8S终止Pod时的等待时间,也就是SRS退出的等待时间,可以通过设置terminationGracePeriodSeconds来指定,默认是30秒。


同样我们新增了几个配置,来控制SRS在linux server或者docker中的平滑退出行为,更多可以参考#1579:

# For gracefully quit, wait for a while then close listeners,# because K8S notify SRS with SIGQUIT and update Service simultaneously,# maybe there is some new connections incoming before Service updated.# @see https://github.com/ossrs/srs/issues/1595#issuecomment-587516567# default: 2300grace_start_wait 2300;# For gracefully quit, final wait for cleanup in milliseconds.# @see https://github.com/ossrs/srs/issues/1579#issuecomment-587414898# default: 3200grace_final_wait 3200;# Whether force gracefully quit, never fast quit.# By default, SIGTERM which means fast quit, is sent by K8S, so we need to# force SRS to treat SIGTERM as gracefully quit for gray release or canary.# @see https://github.com/ossrs/srs/issues/1579#issuecomment-587475077# default: offforce_grace_quit off;

Note: 特别需要注意的是,在K8S中,需要打开force_grace_quit,因为默认会发送SIGTERM导致SRS快速退出,开启这个配置后,SIGTERM和SIGQUIT都会认为是平滑退出。


SRS支持Gracefully Quit后,我们就可以用K8S实现Canary发布,发布前假设SRS都是v4.0.5版本,如下图所示:

SRS: Cloud Native改进知多少

spec: replicas: 3 selector: matchLabels: run: srs-edge-r5 template: metadata: labels: run: srs-edge-r5 app: srs-edge


我们新增一个Deployment应用,标签和前面设置成一样app: srs-edge,这样流量就可以导入到这两个Deployment应用了:

spec: replicas: 1 selector: matchLabels: run: srs-edge-r6 template: metadata: labels: run: srs-edge-r6 app: srs-edge


后面我们只需要控制这两个Deployment的Replicas,就能准确的控制灰度的流量,而老的应用会使用Gracefully Quit机制平滑退出,这个方案也是非常简单而强大的。


One More Thing


SRS做这么多事情,就是为了更容易使用和维护,透露下SRS4的主要目标:

  • 已完成:支持Cloud Native,支持K8S+Docker。

  • 已完成:支持SRT,广电行业上云,远距离推流,互联网分发。

  • 正在MR:支持GB28181,监控行业上云,智能IoT场景互联网分发。

  • 正在开发:支持WebRTC,会议行业上云,看来看去,WebRTC还缺也只缺一个真正的高性能服务器。

  • 计划中:支持录制到云存储,录制是互联网流媒体应用的核心诉求。

  • 计划中:支持AI检测,内容鉴黄,语音转文字等。


欢迎来Github给SRS点个赞(Star)吧:https://github.com/ossrs/srs

以上是关于SRS: Cloud Native改进知多少的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud 配置知多少?

京东知乎天猫等各大平台的K8S架构你知道多少?

Ajax知多少?

webrtc(native C++)+srs(sfu)测延时200ms

一周朋友圈Cloud Native资讯回顾

虚拟DOM知多少