阿里云实现Docker完整部署Janus-Gateway

通过Docker可以极大降低应用多次部署的工作量,特别是用C,C++开发的应用,搭建环境往往是个非常繁复的过程,经常出现依赖的包不全,版本不对导致安装失败。本文以在阿里云ECS上,利用Docker实际部署成功一套完整的Janus-Gateway应用为例,展示了一些编写Dockerfile的实用技巧,真正实现一次编写多次部署

项目地址:https://github.com/jasony62/tms-janus-streaming

阿里云主机:CentOS 7.7 64位,设置了公网ip和域名,安装了docker。

项目简介

Janue-Gateway是一个用C语言开发的开源WebRTC服务器,通过它可以实现实时流媒体服务。

如果Janue-Gateway部署在公网上(例如:阿里云),客户端之间要实现通信,必须(WebRTC是一种点对点通信)解决Nat穿越问题。因此,需要使用TURN服务,有一些可以免费使用TURN服务,本项目用开源项目cotrun自行搭建了TURN服务。

浏览器使用WebRTC时必须用https访问(localhost可以不用),因此,需要支持在运行环境上安装ssl证书,并且开启ssl端口。(注:可以通过配置项chrome://flags/#unsafely-treat-insecure-origin-as-secure关闭浏览器的限制)

Janus-Gateway自带了用于演示功能的前端代码,但是后续项目中Janus只是作为后端服务,前端的代码要单独开发。本项目中将Janus代码复制出来,单独放在nginx中运行。

本项目中,实现了Janus-Gateway,coturn和nginx三个模块的容器化,实现了在运行环境上的一键部署和运行。

配置SSL

基于安全隐私问题,现在Webkit内核的浏览器共享视频、语音、经纬度坐标等必须通过HTTPS形式访问。项目中使用Let's Encrypt申请了ssl证书。

参考:
https://letsencrypt.org
https://certbot.eff.org
https://www.jianshu.com/p/eab4e21c21f5

coturn

本想直接用coturn项目中docker目录中的例子,但是没有成功,在网上发现已经有人做好了例子,就直接拿来试了一下,可用!

其实,coturn到底怎么用目前完全没有搞明白,只是按照例子跑起来了。下面的文章中提到了生成用户名和密码进行测试,经过实践,测试的时候需要,但是在阿里云中运行时并不需要生成的用户名和密码(完全不知道有什么用)。

参考:
https://meetrix.io/blog/webrtc/turnserver/long_term_cred.html
https://meetrix.io/blog/webrtc/coturn/installation.html
https://github.com/coturn/coturn

Janus-Gateway

Janus的安装就是按照官网文档中的步骤进行安装,但是中间还是碰到了一堆坑,特别是和docker的结合,下面挑出Dockerfile中的部分内容进行说明。

# 更新curl>7.62
RUN rpm -Uvh  http://www.city-fan.org/ftp/contrib/yum-repo/rhel6/x86_64/city-fan.org-release-2-1.rhel6.noarch.rpm && \
    yum --showduplicates list curl --disablerepo="*" --enablerepo="city*" && \
    sed -i '5s/enabled=0/enabled=1/' /etc/yum.repos.d/city-fan.org.repo && \
    yum -y install curl

在安装各种包的过程中,经常会碰到需要修改配置文件,例如安装curl包时要修改配置文件/etc/yum.repos.d/city-fan.org.repo,将第5行的内容从enabled=0修改为enabled=1。如果是手动安装,通常用vi打开修改,但是docker中不能进行交互,怎么办?解决的办法是用sed命令直接修改。

sed -i '5s/enabled=0/enabled=1/' /etc/yum.repos.d/city-fan.org.repo

# 找到nice包
ENV PKG_CONFIG_PATH /usr/lib/pkgconfig

有时候,执行脚本需要设置环境变量,如果是手工操作会写成export ENV_XXX=YYY或者写到.bashrc这类文件中,但是在Dockerfile中这些写法都是无效的(亲测的结论),必须用ENV指令进行设置。

ADD start_janus.sh start_janus.sh
RUN chmod +x start_janus.sh

CMD ["./start_janus.sh"]

Dockerfile中无法根据不同条件执行不同的内容,但是我们经常需要根据不同的环境变量,执行不同的命令。这种情况,可以将根据条件执行不同分支的需求放到外部的shell脚本中执行。例如start_janus.sh就是根据,是否指定了ssl证书的环境变量,决定是否修改janus的配置文件开启ssl端口。

#!/bin/bash

echo "启动 Janus server"

if [ "$ssl_certificate" != "" -a "$ssl_certificate_key" != "" ]
then
    echo "启用 Janus ssl 端口"
    sed -i "s/https = false/https = true/g" /opt/janus/etc/janus/janus.transport.http.jcfg
    sed -i "s/#secure_port = 8089/secure_port = 8089/g" /opt/janus/etc/janus/janus.transport.http.jcfg
    sed -i "s?#cert_pem = \"\/path\/to\/cert.pem\"?cert_pem = \"$ssl_certificate\"?g" /opt/janus/etc/janus/janus.transport.http.jcfg
    sed -i "s?#cert_key = \"\/path\/to\/key.pem\"?cert_key = \"$ssl_certificate_key\"?g" /opt/janus/etc/janus/janus.transport.http.jcfg
fi

if [ "$stun_server" != "" ]
then
    p_stun_server="--stun-server=$stun_server"
fi

/opt/janus/bin/janus $p_stun_server 

另外,这里需要注意如果shell中用到环境变量,这些环境变量必须用ENV指令定义,否则无效(后面会提到在docker-compose文件中用env_file设置环境变量)。

nginx

nginx的Dockerfile主要是解决设置nginx.conf的问题。通常采用的方法是在运行docker时指定一个写好的nginx.conf替换掉容器内的文件,但是如果nginx.conf不放在版本库中,会导致每次部署都要手工生成一个nginx.conf,又麻烦又容易出错。在本项目中采用了编写好模版文件(nginx.conf.template),根据指定的环境变量自动完成修改。

ADD nginx.conf.template /etc/nginx/nginx.conf.template

先将模版文件添加到容器中,在start_nginx.sh中进行修改。

模版文件中有如下第一段代码

#ssl_server    server {
#ssl_server        listen 443 ssl;
#ssl_server        ssl_certificate $ssl_certificate;
#ssl_server        ssl_certificate_key $ssl_certificate_key;
#ssl_server        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_server        ssl_ciphers HIGH:!aNULL:!MD5;
#ssl_server        location / {
#ssl_server            root /usr/share/nginx/html;
#ssl_server            index index.html index.htm;
#ssl_server        }
#ssl_server    }

因为是否开启ssl访问是一段代码,默认不开启,所以就先把这一段代码都注释掉,如果需要开启就把注释去掉。start_nginx.sh中对应的代码如下:

if [ "$ssl_certificate" != "" -a "$ssl_certificate_key" != "" ]
then
    echo "启用 Nginx ssl 端口"
    sed -i "s/#ssl_server//" /etc/nginx/nginx.conf.template
fi

配置文件中有多个位置要用环境变量替换,采用逐个替换的方式太麻烦,是否有更简便的方式?

#ssl_server        ssl_certificate $ssl_certificate;
#ssl_server        ssl_certificate_key $ssl_certificate_key;

配置文件模版中ssl证书的位置需要用环境变量替换。

envsubst '$ssl_certificate $ssl_certificate_key' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'

在shell脚本中用envsubst可以实现用环境变量自动输入文件中的内容后输出。

In normal operation mode, standard input is copied to standard output, with references to environment variables of the form $VARIABLE or ${VARIABLE} being replaced with the corresponding values. If a shell-format is given, only those environment variables that are referenced in shell-format are substituted; otherwise all environment variables references occurring in standard input are substituted.

These substitutions are a subset of the substitutions that a shell performs on unquoted and double-quoted strings. Other kinds of substitutions done by a shell, such as {variable-default} or(command-list) or command-list, are not performed by the envsubst program, due to security reasons.

When --variables is used, standard input is ignored, and the output consists of the environment variables that are referenced in shell-format, one per line.

上面是envsubst的说明,不做翻译了,关键是要注意两点:1、$VARIABLE or ${VARIABLE} 这两种写法都代表要替换的环境变量;2、shell-format可以指定哪些是要进行替换的变量。为什么要注意这两条?看配置文件模版。

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

设置日志输出格式的部分中包含了$开头的变量,它们不是环境变量,但是envsubst根据第1点把它们当作变量,因为没有定义就被替换成空了。解决这个问题的方法是用第2点,设置要替换的环境变量,本例中:envsubst '$ssl_certificate $ssl_certificate_key'

参考
http://nginx.org/en/docs/http/configuring_https_servers.html
https://superuser.com/questions/1148950/what-is-shell-format-in-envsubst

docker-compose

因为janus和coturn都需要动态开放端口,进行端口映射比较麻烦,所以在docker-compose.yml中将它们的network_mode设置为host,直接使用宿主机网络,这样不需要端口映射了。需要注意的是host方式只在linux下有效,Window和Mac都不支持。

前面多次提到了设置环境变量,那么到底如何指定?本项目中使用env_file指定环境变量文件。

env_file:
      - ./sample.env

前面也提到了开启ssl端口的问题,ssl证书是janus和coturn公用的,放在了宿主机中,需要在容器运行时进行绑定。但是因为ssl设置是可选项,所以不能在docker-compose写死目录映射关系。

上面的3个问题都涉及到在不同的运行环境中需要进行不同的docker-compose设置,因为我们引入docker-compose.override.yml文件。下面的文件是在我的Mac环境上的文件。

version: "3.7"
services:
  janus:
    network_mode: "bridge"
    ports:
      - "8088:8088"
    # volumes:
    #   - /etc/letsencrypt:/etc/letsencrypt
    env_file:
      - ./local.env

  ue_client:
    # volumes:
    #   - /etc/letsencrypt:/etc/letsencrypt
    env_file:
      - ./local.env

在override文件中我们重新指定了网络模式bridge和要映射的端口;因为在本地运行时不开启ssl,所以这里没有进行ssl证书目录的挂载;将环境变量定义文件指定为本地的local.env文件。

下面的是在阿里云上的override文件。

version: "3.7"
services:
  janus:
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
    env_file:
      - ./local.env

  ue_client:
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
    env_file:
      - ./local.env

没有修改网络模式;指定了ssl证书位置;将环境变量定义文件指定为本地的local.env文件。

local.env文件内容如下:

# ssl证书位置
ssl_certificate=/etc/letsencrypt/live/云主机的域名/fullchain.pem
ssl_certificate_key=/etc/letsencrypt/live/云主机的域名/privkey.pem

# janus stun-server
stun_server=云主机的公网ip:3478

总结

利用docker确实可以实现一次编写多次使用,但还是有非常多的具体问题需要不断进行实践和积累。我认为docker的核心是让应用与运行环境解耦,但是对于运维工作来说运维工作自动化是核心,docker只是支撑手段之一。

参考

https://github.com/atyenoria/janus-webrtc-gateway-docker/blob/master/Dockerfile

https://github.com/voxbone-workshop/janus_gateway

http://webrtc.ventures/2017/10/janus-webrtc-gateway-as-a-sip-gateway-how-to-monitor-it/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 0x01 核心概念 Docker镜像类似于虚拟机镜像,可以理解为一个面向Docker引擎的只读模板,包含了文件系统...
    闲云逸心阅读 4,706评论 0 9
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,370评论 0 5
  • 我时常产生这样一种困惑,每当事情发生的时候,它还会不会再发生,如果会,以何种方式。如果主体改变,事情还是相同的么?...
    Machine_d5e9阅读 227评论 0 0
  • 在朱克曼加的谷仓里,快乐地生活着一群动物,其中小猪威尔伯和蜘蛛夏洛建立了最真挚的友情。然而,一个最丑陋的消息打破了...
    李晓琴_e985阅读 476评论 0 0
  • 1.什么是异步IO ,当请求外部系统或者耗时操作,需要异步IO 2.AsyncDataStream 3.实现原理 ...
    天火燎原_e548阅读 591评论 0 1