feat: move from gunicorn to waitress

Waitress, unlike unicorn, is multi-threaded. As it does not do access logs
by default, the app needs to be wrapped in TransLogger before being passed to
Waitress.

To make the switch, a custom registry is now also used instead of the global REGISTRY
as the default registry for the app. As part of this change, the default
prometheus metrics are then also registered with this new registry.

Closes: #72
This commit is contained in:
Ana Custura 2024-12-04 12:57:09 +00:00 committed by irl
parent ffe097b24f
commit c50d341c26
4 changed files with 27 additions and 15 deletions

View file

@ -4,15 +4,15 @@ ENV APP="bc"
ENV APP_BASE="/srv"
ENV SHELL="/bin/bash"
ENV FLASK_APP="${FLASK_APP:-app}"
ENV GUNICORN_RUN_HOST="${GUNICORN_RUN_HOST:-0.0.0.0}"
ENV GUNICORN_RUN_PORT="${GUNICORN_RUN_PORT:-5000}"
ENV WAITRESS_RUN_HOST="${WAITRESS_RUN_HOST:-0.0.0.0}"
ENV WAITRESS_RUN_PORT="${WAITRESS_RUN_PORT:-5000}"
ENV PYTHONPATH="${APP_BASE}/env/lib/python3.11/site-packages"
ENV PATH="${APP_BASE}/env/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin:/home/${APP}/.local/bin"
ARG CONTAINER_UID="${CONTAINER_UID:-1000}"
ARG CONTAINER_GID="${CONTAINER_GID:-1000}"
ENV GUNICORN_WORKERS="${GUNICORN_WORKERS:-4}"
ENV WAITRESS_THREADS="${WAITRESS_THREADS:-4}"
RUN apt-get update && \
apt-get install --no-install-recommends -y \
@ -49,11 +49,9 @@ USER ${APP}
WORKDIR ${APP_BASE}/${APP}
COPY --chown=${APP}:${APP} . ${APP_BASE}/${APP}
RUN chmod +x ${APP_BASE}/${APP}/run_gunicorn.sh
RUN python3 -m venv ${APP_BASE}/env && \
${APP_BASE}/env/bin/pip install --no-cache-dir -r requirements.txt && \
${APP_BASE}/env/bin/pip install --no-cache-dir psycopg2-binary gunicorn
${APP_BASE}/env/bin/pip install --no-cache-dir psycopg2-binary waitress paste
RUN make install-frontend
ENTRYPOINT ["./run_gunicorn.sh"]
ENTRYPOINT ["python", "./run_waitress.py"]

View file

@ -7,7 +7,7 @@ from flask import Flask, redirect, url_for, send_from_directory
from flask.typing import ResponseReturnValue
from prometheus_client import make_wsgi_app, Metric, CollectorRegistry
from prometheus_client.metrics_core import GaugeMetricFamily, CounterMetricFamily
from prometheus_client.registry import Collector
from prometheus_client.registry import Collector, REGISTRY
from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError
from werkzeug.middleware.dispatcher import DispatcherMiddleware
@ -24,10 +24,18 @@ from app.tfstate import tfstate
app = Flask(__name__)
app.config.from_file("../config.yaml", load=yaml.safe_load)
# create new registry to avoid multiple metrics registration in global REGISTRY
registry = CollectorRegistry()
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, { # type: ignore[method-assign]
'/metrics': make_wsgi_app()
'/metrics': make_wsgi_app(registry)
})
#register default collectors to our new registry
collectors = list(REGISTRY._collector_to_names.keys())
for collector in collectors:
registry.register(collector)
db.init_app(app)
migrate.init_app(app, db, render_as_batch=True)
bootstrap.init_app(app)
@ -129,9 +137,8 @@ class AutomationCollector(Collector):
ok.add_metric(["automation_state"], 0)
yield ok
# register all custom collectors to registry
if not_migrating() and 'DISABLE_METRICS' not in os.environ:
registry = CollectorRegistry()
registry.register(DefinedProxiesCollector())
registry.register(BlockedProxiesCollector())
registry.register(AutomationCollector())

View file

@ -1,4 +0,0 @@
#!/bin/sh
flask db upgrade
gunicorn "$FLASK_APP".__init__:app -w "$GUNICORN_WORKERS" --access-logfile=- -b "$GUNICORN_RUN_HOST:$GUNICORN_RUN_PORT"

11
run_waitress.py Normal file
View file

@ -0,0 +1,11 @@
import os
from waitress import serve
from paste.translogger import TransLogger
from app.__init__ import app
from flask_migrate import upgrade
# equivalent of running flask db upgrade
with app.app_context():
upgrade()
serve(TransLogger(app), host=os.environ["WAITRESS_RUN_HOST"], port=os.environ["WAITRESS_RUN_PORT"], threads=int(os.environ["WAITRESS_THREADS"]))