Components
Process model#
All backend roles run from a single container image. The image entrypoint
dispatches on its first argument (or the ABRQ_PROCESS_KIND environment
variable when no argument is given); any further arguments are passed
through to the underlying command.
| Kind | What it runs |
|---|---|
api |
Gunicorn supervising Uvicorn workers, bound to 0.0.0.0:8000 |
worker |
A Celery worker consuming every task queue by default |
beat |
Celery Beat with a persistent schedule file — run exactly one replica |
migrator |
Waits for the database, applies migrations under an advisory lock, exits |
Ports#
| Service | Port | Notes |
|---|---|---|
| API | 8000 | HTTP; also serves /health, /metrics, /docs, /openapi.json |
| Frontend | 80 (container) | Docker Compose publishes host port 8080 by default (ABRQ_FRONTEND_PORT) |
| PostgreSQL | 5432 | Internal only; not published in the production Compose files |
| Redis | 6379 | Internal only; not published in the production Compose files |
| Workers / Beat | — | No listening ports |
API#
The API is the FastAPI application served by Gunicorn with Uvicorn workers.
The worker-process count defaults to 2 and is tuned with
ABRQ_GUNICORN_WORKERS; the server uses a 30-second graceful shutdown
timeout and a 60-second request timeout. Every REST route lives under
/api/v1; two log streams are Server-Sent Events endpoints.
Frontend#
The frontend is an nginx container serving the built single-page
application. In Docker Compose it also proxies /api/, the SSE routes, and
/metrics to the backend container; on Kubernetes that split is the
ingress's job (the chart routes /api to the API service first, then / to
the frontend). The frontend serves its own lightweight /health location
for container checks.
Workers, Beat, and the migrator#
- Workers execute pipeline runs and maintenance tasks from Redis queues. They expose no ports and scale horizontally. Queue layout, concurrency, and shutdown behaviour are covered in Background processing.
- Beat is the single scheduler process. It only enqueues periodic work — workers do the executing — and must never be scaled beyond one replica. See Background processing for the singleton rationale.
- The migrator brings the framework database schema to the current head before the other components start. See Database migrations below.
Health probes#
Note.
GET /healthis the only health endpoint. There is no/ready,/healthz, or/livez— point liveness and readiness probes at the same path.
GET /health is a combined liveness and readiness check: it runs SELECT 1
against the framework database and pings Redis, returning 200 when both
dependencies are healthy and 503 otherwise, with a body of
{status, version, commit, deps}. It is exempt from authentication and
from license enforcement, so it keeps answering even when the license has
expired.
Default probe wiring in the Helm chart:
| Component | Probe | Default |
|---|---|---|
| API | Liveness | GET /health on 8000 — initial delay 30 s, period 15 s |
| API | Readiness | GET /health on 8000 — initial delay 5 s, period 5 s |
| Worker | Liveness + readiness | exec celery -A abrq_dip.workers.celery_app inspect ping — initial delay 30 s, period 30 s, timeout 10 s (probe timeout is the configured value plus 5) |
| Beat | — | No probes |
The backend image additionally declares a Docker HEALTHCHECK that curls
/health, which is what docker compose ps reports.
Images#
| Image | Base | Notes |
|---|---|---|
abrq-dip-backend |
python:3.12-slim, three-stage build |
Runs as non-root uid 1000 under tini; exposes 8000; one image for api, worker, beat, and migrator; release builds compile the core licensing/crypto modules and embed a signed integrity manifest |
abrq-dip-frontend |
node:20-alpine build stage, served by nginx:1.27-alpine |
Exposes 80 |
The Helm chart defaults to these repository names with the latest tag;
production installs should pin an explicit version tag.
Database migrations (advisory lock)#
The migrator waits for the framework database to accept connections
(retrying for roughly five minutes), takes PostgreSQL advisory lock
49155, runs alembic upgrade head, then releases the lock and exits.
Because the lock serialises concurrent runners, the migration step is safe to run from every replica:
- Kubernetes (default):
migrator.initContainer: trueruns the migrator as an init container on every API, worker, and beat pod. Exactly one init container applies the migrations while the rest block on the lock and then observe that head is already applied. - Kubernetes (external database only):
migrator.enabled: trueswitches to a pre-install/pre-upgrade Helm hook Job instead. Leave it off with the bundled in-cluster PostgreSQL — the hook runs before the database subchart exists on first install. - Docker Compose: the production Compose files run a dedicated one-shot
migratorservice before the long-running services start.