diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..14faba9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,59 @@ +# Git +.git +.gitignore + +# Python +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.log +.git +.mypy_cache +.pytest_cache +.hypothesis + +# Virtual environments +.venv +venv/ +ENV/ +env/ +.env + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Docker +Dockerfile +.dockerignore + +# Documentation +README.md +*.md + +# Qoder IDE +.qoder/ \ No newline at end of file diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..a2ec8a4 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,68 @@ +# Docker Deployment for Markov Economics Simulation + +This directory contains Docker configuration files for containerizing the Markov Economics Simulation App. + +## Files + +- `Dockerfile` - Multi-stage Docker build configuration +- `docker-compose.yml` - Docker Compose configuration for easy deployment +- `.dockerignore` - Files to exclude from Docker build context + +## Quick Start + +### Using Docker Compose (Recommended) + +```bash +# Build and start the application +docker-compose up --build + +# Run in detached mode +docker-compose up -d --build + +# Stop the application +docker-compose down +``` + +### Using Docker directly + +```bash +# Build the image +docker build -t markov-economics . + +# Run the container +docker run -p 5000:5000 markov-economics + +# Run with environment variables +docker run -p 5000:5000 \ + -e FLASK_ENV=production \ + -e FLASK_APP=run.py \ + markov-economics +``` + +## Access + +Once running, the application will be available at: +- Main application: http://localhost:5000 +- Health check: http://localhost:5000/health + +## Configuration + +### Environment Variables + +- `FLASK_ENV` - Flask environment (development/production) +- `FLASK_APP` - Entry point file (run.py) +- `PYTHONUNBUFFERED` - Ensure Python output is not buffered + +### Ports + +- `5000` - Main application port (Flask + SocketIO) + +## Health Check + +The container includes a health check that verifies the application is responding correctly at `/health`. + +## Security + +- Runs as non-root user (`appuser`) +- Uses Python slim base image +- Excludes development files via `.dockerignore` \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6c9c930 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +# Use Python 3.12 slim image as base +FROM python:3.12-slim + +# Set working directory +WORKDIR /app + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV FLASK_APP=run.py +ENV FLASK_ENV=production + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements first for better caching +COPY requirements.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the application code +COPY . . + +# Create a non-root user +RUN adduser --disabled-password --gecos '' appuser && \ + chown -R appuser:appuser /app +USER appuser + +# Expose the port the app runs on +EXPOSE 5000 + +# Health check +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 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..12194e2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.8' + +services: + markov-economics: + build: . + ports: + - "5000:5000" + environment: + - FLASK_ENV=production + - FLASK_APP=run.py + - PYTHONUNBUFFERED=1 + volumes: + # Optional: Mount for persistent data if needed + - ./data:/app/data + restart: unless-stopped + healthcheck: + test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:5000/health', timeout=2)"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - markov-network + +networks: + markov-network: + driver: bridge + +volumes: + markov-data: + driver: local \ No newline at end of file diff --git a/run.py b/run.py index 9ee2672..d9e06d5 100644 --- a/run.py +++ b/run.py @@ -11,4 +11,10 @@ config_name = os.getenv('FLASK_CONFIG', 'development') app = create_app(config_name) if __name__ == '__main__': - socketio.run(app, debug=True, host='0.0.0.0', port=5000) \ No newline at end of file + debug_mode = os.getenv('FLASK_ENV', 'development') == 'development' + + # For production deployment, allow unsafe werkzeug or use a proper WSGI server + if os.getenv('FLASK_ENV') == 'production': + socketio.run(app, debug=False, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True) + else: + socketio.run(app, debug=debug_mode, host='0.0.0.0', port=5000) \ No newline at end of file