From c50d341c2606e57e780d6c20f15aa175fe4de46a Mon Sep 17 00:00:00 2001 From: Ana Custura Date: Wed, 4 Dec 2024 12:57:09 +0000 Subject: [PATCH] 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 --- Dockerfile | 12 +++++------- app/__init__.py | 15 +++++++++++---- run_gunicorn.sh | 4 ---- run_waitress.py | 11 +++++++++++ 4 files changed, 27 insertions(+), 15 deletions(-) delete mode 100644 run_gunicorn.sh create mode 100644 run_waitress.py diff --git a/Dockerfile b/Dockerfile index fcb5682..03da9e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] \ No newline at end of file +ENTRYPOINT ["python", "./run_waitress.py"] \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py index dcdfb44..ce725e1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -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()) diff --git a/run_gunicorn.sh b/run_gunicorn.sh deleted file mode 100644 index d3d7252..0000000 --- a/run_gunicorn.sh +++ /dev/null @@ -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" \ No newline at end of file diff --git a/run_waitress.py b/run_waitress.py new file mode 100644 index 0000000..1a37eda --- /dev/null +++ b/run_waitress.py @@ -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"]))