Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ jobs:
build:
runs-on: ${{ matrix.runner }}
outputs:
# image-arm64: ${{ steps.gen-output.outputs.image-arm64 }}
image-arm64: ${{ steps.gen-output.outputs.image-arm64 }}
image-x64: ${{ steps.gen-output.outputs.image-x64 }}
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-24.04
# - ubuntu-24.04-arm
- ubuntu-24.04-arm
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down Expand Up @@ -77,7 +77,7 @@ jobs:
runs-on: ubuntu-24.04
needs: build
env:
# DOCKER_APP_IMAGE_ARM64: ${{ needs.build.outputs.image-arm64 }}
DOCKER_APP_IMAGE_ARM64: ${{ needs.build.outputs.image-arm64 }}
DOCKER_APP_IMAGE_X64: ${{ needs.build.outputs.image-x64 }}
outputs:
image: ${{ steps.meta.outputs.tags }}
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
run: |
docker buildx imagetools create \
--tag "$DOCKER_METADATA_OUTPUT_TAGS" \
"$DOCKER_APP_IMAGE_X64"
"$DOCKER_APP_IMAGE_ARM64" "$DOCKER_APP_IMAGE_X64"

test:
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -143,7 +143,7 @@ jobs:
- name: Run tests
if: ${{ always() }}
run: |
docker compose run app /opt/app/test/run_tests.py
docker compose exec app venv/bin/python test/run_tests.py
env:
WOWZA_LICENSE_KEY: ${{ secrets.WOWZA_LICENSE_KEY }}

Expand All @@ -153,7 +153,8 @@ jobs:
docker compose cp app:/opt/app/artifacts ./
docker compose logs > artifacts/docker-compose-services.log
docker compose config > artifacts/docker-compose.merged.yml

env:
WOWZA_LICENSE_KEY: EZZZZ-INTEN-TIONA-LLYRE-DACT-EDFROM-BUILDLOGS
- name: Upload the test report
if: ${{ always() }}
uses: actions/upload-artifact@v4
Expand Down
53 changes: 40 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Target: base
#

FROM wowzamedia/wowza-streaming-engine-linux:4.8.25 AS base
FROM wowzamedia/wowza-streaming-engine-linux:4.9.6 AS base

# =============================================================================
# Ports
Expand All @@ -27,28 +27,35 @@ RUN apt-get update -qq
RUN apt-get install -y --no-install-recommends \
curl \
dnsutils \
iputils-ping \
lsb-core
gettext \
iputils-ping

# =============================================================================
# Java

# The upstream Wowza image ships with OpenJDK 9.0.4+11, which is not a
# long-term support release and, importantly, doesn't know about containers:
# https://www.wowza.com/community/t/creating-a-production-worthy-docker-image/53957/3
# The upstream Wowza image may ship with an outdated OpenJDK, and
# previously (even as of 4.8.25) did not include an LTS release or
# one that was aware of containers.
#
# Luckily, Wowza supports manually replacing the JRE:
# https://www.wowza.com/docs/manually-install-and-troubleshoot-java-on-wowza-streaming-engine
# ... so this updates to the latest version of OpenJDK 21 (LTS).
#
# (Unfortunately, this by itself isn't enough to get Wowza to properly
# calculate its own max heap size, so we still need to set that explicitly
# in Tune.xml. Possibly a future version of Wowza will be clever enough to
# use -XX:MaxRAMPercentage instead.)

RUN apt-get install -y --no-install-recommends openjdk-11-jre-headless
RUN apt-get install -y --no-install-recommends openjdk-21-jre-headless

RUN rm -rf /usr/local/WowzaStreamingEngine/java
RUN ln -s /usr/lib/jvm/java-11-openjdk-amd64 /usr/local/WowzaStreamingEngine/java

# for some reason, OpenJDK's default directory includes the architecture
# name and does not symlink it to something more straightforward like
# /usr/lib/jvm/java-21-openjdk so we have to detect the architecture

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) ln -s "/usr/lib/jvm/java-21-openjdk-${arch}" /usr/local/WowzaStreamingEngine/java

# =============================================================================
# Global configuration
Expand All @@ -66,6 +73,10 @@ RUN usermod -u $APP_UID $APP_USER && \
# transfer now-orphaned files to new wowza user (-h to chown symlinks)
RUN find / -xdev -nouser -exec chown -h $APP_USER:$APP_USER {} \;

# set variables to be used by envsubst to overwrite the default wowza config
ENV SUPERVISORD_PID_FILE=/tmp/supervisord.pid
ENV SUPERVISORD_SOCKET_FILE=/tmp/supervisor.sock

# =============================================================================
# Set working directory

Expand All @@ -74,12 +85,13 @@ WORKDIR /opt/app
# =============================================================================
# Tests

RUN apt-get install -y --no-install-recommends python3-pip
RUN pip3 install unittest-xml-reporting
RUN apt-get install -y --no-install-recommends python3-pip python3-venv
RUN python3 -m venv venv
RUN venv/bin/pip3 install unittest-xml-reporting

COPY --chown=$APP_USER test /opt/app/test

# Put artifacts where Jenkins can get at them
# Put artifacts where Github Actions can get at them
RUN mkdir /opt/app/artifacts && \
chown $APP_USER:$APP_USER /opt/app/artifacts

Expand All @@ -96,8 +108,21 @@ RUN for app in vod live; \
# Copy our scripts, configs, templates, etc. into the container
COPY --chown=$APP_USER WowzaStreamingEngine /usr/local/WowzaStreamingEngine
COPY --chown=$APP_USER log4j-templates /opt/app/log4j-templates
COPY --chown=$APP_USER supervisor_templates /opt/app/supervisor_templates
COPY --chown=$APP_USER bin /opt/app/bin

# create supervisord config files from templates
RUN envsubst < \
supervisor_templates/supervisord.conf.tmpl > \
/etc/supervisor/supervisord.conf
RUN envsubst < \
supervisor_templates/conf.d/WowzaStreamingEngine.conf.tmpl > \
/etc/supervisor/conf.d/WowzaStreamingEngine.conf
RUN envsubst < \
supervisor_templates/conf.d/WowzaStreamingEngineManager.conf.tmpl > \
/etc/supervisor/conf.d/WowzaStreamingEngineManager.conf


# =============================================================================
# Additional Java libraries

Expand Down Expand Up @@ -134,11 +159,13 @@ RUN rm -r /opt/app/WEB-INF
RUN apt-get remove -y zip

# =============================================================================
# Run as the wowza user to minimize risk to the host.
# Unlike most of our containers, this container starts as root as privileges
# are dropped by supervisord instead of setting the `USER` set in the
# Dockerfile.

USER $APP_USER

# =============================================================================
# Default command

CMD ["/opt/app/bin/docker-entrypoint.sh"]
ENTRYPOINT ["/opt/app/bin/docker-entrypoint.sh"]
Comment thread
anarchivist marked this conversation as resolved.
65 changes: 65 additions & 0 deletions WowzaStreamingEngine/bin/startup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash
# this is a BerkeleyLibrary modified version of the WSE startup script

# check for root access. If not, put up message and exit
# if [ "$(/usr/bin/id -u)" -ne "0" ] ; then
# echo "The Wowza Streaming Engine requires root access to start. Please run script again using sudo."
# exit
# fi

systemctl >> /dev/null 2>&1
if [ $? -eq 0 ]; then
# Restart XRM service
SERVICE_NAME="xrmd.service"
systemctl list-units --full -all | grep -Fq $SERVICE_NAME

if [ $? -eq 0 ]; then
echo "Restarting XRM service"
systemctl restart $SERVICE_NAME
. /opt/xilinx/xcdr/setup.sh
fi
fi

. /usr/local/WowzaStreamingEngine/bin/setenv.sh
mode=standalone
if [ "$#" -eq 1 ];
then
mode=$1
fi

#chmod 600 /usr/local/WowzaStreamingEngine/conf/jmxremote.password
#chmod 600 /usr/local/WowzaStreamingEngine/conf/jmxremote.access

# NOTE: Here you can configure the JVM's built in JMX interface.
# See the "Server Management Console and Monitoring" chapter
# of the "User's Guide" for more information on how to configure the
# remote JMX interface in the [install-dir]/conf/Server.xml file.

JMXOPTIONS=-Dcom.sun.management.jmxremote=true
#JMXOPTIONS="$JMXOPTIONS -Djava.rmi.server.hostname=192.168.1.7"
#JMXOPTIONS="$JMXOPTIONS -Dcom.sun.management.jmxremote.port=1099"
#JMXOPTIONS="$JMXOPTIONS -Dcom.sun.management.jmxremote.authenticate=true"
#JMXOPTIONS="$JMXOPTIONS -Dcom.sun.management.jmxremote.ssl=false"
#JMXOPTIONS="$JMXOPTIONS -Dcom.sun.management.jmxremote.password.file=$WMSCONFIG_HOME/conf/jmxremote.password"
#JMXOPTIONS="$JMXOPTIONS -Dcom.sun.management.jmxremote.access.file=$WMSCONFIG_HOME/conf/jmxremote.access"

ulimit -n 64000 > /dev/null 2>&1

rc=144
while [ $rc -eq 144 ]
do

WMSTUNE_OPTS=`$WMSAPP_HOME/bin/tune.sh $mode`
export LD_PRELOAD=`$WMSAPP_HOME/bin/ldpreload.sh`

# log interceptor com.wowza.wms.logging.LogNotify - see Javadocs for ILogNotify

$_EXECJAVA $WMSTUNE_OPTS $JMXOPTIONS -Dorg.slf4j.simpleLogger.defaultLogLevel=warn -Dcom.wowza.wms.runmode="$mode" -Dcom.wowza.wms.native.base="linux" -Dlog4j.configurationFile="$WMSCONFIG_HOME/conf/log4j2-config.xml" -Dcom.wowza.wms.AppHome="$WMSAPP_HOME" -Dcom.wowza.wms.ConfigURL="$WMSCONFIG_URL" -Dcom.wowza.wms.ConfigHome="$WMSCONFIG_HOME" -cp $WMSAPP_HOME/bin/wms-bootstrap.jar com.wowza.wms.bootstrap.Bootstrap start

rc=$?
if [ $rc -ge 10 ] && [ $rc -le 15 ] ; then
WSE_EXIT_CODE=$rc
$_EXECJAVA $WMSTUNE_OPTS $JMXOPTIONS -Dcom.wowza.wms.runmode="$mode" -Dcom.wowza.wms.native.base="linux" -Dlog4j.configurationFile="$WMSCONFIG_HOME/conf/log4j2-config.xml" -Dcom.wowza.wms.AppHome="$WMSAPP_HOME" -Dcom.wowza.wms.ConfigURL="$WMSCONFIG_URL" -Dcom.wowza.wms.ConfigHome="$WMSCONFIG_HOME" -cp $WMSAPP_HOME/bin/wms-bootstrap.jar com.wowza.wms.bootstrap.Bootstrap startLicenseUpdateServer
rc=$?
fi
done
65 changes: 10 additions & 55 deletions bin/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
# Streaming Media Engine Manager in subprocesses, and waits
# for them to exit.

# TODO: now that we're running the manager and server in the same container,
# consider getting rid of this (or simplifying it) in favor of the
# upstream container's /sbin/entrypoint.sh, which uses supervisord

BASENAME=$(basename ${BASH_SOURCE})
echo "${BASENAME} running"

Expand All @@ -26,58 +22,17 @@ if [ ! -z "${WOWZA_ENABLE_DOCUMENTATION_SERVER}" ]; then
"${WOWZA_BIN}"/enable-documentation-server.sh
fi

# ########################################
# Start server and manager in background

# shellcheck source=start-server.sh
echo "Invoking ${WOWZA_BIN}"/start-server.sh
"${WOWZA_BIN}"/start-server.sh &
SERVER_PID=$!
echo "SERVER_PID=${SERVER_PID}"

echo "Invoking ${WMSMGR_HOME}"/bin/startmgr.sh
"${WMSMGR_HOME}"/bin/startmgr.sh &
MANAGER_PID=$!
echo "MANAGER_PID=${MANAGER_PID}"

# ########################################
# Make sure child processes exit cleanly

ensure_clean_exit() {
trap - SIGINT SIGTERM # clear the trap
echo "Killing process $$ and all subprocesses"
kill -- -$$
}

sigint_received() {
echo "SIGINT received"
ensure_clean_exit
}

sigterm_received() {
echo "SIGTERM received"
ensure_clean_exit
}
# load secrets into wowza env vars. by default, the Wowza entrypoint
# creates the keyfile in question, which is why we pass the variable
# in using a different name.
. "${WOWZA_BIN}/secrets.sh"

trap sigint_received SIGINT
trap sigterm_received SIGTERM
export WSE_MGR_USER=$WOWZA_MANAGER_USER
export WSE_MGR_PASS=$WOWZA_MANAGER_PASSWORD
export WSE_LIC=$WOWZA_LICENSE_KEY

# ########################################
# Wait for manager or server to exit

wait -n
EXIT_STATUS=$?

# Whichever exited, kill the other one
if ! kill -0 $SERVER_PID 2> /dev/null; then
echo "Wowza Streaming Engine (PID ${SERVER_PID}) exited with ${EXIT_STATUS}"
echo "Stopping Wowza Streaming Engine Manager (PID ${MANAGER_PID})"
kill -- -$MANAGER_PID 2> /dev/null
elif ! kill -0 $MANAGER_PID 2> /dev/null; then
echo "Wowza Streaming Engine Manager (PID ${MANAGER_PID}) exited with ${EXIT_STATUS}"
echo "Stopping Wowza Streaming Engine (PID ${SERVER_PID})"
kill -- -$SERVER_PID 2> /dev/null
fi
# Start server and manager by handing off to Wowza's entrypoint

echo "Exiting with status ${EXIT_STATUS}"
exit $EXIT_STATUS
echo Invoking Wowza\'s /sbin/entrypoint.sh
exec /sbin/entrypoint.sh "$@"
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ services:
- WOWZA_MANAGER_USER=${WOWZA_MANAGER_USER}
- WOWZA_MANAGER_PASSWORD=${WOWZA_MANAGER_PASSWORD}
- WOWZA_ENABLE_DOCUMENTATION_SERVER=yes
healthcheck:
test: curl -s http://localhost:8087 > /dev/null || exit 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned it in our review this morning but worth asking again here: is there a reason not to include this in the Dockerfile? That would make it easier to configure across environments, and it can still be disabled via healthcheck: { disable: true}.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 7bcaad7.

interval: 5s
timeout: 5s
retries: 10
# Uncommment these when editing / running tests locally.
# (Note: slows performance on macOS Catalina, for some reason.)
# volumes:
Expand Down
Binary file removed lib-ucblit/log4j-api-2.17.0.jar
Binary file not shown.
Binary file removed lib-ucblit/log4j-core-2.17.0.jar
Binary file not shown.
Binary file removed lib-ucblit/log4j-layout-template-json-2.17.0.jar
Binary file not shown.
Binary file added lib-ucblit/log4j-layout-template-json-2.23.1.jar
Binary file not shown.
11 changes: 11 additions & 0 deletions supervisor_templates/conf.d/WowzaStreamingEngine.conf.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[program:WowzaStreamingEngine]
priority=10
directory=/usr/local/WowzaStreamingEngine/bin
command=/usr/local/WowzaStreamingEngine/bin/startup.sh
user=${APP_USER}
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
11 changes: 11 additions & 0 deletions supervisor_templates/conf.d/WowzaStreamingEngineManager.conf.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[program:WowzaStreamingEngineManager]
priority=20
directory=/usr/local/WowzaStreamingEngine/manager/bin
command=/usr/local/WowzaStreamingEngine/manager/bin/startmgr.sh
user=${APP_USER}
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes = 0
30 changes: 30 additions & 0 deletions supervisor_templates/supervisord.conf.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
; supervisor config file

[unix_http_server]
file=${SUPERVISORD_SOCKET_FILE} ; (the path to the socket file)
chmod=0700 ; socket file mode (default 0700)

[supervisord]
logfile=/dev/stdout ; send supervisor logs to stdout
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to duplicate the logs to stdout (i.e., they're sent twice for the supervisord's process logs):

$ docker compose logs
WARN[0000] The "CI" variable is not set. Defaulting to a blank string.
app-1  | docker-entrypoint.sh running
app-1  | enable-documentation-server.sh running
app-1  | Enabling documentation server
app-1  | Allowing unauthenticated access to documentation server
app-1  | Swagger/OpenAPI documentation server available at http://localhost:8089/api-docs
app-1  | secrets.sh running
app-1  | secrets.sh complete
app-1  | Invoking Wowza's /sbin/entrypoint.sh
app-1  | 2025-12-02 00:07:21,887 INFO Included extra file "/etc/supervisor/conf.d/WowzaStreamingEngine.conf" during parsing
app-1  | 2025-12-02 00:07:21,887 INFO Included extra file "/etc/supervisor/conf.d/WowzaStreamingEngine.conf" during parsing
app-1  | 2025-12-02 00:07:21,887 INFO Included extra file "/etc/supervisor/conf.d/WowzaStreamingEngineManager.conf" during parsing
app-1  | 2025-12-02 00:07:21,887 INFO Included extra file "/etc/supervisor/conf.d/WowzaStreamingEngineManager.conf" during parsing
app-1  | 2025-12-02 00:07:21,887 INFO Set uid to user 40041 succeeded
app-1  | 2025-12-02 00:07:21,887 INFO Set uid to user 40041 succeeded
app-1  | 2025-12-02 00:07:21,892 INFO RPC interface 'supervisor' initialized
app-1  | 2025-12-02 00:07:21,892 INFO RPC interface 'supervisor' initialized
app-1  | 2025-12-02 00:07:21,892 CRIT Server 'unix_http_server' running without any HTTP authentication checking
app-1  | 2025-12-02 00:07:21,892 CRIT Server 'unix_http_server' running without any HTTP authentication checking
app-1  | 2025-12-02 00:07:21,893 INFO supervisord started with pid 1
app-1  | 2025-12-02 00:07:21,893 INFO supervisord started with pid 1
app-1  | 2025-12-02 00:07:22,906 INFO spawned: 'WowzaStreamingEngine' with pid 19
app-1  | 2025-12-02 00:07:22,906 INFO spawned: 'WowzaStreamingEngine' with pid 19
app-1  | 2025-12-02 00:07:22,912 INFO spawned: 'WowzaStreamingEngineManager' with pid 20
app-1  | 2025-12-02 00:07:22,912 INFO spawned: 'WowzaStreamingEngineManager' with pid 20
app-1  | INFO: Main.main: root: /usr/local/WowzaStreamingEngine/manager
app-1  | INFO: Main.main: loadApplication: launch.Application
app-1  | 2025-12-02 00:07:24,512 INFO success: WowzaStreamingEngine entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
app-1  | 2025-12-02 00:07:24,512 INFO success: WowzaStreamingEngine entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
app-1  | 2025-12-02 00:07:24,512 INFO success: WowzaStreamingEngineManager entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
app-1  | 2025-12-02 00:07:24,512 INFO success: WowzaStreamingEngineManager entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

Copy link
Copy Markdown
Member

@danschmidt5189 danschmidt5189 Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docker recommends a few modifications to this, most notably setting supervisord's logfile to /dev/null:

[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0

[program:app]
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

I'm not sure why they write program logs to /dev/fd/1 versus the more familar /dev/stdout, when as far as I can tell they're equivalent. From this image:

# ls -l /dev/stdout
lrwxrwxrwx 1 root root 15 Dec  2 07:56 /dev/stdout -> /proc/self/fd/1

# ls -l /proc/self/fd/1
lrwx------ 1 root root 64 Dec  2 07:56 /proc/self/fd/1 -> /dev/pts/0

# ls -l /dev/fd/1
lrwx------ 1 root root 64 Dec  2 07:56 /dev/fd/1 -> /dev/pts/0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 7bcaad7. nodaemon=true is unnecessary as the Wowza image entrypoint that our entrypoint script execs into calls supervisord -n.

logfile_maxbytes = 0 ; forcibly disable log rotation
pidfile=${SUPERVISORD_PID_FILE} ; (supervisord pidfile;default supervisord.pid)
childlogdir=/usr/local/supervisor ; ('AUTO' child log dir, default $TEMP)
user=${APP_USER}

; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix://${SUPERVISORD_SOCKET_FILE} ; use a unix:// URL for a unix socket

; The [include] section can just contain the "files" setting. This
; setting can list multiple files (separated by whitespace or
; newlines). It can also contain wildcards. The filenames are
; interpreted as relative to this file. Included files *cannot*
; include files themselves.

[include]
files = /etc/supervisor/conf.d/*.conf
Loading