點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Docker 將 Nginx + FFmpeg 打包 Image

Sam Xiao's Avatar 2021-10-31

Nginx + FFmpeg 雖然能在 Ubuntu 下實現將 RSTP 轉成 HLS,若能包成 Docker Image,則其他電腦也能輕易使用此 Server,或者 Deploy 到 K8S。

Version

Nginx 1.17.5
FFmpeg 4.1.5

Dockerfile

FROM nginx:alpine
WORKDIR /usr/share/nginx/html

COPY default.conf /etc/nginx/conf.d/
COPY entrypoint.sh /

RUN apk update 
RUN apk add ffmpeg -q
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

第 1 行

FROM nginx:alpine

由於要自行安裝 FFmpeg 並建立 image,因此以 nginx:alpine 為基底。

第 2 行

WORKDIR /usr/share/nginx/html

將預設目錄設定在 /usr/share/nginx/html,如此當 login 進 container 時預設目錄就在此,方便 debug。

第 4 行

COPY default.conf /etc/nginx/conf.d/

將自定義了 Nginx configuration 檔複製進 image。

第 5 行

COPY entrypoint.sh /

將自定義的 entrypoint.sh 複製進 image。

為什麼要使用 script ? 稍後會解釋

第 7 行

RUN apk update 
RUN apk add ffmpeg -q

安裝 FFmpeg。

在 alpine 內只能使用 apk,不能使用 apt-get

第 9 行

RUN chmod +x /entrypoint.sh

賦予 entrypoint.sh 有執行權利。

11 行

ENTRYPOINT ["/entrypoint.sh"]

最後執行 entrypoint.sh

Nginx Configuration

default.conf

server {
  listen       80;
  server_name  localhost;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    add_header Access-Control-Allow-Origin *;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }
}

第 8 行

add_header Access-Control-Allow-Origin *;

避免 m3u8ts 可下載而無法播放。

Shell Script

run.sh

#!/bin/sh
ffmpeg -fflags nobuffer -rtsp_transport tcp -i $RTSP_URL -vsync 0 -copyts -vcodec copy -movflags frag_keyframe+empty_moov -an -hls_flags delete_segments+append_list -f segment -segment_list_flags live -segment_time $SEGMENT_TIME -segment_list_size 3 -segment_format mpegts -segment_list /usr/share/nginx/html/index.m3u8 -segment_list_type m3u8 -segment_list_entry_prefix $NGINX_URL -segment_wrap $TS_MAX_NUM /usr/share/nginx/html/%04d.ts &
nginx -g 'daemon off;'

FFmpeg 有幾個 argument 必須由 user 設定,可在 shell script 以 $ 讀取 environment variable,如此就可在 docker-compose 或 K8S 以 environment variable 設定這些 argument。

由於 FFmpeg 與 Nginx 需同時執行,因此在 ffmpeg 之後加上 & 在背景執行。

藉由 shell script 讀取 environment variable 能力,雖然 dockerfile 在 build-time 產生,但也能讀取到 run-time 的 environment variable

Docker Compose

docker-compose.yml

version: "3"

services:
  nginx:
    image: nginx-ffmpeg:0.0.1
    container_name: MyNginx
    restart: always
    environment:
      - RTSP_URL
      - NGINX_URL
      - TS_MAX_NUM
      - SEGMENT_TIME
    ports:
      - "80:80"

第 8 行

environment:
  - RTSP_URL
  - NGINX_URL
  - TS_MAX_NUM
  - SEGMENT_TIME

宣告剛在 shell script 所使用的 environment variable。

.env

RTSP_URL=rtsp://170.93.143.139/rtplive/470011e600ef003a004ee33696235daa
NGINX_URL=http://0.0.0.0/
TS_MAX_NUM=3000
SEGMENT_TIME=1

由 user 設定 environment variable。

TS_MAX_NUM 為最大 ts 數量,可藉由此控制所使用檔案空間

SEGMENT_TIME 可用來 tune latency

NPM Script

{
  "name": "nginx-ffmpeg",
  "version": "0.1.8",
  "private": true,
  "scripts": {
    "d:build": "docker build -t $npm_package_name:$npm_package_version .",
    "d:save": "docker save $npm_package_name:$npm_package_version | gzip > $npm_package_name-$npm_package_version.tar.gz",
    "d:login": "docker exec -it MyNginx sh",
    "d:log": "docker logs MyNginx",
    "d:up": "docker-compose up -d",
    "d:down": "docker-compose down"
  }
}

將常用 Docker command 以 NPM script 管理。

Conclusion

  • 若要在 Dockerfile 讀取 user 設定,可藉由 shell script 讀取 environment variable ,並在 Dockerfile 以 ENTRYPOINT 執行該 script