Make Gunicorn respect config.json settings
- Updated gunicorn.conf.py to read host/port from config.json - Added gunicorn section to config.json for server settings - Created start_gunicorn.py as alternative launcher - Updated config.json with server settings (127.0.0.1:6969) - Updated documentation with new startup options
This commit is contained in:
@@ -35,7 +35,7 @@ python config_manager.py rate_limiting.endpoints.login "10 per minute"
|
|||||||
|
|
||||||
## Running with Gunicorn (Production)
|
## Running with Gunicorn (Production)
|
||||||
|
|
||||||
### Quick Start
|
### Quick Start - Uses config.json settings
|
||||||
```bash
|
```bash
|
||||||
# Activate virtual environment
|
# Activate virtual environment
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
@@ -43,25 +43,28 @@ source .venv/bin/activate
|
|||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# Run with Gunicorn (recommended for production)
|
# Option 1: Run with config file (recommended - uses config.json)
|
||||||
gunicorn --config gunicorn.conf.py app:app
|
gunicorn --config gunicorn.conf.py app:app
|
||||||
|
|
||||||
|
# Option 2: Run with Python launcher (also uses config.json)
|
||||||
|
python start_gunicorn.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Alternative Gunicorn Commands
|
### Manual Gunicorn Commands (ignores config.json)
|
||||||
|
|
||||||
**Basic production run:**
|
**Basic production run:**
|
||||||
```bash
|
```bash
|
||||||
gunicorn -w 4 -b 0.0.0.0:5050 app:app
|
gunicorn -w 4 -b 127.0.0.1:6969 app:app
|
||||||
```
|
```
|
||||||
|
|
||||||
**With more workers (for higher traffic):**
|
**With more workers (for higher traffic):**
|
||||||
```bash
|
```bash
|
||||||
gunicorn -w 8 -b 0.0.0.0:5050 --timeout 30 app:app
|
gunicorn -w 8 -b 127.0.0.1:6969 --timeout 30 app:app
|
||||||
```
|
```
|
||||||
|
|
||||||
**Behind a reverse proxy (nginx/apache):**
|
**Behind a reverse proxy (nginx/apache):**
|
||||||
```bash
|
```bash
|
||||||
gunicorn -w 4 -b 127.0.0.1:5050 app:app
|
gunicorn -w 4 -b 127.0.0.1:6969 app:app
|
||||||
```
|
```
|
||||||
|
|
||||||
### Environment Variables for Production
|
### Environment Variables for Production
|
||||||
|
|||||||
11
config.json
11
config.json
@@ -1,10 +1,17 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"name": "ircquotes",
|
"name": "ircquotes",
|
||||||
"host": "0.0.0.0",
|
"host": "127.0.0.1",
|
||||||
"port": 5050,
|
"port": 6969,
|
||||||
"debug": false
|
"debug": false
|
||||||
},
|
},
|
||||||
|
"gunicorn": {
|
||||||
|
"workers": 4,
|
||||||
|
"timeout": 30,
|
||||||
|
"keepalive": 5,
|
||||||
|
"max_requests": 1000,
|
||||||
|
"preload": true
|
||||||
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"uri": "sqlite:///quotes.db?timeout=20",
|
"uri": "sqlite:///quotes.db?timeout=20",
|
||||||
"pool_timeout": 20,
|
"pool_timeout": 20,
|
||||||
|
|||||||
@@ -1,21 +1,42 @@
|
|||||||
# Gunicorn configuration file for ircquotes
|
# Gunicorn configuration file for ircquotes
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
# Server socket
|
# Load configuration from config.json
|
||||||
bind = "0.0.0.0:5050"
|
def load_app_config():
|
||||||
|
config_file = os.path.join(os.path.dirname(__file__), 'config.json')
|
||||||
|
try:
|
||||||
|
with open(config_file, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except:
|
||||||
|
# Fallback to defaults if config.json not found
|
||||||
|
return {
|
||||||
|
"app": {"host": "0.0.0.0", "port": 5050}
|
||||||
|
}
|
||||||
|
|
||||||
|
app_config = load_app_config()
|
||||||
|
|
||||||
|
# Server socket - use config.json values
|
||||||
|
host = app_config.get('app', {}).get('host', '0.0.0.0')
|
||||||
|
port = app_config.get('app', {}).get('port', 5050)
|
||||||
|
bind = f"{host}:{port}"
|
||||||
backlog = 2048
|
backlog = 2048
|
||||||
|
|
||||||
# Worker processes
|
# Worker processes - use config.json values
|
||||||
workers = multiprocessing.cpu_count() * 2 + 1
|
workers = app_config.get('gunicorn', {}).get('workers', multiprocessing.cpu_count() * 2 + 1)
|
||||||
worker_class = "sync"
|
worker_class = "sync"
|
||||||
worker_connections = 1000
|
worker_connections = 1000
|
||||||
timeout = 30
|
timeout = app_config.get('gunicorn', {}).get('timeout', 30)
|
||||||
keepalive = 5
|
keepalive = app_config.get('gunicorn', {}).get('keepalive', 5)
|
||||||
|
|
||||||
# Restart workers after this many requests, to help prevent memory leaks
|
# Restart workers after this many requests, to help prevent memory leaks
|
||||||
max_requests = 1000
|
max_requests = app_config.get('gunicorn', {}).get('max_requests', 1000)
|
||||||
max_requests_jitter = 100
|
max_requests_jitter = 100
|
||||||
|
|
||||||
|
# Preload app for better performance
|
||||||
|
preload_app = app_config.get('gunicorn', {}).get('preload', True)
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
accesslog = "-" # Log to stdout
|
accesslog = "-" # Log to stdout
|
||||||
errorlog = "-" # Log to stderr
|
errorlog = "-" # Log to stderr
|
||||||
|
|||||||
49
start_gunicorn.py
Normal file
49
start_gunicorn.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Gunicorn launcher that reads configuration from config.json
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from config_loader import config
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Launch Gunicorn with settings from config.json"""
|
||||||
|
|
||||||
|
# Get configuration values
|
||||||
|
host = config.app_host
|
||||||
|
port = config.app_port
|
||||||
|
workers = config.get('gunicorn.workers', 4)
|
||||||
|
|
||||||
|
# Build Gunicorn command
|
||||||
|
cmd = [
|
||||||
|
'gunicorn',
|
||||||
|
'--bind', f'{host}:{port}',
|
||||||
|
'--workers', str(workers),
|
||||||
|
'--timeout', '30',
|
||||||
|
'--keepalive', '5',
|
||||||
|
'--max-requests', '1000',
|
||||||
|
'--max-requests-jitter', '100',
|
||||||
|
'--access-logfile', '-',
|
||||||
|
'--error-logfile', '-',
|
||||||
|
'--log-level', 'info',
|
||||||
|
'--preload',
|
||||||
|
'app:app'
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"Starting Gunicorn on {host}:{port} with {workers} workers...")
|
||||||
|
print(f"Command: {' '.join(cmd)}")
|
||||||
|
|
||||||
|
# Execute Gunicorn
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error starting Gunicorn: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nStopping Gunicorn...")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user