diff --git a/Dockerfile.origin_cluster b/Dockerfile.origin_cluster new file mode 100644 index 00000000..a6911bea --- /dev/null +++ b/Dockerfile.origin_cluster @@ -0,0 +1,88 @@ +ARG ARCH + +FROM ${ARCH}zpcat/srs-proxy:4 AS proxy +FROM ${ARCH}ossrs/node:18 AS node +FROM ${ARCH}zpcat/srs:7-dev AS srs + +RUN mv /usr/local/srs/objs/ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg && \ + ln -sf /usr/local/bin/ffmpeg /usr/local/srs/objs/ffmpeg/bin/ffmpeg + +RUN rm -rf /usr/local/srs/objs/nginx/html/console \ + /usr/local/srs/objs/nginx/html/players + +FROM ${ARCH}ossrs/srs:ubuntu20 AS build + +ARG BUILDPLATFORM +ARG TARGETPLATFORM +ARG TARGETARCH +ARG MAKEARGS +RUN echo "BUILDPLATFORM: $BUILDPLATFORM, TARGETPLATFORM: $TARGETPLATFORM, TARGETARCH: $TARGETARCH, MAKEARGS: $MAKEARGS" + +# For ui build. +COPY --from=node /usr/local/bin /usr/local/bin +COPY --from=node /usr/local/lib /usr/local/lib +# For SRS server, always use the latest release version. +COPY --from=srs /usr/local/srs /usr/local/srs + +ADD releases /g/releases +ADD mgmt /g/mgmt +ADD platform /g/platform +ADD ui /g/ui +ADD usr /g/usr +ADD test /g/test +ADD Makefile /g/Makefile + +# For node to use more memory to fix: JavaScript heap out of memory +ENV NODE_OPTIONS="--max-old-space-size=4096" + +# By default, make all, including platform and ui, but it will take a long time, +# so there is a MAKEARGS to build without UI, see platform.yml. +WORKDIR /g +# We define SRS_NO_LINT to disable the lint check. +RUN export SRS_NO_LINT=1 && \ + make clean && make -j ${MAKEARGS} && make install + +# Use UPX to compress the binary. +# https://serverfault.com/questions/949991/how-to-install-tzdata-on-a-ubuntu-docker-image +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update -y && apt-get install -y upx + +RUN echo "Before UPX for $TARGETARCH" && \ + ls -lh /usr/local/srs/objs/srs /usr/local/oryx/platform/platform && \ + upx --best --lzma /usr/local/srs/objs/srs && \ + upx --best --lzma /usr/local/oryx/platform/platform && \ + echo "After UPX for $TARGETARCH" && \ + ls -lh /usr/local/srs/objs/srs /usr/local/oryx/platform/platform + +# For youtube-dl, see https://github.com/ytdl-org/ytdl-nightly +FROM ${ARCH}python:3.9-slim-bullseye AS ytdl + +RUN apt-get update -y && apt-get install -y binutils curl unzip && \ + pip install pyinstaller + +WORKDIR /g +RUN curl -O -L https://github.com/ytdl-org/youtube-dl/archive/refs/heads/master.zip && \ + unzip -q master.zip && cd youtube-dl-master && \ + pyinstaller --onefile --clean --noconfirm --name youtube-dl youtube_dl/__main__.py && \ + cp dist/youtube-dl /usr/local/bin/ && \ + ldd /usr/local/bin/youtube-dl + +# http://releases.ubuntu.com/focal/ +#FROM ${ARCH}ubuntu:focal AS dist +FROM ${ARCH}ossrs/oryx:focal-1 AS dist + +# Expose ports @see https://github.com/ossrs/oryx/blob/main/DEVELOPER.md#docker-allocated-ports +EXPOSE 2022 2443 1935 8080 5060 9000 8000/udp 10080/udp + +# Copy files from build. +COPY --from=build /usr/local/oryx /usr/local/oryx +COPY --from=build /usr/local/srs /usr/local/srs +COPY --from=ytdl /usr/local/bin/youtube-dl /usr/local/bin/ +COPY --from=proxy /proxy /usr/local/proxy + +# Prepare data directory. +RUN mkdir -p /data && \ + cd /usr/local/oryx/platform/containers && \ + rm -rf data && ln -sf /data . + +CMD ["./bootstrap.origin.cluster"] diff --git a/Makefile b/Makefile index 64b4f9f6..2f7a458f 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ else cp -rf ui/build $(__REAL_INSTALL)/ui/build cp -f platform/platform $(__REAL_INSTALL)/platform/platform cp -f platform/bootstrap $(__REAL_INSTALL)/platform/bootstrap + cp -f platform/bootstrap.origin.cluster $(__REAL_INSTALL)/platform/bootstrap.origin.cluster cp -rf platform/auto $(__REAL_INSTALL)/platform/auto cp -rf platform/containers $(__REAL_INSTALL)/platform/containers (cd platform && cp -P dvr objs record upload vlive vod transcript dub $(__REAL_INSTALL)/platform) diff --git a/platform/auto/start_srs_origin_cluster b/platform/auto/start_srs_origin_cluster new file mode 100755 index 00000000..120ab0ec --- /dev/null +++ b/platform/auto/start_srs_origin_cluster @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# Execute by: bash xxx.sh or bash zzz/yyy/xxx.sh or ./xxx.sh or ./zzz/yyy/xxx.sh source xxx.sh +REALPATH=$(realpath ${BASH_SOURCE[0]}) +SCRIPT_DIR=$(cd $(dirname ${REALPATH}) && pwd) +WORK_DIR=$(cd $(dirname ${REALPATH})/.. && pwd) +echo "BASH_SOURCE=${BASH_SOURCE}, REALPATH=${REALPATH}, SCRIPT_DIR=${SCRIPT_DIR}, WORK_DIR=${WORK_DIR}" +cd ${WORK_DIR} + +echo "Start SRS origin cluster, WORK_DIR:${WORK_DIR}" + +# Load the environment variables for SRS server. +if [[ -f ${WORK_DIR}/containers/data/config/.srs.env ]]; then + echo "Load variables for SRS from ${WORK_DIR}/containers/data/config/.srs.env" + echo "export $(grep -v '^#' ${WORK_DIR}/containers/data/config/.srs.env | xargs)" + export $(grep -v '^#' ${WORK_DIR}/containers/data/config/.srs.env | xargs) +fi + +# Get the metadata of machine. +if [[ -f ${WORK_DIR}/containers/data/config/.env ]]; then source ${WORK_DIR}/containers/data/config/.env; fi +echo "LoadEnv CLOUD=$CLOUD REGION=$REGION, SOURCE=$SOURCE" + +# run srs-proxy +export PROXY_STATIC_FILES="/usr/local/proxy/static" +export PROXY_LOAD_BALANCER_TYPE="redis" +export PROXY_RTMP_SERVER=1935 +export PROXY_HTTP_SERVER=8080 +export PROXY_HTTP_API=1985 +export PROXY_WEBRTC_SERVER=8000 +export PROXY_SRT_SERVER=10080 +export PROXY_SYSTEM_API=12025 + +/usr/local/proxy/srs-proxy & +if [[ $? -ne 0 ]]; then + echo "Warning: srs proxy start with none zero." +fi + +if [[ $(ps aux | grep srs-proxy | grep -q '\/usr\/local\/proxy' || echo no) == no ]]; then + echo "srs proxy stopped, exit" + exit 1 +fi + +# Make sure the config file exists. +mkdir -p ${WORK_DIR}/containers/data/config && +touch ${WORK_DIR}/containers/data/config/srs.server.conf ${WORK_DIR}/containers/data/config/srs.vhost.conf +if [[ $? -ne 0 ]]; then echo "Make sure the config file exists failed"; exit 1; fi + +# TODO: FIXME: Remove it after SRS supports empty config file. +# See https://github.com/ossrs/srs/pull/3768 +if [[ ! -s ${WORK_DIR}/containers/data/config/srs.server.conf ]]; then + echo '# OK' > ${WORK_DIR}/containers/data/config/srs.server.conf +fi +if [[ ! -s ${WORK_DIR}/containers/data/config/srs.vhost.conf ]]; then + echo '# OK' > ${WORK_DIR}/containers/data/config/srs.vhost.conf +fi + +if [[ -z $ORIGIN_SRS_NUMBER ]]; then + ORIGIN_SRS_NUMBER=2 +fi + +echo "Origin SRS instance number is ${ORIGIN_SRS_NUMBER}" + +for ((i=1; i<=$ORIGIN_SRS_NUMBER; i++)) +do + echo "Start Origin SRS instance $i" + cat ${WORK_DIR}/containers/conf/srs.origin.conf | + sed 's/listen *1935;/listen 1935'$i';/g' | + sed 's/listen *8080;/listen 808'$i';/g' | + sed 's/listen *127.0.0.1:1985;/listen 127.0.0.1:1985'$i';/g' | + sed 's/listen *8000;/listen 800'$i';/g' | + sed 's/listen *10080;/listen 1008'$i';/g' | + sed 's/pid *\.\/objs\/origin.pid;/pid \.\/objs\/origin'$i'\.pid;/g' | + sed 's/device_id *origin;/device_id origin'$i';/g' > ${WORK_DIR}/containers/conf/srs.origin${i}.conf + /usr/local/srs/objs/srs -c ${WORK_DIR}/containers/conf/srs.origin${i}.conf + if [[ $? -ne 0 ]]; then echo "Warning: SRS start with none zero, but might be ok, see #3757."; fi + + SRS_PID_FILE="./objs/origin${i}.pid" + + for ((m=0; m<10; m++)); do + if [[ -f $SRS_PID_FILE ]]; then + ps -p `cat $SRS_PID_FILE` 1>/dev/null 2>/dev/null && + echo "SRS origin server started, pid=`cat $SRS_PID_FILE`" && + break + fi + sleep 0.3 + done + + ps -p `cat $SRS_PID_FILE` 1>/dev/null 2>/dev/null + if [[ $? -ne 0 ]]; then echo "Check SRS ${i} failed"; exit 1; fi +done + + diff --git a/platform/auto/stop_srs_origin_cluster b/platform/auto/stop_srs_origin_cluster new file mode 100755 index 00000000..798e6612 --- /dev/null +++ b/platform/auto/stop_srs_origin_cluster @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# Execute by: bash xxx.sh or bash zzz/yyy/xxx.sh or ./xxx.sh or ./zzz/yyy/xxx.sh source xxx.sh +REALPATH=$(realpath ${BASH_SOURCE[0]}) +SCRIPT_DIR=$(cd $(dirname ${REALPATH}) && pwd) +WORK_DIR=$(cd $(dirname ${REALPATH})/.. && pwd) +echo "BASH_SOURCE=${BASH_SOURCE}, REALPATH=${REALPATH}, SCRIPT_DIR=${SCRIPT_DIR}, WORK_DIR=${WORK_DIR}" +cd ${WORK_DIR} + +echo "Stop SRS origin cluster, WORK_DIR:${WORK_DIR}" + + +if [[ -z $ORIGIN_SRS_NUMBER ]]; then + ORIGIN_SRS_NUMBER=2 +fi + +echo "Origin SRS instance number is ${ORIGIN_SRS_NUMBER}" + +for ((i=1; i<=$ORIGIN_SRS_NUMBER; i++)) +do + SRS_PID_FILE="./objs/origin${i}.pid" + if [[ -f $SRS_PID_FILE ]]; then + SRS_PID=`cat $SRS_PID_FILE` + echo "Stopping SRS, pid=$SRS_PID" + kill $SRS_PID 1>/dev/null 2>/dev/null + + # See https://stackoverflow.com/a/41613532/17679565 + timeout 10 tail --pid=$SRS_PID -f /dev/null + echo "Stopped SRS OK, pid=$SRS_PID" + fi +done + +echo "stop srs-proxy?" \ No newline at end of file diff --git a/platform/bootstrap.origin.cluster b/platform/bootstrap.origin.cluster new file mode 100755 index 00000000..41359252 --- /dev/null +++ b/platform/bootstrap.origin.cluster @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Execute by: bash xxx.sh or bash zzz/yyy/xxx.sh or ./xxx.sh or ./zzz/yyy/xxx.sh source xxx.sh +REALPATH=$(realpath ${BASH_SOURCE[0]}) +SCRIPT_DIR=$(cd $(dirname ${REALPATH}) && pwd) +WORK_DIR=$(cd $(dirname ${REALPATH}) && pwd) +echo "BASH_SOURCE=${BASH_SOURCE}, REALPATH=${REALPATH}, SCRIPT_DIR=${SCRIPT_DIR}, WORK_DIR=${WORK_DIR}" +cd ${WORK_DIR} + +APP_ARGS=$@ +echo "Run oryx with args: ${APP_ARGS}, WORK_DIR:${WORK_DIR}" + +# Start redis. +bash auto/start_redis +if [[ $? -ne 0 ]]; then echo "Start redis failed"; exit 1; fi + +# Start SRS. +bash auto/start_srs_origin_cluster +if [[ $? -ne 0 ]]; then echo "Start SRS origin cluster failed"; exit 1; fi + +# Start the platform. +./platform $APP_ARGS & +if [[ $? -ne 0 ]]; then echo "Start platform failed"; exit 1; fi + +stop_services() { + # Quickly save data before stopping, as the process termination may + # potentially result in a timeout. + bash auto/before_stop + + if [[ $(ps aux |grep platform |grep -v grep |grep -v usr |grep -q platform && echo yes) == yes ]]; then + kill -s SIGTERM $(pidof platform) + fi + bash auto/stop_redis + bash auto/stop_srs_origin_cluster +} + +handle_signals() { + echo "Signal $1 received. Cleaning up and exiting..." + stop_services + exit 0 +} + +trap 'handle_signals SIGTERM' SIGTERM +trap 'handle_signals SIGINT' SIGINT + +while true; do + sleep 3 + + if [[ $(ps aux |grep redis |grep -q server || echo no) == no ]]; then + echo "Redis server stopped, exit." + break + fi + if [[ $(ps aux |grep proxy | grep -q srs || echo no) == no ]]; then + echo "SRS proxy server stopped, exit." + break + fi + if [[ $(ps aux |grep srs |grep -q conf || echo no) == no ]]; then + echo "SRS server stopped, exit." + break + fi + if [[ $(ps aux |grep platform |grep -v grep |grep -v usr |grep -q platform || echo no) == no ]]; then + echo "Platform stopped, exit." + break + fi +done + +# Quit by itself. +stop_services +exit 1 diff --git a/platform/containers/conf/srs.origin.conf b/platform/containers/conf/srs.origin.conf new file mode 100644 index 00000000..2ddef869 --- /dev/null +++ b/platform/containers/conf/srs.origin.conf @@ -0,0 +1,95 @@ +# !!! Important: SRS will restore this file during each restart, please never modify it. + +# The config for LigthHouse SRS. +# See https://github.com/ossrs/srs/blob/develop/trunk/conf/full.conf + +listen 1935; +max_connections 1000; +# For docker, please use docker logs to manage the logs of SRS. +# See https://docs.docker.com/config/containers/logging/ +srs_log_tank console; +pid ./objs/origin.pid; +daemon on; +disable_daemon_for_docker off; +http_api { + enabled on; + listen 127.0.0.1:1985; + raw_api { + enabled on; + allow_reload on; + } +} +http_server { + enabled on; + listen 8080; + dir ./objs/nginx/html; +} +rtc_server { + enabled on; + listen 8000; # UDP port + # @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#config-candidate + candidate $CANDIDATE; + # Disable for Oryx. + use_auto_detect_network_ip off; + api_as_candidates off; +} + +# See https://github.com/ossrs/srs/issues/1147 +srt_server { + enabled on; + listen 10080; # UDP port + maxbw 1000000000; + connect_timeout 4000; + latency 20; + peerlatency 20; + recvlatency 20; + # See https://ossrs.io/lts/en-us/docs/v6/doc/srt#high-quality-mode + tlpktdrop off; + tsbpdmode off; +} + +heartbeat { + enabled on; + interval 9; + url http://127.0.0.1:12025/api/v1/srs/register; + device_id origin; + ports on; +} + +include containers/data/config/srs.server.conf; + +vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } + rtc { + enabled on; + nack on; + twcc on; + stun_timeout 30; + dtls_role passive; + # @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc + rtmp_to_rtc on; + keep_bframe off; + # @see https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtc-to-rtmp + rtc_to_rtmp on; + pli_for_rtmp 6.0; + } + srt { + enabled on; + srt_to_rtmp on; + } + + # For backend server to verify client. + http_hooks { + enabled on; + on_publish http://localhost:2022/terraform/v1/hooks/srs/verify; + on_unpublish http://localhost:2022/terraform/v1/hooks/srs/verify; + on_play http://localhost:2022/terraform/v1/hooks/srs/verify; + on_stop http://localhost:2022/terraform/v1/hooks/srs/verify; + on_hls http://localhost:2022/terraform/v1/hooks/srs/hls; + } + + include containers/data/config/srs.vhost.conf; +}