From 2fe272bd006935a3fdabc92e4eb81f19aa49d4e2 Mon Sep 17 00:00:00 2001 From: Markus Busche Date: Tue, 26 Aug 2025 08:36:54 +0000 Subject: [PATCH] Configure Docker to use Gunicorn instead of Flask development server for production deployment. Updated SocketIO configuration for gevent compatibility. --- Dockerfile | 4 ++-- app/__init__.py | 5 +++-- requirements.txt | 4 +++- run.py | 5 ++++- wsgi.py | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 wsgi.py diff --git a/Dockerfile b/Dockerfile index 6c9c930..e9f6df5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,5 +36,5 @@ EXPOSE 5000 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD python -c "import requests; requests.get('http://localhost:5000/health', timeout=2)" || exit 1 -# Run the application -CMD ["python", "run.py"] \ No newline at end of file +# Run the application with Gunicorn using gevent workers for SocketIO compatibility +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "--worker-class", "gevent", "--worker-connections", "1000", "--timeout", "30", "wsgi:app"] \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py index b59029d..561ed5f 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -12,9 +12,10 @@ from config import config from app.models import SimulationManager # Initialize SocketIO with enhanced proxy support +# For Gunicorn compatibility, we need to set the async_mode to 'gevent' socketio = SocketIO( cors_allowed_origins="*", - async_mode='threading', + async_mode='gevent' if os.getenv('FLASK_ENV') == 'production' else 'threading', logger=False, # Disable to avoid log spam in production engineio_logger=False, # Transport order: try websocket first, then polling @@ -54,7 +55,7 @@ def create_app(config_name=None): # Initialize extensions with proxy-friendly settings socketio.init_app( app, - async_mode='threading', + async_mode='gevent' if config_name == 'production' else 'threading', cors_allowed_origins="*", allow_upgrades=True, transports=['websocket', 'polling'], diff --git a/requirements.txt b/requirements.txt index da899ef..90eb398 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,6 @@ Flask>=2.0.0 Flask-SocketIO>=5.0.0 numpy>=1.21.0 matplotlib>=3.5.0 -plotly>=5.0.0 \ No newline at end of file +plotly>=5.0.0 +gunicorn>=20.0.0 +gevent>=21.0.0 \ No newline at end of file diff --git a/run.py b/run.py index 7dbd49d..60682e6 100644 --- a/run.py +++ b/run.py @@ -15,8 +15,11 @@ if __name__ == '__main__': debug_mode = config_name == 'development' port = int(os.getenv('PORT', 5000)) # Use PORT env var or default to 5000 - # For production deployment, allow unsafe werkzeug or use a proper WSGI server + # For production deployment with Gunicorn, SocketIO will be handled by Gunicorn + # For development, we use SocketIO's built-in server if config_name == 'production': + # In production, Gunicorn will handle the server + # This is just a fallback socketio.run(app, debug=False, host='0.0.0.0', port=port, allow_unsafe_werkzeug=True) else: socketio.run(app, debug=debug_mode, host='0.0.0.0', port=port) \ No newline at end of file diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..e36cfc2 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +""" +WSGI entry point for the Markov Economics Flask application. +This file is used by Gunicorn to serve the application in production. +""" + +import os +from app import create_app + +# Use FLASK_ENV if set, otherwise default to production for Gunicorn +config_name = os.getenv('FLASK_ENV', 'production') +app = create_app(config_name) + +if __name__ == "__main__": + # This is just for testing purposes + app.run() \ No newline at end of file