From fd2b14028af821d7e26155ef7c5ec41439383f87 Mon Sep 17 00:00:00 2001 From: OhMyApps <74984020+GiGiDKR@users.noreply.github.com> Date: Sun, 29 Jun 2025 00:00:47 +0200 Subject: [PATCH] feat: add timezone synchronization volume and update deployment scripts for health checks Update the Docker README and create a Docker Deployment guide in the docs folder --- docker-compose.yml | 1 + docker/README.md | 77 ++++-- docker/scripts/deploy.ps1 | 13 +- docker/scripts/deploy.sh | 1 + docker/scripts/healthcheck.py | 29 -- docs/docker-deployment.md | 500 ++++++++++++++++++++++++++++++++++ 6 files changed, 563 insertions(+), 58 deletions(-) create mode 100644 docs/docker-deployment.md diff --git a/docker-compose.yml b/docker-compose.yml index 130e37a..1acd79c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,7 @@ services: volumes: - ./logs:/app/logs - zen-mcp-config:/app/conf + - /etc/localtime:/etc/localtime:ro # Network configuration networks: diff --git a/docker/README.md b/docker/README.md index da26b5f..bf8c1e2 100644 --- a/docker/README.md +++ b/docker/README.md @@ -112,10 +112,34 @@ The container includes health checks that verify: - Log directory is writable - API keys are configured -## Volumes +## Volumes and Persistent Data -- `./logs:/app/logs` - Persistent log storage -- `zen-mcp-config:/app/conf` - Configuration persistence +The Docker setup includes persistent volumes to preserve data between container runs: + +- **`./logs:/app/logs`** - Persistent log storage (local folder mount) +- **`zen-mcp-config:/app/conf`** - Configuration persistence (named Docker volume) +- **`/etc/localtime:/etc/localtime:ro`** - Host timezone synchronization (read-only) + +### How Persistent Volumes Work + +The `zen-mcp` service (used by `zen-docker-compose` and Docker Compose commands) mounts the named volume `zen-mcp-config` persistently. All data placed in `/app/conf` inside the container is preserved between runs thanks to this Docker volume. + +In the `docker-compose.yml` file, you will find: + +```yaml +volumes: + - ./logs:/app/logs + - zen-mcp-config:/app/conf + - /etc/localtime:/etc/localtime:ro +``` + +and the named volume definition: + +```yaml +volumes: + zen-mcp-config: + driver: local +``` ## Security @@ -189,9 +213,7 @@ docker run --rm -i --env-file .env zen-mcp-server:latest 2>&1 | tee docker.log ## MCP Integration (Claude Desktop) -### Configuration File Setup - -Add to your Claude Desktop MCP configuration: +### Recommended Configuration (docker run) ```json { @@ -206,21 +228,14 @@ Add to your Claude Desktop MCP configuration: "/absolute/path/to/zen-mcp-server/.env", "-v", "/absolute/path/to/zen-mcp-server/logs:/app/logs", - "zen-mcp-server:latest", - "python", - "server.py" - ], - "env": { - "DOCKER_BUILDKIT": "1" - } + "zen-mcp-server:latest" + ] } } } ``` -### Windows MCP Configuration - -For Windows users, use forward slashes in paths: +### Windows Example ```json { @@ -235,13 +250,27 @@ For Windows users, use forward slashes in paths: "C:/Users/YourName/path/to/zen-mcp-server/.env", "-v", "C:/Users/YourName/path/to/zen-mcp-server/logs:/app/logs", - "zen-mcp-server:latest", - "python", - "server.py" - ], - "env": { - "DOCKER_BUILDKIT": "1" - } + "zen-mcp-server:latest" + ] + } + } +} +``` + +### Advanced Option: docker-compose run (uses compose configuration) + +```json +{ + "servers": { + "zen-docker": { + "command": "docker-compose", + "args": [ + "-f", + "/absolute/path/to/zen-mcp-server/docker-compose.yml", + "run", + "--rm", + "zen-mcp" + ] } } } @@ -249,7 +278,7 @@ For Windows users, use forward slashes in paths: ### Environment File Template -Create `.env` file with at least one API key: +Create a `.env` file with at least one API key: ```bash # Required: At least one API key diff --git a/docker/scripts/deploy.ps1 b/docker/scripts/deploy.ps1 index 97ed1d2..92ee4cd 100644 --- a/docker/scripts/deploy.ps1 +++ b/docker/scripts/deploy.ps1 @@ -158,7 +158,7 @@ try { if (!$SkipHealthCheck) { Write-ColorText "Waiting for service to be healthy..." -Color Green - # Simple timeout approach for health check + # Try simple timeout first, then use exponential backoff if needed $timeout = $HealthCheckTimeout $elapsed = 0 $healthy = $false @@ -182,10 +182,13 @@ if (!$SkipHealthCheck) { } if (!$healthy) { - Write-ColorText "Service failed to become healthy within timeout" -Color Red - Write-ColorText "Checking logs:" -Color Yellow - docker-compose logs zen-mcp - exit 1 + # Use exponential backoff retry mechanism + if (!(Wait-ForHealth)) { + Write-ColorText "Service failed to become healthy" -Color Red + Write-ColorText "Checking logs:" -Color Yellow + docker-compose logs zen-mcp + exit 1 + } } } diff --git a/docker/scripts/deploy.sh b/docker/scripts/deploy.sh index fba78a6..b207c5c 100644 --- a/docker/scripts/deploy.sh +++ b/docker/scripts/deploy.sh @@ -81,6 +81,7 @@ docker-compose up -d # Wait for health check echo -e "${GREEN}Waiting for service to be healthy...${NC}" timeout 60 bash -c 'while [[ "$(docker-compose ps -q zen-mcp | xargs docker inspect -f "{{.State.Health.Status}}")" != "healthy" ]]; do sleep 2; done' || { + wait_for_health echo -e "${RED}Service failed to become healthy${NC}" echo -e "${YELLOW}Checking logs:${NC}" docker-compose logs zen-mcp diff --git a/docker/scripts/healthcheck.py b/docker/scripts/healthcheck.py index 118c835..2ad4c23 100644 --- a/docker/scripts/healthcheck.py +++ b/docker/scripts/healthcheck.py @@ -7,8 +7,6 @@ import os import subprocess import sys -import openai - def check_process(): """Check if the main server process is running""" @@ -78,33 +76,6 @@ def check_environment(): print(f"API key {key} appears too short or invalid", file=sys.stderr) return False - # Optionally, try a minimal connectivity check for OpenAI and Google Gemini - # (only if their API keys are present) - try: - if os.getenv("OPENAI_API_KEY"): - openai.api_key = os.getenv("OPENAI_API_KEY") - try: - openai.Model.list() - except Exception as e: - print(f"OpenAI connectivity check failed: {e}", file=sys.stderr) - return False - except ImportError: - pass # Already checked in check_python_imports - - try: - if os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY"): - import google.genai as genai - - key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY") - genai.configure(api_key=key) - try: - genai.list_models() - except Exception as e: - print(f"Google Gemini connectivity check failed: {e}", file=sys.stderr) - return False - except ImportError: - pass # Already checked in check_python_imports - return True diff --git a/docs/docker-deployment.md b/docs/docker-deployment.md new file mode 100644 index 0000000..fc94d6f --- /dev/null +++ b/docs/docker-deployment.md @@ -0,0 +1,500 @@ +# Docker Deployment Guide + +This guide covers deploying Zen MCP Server using Docker and Docker Compose for production environments. + +## Quick Start + +1. **Clone the repository**: + ```bash + git clone https://github.com/BeehiveInnovations/zen-mcp-server.git + cd zen-mcp-server + ``` + +2. **Configure environment variables**: + ```bash + cp .env.example .env + # Edit .env with your API keys + ``` + +3. **Deploy with Docker Compose**: + ```bash + # Linux/macOS + ./docker/scripts/deploy.sh + + # Windows PowerShell + .\docker\scripts\deploy.ps1 + ``` + +## Environment Configuration + +### Required API Keys + +At least one API key must be configured in your `.env` file: + +```env +# Google Gemini (Recommended) +GEMINI_API_KEY=your_gemini_api_key_here + +# OpenAI +OPENAI_API_KEY=your_openai_api_key_here + +# X.AI GROK +XAI_API_KEY=your_xai_api_key_here + +# OpenRouter (unified access) +OPENROUTER_API_KEY=your_openrouter_api_key_here + +# Additional providers +DIAL_API_KEY=your_dial_api_key_here +DIAL_API_HOST=your_dial_host +``` + +### Optional Configuration + +```env +# Default model selection +DEFAULT_MODEL=auto + +# Logging +LOG_LEVEL=INFO +LOG_MAX_SIZE=10MB +LOG_BACKUP_COUNT=5 + +# Advanced settings +DEFAULT_THINKING_MODE_THINKDEEP=high +DISABLED_TOOLS= +MAX_MCP_OUTPUT_TOKENS= + +# Timezone +TZ=UTC +``` + +## Deployment Scripts + +### Linux/macOS Deployment + +Use the provided bash script for robust deployment: + +```bash +./docker/scripts/deploy.sh +``` + +**Features:** +- ✅ Environment validation +- ✅ Exponential backoff health checks +- ✅ Automatic log management +- ✅ Service status monitoring + +### Windows PowerShell Deployment + +Use the PowerShell script for Windows environments: + +```powershell +.\docker\scripts\deploy.ps1 +``` + +**Additional Options:** +```powershell +# Skip health check +.\docker\scripts\deploy.ps1 -SkipHealthCheck + +# Custom timeout +.\docker\scripts\deploy.ps1 -HealthCheckTimeout 120 +``` + +## Docker Architecture + +### Multi-Stage Build + +The Dockerfile uses a multi-stage build for optimal image size: + +1. **Builder Stage**: Installs dependencies and creates virtual environment +2. **Runtime Stage**: Copies only necessary files for minimal footprint + +### Security Features + +- **Non-root user**: Runs as `zenuser` (UID/GID 1000) +- **Read-only filesystem**: Container filesystem is immutable +- **No new privileges**: Prevents privilege escalation +- **Secure tmpfs**: Temporary directories with strict permissions + +### Resource Management + +Default resource limits: +```yaml +deploy: + resources: + limits: + memory: 512M + cpus: '0.5' + reservations: + memory: 256M + cpus: '0.25' +``` + +## Service Management + +### Starting the Service + +```bash +# Start in background +docker-compose up -d + +# Start with logs +docker-compose up +``` + +### Monitoring + +```bash +# View service status +docker-compose ps + +# Follow logs +docker-compose logs -f zen-mcp + +# View health status +docker inspect zen-mcp-server --format='{{.State.Health.Status}}' +``` + +### Stopping the Service + +```bash +# Graceful stop +docker-compose down + +# Force stop +docker-compose down --timeout 10 +``` + +## Health Checks + +The container includes comprehensive health checks: + +- **Process check**: Verifies server.py is running +- **Import check**: Validates critical Python modules +- **Directory check**: Ensures log directory is writable +- **API check**: Tests provider connectivity + +Health check configuration: +```yaml +healthcheck: + test: ["CMD", "python", "/usr/local/bin/healthcheck.py"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s +``` + +## Persistent Data + +### Volumes + +- **Logs**: `./logs:/app/logs` - Application logs +- **Config**: `zen-mcp-config:/app/conf` - Configuration persistence +- **Time sync**: `/etc/localtime:/etc/localtime:ro` - Host timezone sync + +**Note:** The `zen-mcp-config` is a named Docker volume that persists configuration data between container restarts. All data placed in `/app/conf` inside the container is preserved thanks to this persistent volume. This applies to both `docker-compose run` and `docker-compose up` commands. + +### Log Management + +Logs are automatically rotated with configurable retention: + +```env +LOG_MAX_SIZE=10MB # Maximum log file size +LOG_BACKUP_COUNT=5 # Number of backup files to keep +``` + +## Networking + +### Default Configuration + +- **Network**: `zen-network` (bridge) +- **Subnet**: `172.20.0.0/16` +- **Isolation**: Container runs in isolated network + +### Port Exposure + +By default, no ports are exposed. The MCP server communicates via stdio when used with Claude Desktop or other MCP clients. + +For external access (advanced users): +```yaml +ports: + - "3000:3000" # Add to service configuration if needed +``` + +## Troubleshooting + +### Common Issues + +**1. Health check failures:** +```bash +# Check logs +docker-compose logs zen-mcp + +# Manual health check +docker exec zen-mcp-server python /usr/local/bin/healthcheck.py +``` + +**2. Permission errors:** +```bash +# Fix log directory permissions +sudo chown -R 1000:1000 ./logs +``` + +**3. Environment variables not loaded:** +```bash +# Verify .env file exists and is readable +ls -la .env +cat .env +``` + +**4. API key validation errors:** +```bash +# Check environment variables in container +docker exec zen-mcp-server env | grep -E "(GEMINI|OPENAI|XAI)" +``` + +### Debug Mode + +Enable verbose logging for troubleshooting: + +```env +LOG_LEVEL=DEBUG +``` + +## Production Considerations + +### Security + +1. **Use Docker secrets** for API keys in production: + ```yaml + secrets: + gemini_api_key: + external: true + ``` + +2. **Enable AppArmor/SELinux** if available + +3. **Regular security updates**: + ```bash + docker-compose pull + docker-compose up -d + ``` + +### Monitoring + +Consider integrating with monitoring solutions: + +- **Prometheus**: Health check metrics +- **Grafana**: Log visualization +- **AlertManager**: Health status alerts + +### Backup + +Backup persistent volumes: +```bash +# Backup configuration +docker run --rm -v zen-mcp-config:/data -v $(pwd):/backup alpine tar czf /backup/config-backup.tar.gz -C /data . + +# Restore configuration +docker run --rm -v zen-mcp-config:/data -v $(pwd):/backup alpine tar xzf /backup/config-backup.tar.gz -C /data +``` + +## Performance Tuning + +### Resource Optimization + +Adjust limits based on your workload: + +```yaml +deploy: + resources: + limits: + memory: 1G # Increase for heavy workloads + cpus: '1.0' # More CPU for concurrent requests +``` + +### Memory Management + +Monitor memory usage: +```bash +docker stats zen-mcp-server +``` + +Adjust Python memory settings if needed: +```env +PYTHONMALLOC=pymalloc +MALLOC_ARENA_MAX=2 +``` + +## Integration with Claude Desktop + +Configure Claude Desktop to use the containerized server. **Choose one of the configurations below based on your needs:** + +### Option 1: Direct Docker Run (Recommended) + +**The simplest and most reliable option for most users.** + +```json +{ + "mcpServers": { + "zen-mcp": { + "command": "docker", + "args": [ + "run", + "--rm", + "-i", + "--env-file", + "/absolute/path/to/zen-mcp-server/.env", + "-v", + "/absolute/path/to/zen-mcp-server/logs:/app/logs", + "zen-mcp-server:latest" + ] + } + } +} +``` + +**Exemple Windows** : +```json +{ + "mcpServers": { + "zen-mcp": { + "command": "docker", + "args": [ + "run", + "--rm", + "-i", + "--env-file", + "C:/path/to/zen-mcp-server/.env", + "-v", + "C:/path/to/zen-mcp-server/logs:/app/logs", + "zen-mcp-server:latest" + ] + } + } +} +``` + +### Option 2: Docker Compose Run (one-shot, uses docker-compose.yml) + +**To use the advanced configuration from docker-compose.yml without a persistent container.** + +```json +{ + "mcpServers": { + "zen-mcp": { + "command": "docker-compose", + "args": [ + "-f", "/absolute/path/to/zen-mcp-server/docker-compose.yml", + "run", "--rm", "zen-mcp" + ] + } + } +} +``` + +### Option 3: Inline Environment Variables (Advanced) + +**For highly customized needs.** + +```json +{ + "mcpServers": { + "zen-mcp": { + "command": "docker", + "args": [ + "run", + "--rm", + "-i", + "-e", "GEMINI_API_KEY=your_key_here", + "-e", "LOG_LEVEL=INFO", + "-e", "DEFAULT_MODEL=auto", + "-v", "/path/to/logs:/app/logs", + "zen-mcp-server:latest" + ] + } + } +} +``` + +### Configuration Notes + +**Important notes:** +- Replace `/absolute/path/to/zen-mcp-server` with the actual path to your project. +- Always use forward slashes `/` for Docker volumes, even on Windows. +- Ensure the `.env` file exists and contains your API keys. +- **Persistent volumes**: Docker Compose options (Options 2) automatically use the `zen-mcp-config` named volume for persistent configuration storage. + +**Environment file requirements:** +```env +# At least one API key is required +GEMINI_API_KEY=your_gemini_key +OPENAI_API_KEY=your_openai_key +# ... other keys +``` + +**Troubleshooting:** +- If Option 1 fails: check that the Docker image exists (`docker images zen-mcp-server`). +- If Option 2 fails: verify the compose file path and ensure the service is not already in use. +- Permission issues: make sure the `logs` folder is writable. + +## Advanced Configuration + +### Custom Networks + +For complex deployments: +```yaml +networks: + zen-network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + gateway: 172.20.0.1 +``` + +### Multiple Instances + +Run multiple instances with different configurations: +```bash +# Copy compose file +cp docker-compose.yml docker-compose.dev.yml + +# Modify service names and ports +# Deploy with custom compose file +docker-compose -f docker-compose.dev.yml up -d +``` + +## Migration and Updates + +### Updating the Server + +```bash +# Pull latest changes +git pull origin main + +# Rebuild and restart +docker-compose down +docker-compose build --no-cache +./docker/scripts/deploy.sh +``` + +### Data Migration + +When upgrading, configuration is preserved in the named volume `zen-mcp-config`. + +For major version upgrades, check the [CHANGELOG](../CHANGELOG.md) for breaking changes. + +## Support + +For any questions, open an issue on GitHub or consult the official documentation. + + +--- + +**Next Steps:** +- Review the [Configuration Guide](configuration.md) for detailed environment variable options +- Check [Advanced Usage](advanced-usage.md) for custom model configurations +- See [Troubleshooting](troubleshooting.md) for common issues and solutions