Make container more robust #95

Merged
lukas merged 5 commits from container-improvements into main 2026-05-12 16:29:35 +00:00
9 changed files with 119 additions and 64 deletions
Showing only changes of commit 0a52c4da7b - Show all commits
-1
View File
@@ -9,7 +9,6 @@ static
.drone.yml .drone.yml
.editorconfig .editorconfig
.gitignore .gitignore
Caddyfile
CHANGELOG.md CHANGELOG.md
db.sqlite3 db.sqlite3
docker-compose* docker-compose*
+5 -4
View File
@@ -1,14 +1,15 @@
{ {
auto_https off auto_https off
admin off
} }
:8000 { :8000 {
handle_path /static/* { handle_path /static/* {
root * /usr/share/caddy root * /home/timetracker/app/static
file_server file_server
} }
handle { handle /robots.txt {
reverse_proxy backend:8001 root * /home/timetracker/app/games/static
file_server
} }
reverse_proxy localhost:8001
} }
+15
View File
@@ -0,0 +1,15 @@
{
auto_https off
}
:8000 {
handle_path /static/* {
root * static
file_server browse
}
handle /robots.txt {
root * games/static
file_server browse
}
reverse_proxy :8001
}
+15 -6
View File
@@ -22,20 +22,29 @@ ENV PROD=1 \
PYTHONUNBUFFERED=1 \ PYTHONUNBUFFERED=1 \
PATH="/home/timetracker/app/.venv/bin:$PATH" PATH="/home/timetracker/app/.venv/bin:$PATH"
RUN useradd -m --uid 1000 timetracker \ RUN apt-get update && apt-get install -y --no-install-recommends \
&& mkdir -p /var/www/django/static \ curl \
&& chown timetracker:timetracker /var/www/django/static ca-certificates \
libcap2-bin \
supervisor \
&& rm -rf /var/lib/apt/lists/* \
&& useradd -m --uid 1000 timetracker \
&& mkdir -p /var/log/supervisor /etc/supervisor/conf.d /home/timetracker/data \
&& chown timetracker:timetracker /var/log/supervisor /home/timetracker/data
RUN curl -sL "https://caddyserver.com/api/download?os=linux&arch=amd64" \
-o /usr/local/bin/caddy && chmod +x /usr/local/bin/caddy
WORKDIR /home/timetracker/app WORKDIR /home/timetracker/app
COPY --from=builder --chown=timetracker:timetracker /home/timetracker/app /home/timetracker/app COPY --from=builder --chown=timetracker:timetracker /home/timetracker/app /home/timetracker/app
COPY --chown=timetracker:timetracker Caddyfile /etc/caddy/Caddyfile
COPY --chown=timetracker:timetracker supervisor.conf /etc/supervisor/conf.d/supervisor.conf
COPY --chown=timetracker:timetracker entrypoint.sh / COPY --chown=timetracker:timetracker entrypoint.sh /
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
USER timetracker
ENV VERSION_NUMBER=1.6.1 ENV VERSION_NUMBER=1.6.1
EXPOSE 8000 EXPOSE 8000
CMD [ "/entrypoint.sh" ] ENTRYPOINT ["/entrypoint.sh"]
+4 -3
View File
@@ -41,9 +41,10 @@ caddy:
dev-prod: migrate collectstatic dev-prod: migrate collectstatic
@npx concurrently \ @npx concurrently \
--names "Django,Django-Q" \ --names "Caddy,Django,Django-Q" \
"PROD=1 uv run python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker" "caddy run --config Caddyfile.dev" \
"uv run manage.py qcluster" "PROD=1 uv run python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker" \
"PROD=1 uv run manage.py qcluster"
dumpgames: dumpgames:
uv run python manage.py dumpdata --format yaml games --output tracker_fixture.yaml uv run python manage.py dumpdata --format yaml games --output tracker_fixture.yaml
+11 -21
View File
@@ -1,30 +1,20 @@
--- ---
services: services:
backend: timetracker:
image: registry.kucharczyk.xyz/timetracker image: ${REGISTRY_URL:-registry.kucharczyk.xyz}/timetracker:1.7.0
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: timetracker
environment: environment:
- TZ=Europe/Prague - TZ=${TZ:-Europe/Prague}
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz" - CSRF_TRUSTED_ORIGINS=https://tracker.kucharczyk.xyz
user: "1000" - PUID=${PUID:-1000}
- PGID=${PGID:-100}
ports:
- "${TIMETRACKER_EXTERNAL_PORT:-8000}:8000"
volumes: volumes:
- "static-files:/var/www/django/static" - "${DOCKER_STORAGE_PATH:-/tmp}/timetracker/data:/home/timetracker/data"
- "$PWD/db.sqlite3:/home/timetracker/app/db.sqlite3" - "${DOCKER_STORAGE_PATH:-/tmp}/timetracker/backups:/home/timetracker/app/games/fixtures/backups"
restart: unless-stopped restart: unless-stopped
frontend:
image: caddy
volumes:
- "static-files:/usr/share/caddy:ro"
- "$PWD/Caddyfile:/etc/caddy/Caddyfile"
ports:
- "8000:8000"
depends_on:
- backend
volumes:
static-files:
+18 -18
View File
@@ -1,23 +1,23 @@
#!/bin/bash #!/bin/bash
# Apply database migrations
set -euo pipefail set -euo pipefail
echo "Apply database migrations"
python manage.py migrate
echo "Collect static files" PUID=${PUID:-1000}
PGID=${PGID:-100}
USERHOME=$(grep timetracker /etc/passwd | cut -d ":" -f6)
usermod -d "/root" timetracker
groupmod -o -g "$PGID" timetracker
usermod -o -u "$PUID" timetracker
usermod -d "${USERHOME}" timetracker
mkdir -p /home/timetracker/data /var/log/supervisor
chmod 755 /home/timetracker/app
chmod 755 /home/timetracker/app/.venv
chown "$PUID:$PGID" /home/timetracker/data
chown "$PUID:$PGID" /var/log/supervisor
python manage.py migrate
python manage.py collectstatic --clear --no-input python manage.py collectstatic --clear --no-input
_term() { exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisor.conf
echo "Caught SIGTERM signal!"
kill -SIGTERM "$gunicorn_pid"
kill -SIGTERM "$django_q_pid"
}
trap _term SIGTERM
echo "Starting Django-Q cluster"
python manage.py qcluster & django_q_pid=$!
echo "Starting app"
python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker --access-logfile - --error-logfile - & gunicorn_pid=$!
wait "$gunicorn_pid" "$django_q_pid"
+40
View File
@@ -0,0 +1,40 @@
[supervisord]
nodaemon=true
user=root
logfile=/dev/stdout
logfile_maxbytes=0
[program:caddy]
command=/usr/local/bin/caddy run --config /etc/caddy/Caddyfile
directory=/home/timetracker/app
autostart=true
autorestart=true
stderr_logfile=/dev/stderr
stdout_logfile=/dev/stdout
stderr_logfile_maxbytes=0
stdout_logfile_maxbytes=0
user=timetracker
[program:gunicorn]
command=python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker --access-logfile - --error-logfile -
directory=/home/timetracker/app
autostart=true
autorestart=true
stderr_logfile=/dev/stderr
stdout_logfile=/dev/stdout
stderr_logfile_maxbytes=0
stdout_logfile_maxbytes=0
process_name=%(program_name)s
user=timetracker
[program:qcluster]
command=python manage.py qcluster
directory=/home/timetracker/app
autostart=true
autorestart=true
stderr_logfile=/dev/stderr
stdout_logfile=/dev/stdout
stderr_logfile_maxbytes=0
stdout_logfile_maxbytes=0
process_name=%(program_name)s
user=timetracker
+3 -3
View File
@@ -4,7 +4,7 @@ Django settings for timetracker project.
Generated by 'django-admin startproject' using Django 4.1.4. Generated by 'django-admin startproject' using Django 4.1.4.
For more information on this file, see For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/ https://docs.djangoproject.com/en/4.1/topics/deployment/checklist/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/ https://docs.djangoproject.com/en/4.1/ref/settings/
@@ -110,7 +110,7 @@ WSGI_APPLICATION = "timetracker.wsgi.application"
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.sqlite3", "ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3", "NAME": Path(os.environ.get("DATA_DIR", "/home/timetracker/data")) / "db.sqlite3",
"OPTIONS": { "OPTIONS": {
"timeout": 20, "timeout": 20,
"init_command": "PRAGMA synchronous=FULL; PRAGMA journal_mode=WAL;", "init_command": "PRAGMA synchronous=FULL; PRAGMA journal_mode=WAL;",
@@ -154,7 +154,7 @@ USE_TZ = True
# https://docs.djangoproject.com/en/4.1/howto/static-files/ # https://docs.djangoproject.com/en/4.1/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "static" if DEBUG else "/var/www/django/static" STATIC_ROOT = BASE_DIR / "static"
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field