首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >SRS: Cloud Native改进知多少

SRS: Cloud Native改进知多少

作者头像
Winlin
发布2022-03-18 17:11:07
发布2022-03-18 17:11:07
1K0
举报
文章被收录于专栏:SRS开源服务器SRS开源服务器

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

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

Daemon

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

代码语言:javascript
复制
# whether start as daemon# @remark: do not support reload.# default: ondaemon              on;

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

代码语言:javascript
复制
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直接启动时出现问题:

代码语言:javascript
复制
# 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如何检测docker呢,通过读取文件/proc/1/cgroup的内容,如果有docker则认为是在docker中运行:

代码语言:javascript
复制
[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上运行时,这个文件的内容如下:

代码语言:javascript
复制
[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上的支持问题,简单易用。

Service Discovery

在典型的SRS边缘架构中,需要在边缘服务器的配置文件中,配置源站服务器的IP地址,这本质上是一个服务发现的机制。同样的,源站集群之间也需要配置彼此可以访问的IP地址。如下图所示:

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

也就是说,每个源站必须要有一个可精确寻址的地址,因为源站有“流”这个状态信息。而边缘是完全无状态的服务器,所以可以通过Deployment无状态服务方式部署,通过Service提供服务。

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

Docker Images

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

代码语言:javascript
复制
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

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

代码语言:javascript
复制
echo "For CentOS7" &&unzip -q SRS-CentOS7-x86_64-*.zip && cd SRS-CentOS7-x86_64-* && sudo bash INSTALL &&sudo /etc/init.d/srs start

就可以启动服务了:

代码语言:javascript
复制
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查看可用的版本。

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中,如下图所示:

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

代码语言:javascript
复制
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的描述。

Storage & ConfigMap

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

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

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

代码语言:javascript
复制
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,我们新增了两个配置:

代码语言:javascript
复制
# 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的描述。

Canary & Gracefully Quit

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

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

代码语言:javascript
复制
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:

代码语言:javascript
复制
# 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版本,如下图所示:

代码语言:javascript
复制
spec:  replicas: 3  selector:    matchLabels:      run: srs-edge-r5  template:    metadata:      labels:        run: srs-edge-r5        app: srs-edge

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

代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SRS开源服务器 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档