From 02b68b7d2101599c924364fcd01d5e054caea2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Birrer?= Date: Tue, 22 Nov 2022 17:48:31 +0100 Subject: [PATCH 1/2] chore(deps): replace uswsgi with gunicorn dependencies --- document_merge_service/cmd.sh | 3 +++ poetry.lock | 48 +++++++++++++++++++++-------------- pyproject.toml | 2 +- 3 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 document_merge_service/cmd.sh diff --git a/document_merge_service/cmd.sh b/document_merge_service/cmd.sh new file mode 100644 index 00000000..b951b40a --- /dev/null +++ b/document_merge_service/cmd.sh @@ -0,0 +1,3 @@ +#!/bin/bash + + WSGI_INI /app/wsgi.ini diff --git a/poetry.lock b/poetry.lock index f920fcdc..4ba230fc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -32,7 +32,7 @@ python-versions = ">=3.5" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "Babel" @@ -111,7 +111,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "click" @@ -537,6 +537,23 @@ python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + [[package]] name = "idna" version = "3.4" @@ -595,9 +612,9 @@ python-versions = ">=3.6.1,<4.0" [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] plugins = ["setuptools"] -requirements_deprecated_finder = ["pip-api", "pipreqs"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "jaraco.classes" @@ -1146,7 +1163,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-mock" @@ -1421,14 +1438,6 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "uWSGI" -version = "2.0.21" -description = "The uWSGI server" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "wasmer" version = "1.1.0" @@ -1523,7 +1532,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "8a3bcc734aeb4d0e7fdb1f1756578851d90d9721ec78aced23c80bcdcff21fe9" +content-hash = "82b3e9582801dcbd07f2fc2254f3b894524b109a086a2262d4000f2c20a511cb" [metadata.files] arrow = [ @@ -1846,6 +1855,10 @@ GitPython = [ {file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"}, {file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"}, ] +gunicorn = [ + {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] idna = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, @@ -2124,6 +2137,8 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e67b3c26e9b6d37b370c83aa790bbc121775c57bfb096c2e77eacca25fd0233b"}, {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5fc447058d083b8c6ac076fc26b446d44f0145308465d745fba93a28c14c9e32"}, {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d892bfa1d023c3781a3cab8dd5af76b626c483484d782e8bd047c180db590e4c"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-win32.whl", hash = "sha256:2abccab84d057723d2ca8f99ff7b619285d40da6814d50366f61f0fc385c3903"}, + {file = "psycopg2_binary-2.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:bef7e3f9dc6f0c13afdd671008534be5744e0e682fb851584c8c3a025ec09720"}, {file = "psycopg2_binary-2.9.5-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:6e63814ec71db9bdb42905c925639f319c80e7909fb76c3b84edc79dadef8d60"}, {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd"}, {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8a9bcab7b6db2e3dbf65b214dfc795b4c6b3bb3af922901b6a67f7cb47d5f8"}, @@ -2196,8 +2211,6 @@ pyparsing = [ {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pyreadline = [ - {file = "pyreadline-2.1.win-amd64.exe", hash = "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b"}, - {file = "pyreadline-2.1.win32.exe", hash = "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e"}, {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, ] pyrepl = [ @@ -2370,9 +2383,6 @@ urllib3 = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] -uWSGI = [ - {file = "uwsgi-2.0.21.tar.gz", hash = "sha256:35a30d83791329429bc04fe44183ce4ab512fcf6968070a7bfba42fc5a0552a9"}, -] wasmer = [ {file = "wasmer-1.1.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c2af4b907ae2dabcac41e316e811d5937c93adf1f8b05c5d49427f8ce0f37630"}, {file = "wasmer-1.1.0-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:ab1ae980021e5ec0bf0c6cdd3b979b1d15a5f3eb2b8a32da8dcb1156e4a1e484"}, diff --git a/pyproject.toml b/pyproject.toml index e65eebce..3ad63eaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,9 +22,9 @@ psycopg2-binary = "^2.9.5" python-dateutil = "^2.8.2" python-memcached = "^1.59" requests = "^2.28.1" -uWSGI = "^2.0.21" xltpl = "^0.16" openpyxl = "^3.0.10" +gunicorn = "^20.1.0" [tool.poetry.group.dev.dependencies] black = "22.10.0" From 145f4ad61b91f51fc117c53e4e5fc5f97936db70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Birrer?= Date: Tue, 22 Nov 2022 18:29:55 +0100 Subject: [PATCH 2/2] fix(app-server): configure app to use gunicorn --- Dockerfile | 3 +- cmd.sh | 5 +++ document_merge_service/cmd.sh | 3 -- uwsgi.ini | 71 ----------------------------------- 4 files changed, 6 insertions(+), 76 deletions(-) create mode 100755 cmd.sh delete mode 100644 document_merge_service/cmd.sh delete mode 100644 uwsgi.ini diff --git a/Dockerfile b/Dockerfile index dac2c908..92b420d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,6 @@ USER document-merge-service ENV PYTHONUNBUFFERED=1 ENV DJANGO_SETTINGS_MODULE document_merge_service.settings ENV APP_HOME=/app -ENV UWSGI_INI /app/uwsgi.ini ENV MEDIA_ROOT /var/lib/document-merge-service/media ARG ENV=docker @@ -32,4 +31,4 @@ COPY . $APP_HOME EXPOSE 8000 -CMD /bin/sh -c "poetry run python ./manage.py migrate && poetry run uwsgi" +CMD ./cmd.sh diff --git a/cmd.sh b/cmd.sh new file mode 100755 index 00000000..8eb8cd2b --- /dev/null +++ b/cmd.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +GUNICORN_WORKERS="${GUNICORN_WORKERS:-8}" + +poetry run gunicorn --workers=$GUNICORN_WORKERS --bind=0.0.0.0:8000 document_merge_service.wsgi:application diff --git a/document_merge_service/cmd.sh b/document_merge_service/cmd.sh deleted file mode 100644 index b951b40a..00000000 --- a/document_merge_service/cmd.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - - WSGI_INI /app/wsgi.ini diff --git a/uwsgi.ini b/uwsgi.ini deleted file mode 100644 index 1d9f921a..00000000 --- a/uwsgi.ini +++ /dev/null @@ -1,71 +0,0 @@ -# These settings can be overridden by environment-variables: https://git.io/JemA2 -[uwsgi] -http = 0.0.0.0:8000 - -# Crash if there are any unknown configuration parameters in the ini file. -strict = true - -# Path to wsgi.py -wsgi-file = /app/document_merge_service/wsgi.py - -# Enable graceful shutdown of workers (built-in prefork+threading multi-worker -# management mode) -master = true - -# By default threads are disabled. This can cause issues if your application -# uses background threads. Without setting this parameter to true, your -# threads will not run. -enable-threads = true - -# Delete temporary file (sockets, pidfiles, ...) during shutdown -vacuum = true - -# Disable single interpreter since certain C extenions can not cope with it. -# Since we don't run multiple applications on the same workers we can safely -# disable this. -single-interpreter = true - -# By default SIGTERM will brutally reload uWSGI instead of shuting it down. With -# this option enabled SIGTERM shuts down uWSGI as everybody would expect. -die-on-term = true - -# Let uWSGI crash if it is not able to load the application module. -need-app = true - -# uWSGI's logging is rather verbose. Instead of logging every request (which -# already happens on the reverse proxy level), logging all 4xx and 5xx errors -# should suffice. -disable-logging = true -log-4xx = true -log-5xx = true - -# It is a good idea to recycle workers every now and then to prevent memory -# leaks or unintentional states. -max-requests = 1000 ; Restart workers after this many requests -max-worker-lifetime = 3600 ; Restart workers after this many seconds -reload-on-rss = 2048 ; Restart workers after this much resident memory -worker-reload-mercy = 60 ; How long to wait before forcefully killing workers - -# The busyness algorithm attempts to always have spare workers available, -# which is useful when anticipating unexpected traffic surges. -cheaper-algo = busyness -processes = 64 ; Maximum number of workers allowed -cheaper = 8 ; Minimum number of workers allowed -cheaper-initial = 16 ; Workers created at startup -cheaper-overload = 1 ; Length of a cycle in seconds -cheaper-step = 16 ; How many workers to spawn at a time - -cheaper-busyness-multiplier = 30 ; How many cycles to wait before killing workers -cheaper-busyness-min = 20 ; Below this threshold, kill workers (if stable for multiplier cycles) -cheaper-busyness-max = 70 ; Above this threshold, spawn new workers -cheaper-busyness-backlog-alert = 16 ; Spawn emergency workers if more than this many requests are waiting in the queue -cheaper-busyness-backlog-step = 2 ; How many emergency workers to create if there are too many requests in the queue - -# SIGKILL workers after certain timeout if they get stuck. -harakiri = 60 - -# Auto rename worker processes -auto-procname = true -procname-prefix = "dms " ; note the space - -buffer-size = 32768