# πŸš€ Production Deployment Guide ## Overview This guide covers deploying the FastMCP ESPTool server in production environments, including containerization, scaling, monitoring, and enterprise integration patterns. ## 🏭 Production Architecture ### Deployment Topology ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Production Environment β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Load β”‚ β”‚ Reverse β”‚ β”‚ SSL/TLS β”‚ β”‚ β”‚ β”‚ Balancer │───▢│ Proxy │───▢│ Termination β”‚ β”‚ β”‚ β”‚ (HAProxy) β”‚ β”‚ (Caddy) β”‚ β”‚ (Cert) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ MCP ESPTool Server Cluster β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ Server β”‚ β”‚ Server β”‚ β”‚ Server β”‚ β”‚ β”‚ β”‚ β”‚ Instance 1 β”‚ β”‚ Instance 2 β”‚ β”‚ Instance 3 β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Port: 8080 β”‚ β”‚ Port: 8081 β”‚ β”‚ Port: 8082 β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ └────────────────────────────────────────────────────────── β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ Shared Services β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ Redis β”‚ β”‚ PostgreSQL β”‚ β”‚ Monitoring β”‚ β”‚ β”‚ β”‚ β”‚ Cache β”‚ β”‚ Database β”‚ β”‚ (Grafana) β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ └────────────────────────────────────────────────────────── β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ Hardware Interface β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ ESP Device β”‚ β”‚ ESP Device β”‚ β”‚ ESP Device β”‚ β”‚ β”‚ β”‚ β”‚ Station 1 β”‚ β”‚ Station 2 β”‚ β”‚ Station N β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚/dev/ttyUSB0 β”‚ β”‚/dev/ttyUSB1 β”‚ β”‚/dev/ttyUSBN β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## 🐳 Container Production Setup ### Production Dockerfile ```dockerfile # Multi-stage production Dockerfile FROM python:3.11-slim-bookworm AS base # Install system dependencies RUN apt-get update && apt-get install -y \ git \ curl \ build-essential \ cmake \ ninja-build \ libusb-1.0-0-dev \ udev \ && rm -rf /var/lib/apt/lists/* # Install uv for fast package management RUN pip install uv # Create non-root user for security RUN groupadd -r esptool && useradd -r -g esptool -d /app -s /bin/bash esptool RUN mkdir -p /app && chown esptool:esptool /app # Production stage FROM base AS production USER esptool WORKDIR /app # Copy project files COPY --chown=esptool:esptool pyproject.toml ./ COPY --chown=esptool:esptool src/ ./src/ # Install production dependencies RUN uv venv .venv && \ . .venv/bin/activate && \ uv pip install -e ".[production]" && \ uv pip install esptool esp-idf-tools # Set up ESP-IDF (production minimal) RUN git clone --depth 1 --branch v5.1 \ https://github.com/espressif/esp-idf.git /opt/esp-idf && \ cd /opt/esp-idf && \ ./install.sh --targets esp32,esp32s3,esp32c3 # Environment setup ENV ESP_IDF_PATH=/opt/esp-idf ENV PATH="/opt/esp-idf/tools:/app/.venv/bin:$PATH" ENV PYTHONPATH=/app/src # Create directories for data persistence RUN mkdir -p /app/data /app/logs /app/config # Health check script COPY --chown=esptool:esptool scripts/health-check.py ./health-check.py RUN chmod +x health-check.py # Security: Remove package management tools in production USER root RUN apt-get remove -y git curl build-essential cmake && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* USER esptool # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ CMD python health-check.py || exit 1 # Expose port EXPOSE 8080 # Production startup CMD ["/app/.venv/bin/python", "-m", "mcp_esptool_server.server", "--production"] ``` ### Production Docker Compose ```yaml # docker-compose.prod.yml version: '3.8' services: # Load balancer haproxy: image: haproxy:2.8-alpine ports: - "80:80" - "443:443" - "8404:8404" # Stats volumes: - ./config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro - ./certs:/etc/ssl/certs:ro networks: - frontend - backend restart: unless-stopped depends_on: - esptool-server-1 - esptool-server-2 - esptool-server-3 # MCP ESPTool Server Instances esptool-server-1: &esptool-server build: context: . dockerfile: Dockerfile target: production image: mcp-esptool-server:${VERSION:-latest} environment: - SERVER_ID=1 - SERVER_PORT=8080 - LOG_LEVEL=${LOG_LEVEL:-INFO} - DATABASE_URL=postgresql://esptool:${DB_PASSWORD}@postgres:5432/esptool - REDIS_URL=redis://redis:6379/0 - ESP_DEVICE_BASE_PATH=/dev/serial - PRODUCTION_MODE=true - MAX_CONCURRENT_OPERATIONS=10 - ENABLE_METRICS=true - METRICS_PORT=9090 volumes: - ./data/server-1:/app/data - ./logs:/app/logs - ./config:/app/config:ro - /dev/serial:/dev/serial:rw networks: - backend restart: unless-stopped depends_on: - postgres - redis labels: - "com.docker.compose.service=esptool-server" esptool-server-2: <<: *esptool-server environment: - SERVER_ID=2 - SERVER_PORT=8080 - LOG_LEVEL=${LOG_LEVEL:-INFO} - DATABASE_URL=postgresql://esptool:${DB_PASSWORD}@postgres:5432/esptool - REDIS_URL=redis://redis:6379/0 - ESP_DEVICE_BASE_PATH=/dev/serial - PRODUCTION_MODE=true volumes: - ./data/server-2:/app/data - ./logs:/app/logs - ./config:/app/config:ro - /dev/serial:/dev/serial:rw esptool-server-3: <<: *esptool-server environment: - SERVER_ID=3 - SERVER_PORT=8080 - LOG_LEVEL=${LOG_LEVEL:-INFO} - DATABASE_URL=postgresql://esptool:${DB_PASSWORD}@postgres:5432/esptool - REDIS_URL=redis://redis:6379/0 - ESP_DEVICE_BASE_PATH=/dev/serial - PRODUCTION_MODE=true volumes: - ./data/server-3:/app/data - ./logs:/app/logs - ./config:/app/config:ro - /dev/serial:/dev/serial:rw # Database postgres: image: postgres:15-alpine environment: POSTGRES_DB: esptool POSTGRES_USER: esptool POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - postgres-data:/var/lib/postgresql/data - ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro networks: - backend restart: unless-stopped # Cache and session store redis: image: redis:7-alpine command: redis-server --appendonly yes --maxmemory 1gb --maxmemory-policy allkeys-lru volumes: - redis-data:/data networks: - backend restart: unless-stopped # Monitoring and metrics prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus networks: - backend - monitoring restart: unless-stopped grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD} volumes: - grafana-data:/var/lib/grafana - ./config/grafana:/etc/grafana/provisioning:ro networks: - monitoring restart: unless-stopped depends_on: - prometheus # Log aggregation loki: image: grafana/loki:latest ports: - "3100:3100" volumes: - loki-data:/tmp/loki - ./config/loki.yml:/etc/loki/local-config.yaml:ro networks: - monitoring restart: unless-stopped volumes: postgres-data: redis-data: prometheus-data: grafana-data: loki-data: networks: frontend: driver: bridge backend: driver: bridge monitoring: driver: bridge ``` ## βš™οΈ Configuration Management ### Production Configuration ```python # src/mcp_esptool_server/config/production.py import os from dataclasses import dataclass, field from typing import List, Dict, Optional from pathlib import Path @dataclass class ProductionConfig: """Production environment configuration""" # Server settings server_id: str = field(default_factory=lambda: os.getenv('SERVER_ID', '1')) server_port: int = int(os.getenv('SERVER_PORT', '8080')) max_workers: int = int(os.getenv('MAX_WORKERS', '4')) max_concurrent_operations: int = int(os.getenv('MAX_CONCURRENT_OPERATIONS', '10')) # Database configuration database_url: str = os.getenv('DATABASE_URL', 'postgresql://localhost:5432/esptool') redis_url: str = os.getenv('REDIS_URL', 'redis://localhost:6379/0') # Security settings enable_auth: bool = os.getenv('ENABLE_AUTH', 'true').lower() == 'true' jwt_secret: str = os.getenv('JWT_SECRET', 'change-me-in-production') api_key_required: bool = os.getenv('API_KEY_REQUIRED', 'true').lower() == 'true' # ESP device settings esp_device_base_path: str = os.getenv('ESP_DEVICE_BASE_PATH', '/dev/serial') max_device_connections: int = int(os.getenv('MAX_DEVICE_CONNECTIONS', '20')) device_timeout: int = int(os.getenv('DEVICE_TIMEOUT', '30')) # Logging and monitoring log_level: str = os.getenv('LOG_LEVEL', 'INFO') enable_metrics: bool = os.getenv('ENABLE_METRICS', 'true').lower() == 'true' metrics_port: int = int(os.getenv('METRICS_PORT', '9090')) # Performance tuning enable_caching: bool = os.getenv('ENABLE_CACHING', 'true').lower() == 'true' cache_ttl: int = int(os.getenv('CACHE_TTL', '300')) # Factory programming settings enable_factory_mode: bool = os.getenv('ENABLE_FACTORY_MODE', 'false').lower() == 'true' factory_batch_size: int = int(os.getenv('FACTORY_BATCH_SIZE', '100')) # Data retention log_retention_days: int = int(os.getenv('LOG_RETENTION_DAYS', '30')) metrics_retention_days: int = int(os.getenv('METRICS_RETENTION_DAYS', '90')) def __post_init__(self): """Validate configuration after initialization""" if self.enable_auth and self.jwt_secret == 'change-me-in-production': raise ValueError("JWT_SECRET must be set in production with authentication enabled") if not Path(self.esp_device_base_path).exists(): raise ValueError(f"ESP device base path does not exist: {self.esp_device_base_path}") ``` ### Environment Configuration ```bash # .env.production # Core server settings SERVER_ID=1 SERVER_PORT=8080 MAX_WORKERS=4 MAX_CONCURRENT_OPERATIONS=10 # Database and cache DATABASE_URL=postgresql://esptool:secure_password@postgres:5432/esptool REDIS_URL=redis://redis:6379/0 # Security (CHANGE THESE IN PRODUCTION) ENABLE_AUTH=true JWT_SECRET=your-256-bit-secret-key-here API_KEY_REQUIRED=true # ESP device configuration ESP_DEVICE_BASE_PATH=/dev/serial MAX_DEVICE_CONNECTIONS=20 DEVICE_TIMEOUT=30 # Logging and monitoring LOG_LEVEL=INFO ENABLE_METRICS=true METRICS_PORT=9090 # Performance ENABLE_CACHING=true CACHE_TTL=300 # Factory programming ENABLE_FACTORY_MODE=true FACTORY_BATCH_SIZE=50 # Data retention LOG_RETENTION_DAYS=30 METRICS_RETENTION_DAYS=90 # External services GRAFANA_PASSWORD=secure_grafana_password DB_PASSWORD=secure_db_password ``` ## πŸ” Security Configuration ### Authentication and Authorization ```python # src/mcp_esptool_server/security/auth.py import jwt from datetime import datetime, timedelta from typing import Optional, Dict, Any from fastapi import HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials class ProductionAuth: """Production authentication and authorization""" def __init__(self, config: ProductionConfig): self.config = config self.security = HTTPBearer() self.valid_api_keys: set = self._load_api_keys() def _load_api_keys(self) -> set: """Load valid API keys from configuration""" # In production, load from secure key management system api_keys_file = Path(self.config.config_dir) / "api_keys.txt" if api_keys_file.exists(): return set(api_keys_file.read_text().strip().split('\n')) return set() def create_access_token(self, data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str: """Create JWT access token""" to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, self.config.jwt_secret, algorithm="HS256") return encoded_jwt def verify_token(self, credentials: HTTPAuthorizationCredentials) -> Dict[str, Any]: """Verify JWT token""" try: payload = jwt.decode( credentials.credentials, self.config.jwt_secret, algorithms=["HS256"] ) return payload except jwt.PyJWTError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials" ) def verify_api_key(self, api_key: str) -> bool: """Verify API key""" return api_key in self.valid_api_keys # Security middleware class SecurityMiddleware: """Production security middleware""" def __init__(self, app: FastMCP, auth: ProductionAuth): self.app = app self.auth = auth self._setup_security_headers() def _setup_security_headers(self): """Configure security headers""" @self.app.middleware("http") async def add_security_headers(request, call_next): response = await call_next(request) # Security headers response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" response.headers["Content-Security-Policy"] = "default-src 'self'" return response ``` ## πŸ“Š Monitoring and Observability ### Metrics Collection ```python # src/mcp_esptool_server/monitoring/metrics.py from prometheus_client import Counter, Histogram, Gauge, start_http_server from typing import Dict, Any import time class ProductionMetrics: """Production metrics collection""" def __init__(self, config: ProductionConfig): self.config = config # Operation metrics self.operations_total = Counter( 'esptool_operations_total', 'Total number of ESPTool operations', ['operation_type', 'status', 'server_id'] ) self.operation_duration = Histogram( 'esptool_operation_duration_seconds', 'Duration of ESPTool operations', ['operation_type', 'server_id'] ) # Device metrics self.connected_devices = Gauge( 'esptool_connected_devices', 'Number of connected ESP devices', ['server_id'] ) self.device_operations = Counter( 'esptool_device_operations_total', 'Total device operations by port', ['port', 'operation', 'status', 'server_id'] ) # System metrics self.active_connections = Gauge( 'esptool_active_connections', 'Number of active MCP connections', ['server_id'] ) self.memory_usage = Gauge( 'esptool_memory_usage_bytes', 'Memory usage in bytes', ['server_id'] ) # Error metrics self.errors_total = Counter( 'esptool_errors_total', 'Total number of errors', ['error_type', 'component', 'server_id'] ) if config.enable_metrics: start_http_server(config.metrics_port) def record_operation(self, operation_type: str, duration: float, status: str): """Record operation metrics""" self.operations_total.labels( operation_type=operation_type, status=status, server_id=self.config.server_id ).inc() self.operation_duration.labels( operation_type=operation_type, server_id=self.config.server_id ).observe(duration) def record_device_operation(self, port: str, operation: str, status: str): """Record device-specific operation""" self.device_operations.labels( port=port, operation=operation, status=status, server_id=self.config.server_id ).inc() def update_connected_devices(self, count: int): """Update connected devices count""" self.connected_devices.labels(server_id=self.config.server_id).set(count) def record_error(self, error_type: str, component: str): """Record error occurrence""" self.errors_total.labels( error_type=error_type, component=component, server_id=self.config.server_id ).inc() ``` ### Logging Configuration ```python # src/mcp_esptool_server/logging/production.py import logging import logging.config from pathlib import Path import json LOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "json": { "()": "pythonjsonlogger.jsonlogger.JsonFormatter", "format": "%(asctime)s %(name)s %(levelname)s %(message)s %(pathname)s %(lineno)d" }, "standard": { "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s" } }, "handlers": { "console": { "level": "INFO", "class": "logging.StreamHandler", "formatter": "standard", "stream": "ext://sys.stdout" }, "file": { "level": "DEBUG", "class": "logging.handlers.RotatingFileHandler", "formatter": "json", "filename": "/app/logs/esptool-server.log", "maxBytes": 10485760, # 10MB "backupCount": 5 }, "error_file": { "level": "ERROR", "class": "logging.handlers.RotatingFileHandler", "formatter": "json", "filename": "/app/logs/esptool-errors.log", "maxBytes": 10485760, "backupCount": 5 } }, "loggers": { "mcp_esptool_server": { "level": "DEBUG", "handlers": ["console", "file", "error_file"], "propagate": False }, "esptool": { "level": "INFO", "handlers": ["file"], "propagate": False } }, "root": { "level": "INFO", "handlers": ["console"] } } def setup_production_logging(config: ProductionConfig): """Set up production logging configuration""" # Ensure log directory exists log_dir = Path("/app/logs") log_dir.mkdir(exist_ok=True) # Update logging level from configuration LOGGING_CONFIG["handlers"]["console"]["level"] = config.log_level LOGGING_CONFIG["loggers"]["mcp_esptool_server"]["level"] = config.log_level # Configure logging logging.config.dictConfig(LOGGING_CONFIG) # Set up structured logging context logger = logging.getLogger("mcp_esptool_server") logger.info("Production logging initialized", extra={ "server_id": config.server_id, "log_level": config.log_level, "environment": "production" }) ``` ## πŸš€ Deployment Automation ### Kubernetes Deployment ```yaml # k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: mcp-esptool-server labels: app: mcp-esptool-server spec: replicas: 3 selector: matchLabels: app: mcp-esptool-server template: metadata: labels: app: mcp-esptool-server spec: containers: - name: mcp-esptool-server image: mcp-esptool-server:latest ports: - containerPort: 8080 - containerPort: 9090 # Metrics env: - name: SERVER_ID valueFrom: fieldRef: fieldPath: metadata.name - name: DATABASE_URL valueFrom: secretKeyRef: name: esptool-secrets key: database-url - name: REDIS_URL valueFrom: secretKeyRef: name: esptool-secrets key: redis-url - name: JWT_SECRET valueFrom: secretKeyRef: name: esptool-secrets key: jwt-secret volumeMounts: - name: device-access mountPath: /dev/serial - name: config mountPath: /app/config readOnly: true - name: data mountPath: /app/data resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 volumes: - name: device-access hostPath: path: /dev/serial - name: config configMap: name: esptool-config - name: data persistentVolumeClaim: claimName: esptool-data --- apiVersion: v1 kind: Service metadata: name: mcp-esptool-service spec: selector: app: mcp-esptool-server ports: - name: http port: 80 targetPort: 8080 - name: metrics port: 9090 targetPort: 9090 type: ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: mcp-esptool-ingress annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: - hosts: - esptool.yourdomain.com secretName: esptool-tls rules: - host: esptool.yourdomain.com http: paths: - path: / pathType: Prefix backend: service: name: mcp-esptool-service port: number: 80 ``` ### CI/CD Pipeline ```yaml # .github/workflows/production-deploy.yml name: Production Deployment on: push: tags: - 'v*' env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | pip install uv uv venv uv pip install -e ".[dev,testing]" - name: Run tests run: | uv run pytest tests/ --cov=src --cov-fail-under=85 - name: Security scan run: | uv run bandit -r src/ uv run safety check build: needs: test runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=tag type=raw,value=latest - name: Build and push uses: docker/build-push-action@v5 with: context: . target: production push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max deploy: needs: build runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 - name: Configure kubectl run: | echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig export KUBECONFIG=kubeconfig - name: Deploy to production run: | kubectl set image deployment/mcp-esptool-server \ mcp-esptool-server=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} kubectl rollout status deployment/mcp-esptool-server --timeout=300s - name: Verify deployment run: | kubectl get pods -l app=mcp-esptool-server kubectl logs -l app=mcp-esptool-server --tail=50 ``` ## πŸ“‹ Production Checklist ### Pre-Deployment - [ ] **Security Configuration** - [ ] Change default passwords and secrets - [ ] Configure JWT secrets - [ ] Set up API key management - [ ] Configure TLS certificates - [ ] Review firewall rules - [ ] **Infrastructure Setup** - [ ] Database cluster configured - [ ] Redis cache configured - [ ] Load balancer configured - [ ] Monitoring stack deployed - [ ] Log aggregation configured - [ ] **Hardware Access** - [ ] ESP device permissions configured - [ ] USB device access verified - [ ] Device path mapping documented - [ ] Hardware redundancy planned ### Post-Deployment - [ ] **Monitoring Setup** - [ ] Grafana dashboards configured - [ ] Alerting rules configured - [ ] Log analysis configured - [ ] Performance baselines established - [ ] **Operational Procedures** - [ ] Backup procedures documented - [ ] Disaster recovery tested - [ ] Scaling procedures documented - [ ] Maintenance windows scheduled - [ ] **Team Training** - [ ] Operations team trained - [ ] Troubleshooting guides created - [ ] Escalation procedures documented - [ ] User documentation published This production deployment guide ensures enterprise-grade reliability, security, and scalability for the FastMCP ESPTool server in production environments. `β˜… Insight ─────────────────────────────────────` **Enterprise-Grade Deployment**: This production guide demonstrates how to scale the FastMCP ESPTool server from development to enterprise production environments with proper security, monitoring, and operational procedures. **Hardware Integration Challenges**: The unique aspect of this deployment is managing physical ESP device access in containerized environments, requiring careful consideration of USB device mapping and permissions across different deployment platforms. **Observability Excellence**: The comprehensive monitoring, logging, and metrics collection ensures production visibility and enables proactive problem resolution, critical for embedded development workflows where hardware issues can be difficult to diagnose. `─────────────────────────────────────────────────` [{"content": "Create project initialization and setup documentation", "status": "completed", "activeForm": "Creating project initialization and setup documentation"}, {"content": "Design concrete implementation examples", "status": "completed", "activeForm": "Designing concrete implementation examples"}, {"content": "Create production deployment guide", "status": "completed", "activeForm": "Creating production deployment guide"}]