2020年11月24日火曜日

Dockerコンテナ化させたHLSのwebストリーミング配信環境構築(さくらのクラウド,Centos, Nginx,FFmpeg,docker) | ライタス株式会社

こんにちわ。
10月より新入社員として入社しました田所です。

今回もストリーミングサーバーネタです!
今回は前回構築したffmpegとnginxを使ったHLSでwebストリーミング配信環境をDockerでコンテナにまとめて、 どこでも簡単に環境を構築できるようにしちゃいます(^^♪
前回の記事↓
https://blog.litus.co.jp/2020/11/hlsflashplayerwebcentos-nginxffmpeg.html

さて、今回もコンテナ化するにあたってアーキテクト図を書きます!
参考記事↓
https://blog.soushi.me/entry/2017/02/17/135834/

基本は前回のcentos上で構築したもののと同じにするのですが、下記の通りコンテナならではの構成に若干変更しました。
  • RTMP→HLS変換するコンテナとweb配信するコンテナで分割
  • 二つのコンテナをdocker-composeでまとめて起動、停止可能
  • hls配信に必要なm3u8ファイル等はdockerボリューム上に置き、コンテナ間で共有させる

なんだかモダンでかっこいい!!(笑)



今回はローカル環境にDockerを入れてローカル上で構築を確認できたら、まるっと本番に移します。
というわけで本日の作業は環境構築含め以下の内容で進めていきます
  • Docker for windowsをインストール(起動確認まで)
  • Dockerfile、docker-compose.yml等必要なファイルを作成
  • ローカルでイメージをビルド→コンテナ起動→確認
  • さくらのクラウド(CentOS)上で起動→確認


1.Docker for windowsをインストール

※Windows10 Pro 64bitでのDockerインストールした手順について記述しています。
参考記事↓
https://www.public.ne.jp/2020/06/02/%E3%80%90docker%E3%80%91%E7%AC%AC9%E5%9B%9E%E3%80%80windows-10-pro-%E3%81%B8-docker-desktop-for-windows-%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B/
https://docs.docker.jp/docker-for-windows/install.html


Hyper-Vを有効化


[コントロール パネル] > [プログラムと機能] > [Windowsの機能の有効化または無効化] > [Hyper-V]にチェック
変更後に再起動が必要

Docker Desktop for Windowsをインストール


↓Docker Hubにインストーラーがあるので「Get Docker」をクリックしてダウンロード
https://hub.docker.com/editions/community/docker-ce-desktop-windows/
ダウンロードが完了したら実行し、ポチポチと押して進めていきます(※基本OKを押してインストールまで終わらして問題ないので、割愛)
完了したら再起動します
再起動後は自動でDockerが起動する(はず)のでタスクトレイからDockerが起動していることを確認する。

起動確認


コマンドプロンプト(またはpowershell)でDockerとdocker-composeバージョン確認
> docker version
Client: Docker Engine - Community
 Cloud integration: 1.0.2
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 17:00:27 2020
 OS/Arch:           windows/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46d9d
  Built:            Wed Sep 16 17:07:04 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
docker-compose の確認
> docker-compose --version
 docker-compose version 1.27.4, build 40524192
完璧です!
お試しでHello Worldを出力するだけのコンテナを実行してみます!
> docker run --rm hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
※--rm オプションで実行後にコンテナを自動で削除するようにしています。
上記のようなメッセージが出力されたら成功です。
ついでに「docker run -it ubuntu bashでubuntuコンテナ起動してみてね。」って言っているのでやってみましょう。
> docker run -it --rm ubuntu bash
root@703f8d0cae30:/#    
※ここも--rmつけて停止後にコンテナを破棄するようにしています。
起動したubuntuコンテナ内にbashで操作できるようになりました。
一応OSの確認。
root@703f8d0cae30:/# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.1 LTS"
「exit」コマンド or 「Ctrl + D」でコンテナから抜け出せます。
セットアップ完了です!
超簡単にDockerを構築できました!



2.Dockerfile、docker-compose.yml等必要なファイルを作成

ファイル構成は以下の通りとしました。
streamserver/
├─ rtmp/
|  ├─ Dockerfile
|  └─ nginx.conf
├─ web/
|  ├─ Dockerfile
|  ├─ index.html
|  └─ nginx.conf
└─ docker-compose.yml

各ファイル内容は以下の通りです。
  • streamserver/docker-compose.yml
  • version: "3"
    services:
      rtmp_ffmpg:
        build:
          context: ./rtmp
        volumes:
          - hls:/var/www/vhosts/live_stream
        ports:
          - 1935:1935
      web:
        build:
          context: ./web
        volumes:
          - hls:/var/www/vhosts/live_stream
          - ./web/index.html:/usr/share/nginx/html/index.html
        depends_on:
          - rtmp_ffmpg
        ports:
          - 80:80
    volumes:
      hls:
    
    ポイントはvolumesでhlsというdockerボリュームを作成し、rtmp_ffmpgとwebコンテナで共有している点です。
    このhlsボリュームにffmpegがコンバートしたhlsファイルが置かれるようにします

  • streamserver/rtmp/Dockerfile
  • FROM tiangolo/nginx-rtmp:latest
    COPY nginx.conf /etc/nginx/nginx.conf
    RUN apt-get -y update \
      && apt install -y ffmpeg \
      && mkdir -p /var/www/vhosts/live_stream \
      && chmod -R o+rwx /var
    
    tiangolo/nginx-rtmpというイメージを拝借しています。
    このイメージ単体で初回構築したときのような RTMPでのストリーミング配信が可能です。
    これに更にffmpegを追加インストールし、rtmp/nginx.confをコンテナ内にコピーしています。
    ちみなに
    && mkdir -p /var/www/vhosts/live_stream \
    && chmod -R o+rwx /var
    
    この部分はffmpegがhlsファイルを吐き出すディレクトリを作成しています。更にパーミッションを変更しています。
    ※パーミッションが変更されていないとコンテナ起動時にffmpegにPermission Deniedと怒られました。
    docker-compose.ymlにコンテナ間で共有するディレクトリとして指定しているのが原因と思われます。

  • streamserver/rtmp/nginx.conf
  • worker_processes auto;
    rtmp_auto_push on;
    events {}
    rtmp {
       server {
         listen 1935;
         allow play all;
         access_log /var/log/nginx/rtmp_access.log;
         application live1 {
         live on;
         exec ffmpeg -i rtmp://localhost/live1/$name -async 1 -vsync cfr -acodec copy -c:v libx264 -b:v 128K -f flv rtmp://localhost/live2/$name_low;
       }
       application live2 {
         live on;
         hls on;
         hls_path /var/www/vhosts/live_stream;
         hls_variant _low  BANDWIDTH=300000;
       }
      }
    }    
    
    1935ポートでRTMPを受け付けて、ffmpegが/var/www/vhosts/live_streamにhlsファイルを吐き出す旨を書いています。


  • streamserver/web/Dockerfile
  • FROM nginx:latest
    
    RUN apt-get -y update \
      && mkdir -p /var/www/vhosts/live_stream \
      && chmod -R 777 /var
    COPY nginx.conf /etc/nginx/nginx.conf
    COPY index.html /usr/share/nginx/html/index.html  
    
    webサーバーのnginxコンテナイメージを使用しています
    コンテナ間の共有ディレクトリとして/var/www/vhosts/live_streamを作成し、
    nginx.confとindex.htmlをコンテナ内にコピーさせています。


  • streamserver/web/index.html

  • <html>
      <head>
        <title>HLS Test</title>
        <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
      </head>

      <body>
        <video-js id=example-video width=640 height=360 class="vjs-default-skin" controls>
          <source src="http://「IPアドレス」/live_stream/「OBSストリームキー」_low.m3u8" type="application/x-mpegURL">
        </video-js>
        <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
        <script>
          var player = videojs('example-video');
        </script>
      </body>
    </html>

    「IPアドレス」と「OBSストリームキー」は各自で書き換えてください。
    ローカル上で動きを確認する場合はIPアドレスは「localhost」でよろしいかと思います。


  • streamserver/web/nginx.conf
  • user  nginx;
    worker_processes  2;
    
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    
    
    events {
        worker_connections  1024;
    }
    
    http {
      include mime.types;
      default_type application/octet-stream;
      sendfile on;
      server {
        listen 80;
        server_name  localhost;
        location / {
        root /usr/share/nginx/html/;
        index index.html index.htm;
        }
        location /live_stream {
        types {
            application/vnd.apple.mpegurl m3u8;
        }
        root /var/www/vhosts/;
        }
      }
    }
    
    webサーバーとして80番ポートで受け付けています。

    準備が整いました!!


    3.ローカルでイメージをビルド→コンテナ起動→確認

    とりあえずローカル上での挙動確認のため、obsの配信設定はlocalhostに配信するように設定を変更しておきます。
    さっそくイメージをビルドします。
    docker-compose.ymlがあるstreamserverディレクトリまでpowershellやコマンドプロンプトで移動し、以下のコマンドを実行します。
    > docker-compose build
    
    時間がかかるのでしばらく待ちます。
    終了したらイメージ一覧を確認します
     > docker-compose images
    REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
    streamserver_nginx             latest              ef56cc44a48e        34 minutes ago      156MB
    streamserver_rtmp_ffmpg        latest              f373c5e5f450        47 minutes ago      1.06GB
    nginx                          latest              daee903b4e43        5 days ago          133MB
    tiangolo/nginx-rtmp            latest              e2efd1d48936        3 months ago        850MB
    
    4つイメージがあれば成功です!!
    ではビルドしたイメージを起動します!
    docker-compose.ymlがあるstreamserverディレクトリで以下を実行
    > docker-compose up -d
    Creating network "streamserver_default" with the default driver
    Creating streamserver_rtmp_ffmpg_1 ... done
    Creating streamserver_nginx_1      ... done 
    
    起動しているか確認します
    > docker ps
    CONTAINER ID        IMAGE                     COMMAND                  CREATED              STATUS              PORTS                    NAMES
    741416b21ea2        streamserver_nginx        "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp       streamserver_nginx_1
    83d8673c3580        streamserver_rtmp_ffmpg   "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:1935->1935/tcp   streamserver_rtmp_ffmpg_1
    
    STATUSが2つともUPとなっているので問題なさそうです!
    まずはwebサーバーが起動しているか確認します
    ブラウザからlocalhostにアクセスします。
    問題なさそうです!
    続いてOBSでlocalhostに向けて配信を開始します!
    上手くいってそうです!!
    再度localhostにアクセスすると...

    きたー!!!!(^_-)-☆

    問題なく見れています!
    コンテナをまとめて終了させる際は
    > docker-compose down
    
    でコンテナが破棄されます。


    4.さくらのクラウド(CentOS)上で起動→確認

    環境は前回までで構築したインスタンスをそのまま使います。
    作業としてはsshでサーバーへ接続してdockerとdocker-composeをインストールするだけですね
    sshで接続するまでの工程は割愛します(詳細は前々回の記事参照)
    参考記事↓
    https://qiita.com/subretu/items/549bc720165004bca3c3
    以下のコマンドを実行し、インストールする。
    #dockerインストール
    yum install -y yum-utils device-mapper-persistent-data lvm2
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum -y install docker-ce
    #Docker Composeインストール
    curl -L https://github.com/docker/compose/releases/download/1.27.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
    
    これでエラーがなければバージョンが表示されるはずです
    docker --version
    docker-compose --version
    
    続いてローカルで作ったstreamserverディレクトリをサーバーの中へコピーします
    teratermであればフォルダをzipとかに圧縮してドラッグ&ドロップします
    SCPを選んでコピー先を指定します。(今回はホームディレクトリ(~))
    解凍
    > cd ~
    > ls
    streamServer.zip
    > unzip streamServer.zip
    > ls
    streamServer streamServer.zip
    
    ※web/index.htmlの「IPアドレス」、「OBSストリームキー」は適宜vimやnanoを使って変更してください
    ※OBS設定で配信先をサーバーURLに変更しておいてください
    では、本題!!
    解凍したディレクトリへ入って起動させます!
    > cd ~/streamServer
    > docker-compose up -d
    Creating network "streamserver_default" with the default driver
    Creating streamserver_rtmp_ffmpg_1 ... done
    Creating streamserver_nginx_1      ... done 
    
    2つのコンテナが起動し、Doneと出力されたら完了です

    ブラウザでサーバーへアクセス
    webサーバーコンテナは無事動いています。
    ではOBSで配信を行います!
    やったーーーーー!!!
    これで無事Dockerで環境構築できました!



    今後の課題、目標等

    • 前回に引き続き遅延はかなりのあるので最小限にできるよう改善する
    • docker-composeではなく、ECS、EKS、kubernetes等のコンテナオーケストレーションツールで実装してみる

    これからも精進いたします!!😉
    それでは。

    0 件のコメント:

    コメントを投稿