-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.prod.yml
More file actions
304 lines (292 loc) · 11.4 KB
/
docker-compose.prod.yml
File metadata and controls
304 lines (292 loc) · 11.4 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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
version: '3.8'
x-logging: &default-logging
driver: 'json-file'
options:
max-size: '10m'
max-file: '3'
services:
db:
# Digest pinned from `docker pull postgres:15-alpine` on 2026-04-21 (resolved to postgres 15.17).
# To bump: pull the new tag, copy the Digest line, update the exact version in the tag for readability.
image: postgres:15.17-alpine@sha256:1c52f5ad23db5d7648a63634444af76de48e63b860fccbe3e3a5458b2812eaed
logging: *default-logging
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=${POSTGRES_DB:-makerspace_inventory}
- POSTGRES_USER=${POSTGRES_USER:-makerspace}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-makerspace} -d ${POSTGRES_DB:-makerspace_inventory}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
redis:
# Digest pinned from `docker pull redis:7-alpine` on 2026-04-21 (resolved to Redis 7.4.8).
# To bump: pull the new tag, copy the Digest line, update the exact version in the tag for readability.
image: redis:7.4.8-alpine@sha256:7aec734b2bb298a1d769fd8729f13b8514a41bf90fcdd1f38ec52267fbaa8ee6
logging: *default-logging
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
emqx:
# Digest pinned from `docker pull emqx/emqx-enterprise:6.2.0` on 2026-04-28.
# To bump: pull the new tag, copy the Digest line, update the exact version in the tag for readability.
image: emqx/emqx-enterprise:6.2.0@sha256:da01e1b51c2b60fa735a32922c697bcaa5bcf45c4c76b6ca8214111da752ff25
container_name: oms-emqx
logging: *default-logging
ports:
- "1883:1883" # MQTT
- "8083:8083" # MQTT-over-WebSocket
- "8084:8084" # MQTT-over-WSS
- "8883:8883" # MQTTS
- "18083:18083" # Dashboard + REST API
volumes:
- emqx_data:/opt/emqx/data
- emqx_log:/opt/emqx/log
# bootstrap_users is rendered by deploy.sh from EMQX_DASHBOARD_PASSWORD;
# EMQX re-applies it on every boot, so the admin login stays in sync with
# the .env value (oms-f9z).
- ./scripts/emqx/bootstrap-admins.txt:/opt/emqx/etc/bootstrap_users:ro
environment:
- EMQX_NAME=oms-emqx
- EMQX_HOST=127.0.0.1
- EMQX_DASHBOARD__DEFAULT_PASSWORD=${EMQX_DASHBOARD_PASSWORD}
- EMQX_DASHBOARD__BOOTSTRAP_USERS_FILE=/opt/emqx/etc/bootstrap_users
healthcheck:
test: ["CMD", "/opt/emqx/bin/emqx", "ctl", "status"]
interval: 30s
timeout: 5s
retries: 5
start_period: 60s
restart: unless-stopped
networks:
- app-network
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
logging: *default-logging
# --max-requests + --max-requests-jitter recycles each worker after ~1000
# requests so any slow leak (paho buffer growth, sentry buffer, multipart
# spool retention) is bounded by the recycle window instead of unbounded
# process lifetime. --graceful-timeout 30 lets in-flight requests finish
# before SIGKILL on recycle. See docs/INCIDENTS/oom-backend.md (oms-9t2).
command: >-
gunicorn config.wsgi:application
--bind 0.0.0.0:8000
--workers 4
--threads 8
--worker-class gthread
--timeout 120
--max-requests 1000
--max-requests-jitter 100
--graceful-timeout 30
deploy:
resources:
limits:
memory: 4G
reservations:
memory: 1G
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
expose:
- 8000
env_file:
- .env
environment:
- DEBUG=0
- DATABASE_URL=postgresql://${POSTGRES_USER:-makerspace}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-makerspace_inventory}
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
# Always append `backend,localhost` so EMQX (which fetches JWKS via
# http://backend:8000/api/forgekey/jwks/ on the docker network) and
# in-container healthchecks always pass Django ALLOWED_HOSTS even if an
# operator forgets to include them in .env. Duplicates are harmless.
- ALLOWED_HOSTS=${ALLOWED_HOSTS:-oms.example.com,localhost},backend,localhost
- CSRF_TRUSTED_ORIGINS=https://${DOMAIN:-oms.example.com}
- CORS_ALLOWED_ORIGINS=https://${DOMAIN:-oms.example.com}
- FRONTEND_URL=https://${DOMAIN:-oms.example.com}
- HIGHLIGHT_PROJECT_ID=${HIGHLIGHT_PROJECT_ID:-}
- HIGHLIGHT_OTLP_ENDPOINT=${HIGHLIGHT_OTLP_ENDPOINT:-}
- HIGHLIGHT_ENVIRONMENT=${HIGHLIGHT_ENVIRONMENT:-production}
- HIGHLIGHT_SERVICE_VERSION=${GIT_HASH:-}
- MQTT_BROKER_HOST=emqx
- MQTT_BROKER_PORT=1883
- MQTT_BROKER_USERNAME=${MQTT_BROKER_USERNAME:-}
- MQTT_BROKER_PASSWORD=${MQTT_BROKER_PASSWORD:-}
- EMQX_API_URL=http://emqx:18083/api/v5
- EMQX_API_KEY=${EMQX_API_KEY:-}
- EMQX_API_SECRET=${EMQX_API_SECRET:-}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
emqx:
# service_started (not service_healthy): backend MQTT connect is lazy
# (forgekey.tasks.get_mqtt_client) and the EMQX REST API is only used
# during provisioning. Blocking backend boot on EMQX reaching healthy
# extends the dependency chain that contributed to oms-801, where the
# 60s EMQX start_period plus a slow first-healthcheck could push
# backend startup past the 120s gunicorn worker timeout.
condition: service_started
healthcheck:
# Liveness, not readiness (oms-801): /api/health/livez/ is dep-free and
# returns 200 if the gunicorn worker is alive. The previous probe hit
# /api/dashboard/health/ which executes a DB query, so a transient DB
# hiccup turns the healthcheck red and triggers a container restart even
# when the process is fine. Readiness is exposed at /api/health/readyz/
# for callers that do need the dependency check.
test:
- CMD
- python
- -c
- "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health/livez/', timeout=5)"
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
restart: unless-stopped
networks:
- app-network
celery:
build:
context: ./backend
dockerfile: Dockerfile.prod
logging: *default-logging
# --max-tasks-per-child mirrors the gunicorn --max-requests recycle so
# per-task leaks in MQTT publish, image processing, etc. are bounded.
command: celery -A config worker -l info --max-tasks-per-child=200
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 256M
volumes:
- media_volume:/app/media
env_file:
- .env
environment:
- DEBUG=0
- DATABASE_URL=postgresql://${POSTGRES_USER:-makerspace}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-makerspace_inventory}
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
- SKIP_DB_MIGRATIONS=1
- HIGHLIGHT_PROJECT_ID=${HIGHLIGHT_PROJECT_ID:-}
- HIGHLIGHT_OTLP_ENDPOINT=${HIGHLIGHT_OTLP_ENDPOINT:-}
- HIGHLIGHT_ENVIRONMENT=${HIGHLIGHT_ENVIRONMENT:-production}
- HIGHLIGHT_SERVICE_VERSION=${GIT_HASH:-}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- app-network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
args:
- REACT_APP_API_URL=/api
- REACT_APP_HIGHLIGHT_PROJECT_ID=${REACT_APP_HIGHLIGHT_PROJECT_ID:-}
- REACT_APP_HIGHLIGHT_OTLP_ENDPOINT=${REACT_APP_HIGHLIGHT_OTLP_ENDPOINT:-}
- REACT_APP_HIGHLIGHT_ENVIRONMENT=${REACT_APP_HIGHLIGHT_ENVIRONMENT:-production}
- REACT_APP_GIT_HASH=${GIT_HASH}
- REACT_APP_GITHUB_URL=${GITHUB_URL:-https://github.com/uid0/openmakersuite}
logging: *default-logging
volumes:
- frontend_build:/app/frontend
healthcheck:
test: ["CMD", "test", "-f", "/app/frontend/index.html"]
interval: 15s
timeout: 3s
retries: 3
start_period: 15s
restart: unless-stopped
networks:
- app-network
flower:
build:
context: ./backend
dockerfile: Dockerfile
logging: *default-logging
command: celery -A config flower --port=5555 --broker=redis://redis:6379/0 --broker_api=redis://redis:6379/0
volumes:
- ./backend:/app
environment:
- DEBUG=1
- DATABASE_URL=postgresql://${POSTGRES_USER:-makerspace}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-makerspace_inventory}
- REDIS_URL=redis://redis:6379/0
- CELERY_BROKER_URL=redis://redis:6379/0
ports:
- "5555:5555"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
celery:
condition: service_started
networks:
- app-network
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
logging: *default-logging
ports:
- "80:80"
- "443:443"
volumes:
- static_volume:/app/staticfiles:ro
- media_volume:/app/media:ro
- frontend_build:/app/frontend:ro
- letsencrypt_certs:/etc/letsencrypt
- letsencrypt_lib:/var/lib/letsencrypt
- letsencrypt_logs:/var/log/letsencrypt
- certbot_challenges:/var/www/certbot
environment:
- DOMAIN=${DOMAIN:-oms.example.com}
- LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL:-}
- LETSENCRYPT_DOMAINS=${LETSENCRYPT_DOMAINS:-oms.example.com}
depends_on:
backend:
condition: service_healthy
frontend:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
static_volume:
media_volume:
frontend_build:
letsencrypt_certs:
letsencrypt_lib:
letsencrypt_logs:
certbot_challenges:
emqx_data:
emqx_log:
networks:
app-network:
driver: bridge