diff --git a/Dockerfile b/Dockerfile index e45b66358d793bb4a004e697ae67c0ed38141548..06464155b1e1aee49bd3a83ba48cbb4a73699817 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ RUN \ # Install arkindex and its deps # Uses a source archive instead of full local copy to speedup docker build COPY --from=build /build/dist/arkindex-*.tar.gz /tmp/arkindex.tar.gz -RUN pip install /tmp/arkindex.tar.gz && rm /tmp/arkindex.tar.gz +RUN pip install gunicorn /tmp/arkindex.tar.gz && rm /tmp/arkindex.tar.gz # Allow access to static files directory RUN mkdir -p /backend_static @@ -42,15 +42,6 @@ RUN chown -R ark:teklia /backend_static COPY VERSION /etc/arkindex.version # Run with Daphne +ENV PORT 80 EXPOSE 80 -CMD [ \ - "daphne", \ - "--http-timeout=30", \ - "--websocket_connect_timeout=10", \ - "--websocket_timeout=120", \ - "--ping-timeout=120", \ - "--application-close-timeout=3", \ - "--verbosity=1", \ - "--bind=0.0.0.0", \ - "--port=80", \ - "arkindex.project.asgi:application"] +CMD ["manage.py", "gunicorn", "--host=0.0.0.0"] diff --git a/arkindex/documents/management/commands/gunicorn.py b/arkindex/documents/management/commands/gunicorn.py new file mode 100644 index 0000000000000000000000000000000000000000..8a1663f40dbdb8cb01445acbd23498221cdf4d49 --- /dev/null +++ b/arkindex/documents/management/commands/gunicorn.py @@ -0,0 +1,70 @@ +from django.core.management.base import BaseCommand, CommandError +import sys +import os +import multiprocessing +from django.core.wsgi import get_wsgi_application + +import logging + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", +) +logger = logging.getLogger() + + +class Command(BaseCommand): + help = "Run Arkindex API server through Gunicorn" + + def add_arguments(self, parser): + parser.add_argument( + "--host", + type=str, + help="Host to bind gunicorn", + default=os.environ.get("HOST", "localhost"), + ) + parser.add_argument( + "--port", + type=int, + help="Port to bind gunicorn", + default=int(os.environ.get("PORT", 8000)), + ) + parser.add_argument( + "--max-workers", + help="Add a limit to the number of workers", + type=int, + default=int(os.environ.get("MAX_WORKERS", 0)), + ) + + def handle(self, host, port, max_workers, *args, **kwargs): + try: + from gunicorn.app.base import Application + except ImportError: + raise CommandError("Gunicorn is not available") + + # Calc max workers + workers = (multiprocessing.cpu_count() * 2) + 1 + if max_workers > 0: + workers = min(workers, max_workers) + + # Build bind string + bind = f"{host}:{port}" + logger.info(f"Running server on {bind} with {workers} workers") + + # Do not send out CLI args to gunicorn as they are not compatible + # and we override directly the configuration to pass the WSGI application + # This is needed to run through Nuitka + sys.argv = sys.argv[:2] + + class ArkindexServer(Application): + """Run the Django WSGI app through gunicorn""" + def init(self, *args, **kwargs): + return { + "bind": bind, + "workers": workers + } + + def load(self): + return get_wsgi_application() + + ArkindexServer().run()