Skip to content

Commit e17c001

Browse files
authored
Merge pull request #309 from unicef/fix-compose-and-docs
fix compose and docs
2 parents 5538f49 + d291232 commit e17c001

8 files changed

Lines changed: 201 additions & 194 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.*
2+
.env
23
~*
34
__pycache__
45
!.dockerignore
@@ -15,6 +16,8 @@ __pycache__
1516
!tests/.coveragerc
1617
!docs/**/.pages
1718

19+
var/
20+
1821
junit.xml
1922
coverage.xml
2023

compose.yml

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,94 @@
1+
# Requires a .env file next to this compose.yml (gitignored):
2+
# cp env.sample .env # then fill in the values — a working development
3+
# configuration is in docs/src/development/getting-started.md.
4+
15
x-common: &common
26
build:
37
context: .
48
dockerfile: docker/Dockerfile
59
target: tests
610
platform: linux/amd64
7-
environment:
8-
- ADMIN_EMAIL=adm@hde.org
9-
- ADMIN_PASSWORD=123
10-
- ALLOWED_HOSTS=localhost,127.0.0.1
11-
- CACHE_URL=redis://redis:6379/1
12-
- CELERY_BROKER_URL=redis://redis:6379/9
13-
- CELERY_TASK_ALWAYS_EAGER=False
14-
- CSRF_COOKIE_SECURE=False
15-
- DATABASE_URL=postgres://hde:password@db:5432/hope_dedupe_engine
16-
- DEFAULT_ROOT=/var/hope_dedupe_engine/default
17-
- DJANGO_SETTINGS_MODULE=hope_dedup_engine.config.settings
18-
- FILE_STORAGE_DEFAULT=django.core.files.storage.FileSystemStorage
19-
- FILE_STORAGE_DNN=storages.backends.azure_storage.AzureStorage?azure_container=dnn&overwrite_files=True&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
20-
- FILE_STORAGE_HOPE=storages.backends.azure_storage.AzureStorage?azure_container=hope&overwrite_files=True&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
21-
- FILE_STORAGE_MEDIA=storages.backends.azure_storage.AzureStorage?azure_container=media&overwrite_files=True&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
22-
- FILE_STORAGE_STATIC=storages.backends.azure_storage.AzureStorage?azure_container=static&overwrite_files=True&custom_domain=localhost:10000/&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
23-
- MEDIA_ROOT=/var/hope_dedupe_engine/media
24-
- PYTHONPATH=/code/src/:/code/__pypackages__/3.12/lib/
25-
- SECRET_KEY=very-secret-key
26-
- SECURE_SSL_REDIRECT=False
27-
- SESSION_COOKIE_DOMAIN=
28-
- SESSION_COOKIE_SECURE=False
29-
- SOCIAL_AUTH_REDIRECT_IS_HTTPS=False
30-
- STATIC_ROOT=/var/hope_dedupe_engine/static
11+
env_file:
12+
- .env
13+
extra_hosts:
14+
- "host.docker.internal:host-gateway"
3115
volumes:
32-
- .:/code
16+
- .:/app
3317
- /var/run/docker.sock:/var/run/docker.sock
18+
- ./weights:/var/run/app/deepface/.deepface/weights
19+
- ./ofiq_omdels:/root/.ofiq/data
20+
- ${IMAGES_HOST_PATH:-./var/data}:/var/data
3421
restart: always
3522
depends_on:
3623
db:
3724
condition: service_healthy
3825
redis:
3926
condition: service_healthy
27+
azurite:
28+
condition: service_started
4029

4130

4231
services:
4332
backend:
4433
<<: *common
4534
ports:
4635
- 8000:8000
36+
- 5689:5680
4737
# command: ["tail", "-f", "/dev/null"]
4838
command: >
4939
/bin/sh -c "
5040
django-admin demo --skip-checks &&
5141
django-admin upgrade &&
5242
django-admin runserver 0.0.0.0:8000
5343
"
44+
# Debug variant (debugpy on host port 5689):
45+
# command: >
46+
# /bin/sh -c "
47+
# django-admin demo --skip-checks &&
48+
# django-admin upgrade --no-sync-models &&
49+
# python -Xfrozen_modules=off -u -m debugpy --listen 0.0.0.0:5680 manage.py runserver 0.0.0.0:8000
50+
# "
5451
healthcheck:
5552
test: ["CMD", "curl", "-f", "http://localhost:8000/healthcheck"]
5653
interval: 10s
5754
timeout: 5s
5855
retries: 5
5956

57+
celery-worker:
58+
<<: *common
59+
command: >
60+
sh -c '
61+
mkdir -p /var/hope_dedupe_engine/default &&
62+
export C_FORCE_ROOT=1 &&
63+
celery -A hope_dedup_engine.config.celery worker -E --loglevel=WARNING --pool=prefork --concurrency=${CELERY_CONCURRENCY:-2}
64+
'
65+
# Debug variant:
66+
# command: >
67+
# sh -c '
68+
# mkdir -p /var/hope_dedupe_engine/default &&
69+
# export C_FORCE_ROOT=1 &&
70+
# python -Xfrozen_modules=off -u -m debugpy --listen 0.0.0.0:5680 -m celery -A hope_dedup_engine.config.celery worker -E --loglevel=WARNING --concurrency=1
71+
# '
72+
73+
celery-beat:
74+
<<: *common
75+
entrypoint: ["sh", "-c", "exec docker-entrypoint.sh \"$0\" \"$@\""]
76+
command: >
77+
sh -c '
78+
mkdir -p /var/hope_dedupe_engine/default &&
79+
export C_FORCE_ROOT=1 &&
80+
celery -A hope_dedup_engine.config.celery beat --loglevel=INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
81+
'
82+
83+
celery-flower:
84+
<<: *common
85+
ports:
86+
- 5555:5555
87+
command: >
88+
sh -c "
89+
exec celery -A hope_dedup_engine.config.celery flower --address=0.0.0.0
90+
"
91+
6092
db:
6193
image: postgres:16
6294
environment:
@@ -66,7 +98,7 @@ services:
6698
volumes:
6799
- postgres_data:/var/lib/postgresql/data/
68100
ports:
69-
- 5432:5432
101+
- ${DB_PORT:-5432}:5432
70102
restart: always
71103
healthcheck:
72104
test: ["CMD", "pg_isready", "-U", "hde", "-d", "hope_dedupe_engine"]
@@ -79,7 +111,7 @@ services:
79111
redis:
80112
image: redis:7.2
81113
ports:
82-
- 6379:6379
114+
- ${REDIS_PORT:-6379}:6379
83115
restart: always
84116
healthcheck:
85117
test: ["CMD", "redis-cli", "ping"]
@@ -94,35 +126,16 @@ services:
94126
command: "azurite -l /workspace -d /workspace/debug.log --blobPort 10000 --blobHost 0.0.0.0 --loose --silent --skipApiVersionCheck"
95127
restart: always
96128
ports:
97-
- "10000:10000" # Blob service
129+
- "10000:10000"
98130
volumes:
99131
- azurite_data:/workspace
100132

101-
celery-worker:
102-
<<: *common
103-
# entrypoint: ["sh", "-c", "exec docker-entrypoint.sh \"$0\" \"$@\""]
104-
# command: worker
105-
command: >
106-
sh -c '
107-
mkdir -p /var/hope_dedupe_engine/default &&
108-
chown -R user:app /var/hope_dedupe_engine &&
109-
gosu user:app django-admin syncdnn &&
110-
gosu user:app celery -A hope_dedup_engine.config.celery worker -E --loglevel=WARNING --concurrency=4
111-
'
112-
113-
celery-beat:
114-
<<: *common
115-
entrypoint: ["sh", "-c", "exec docker-entrypoint.sh \"$0\" \"$@\""]
116-
command: beat
117-
118-
celery-flower:
133+
docs:
119134
<<: *common
135+
profiles: ["docs"]
120136
ports:
121-
- 5555:5555
122-
command: >
123-
sh -c "
124-
exec celery -A hope_dedup_engine.config.celery flower --address=0.0.0.0
125-
"
137+
- 8012:8012
138+
command: ["uv", "run", "mkdocs", "serve", "-a", "0.0.0.0:8012", "--no-strict"]
126139

127140

128141
volumes:

docs/src/admin/environment.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ $ docker run -it -t unicef/hope-dedup-engine:<tag> django-admin env # c
77
$ docker run -it -t unicef/hope-dedup-engine:<tag> django-admin env --check # verify required ones
88
```
99

10-
An auto-generated reference of every known variable is on the [Settings](../settings.md) page; below are the ones that matter operationally, grouped by purpose.
10+
An auto-generated reference of every known variable is on the [Settings](settings.md) page; below are the ones that matter operationally, grouped by purpose.
11+
12+
!!! note "Local development"
13+
When running via the repository's `compose.yml`, all of these variables are read from a gitignored `.env` file (template: `env.sample`) — see [Getting Started](../development/getting-started.md#configuration) for a working development configuration.
1114

1215
## Required
1316

docs/src/development/demo.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
# Demo App and API Scripts
22

3-
A self-contained demo environment with sample images and helper scripts lets you exercise the whole system — including the REST API — without configuring anything.
3+
A self-contained demo environment with sample images and helper scripts lets you exercise the whole system — including the REST API.
44

55
## Running the demo server
66

7+
Use the root `compose.yml` — on startup the backend seeds the demo data via the `demo` management command. It needs a `.env` file first (see [Getting Started](getting-started.md#configuration) for a working development configuration):
8+
79
```console
8-
$ docker compose -f tests/extras/demoapp/compose.yml up --build
10+
$ cp env.sample .env # fill in the values from Getting Started
11+
$ docker compose up --build
912
```
1013

11-
(The root `compose.yml` also seeds the same demo data via the `demo` management command.)
14+
(To demo a published release instead of building locally, follow the [deployment guide](../admin/deployment.md) with the desired image tag — but note the API scripts below track the current code, so they may not work against older releases.)
1215

1316
Admin panel access:
1417

1518
- URL: [http://localhost:8000/admin](http://localhost:8000/admin)
16-
- Username: `adm@hde.org`
17-
- Password: `123`
19+
- Username / password: the `ADMIN_EMAIL` / `ADMIN_PASSWORD` values from your `.env`
1820

1921
The demo setup creates an external system and an API token you can find under `Home › Api › Tokens`.
2022

2123
## API interaction scripts
2224

23-
Shell scripts in `tests/extras/demoapp/scripts/` drive the API end to end. They require [httpie](https://httpie.io/) and [jq](https://jqlang.github.io/jq/).
25+
Shell scripts in `tests/extras/demoapp/scripts/` drive the API end to end. They require [httpie](https://httpie.io/), [jq](https://jqlang.github.io/jq/), and the `mimetype` command (Debian/Ubuntu package `libfile-mimeinfo-perl`).
2426

2527
### Configuration
2628

docs/src/development/getting-started.md

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,76 @@ Two ways to develop locally: **Docker Compose** (recommended — everything incl
1111

1212
## Option A: Docker Compose (recommended)
1313

14-
The repository's `compose.yml` starts the full stack — backend, Celery worker, Celery beat, Flower, PostgreSQL, and Redis:
14+
The repository's `compose.yml` starts the full stack — backend, Celery worker, Celery beat, Flower, PostgreSQL, Redis, and Azurite (local Azure blob emulator). It reads all environment configuration from a `.env` file (gitignored) next to `compose.yml`:
1515

1616
```console
17+
$ cp env.sample .env # then fill in the values (see below)
1718
$ docker compose up --build
1819
```
1920

20-
On startup the backend seeds demo data, applies migrations, and serves at [http://localhost:8000](http://localhost:8000). The admin panel is at `/admin/` (credentials from the compose environment: `adm@hde.org` / `123`), the API docs at `/api/rest/swagger/`.
21+
On startup the backend seeds demo data, applies migrations, and serves at [http://localhost:8000](http://localhost:8000). The admin panel is at `/admin/` (credentials `ADMIN_EMAIL` / `ADMIN_PASSWORD` from your `.env`), the API docs at `/api/rest/swagger/`.
22+
23+
### Configuration
24+
25+
`env.sample` lists every required key; `.env` is never committed, so real tokens and connection strings are safe there. This configuration works with the compose stack as-is (Azurite for blob storages, local filesystem for uploaded images):
26+
27+
```bash
28+
# ── Django core ──────────────────────────────────────────────
29+
ADMIN_EMAIL=adm@hde.org
30+
ADMIN_PASSWORD=123
31+
ALLOWED_HOSTS=localhost,127.0.0.1
32+
SECRET_KEY=very-secret-key
33+
DJANGO_SETTINGS_MODULE=hope_dedup_engine.config.settings
34+
PYTHONPATH=/app/src
35+
36+
# ── Database / cache / broker ────────────────────────────────
37+
DATABASE_URL=postgres://hde:password@db:5432/hope_dedupe_engine
38+
CACHE_URL=redis://redis:6379/1
39+
CELERY_BROKER_URL=redis://redis:6379/9
40+
CELERY_TASK_ALWAYS_EAGER=False
41+
42+
# ── Security (local dev — all disabled) ──────────────────────
43+
CSRF_COOKIE_SECURE=False
44+
SECURE_SSL_REDIRECT=False
45+
SESSION_COOKIE_DOMAIN=
46+
SESSION_COOKIE_SECURE=False
47+
SOCIAL_AUTH_REDIRECT_IS_HTTPS=False
48+
49+
# ── File storage backends ────────────────────────────────────
50+
FILE_STORAGE_DEFAULT=django.core.files.storage.FileSystemStorage
51+
FILE_STORAGE_IMAGES=django.core.files.storage.FileSystemStorage
52+
FILE_STORAGE_DNN=storages.backends.azure_storage.AzureStorage?azure_container=dnn&overwrite_files=True&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
53+
FILE_STORAGE_MEDIA=storages.backends.azure_storage.AzureStorage?azure_container=media&overwrite_files=True&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
54+
FILE_STORAGE_STATIC=storages.backends.azure_storage.AzureStorage?azure_container=static&overwrite_files=True&custom_domain=localhost:10000/&connection_string=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
55+
56+
# ── Filesystem roots ─────────────────────────────────────────
57+
DEFAULT_ROOT=/var/hope_dedupe_engine/default
58+
IMAGES_ROOT=/var/data
59+
MEDIA_ROOT=/var/hope_dedupe_engine/media
60+
STATIC_ROOT=/var/hope_dedupe_engine/static
61+
62+
# ── ML models ────────────────────────────────────────────────
63+
DEEPFACE_HOME=/var/run/app/deepface
64+
OFIQ_DATA_DIR=/root/.ofiq/data
65+
66+
# ── Performance tuning ───────────────────────────────────────
67+
OMP_NUM_THREADS=2
68+
TF_NUM_INTRA_OP_THREADS=2
69+
TF_NUM_INTER_OP_THREADS=2
70+
71+
# ── Integration ──────────────────────────────────────────────
72+
HOPE_API_TOKEN=
73+
```
74+
75+
(The `AccountKey` above is [Azurite's public well-known development key](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite#well-known-storage-account-and-key), not a secret.)
2176

22-
Things to know:
77+
Compose-level knobs (volume paths and port bindings) can also be set in `.env`: `IMAGES_HOST_PATH` (host directory for uploaded images, default `./var/data`), `DB_PORT`, `REDIS_PORT`, `CELERY_CONCURRENCY`.
78+
79+
### Things to know
2380

2481
- The repo is bind-mounted into the containers, so code changes are picked up by `runserver` automatically. The Celery worker does **not** auto-reload — restart it after changing task code.
2582
- Model weight files are mounted from `./weights` (DeepFace) and `./ofiq_omdels` (OFIQ). See the comments in `compose.yml` for debugging variants of the service commands (debugpy).
83+
- Uploaded images are stored under `./var/data` on the host — the same directory is mounted into the backend and the workers, mirroring the shared storage mount used in production.
2684
- Run any management command inside the stack with `docker compose run --rm backend django-admin <command>`.
2785

2886
## Option B: Native virtualenv
@@ -73,6 +131,8 @@ $ uv sync --group docs
73131
$ uv run mkdocs serve # http://127.0.0.1:8001
74132
```
75133

134+
Alternatively, serve them from the compose stack (port 8012): `docker compose --profile docs up docs`.
135+
76136
## Typical feature workflow
77137

78138
1. Create a branch from `develop`.

env.sample

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Template for the .env file used by compose.yml (gitignored).
2+
# Copy it (`cp env.sample .env`) and fill in the values — a complete working
3+
# development configuration is available in the documentation:
4+
# docs/src/development/getting-started.md ("Configuration").
5+
# See docs/src/admin/environment.md for the description of every variable.
6+
7+
# ── Django core ──────────────────────────────────────────────
8+
ADMIN_EMAIL=
9+
ADMIN_PASSWORD=
10+
ALLOWED_HOSTS=
11+
SECRET_KEY=
12+
DJANGO_SETTINGS_MODULE=
13+
PYTHONPATH=
14+
15+
# ── Database / cache / broker ────────────────────────────────
16+
DATABASE_URL=
17+
CACHE_URL=
18+
CELERY_BROKER_URL=
19+
CELERY_TASK_ALWAYS_EAGER=
20+
21+
# ── Security ─────────────────────────────────────────────────
22+
CSRF_COOKIE_SECURE=
23+
SECURE_SSL_REDIRECT=
24+
SESSION_COOKIE_DOMAIN=
25+
SESSION_COOKIE_SECURE=
26+
SOCIAL_AUTH_REDIRECT_IS_HTTPS=
27+
28+
# ── File storage backends ────────────────────────────────────
29+
FILE_STORAGE_DEFAULT=
30+
FILE_STORAGE_IMAGES=
31+
FILE_STORAGE_DNN=
32+
FILE_STORAGE_MEDIA=
33+
FILE_STORAGE_STATIC=
34+
35+
# ── Filesystem roots ─────────────────────────────────────────
36+
DEFAULT_ROOT=
37+
IMAGES_ROOT=
38+
MEDIA_ROOT=
39+
STATIC_ROOT=
40+
41+
# ── ML models ────────────────────────────────────────────────
42+
DEEPFACE_HOME=
43+
OFIQ_DATA_DIR=
44+
45+
# ── Performance tuning ───────────────────────────────────────
46+
OMP_NUM_THREADS=
47+
TF_NUM_INTRA_OP_THREADS=
48+
TF_NUM_INTER_OP_THREADS=
49+
50+
# ── Integration ──────────────────────────────────────────────
51+
HOPE_API_TOKEN=
52+
53+
# ── Compose-level (volume paths, port bindings) ──────────────
54+
# Only used for ${VAR} interpolation in compose.yml, not passed to Django.
55+
# IMAGES_HOST_PATH=
56+
# DB_PORT=
57+
# REDIS_PORT=
58+
# CELERY_CONCURRENCY=

0 commit comments

Comments
 (0)