Otra de las pruebas de concepto en la que he estado trabajando estos días atrás ha sido en montar Unified Origin, de Unified Streaming Platform, en Azure. Este servicio se apoya en Apache HTTP Server y, como el objetivo es servir videos en diferentes calidades, he utilizado AKS y Azure Files para montarlo. En este artículo te cuento cómo.
Un clúster de Azure Kubernetes Service y una cuenta de almacenamiento
Si todavía no tienes uno, lo primero que necesitas es un clúster y una cuenta de almacenamiento donde guardaremos lo que queremos servir. Para crear ambos recursos puedes hacerlo siguiendo estos comandos:
#Variables
RESOURCE_GROUP="unified-streaming-platform-on-aks"
LOCATION="northeurope"
AKS_NAME="usp-demo"
AZURE_STORAGE_NAME="originfiles"
SHARE_NAME="assets"
#Create resource group
az group create -n $RESOURCE_GROUP -l $LOCATION
# Create AKS cluster
az aks create -n $AKS_NAME -g $RESOURCE_GROUP --node-count 1 --generate-ssh-keys
#Get the context for the new AKS
az aks get-credentials -n $AKS_NAME -g $RESOURCE_GROUP
#Create Azure Storage Account
az storage account create --name $AZURE_STORAGE_NAME --resource-group $RESOURCE_GROUP --location $LOCATION --sku Standard_LRS
#Create File Share
az storage share create --account-name $AZURE_STORAGE_NAME --name $SHARE_NAME
Subir los archivos de ejemplo
Para probar este entorno, me he basado en el ejemplo que Unified Streaming Platform utiliza en su propia documentación. Por lo que ahora que ya tenemos una cuenta de almacenamiento, con un file share llamado assets, vamos a subir el contenido de Tears of steel que se utiliza en su ejemplo:
#Download tears of steel locally
wget http://repository.unified-streaming.com/tears-of-steel.zip
#Unzip it
unzip tears-of-steel.zip -d tears-of-steel
#Upload it to assets file share
STORAGE_KEY=$(az storage account keys list -g $RESOURCE_GROUP -n $AZURE_STORAGE_NAME --query '[0].value' -o tsv)
az storage file upload-batch --destination $SHARE_NAME --source tears-of-steel/. --account-name $AZURE_STORAGE_NAME --account-key $STORAGE_KEY
Con esto el resultado debería de ser lo siguiente:

Despliegue de Unified Origin en AKS
Ahora que ya tenemos el clúster y los archivos que queremos servir, falta desplegar Unified Origin en Azure Kubernetes Service. Antes de ello, es necesario crear un secreto con la key de evaluación de Unified Streaming Platform (esta la he pegado en un archivo llamado key) y otro para el acceso a nuestra cuenta de almacenamiento:
#Create a secret with the USP key
kubectl create secret generic usp-licence --from-file=key
#Create a secret with azure storage credentials
kubectl create secret generic azure-secret --from-literal=azurestorageaccountname=$AZURE_STORAGE_NAME --from-literal=azurestorageaccountkey=$STORAGE_KEY
Respecto a la configuración de Unified Origin me he descargado el contenido de unifiedstreaming/origin y he modificado el archivo Dockerfile con lo siguiente:
ARG UBUNTUVERSION=focal
FROM ubuntu:$UBUNTUVERSION
# ARGs declared before FROM are in a different scope, so need to be stated again
# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG UBUNTUVERSION
ARG REPO=https://stable.apt.unified-streaming.com
ARG VERSION=1.11.1
# noninteractive installs
ENV DEBIAN_FRONTEND=noninteractive
# Install wget and gnupg
RUN apt-get update \
&& apt-get install -y \
wget \
gnupg \
unzip
# Add tears of steel first for Docker build/cache purposes
# RUN wget http://repository.unified-streaming.com/tears-of-steel.zip
# RUN mkdir -p /var/www/unified-origin \
# && unzip tears-of-steel.zip -d /var/www/unified-origin
RUN mkdir -p /var/www/unified-origin
# Add the Unified Streaming public key
RUN wget $REPO/unifiedstreaming.pub \
&& apt-key add unifiedstreaming.pub
# Add repository
RUN echo "deb [arch=amd64] $REPO $UBUNTUVERSION multiverse" > /etc/apt/sources.list.d/unified-streaming.list
# Install Origin
RUN apt-get update \
&& apt-get install -y \
apache2 \
mp4split=$VERSION \
libapache2-mod-smooth-streaming=$VERSION
# Set up directories and log file redirection
RUN mkdir -p /run/apache2 \
&& rm -f /var/log/apache2/error.log \
&& ln -s /dev/stderr /var/log/apache2/error.log \
&& rm -f /var/log/apache2/access.log \
&& ln -s /dev/stdout /var/log/apache2/access.log
# Enable extra modules and disable default site
RUN a2enmod \
headers \
proxy \
ssl \
mod_smooth_streaming \
&& a2dissite 000-default
# Copy apache config and entrypoint script
COPY unified-origin.conf.in /etc/apache2/sites-enabled/unified-origin.conf.in
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 80
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["-D", "FOREGROUND"]
el entrypoint.sh
#!/bin/sh
set -e
# set env vars to defaults if not already set
if [ -z "$LOG_LEVEL" ]
then
export LOG_LEVEL=warn
fi
if [ -z "$LOG_FORMAT" ]
then
export LOG_FORMAT="%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D"
fi
if [ -z "$REMOTE_PATH" ]
then
export REMOTE_PATH=remote
fi
if [ $USP_LICENSE_KEY ]
then
export UspLicenseKey=$USP_LICENSE_KEY
fi
# validate required variables are set
if [ -z "$UspLicenseKey" ]
then
echo >&2 "Error: UspLicenseKey environment variable is required but not set."
exit 1
fi
# update configuration based on env vars
# log levels
/bin/sed "s@{{LOG_LEVEL}}@${LOG_LEVEL}@g; s@{{LOG_FORMAT}}@'${LOG_FORMAT}'@g;" /etc/apache2/sites-enabled/unified-origin.conf.in > /etc/apache2/sites-enabled/unified-origin.conf
# USP license
echo $UspLicenseKey > /etc/usp-license.key
rm -f /run/apache2/apache2.pid
# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
set -- apachectl "$@"
fi
exec "$@"
el archivo unified-origin.conf.in añadiendo la propiedad UspEnableMMAP a Off.
LoadModule smooth_streaming_module modules/mod_smooth_streaming.so
AddHandler smooth-streaming.extensions .ism .isml
ServerName unified-origin
<Location />
UspHandleIsm on
UspEnableSubreq on
</Location>
UspLicenseKey /etc/usp-license.key
LogFormat {{LOG_FORMAT}} log_format
<VirtualHost *:80>
CustomLog /dev/stdout log_format
ErrorLog /dev/stderr
LogLevel {{LOG_LEVEL}}
AddHandler smooth-streaming.extensions .ism .isml .mp4
SSLProxyEngine on
DocumentRoot /var/www/unified-origin
Header set Access-Control-Allow-Headers "origin, range"
Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Expose-Headers "Server,range"
</VirtualHost>
<Directory /var/www/unified-origin>
UspEnableMMAP Off
Require all granted
</Directory>
Al igual que comenté en el artículo anterior, en este ejemplo también es necesario deshabilitar EnableMMAP para evitar el bug que existe entre Apache y SMB. En este sentido Unified Streaming Platform tiene su propia propiedad llamada UspEnableMMAP y es la que debes utilizar para que se deshabilite correctamente. Ahora ya puedo generar una nueva imagen y publicarla en un registro al que mi AKS pueda acceder para descargarla, en este caso Docker Hub:
#Generate new image and push to Docker Hub
cd usp-origin
docker build --no-cache -t 0gis0/usp-origin .
docker push 0gis0/usp-origin
Desde el punto de vista de los recursos de Kubernetes, deberíamos de generar un despliegue y un service para poder acceder al servicio.
apiVersion: apps/v1
kind: Deployment
metadata:
name: usp-origin
spec:
selector:
matchLabels:
app: usp-origin
template:
metadata:
labels:
app: usp-origin
spec:
containers:
- name: usp-origin
image: 0gis0/usp-origin
imagePullPolicy: Always
env:
- name: USP_LICENSE_KEY
valueFrom:
secretKeyRef:
key: key
name: usp-licence
ports:
- containerPort: 80
volumeMounts:
- name: assets
mountPath: /var/www/unified-origin
volumes:
- name: assets
azureFile:
shareName: assets
secretName: azure-secret
---
apiVersion: v1
kind: Service
metadata:
name: usp-service
spec:
type: LoadBalancer
selector:
app: usp-origin
ports:
- port: 80
targetPort: 80
Si ahora accedes a la IP pública deberías de ver perfectamente la página de demo:
❯ kubectl get svc usp-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
usp-service LoadBalancer 10.0.224.79 20.67.219.237 80:31718/TCP 77m
y poder ejecutar todos los ejemplos que trae consigo:
El código de este ejemplo lo tienes en mi GitHub.
¡Saludos!