template tutoring-admin-dashboard v1
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Two processes in one container, and the rules that make that survivable.
|
||||
#
|
||||
# A container with two processes needs something that reaps them, restarts
|
||||
# them, and — most importantly — dies when either one dies for good. Without
|
||||
# that last part the container stays "up" with half an app in it, the
|
||||
# orchestrator sees a healthy container, and the only symptom is that every
|
||||
# request 502s.
|
||||
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
pidfile=/tmp/supervisord.pid
|
||||
|
||||
# Without these three, `supervisorctl` inside the container refuses to run at
|
||||
# all — "ini file does not include supervisorctl section" — so the one tool for
|
||||
# asking which half of the app is down, or restarting it without killing the
|
||||
# container, is unavailable exactly when it is needed. The socket is a file in
|
||||
# the container with no port and no network listener, so this costs nothing.
|
||||
[unix_http_server]
|
||||
file=/tmp/supervisor.sock
|
||||
chmod=0700
|
||||
|
||||
[supervisorctl]
|
||||
serverurl=unix:///tmp/supervisor.sock
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[program:backend]
|
||||
command=uvicorn main:app --host 127.0.0.1 --port 8001 --root-path /api
|
||||
directory=/app/backend
|
||||
autostart=true
|
||||
autorestart=true
|
||||
# Three failures in a row is a real failure, not a slow start. Retrying forever
|
||||
# hides a crash-looping app behind a container that never exits.
|
||||
startretries=3
|
||||
startsecs=5
|
||||
priority=10
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
# Python buffers stdout when it is a pipe, and supervisord gives it a pipe.
|
||||
# Without this the app's logs appear only when the buffer fills — which for a
|
||||
# crash means never, and the traceback that would explain it is lost.
|
||||
environment=PYTHONUNBUFFERED="1"
|
||||
|
||||
[program:nginx]
|
||||
command=nginx -g "daemon off;"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startretries=3
|
||||
# Started after the backend so the first proxied request has somewhere to go.
|
||||
# It is only ordering, not waiting: nginx starts whether or not the backend is
|
||||
# ready, which is correct — it serves the frontend meanwhile.
|
||||
priority=20
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[eventlistener:quit_on_failure]
|
||||
# The part that matters. When either program enters FATAL — supervisord has
|
||||
# given up restarting it — this stops supervisord, so the container exits and
|
||||
# whatever supervises it can react. Otherwise the container lives on with one
|
||||
# half of the app missing: healthy from outside, a total outage from a browser.
|
||||
#
|
||||
# A real script rather than an inline `sh -c`, because the listener protocol is
|
||||
# a length-prefixed exchange and getting it wrong does not raise — it leaves a
|
||||
# listener reading forever while the thing it watches stays broken. See the
|
||||
# script's own docstring; the shell version of this genuinely did that.
|
||||
command=python3 -u /app/quit_on_failure.py
|
||||
events=PROCESS_STATE_FATAL
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
Reference in New Issue
Block a user