forked from ungleich-public/dynamicweb
Add docker compose stack for test deployments
Runs postgres, redis, the web app and a celery worker. Dev defaults live in .env.docker with an optional .env override. The entrypoint generates and persists the Django secret key and waits for postgres; the web service migrates and creates a Site row for every configured domain. SQL dumps placed in db-init/ are restored on first start and are git-ignored.
This commit is contained in:
parent
7fdd70022b
commit
619a20394e
7 changed files with 154 additions and 8 deletions
|
|
@ -1,2 +1,5 @@
|
|||
.git
|
||||
.env
|
||||
*.pyc
|
||||
__pycache__
|
||||
dynamicweb/settings/dynamic.py
|
||||
|
|
|
|||
41
.env.docker
Normal file
41
.env.docker
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Dev defaults for docker compose. Override any value in a local ".env" file.
|
||||
# DEBUG=True makes settings load local.py (ALLOWED_HOSTS=*, console email).
|
||||
DEBUG=True
|
||||
ENABLE_LOGGING=False
|
||||
LOG_LEVEL=DEBUG
|
||||
|
||||
# Leave empty: the entrypoint generates and persists one in the "appdata" volume
|
||||
DJANGO_SECRET_KEY=
|
||||
|
||||
POSTGRES_DB=app
|
||||
POSTGRES_USER=app
|
||||
POSTGRES_PASSWORD=app
|
||||
POSTGRES_HOST=db
|
||||
|
||||
CELERY_BROKER_URL=redis://redis:6379/0
|
||||
CELERY_RESULT_BACKEND=redis://redis:6379/0
|
||||
|
||||
# Host -> urlconf. Add the VPS IP / domain here if you access it that way.
|
||||
UNGLEICH_SITE_CONFIGS={"ungleich.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"blog.ungleich.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"comic.ungleich.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"digitalglarus.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"datacenterlight.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"rails-hosting.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"django-hosting.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"node-hosting.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"devuanhosting.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"devuanhosting.com":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"ipv6onlyhosting.com":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"digitalezukunft.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"hack4glarus.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"xn--nglarus-n2a.ch":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"localhost:8000":{"MULTISITE_CMS_URL":"dynamicweb.urls"},"127.0.0.1:8000":{"MULTISITE_CMS_URL":"dynamicweb.urls"}}
|
||||
UNGLEICH_BLOG_SITE_ID=1
|
||||
MULTISITE_CMS_FALLBACK=
|
||||
|
||||
EMAIL_HOST=localhost
|
||||
DCL_ERROR_EMAILS_TO=dev@example.com
|
||||
ADMIN_EMAIL=dev@example.com
|
||||
DCL_SUPPORT_FROM_ADDRESS=dev@example.com
|
||||
WEBHOOK_SECRET=dev
|
||||
RECAPTCHA_PUBLIC_KEY=dev
|
||||
RECAPTCHA_PRIVATE_KEY=dev
|
||||
STRIPE_API_PRIVATE_KEY=
|
||||
STRIPE_API_PUBLIC_KEY=
|
||||
STRIPE_API_PRIVATE_KEY_TEST=
|
||||
|
||||
# OpenNebula / LDAP are not available in the test env
|
||||
OPENNEBULA_USERNAME=oneadmin
|
||||
OPENNEBULA_PASSWORD=opennebula
|
||||
OPENNEBULA_PROTOCOL=http
|
||||
# "test_domain" skips the import-time VMTemplate query (tables do not exist before migrate)
|
||||
OPENNEBULA_DOMAIN=test_domain
|
||||
OPENNEBULA_PORT=2633
|
||||
OPENNEBULA_ENDPOINT=/RPC2
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -47,3 +47,10 @@ secret-key
|
|||
2024-*
|
||||
2025-*
|
||||
2023-*
|
||||
|
||||
.env
|
||||
dynamicweb/settings/dynamic.py
|
||||
|
||||
# database dumps are kept out of git (personal data)
|
||||
db-init/*.sql
|
||||
db-init/*.sql.gz
|
||||
|
|
|
|||
1
db-init/README.md
Normal file
1
db-init/README.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Put a Postgres dump here (*.sql or *.sql.gz); it is restored on first start.
|
||||
42
docker-compose.yml
Normal file
42
docker-compose.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
x-app: &app
|
||||
build: .
|
||||
env_file:
|
||||
- .env.docker
|
||||
- path: .env # optional local overrides
|
||||
required: false
|
||||
volumes:
|
||||
- appdata:/data # generated secret key
|
||||
- media:/usr/src/app/media
|
||||
- logs:/usr/src/app/logs
|
||||
depends_on: [db, redis]
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:12-alpine
|
||||
environment:
|
||||
POSTGRES_DB: app
|
||||
POSTGRES_USER: app
|
||||
POSTGRES_PASSWORD: app
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
# any *.sql / *.sql.gz here is restored on the first start (empty pgdata)
|
||||
- ./db-init:/docker-entrypoint-initdb.d:ro
|
||||
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
|
||||
web:
|
||||
<<: *app
|
||||
command: sh start-web.sh
|
||||
ports:
|
||||
- "8000:8000"
|
||||
|
||||
celery:
|
||||
<<: *app
|
||||
command: celery -A dynamicweb worker -l info
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
appdata:
|
||||
media:
|
||||
logs:
|
||||
|
|
@ -1,19 +1,50 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -uex
|
||||
set -ue
|
||||
|
||||
cd /usr/src/app/
|
||||
cat > dynamicweb/settings/dynamic.py <<EOF
|
||||
|
||||
# Persist a generated Django secret key in a shared volume so that every
|
||||
# container (web, celery) uses the same one across restarts.
|
||||
if [ -z "${DJANGO_SECRET_KEY:-}" ]; then
|
||||
KEY_FILE="${SECRET_KEY_FILE:-/data/secret-key}"
|
||||
mkdir -p "$(dirname "$KEY_FILE")"
|
||||
if [ ! -s "$KEY_FILE" ]; then
|
||||
TMP_KEY="$KEY_FILE.$$"
|
||||
python -c 'import random,string;print("".join(random.SystemRandom().choice(string.ascii_letters+string.digits) for _ in range(64)))' > "$TMP_KEY"
|
||||
# hard link is atomic: only one container wins the race
|
||||
ln "$TMP_KEY" "$KEY_FILE" 2>/dev/null || true
|
||||
rm -f "$TMP_KEY"
|
||||
fi
|
||||
DJANGO_SECRET_KEY="$(cat "$KEY_FILE")"
|
||||
export DJANGO_SECRET_KEY
|
||||
fi
|
||||
|
||||
cat > dynamicweb/settings/dynamic.py <<PYEOF
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': '${POSTGRES_DB}',
|
||||
'USER': '${POSTGRES_USER}',
|
||||
'PASSWORD': '${POSTGRES_PASSWORD}',
|
||||
'HOST': '${POSTGRES_HOST}',
|
||||
'PORT': '5432',
|
||||
'NAME': '${POSTGRES_DB:-app}',
|
||||
'USER': '${POSTGRES_USER:-app}',
|
||||
'PASSWORD': '${POSTGRES_PASSWORD:-app}',
|
||||
'HOST': '${POSTGRES_HOST:-db}',
|
||||
'PORT': '${POSTGRES_PORT:-5432}',
|
||||
}
|
||||
}
|
||||
EOF
|
||||
PYEOF
|
||||
|
||||
# Wait for postgres to accept connections
|
||||
python - <<'PYEOF'
|
||||
import os, socket, sys, time
|
||||
host = os.environ.get('POSTGRES_HOST', 'db')
|
||||
port = int(os.environ.get('POSTGRES_PORT', 5432))
|
||||
for _ in range(60):
|
||||
try:
|
||||
socket.create_connection((host, port), 2).close()
|
||||
sys.exit(0)
|
||||
except OSError:
|
||||
time.sleep(1)
|
||||
sys.exit("postgres at %s:%s not reachable" % (host, port))
|
||||
PYEOF
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
21
start-web.sh
Normal file
21
start-web.sh
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
# Container start-up for the web service: migrate, make sure every configured
|
||||
# site exists in the DB, then serve.
|
||||
set -ue
|
||||
cd /usr/src/app
|
||||
export DJANGO_SETTINGS_MODULE=dynamicweb.settings
|
||||
|
||||
python - <<'PYEOF'
|
||||
import json, os
|
||||
import django
|
||||
django.setup()
|
||||
from django.core.management import call_command
|
||||
# system checks import urls.py, which queries the DB, so skip them here
|
||||
call_command('migrate', interactive=False, skip_checks=True)
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
for domain in json.loads(os.environ.get('UNGLEICH_SITE_CONFIGS') or '{}'):
|
||||
Site.objects.get_or_create(domain=domain, defaults={'name': domain})
|
||||
PYEOF
|
||||
|
||||
exec python manage.py runserver 0.0.0.0:8000
|
||||
Loading…
Add table
Add a link
Reference in a new issue