阿里云实现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 (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/

你可能感兴趣的:(阿里云实现Docker完整部署Janus-Gateway)