mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 00:27:41 +01:00
This change moves the virtualenv creation in the `Dockerfile` to a separate stage, to simplify isolating the process and reduce the need for uninstalling build dependencies. The approach is similar to the one explained in [1]. It relies on building a virtualenv folder, and copying it in the final stage. Changing the `PATH` environment variable makes the virtualenv usable by default, without affecting the default Python installation. Also, added Dockerfile arguments for Alpine, nginx, and Python versions, as some of them are reused, and also simplifies testing new versions. An extra side effect is that the image size for the final stage is reduced from 315MB to 262MB. [1] https://scribe.rip/@albertazzir/blazing-fast-python-docker-builds-with-poetry-a78a66f5aed0
168 lines
4.7 KiB
Bash
Executable File
168 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -o errexit # treat errors as fatal
|
|
set -o nounset # treat unset variables as an error
|
|
set -o pipefail # treat errors in pipes as fatal
|
|
shopt -s inherit_errexit # inherit errexit
|
|
|
|
# make it possible to disable the inotify watcher process
|
|
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE="${ENABLE_RESCAN_ON_FILESYSTEM_CHANGE:="true"}"
|
|
|
|
# Set INIT_DEBUG to false if not set by docker env
|
|
# (this env var is currently undocumented and usually just needed for development purposes)
|
|
INIT_DEBUG="${INIT_DEBUG:="false"}"
|
|
|
|
# print debug log output if enabled
|
|
debug_log() {
|
|
if [[ ${INIT_DEBUG} == "true" ]]; then
|
|
echo "DEBUG: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
|
|
fi
|
|
}
|
|
|
|
# print debug log output if enabled
|
|
info_log() {
|
|
echo "INFO: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
|
|
}
|
|
|
|
# print error log output if enabled
|
|
error_log() {
|
|
echo "ERROR: [init][$(date +"%Y-%m-%d %T")]" "${@}" || true
|
|
exit 1
|
|
}
|
|
|
|
wait_for_gunicorn_socket() {
|
|
info_log "waiting for gunicorn socket file..."
|
|
while [[ ! -S /tmp/gunicorn.sock ]]; do
|
|
sleep 1
|
|
done
|
|
info_log "gunicorn socket file found"
|
|
}
|
|
|
|
# function that runs or main process and creates a corresponding PID file,
|
|
start_bin_gunicorn() {
|
|
# cleanup potentially leftover socket
|
|
rm /tmp/gunicorn.sock -f
|
|
|
|
# commands to start our main application and store its PID to check for crashes
|
|
info_log "starting gunicorn"
|
|
gunicorn \
|
|
--access-logfile - \
|
|
--error-logfile - \
|
|
--worker-class uvicorn.workers.UvicornWorker \
|
|
--bind=0.0.0.0:5000 \
|
|
--bind=unix:/tmp/gunicorn.sock \
|
|
--pid=/tmp/gunicorn.pid \
|
|
--workers "${GUNICORN_WORKERS:=2}" \
|
|
main:app &
|
|
}
|
|
|
|
# Commands to start nginx (handling PID creation internally)
|
|
start_bin_nginx() {
|
|
wait_for_gunicorn_socket
|
|
|
|
info_log "starting nginx"
|
|
if [[ ${EUID} -ne 0 ]]; then
|
|
nginx
|
|
else
|
|
# if container runs as root, drop permissions
|
|
nginx -g 'user romm;'
|
|
fi
|
|
}
|
|
|
|
start_bin_redis-server() {
|
|
info_log "starting redis-server"
|
|
# Check if /usr/local/etc/redis/redis.conf exists and use it if so
|
|
if [[ -f /usr/local/etc/redis/redis.conf ]]; then
|
|
redis-server /usr/local/etc/redis/redis.conf &
|
|
else
|
|
redis-server --dir /redis-data &
|
|
fi
|
|
REDIS_PID=$!
|
|
echo "${REDIS_PID}" >/tmp/redis-server.pid
|
|
}
|
|
|
|
# function that runs our independent python scripts and creates corresponding PID files,
|
|
start_python() {
|
|
SCRIPT="${1}"
|
|
info_log "starting ${SCRIPT}.py"
|
|
python3 "${SCRIPT}.py" &
|
|
WATCHER_PID=$!
|
|
echo "${WATCHER_PID}" >"/tmp/${SCRIPT}.pid"
|
|
}
|
|
|
|
watchdog_process_pid() {
|
|
TYPE=$1
|
|
PROCESS=$2
|
|
if [[ -f "/tmp/${PROCESS}.pid" ]]; then
|
|
# check if the pid we last wrote to our state file is actually active
|
|
PID=$(cat "/tmp/${PROCESS}.pid") || true
|
|
if [[ -d "/proc/${PID}" ]]; then
|
|
debug_log "${PROCESS} still running, no need to start"
|
|
else
|
|
if [[ ${TYPE} == "bin" ]]; then
|
|
start_bin_"${PROCESS}"
|
|
elif [[ ${TYPE} == "python" ]]; then
|
|
start_python "${PROCESS}"
|
|
fi
|
|
fi
|
|
else
|
|
if [[ ${TYPE} == "bin" ]]; then
|
|
start_bin_"${PROCESS}"
|
|
elif [[ ${TYPE} == "python" ]]; then
|
|
start_python "${PROCESS}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# switch to backend directory
|
|
cd /backend || { error_log "/backend directory doesn't seem to exist"; }
|
|
|
|
info_log "Starting up, please wait..."
|
|
|
|
# clear any leftover PID files
|
|
rm /tmp/*.pid -f
|
|
|
|
# function definition done, lets start our main loop
|
|
while true; do
|
|
# check for died processes every 5 seconds
|
|
sleep 5
|
|
|
|
# Start redis server if we dont have a corresponding PID file
|
|
# and REDIS_HOST is not set (which would mean we're using an external redis)
|
|
if [[ -z ${REDIS_HOST:=""} ]]; then
|
|
watchdog_process_pid bin redis-server
|
|
fi
|
|
|
|
# Run needed database migrations on startup,
|
|
# but only if it was not successful since the last full docker container start
|
|
if [[ ${ALEMBIC_SUCCESS:="false"} == "false" ]]; then
|
|
if alembic upgrade head; then
|
|
debug_log "database schema migrations suceeded"
|
|
ALEMBIC_SUCCESS="true"
|
|
else
|
|
error_log "Something went horribly wrong with our database"
|
|
fi
|
|
else
|
|
debug_log "database schema already upgraded during current container lifecycle"
|
|
fi
|
|
|
|
# Start gunicorn if we dont have a corresponding PID file
|
|
watchdog_process_pid bin gunicorn
|
|
|
|
# Start nginx if we dont have a corresponding PID file
|
|
watchdog_process_pid bin nginx
|
|
|
|
# only start the watcher.py if we actually want to use the rescan on fs change feature
|
|
if [[ ${ENABLE_RESCAN_ON_FILESYSTEM_CHANGE} == "true" ]]; then
|
|
# Start watcher if we dont have a corresponding PID file
|
|
watchdog_process_pid python watcher
|
|
fi
|
|
|
|
# Start background worker processes
|
|
debug_log "Starting worker and scheduler"
|
|
# Start worker if we dont have a corresponding PID file
|
|
watchdog_process_pid python worker
|
|
# Start scheduler if we dont have a corresponding PID file
|
|
watchdog_process_pid python scheduler
|
|
done
|