Nginx-Rtmp en Azure Container Instances

Este fin de semana he estado entretenida montando un par de ejemplos con el servidor Nginx, el módulo que dispone este para el protocolo RTMP, y con el módulo de Kaltura para VoD, muy utilizados en el mundo media. Como no quería montarme una máquina para hacer mis pruebas, me he creado un par de contenedores Docker, uno para Live Streaming y otro para Video On Demand y los he desplegado en Azure Container Instances. Hoy quiero compartir contigo el que he usado para los eventos en directo, por si te fuera de utilidad.

nginx.conf

Para el video en directo, o Live Streaming, me he inspirado en este artículo donde un arquitecto de Nginx nos cuenta la configuración mínima necesaria para este escenario. En mi ejemplo, he habilitado además la opción record all para que se guarde todo lo que se reproduzca, ubicándolo en la carpeta /recordings y transformándolo a formato MP4 cuando termine la ingesta.

worker_processes auto;

events {
    worker_connections 768;
}

rtmp{
    server{
        listen 1935;      

        #rtmp endpoint /live
        application live{ 
            live on;
            
            record all;
            record_path /recordings;
            record_unique on;
            # convert recorded file to mp4 format
            exec_record_done ffmpeg -i $path -c copy $dirname/$basename.mp4;

            #optimization for video and audio in the same package
            interleave on;
            #HLS for iOs/MacOs devices
            hls on;
            #The path where the fragments will go for HLS
            hls_path /tmp/hls;
            #How long the fragments are gonna be
            hls_fragment 15s;
            #The same for dash
            dash on;
            dash_path /tmp/dash;
            dash_fragment 15s;
        }        
    }
}

http{
    default_type application/octet-stream;

    server{
        listen 80;

        location / {
            #Where the fragments for HLS and DASH are
            root /tmp;

            # CORS setup
            add_header 'Access-Control-Allow-Origin' '*' always;
            add_header 'Access-Control-Expose-Headers' 'Content-Length';

            # allow CORS preflight requests
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain charset=UTF-8';
                add_header 'Content-Length' 0;
                return 204;
            }

        }     

        types {
            #HLS
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
            #HTML
            text/html html;
            #DASH
            application/dash+xml mpd;
        }
    }
}

Te recomiendo que veas el video de dicho artículo para que entiendas cada parte, aunque he dejado mis anotaciones en el código 🙂

Dockerfile

Como quiero utilizar Azure Container Instances para montar mi servidor Nginx, necesito un Dockerfile que me permita generar la imagen:

FROM buildpack-deps:stretch

# Versions of Nginx and nginx-rtmp-module
ENV NGINX_VERSION nginx-1.17.10
ENV NGINX_RTMP_MODULE_VERSION 1.2.1

# Install dependencies
RUN apt-get update && \
    apt-get install -y ca-certificates openssl libssl-dev ffmpeg && \
    rm -rf /var/lib/apt/lists/*

# Download and decompress Nginx
RUN mkdir -p /tmp/build/nginx && \
    cd /tmp/build/nginx && \
    wget -O ${NGINX_VERSION}.tar.gz https://nginx.org/download/${NGINX_VERSION}.tar.gz && \
    tar -zxf ${NGINX_VERSION}.tar.gz

# Download and decompress RTMP module
RUN mkdir -p /tmp/build/nginx-rtmp-module && \
    cd /tmp/build/nginx-rtmp-module && \
    wget -O nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}.tar.gz https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_MODULE_VERSION}.tar.gz && \
    tar -zxf nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}.tar.gz && \
    cd nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION}

# Build and install Nginx
RUN cd /tmp/build/nginx/${NGINX_VERSION} && \
    ./configure \
        --sbin-path=/usr/local/sbin/nginx \
        --conf-path=/etc/nginx/nginx.conf \
        --error-log-path=/var/log/nginx/error.log \
        --pid-path=/var/run/nginx/nginx.pid \
        --lock-path=/var/lock/nginx/nginx.lock \
        --http-log-path=/var/log/nginx/access.log \
        --http-client-body-temp-path=/tmp/nginx-client-body \
        --with-http_ssl_module \
        --with-threads \
        --with-ipv6 \
        --add-module=/tmp/build/nginx-rtmp-module/nginx-rtmp-module-${NGINX_RTMP_MODULE_VERSION} && \
    make -j $(getconf _NPROCESSORS_ONLN) && \
    make install && \
    mkdir /var/lock/nginx && \
    rm -rf /tmp/build

# Set up config file
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 1935 80
CMD ["nginx", "-g", "daemon off;"]

En este simplemente descargo tanto el servidor Nginx como el módulo Nginx-rtmp, los descomprimo e instalo el servidor con una serie de parámetros para este. Por último, copio el archivo de configuración del apartado anterior, expongo los dos puertos que utilizo en el mismo y por último ejecuto Nginx para que arranque el servidor.

Desplegar Nginx en Azure Container Instances

Ahora que ya tienes la configuración y el Dockerfile para generar la imagen, puedes utilizar los siguientes comando para desplegar el contenedor en ACI:

#Build and push your image
docker build . -t YOUR_USERNAME/nginx-rtmp-live
docker push YOUR_USERNAME/nginx-rtmp-live

#Azure Storage Account where I'll save the live streaming events
RESOURCE_GROUP="Nginx-Demos"
AZ_STORAGE_NAME="media"

#Get the Azure Storage Account Key
AZ_STORAGE_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP --account-name $AZ_STORAGE_NAME --query "[0].value" --output tsv)

#Create a the Live container with nginx-rtmp
az container create \
--resource-group $RESOURCE_GROUP \
--name nginx-rtmp-live \
--image YOUR_USERNAME/nginx-rtmp-live \
--dns-name-label nginx-rtmp-live --ports 1935 80 \
--cpu 2 --memory 3.5 \
--azure-file-volume-share-name recordings \
--azure-file-volume-account-name $AZ_STORAGE_NAME \
--azure-file-volume-account-key $AZ_STORAGE_KEY \
--azure-file-volume-mount-path /recordings

Para comprobar que funciona correctamente puedes utilizar ffmpeg en local haciendo streming de un archivo en local a tu contenedor, utilizando el protocolo RTMP:

ffmpeg -re -i ../videos/sample.mkv -vcodec copy -loop -1 -c:a aac -b:a 160k -ar 44100 -strict -2 -f flv rtmp://nginx-rtmp-live.northeurope.azurecontainer.io/live/test

O bien puedes utilizar herramientas como OBS Studio para compartir directamente tu webcam.

Nota: Siguiendo el ejemplo anterior, si usas OBS el Stream Key es el nombre del evento, en este caso test.

Si abres un reproductor como VLC, que soporta RMTP, puedes abrir la ubicación de red, en mi caso, rtmp://nginx-rtmp-live.northeurope.azurecontainer.io/live/sample a través de File > Open Network… y ver que efectivamente se está retransmitiendo tu archivo desde ACI.

Por otro lado, una vez que comiences probar tu evento en directo, gracias a record all, lo que estés retransmitiendo se grabará en el path recordings, que a su vez está mapeado con una carpeta en Azure Files con el mismo nombre. Además, una vez que finalice el evento se generará de manera automática el mismo archivo en formato MP4.

Como en el archivo de configuración de Nginx también está habilitado HLS y DASH, puedes utilizar un ejemplo como el siguiente para reproducirlo en un sitio web:

El código lo tienes en mi GitHub.

¡Saludos!