-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathDockerfile.mono
More file actions
148 lines (123 loc) · 5.85 KB
/
Dockerfile.mono
File metadata and controls
148 lines (123 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
########## Stage 1: Build frontend ##########
FROM node:20.19.0-alpine3.21 AS frontend-builder
WORKDIR /app/frontend
# Install dependencies (cached layer)
COPY apps/frontend/package.json apps/frontend/package-lock.json ./
RUN npm ci
# Copy source and build — syncversion reads ../../VERSION to stamp the bundle
COPY VERSION /VERSION
COPY apps/frontend/ ./
RUN npm run build
########## Stage 2: Build backend & Python deps ##########
FROM python:3.12.9-slim-bookworm AS backend-builder
WORKDIR /app/backend
# Mono image tracks security updates from Debian bookworm; pinning apt package
# versions here would block unattended base-image security rollups.
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
g++ \
libjpeg-dev \
zlib1g-dev \
libffi-dev \
libssl-dev \
cargo \
&& rm -rf /var/lib/apt/lists/*
# Upgrade pip before installing deps — fixes CVE-2025-8869 (≥25.3) and CVE-2026-1703 (≥26.0)
RUN pip install --no-cache-dir --upgrade "pip>=26.0"
# Dependency layer from pinned requirements.txt + PostgreSQL drivers (mono image uses Postgres)
COPY apps/backend/requirements.txt apps/backend/requirements-pg.txt ./
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --timeout 120 --retries 5 -r requirements.txt -r requirements-pg.txt \
&& find /usr/local/lib/python3.12 -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true \
&& find /usr/local/lib/python3.12 -name '*.so' -exec strip --strip-unneeded {} + 2>/dev/null || true
# App layer
COPY VERSION /VERSION
COPY apps/backend/pyproject.toml ./
COPY apps/backend/src ./src
RUN pip install --no-cache-dir --no-deps .
########## Stage 3: Runtime image (all services + supervisor) ##########
# checkov:skip=CKV_DOCKER_3: Mono runtime intentionally starts as root to run entrypoint bootstrap tasks (volume ownership, embedded service wiring) before supervised processes run as breaker/root where required.
FROM python:3.12.9-slim-bookworm
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONOPTIMIZE=2 \
PIP_NO_CACHE_DIR=1
WORKDIR /app/backend
# System services and tools:
# - nginx: serves SPA and proxies /api + WebSockets
# - supervisor: process manager for Postgres, NATS, backend, workers, nginx
# - postgresql: embedded DB (Debian Bookworm default is PostgreSQL 15)
# - nats-server: JetStream message bus
# - ca-certificates, curl: TLS + simple debugging
# Mono runtime tracks security updates from Debian bookworm; version pinning on
# apt packages is intentionally avoided to keep CVE fixes current.
# hadolint ignore=DL3008,DL3005
RUN apt-get update && apt-get upgrade -y --no-install-recommends && apt-get install -y --no-install-recommends \
nginx \
supervisor \
postgresql \
postgresql-client \
nats-server \
redis-server \
pgbouncer \
ca-certificates \
curl \
bash \
jq \
netcat-traditional \
openssl \
tini \
nmap \
snmp \
libmagic1 \
ipmitool \
&& rm -f /etc/ssl/private/ssl-cert-snakeoil.key /etc/ssl/certs/ssl-cert-snakeoil.pem \
&& if [ -d /etc/ssl/private ] && [ -n "$(find /etc/ssl/private -maxdepth 1 -type f -print -quit)" ]; then \
echo "Unexpected private key material found in /etc/ssl/private"; \
find /etc/ssl/private -maxdepth 1 -type f; \
exit 1; \
fi \
&& rm -rf /var/lib/apt/lists/* \
&& ln -sf /usr/local/bin/python3 /usr/bin/python
# Copy Python deps and app from builder
COPY --from=backend-builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=backend-builder /usr/local/bin /usr/local/bin
COPY --from=backend-builder /app/backend/src ./src
RUN rm -rf /usr/share/doc /usr/share/man /usr/share/locale /usr/share/info \
&& find /usr/local/lib/python3.12 -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
COPY apps/backend/alembic.ini ./alembic.ini
# Migrations are baked into the image; add new migration files then rebuild (e.g. build --no-cache) so the container can run them.
COPY apps/backend/migrations ./migrations
# Copy built frontend assets from frontend builder
COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
# Mono-specific configs and init scripts
COPY docker/supervisord.mono.conf /etc/supervisor/conf.d/supervisord.conf
COPY docker/nginx.mono.conf /etc/nginx/nginx.conf
COPY docker/pgbouncer.ini /etc/pgbouncer/pgbouncer.ini
COPY docker/10-init-postgres.sh docker/20-migrate.sh docker/30-oobe.sh docker/entrypoint-mono.sh docker/run-postgres-if-embedded.sh /docker/
RUN chmod +x /docker/*.sh
# Runtime directories: /data/run/postgresql for socket/lock (breaker-owned); symlink so Postgres finds it
RUN mkdir -p /app /data /data/uploads /data/tls /data/certs /data/redis /data/run/postgresql /var/log/circuitbreaker \
&& chmod 1777 /data/run/postgresql \
&& rm -rf /var/run/postgresql \
&& ln -s /data/run/postgresql /var/run/postgresql \
&& groupadd -g 1000 breaker \
&& useradd -u 1000 -g 1000 --no-create-home --shell /sbin/nologin breaker \
&& chown -R breaker:breaker /app /data /var/log/circuitbreaker
# Environment defaults for mono image (CB_DB_URL set at runtime in entrypoint from CB_DB_PASSWORD)
ENV PYTHONPATH=/app/backend/src \
CB_ALEMBIC_INI=/app/backend/alembic.ini \
ALEMBIC_CONFIG=/app/backend/alembic.ini \
STATIC_DIR=/app/frontend/dist \
CB_DATA_DIR=/data \
UPLOADS_DIR=/data/uploads \
NATS_URL=nats://127.0.0.1:4222 \
CB_REDIS_URL=redis://127.0.0.1:6379/0
VOLUME ["/data"]
EXPOSE 8080 8443
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://127.0.0.1:8080/api/v1/health || exit 1
# tini as PID1 (reaps zombies, forwards signals). Entrypoint starts as root to fix
# volume ownership, then supervisord drops to breaker for application processes.
ENTRYPOINT ["/usr/bin/tini", "--", "/bin/bash", "/docker/entrypoint-mono.sh"]