add docker support

This commit is contained in:
bright8192 2025-07-01 17:22:45 +08:00
parent 7b12024005
commit a2f4c709c5
6 changed files with 689 additions and 0 deletions

80
.dockerignore Normal file
View File

@ -0,0 +1,80 @@
# Git related files
.git
.gitignore
.gitattributes
# Python related
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual environments
venv/
env/
ENV/
.venv/
# IDEs and editors
.vscode/
.idea/
*.swp
*.swo
*~
# Operating systems
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Log files
logs/
*.log
# Temporary files
tmp/
temp/
.tmp/
# Docker related
Dockerfile*
.dockerignore
docker-compose*.yml
Makefile
# Documentation
*.md
docs/
# Configuration files (avoid including sensitive information)
config.yaml
*.yaml
!config.yaml.sample
config/
.env
.env.*
# Backup files
*.bak
*.backup

67
Dockerfile Normal file
View File

@ -0,0 +1,67 @@
# Use official Python runtime as base image
FROM python:3.11-slim as builder
# Set working directory
WORKDIR /app
# Install system dependencies for building
RUN apt-get update && apt-get install -y \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements file
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir --user -r requirements.txt
# Production stage
FROM python:3.11-slim
# Create non-root user for security
RUN groupadd -r mcpuser && useradd -r -g mcpuser mcpuser
# Set working directory
WORKDIR /app
# Install only runtime system dependencies (including bash for entrypoint script)
RUN apt-get update && apt-get install -y \
ca-certificates \
bash \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Copy Python packages from builder stage
COPY --from=builder /root/.local /home/mcpuser/.local
# Copy application code
COPY server.py .
COPY config.yaml.sample .
COPY docker-entrypoint.sh .
# Make entrypoint script executable
RUN chmod +x docker-entrypoint.sh
# Create necessary directories and set permissions
RUN mkdir -p /app/logs /app/config \
&& chown -R mcpuser:mcpuser /app
# Set environment variables
ENV PATH=/home/mcpuser/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Switch to non-root user
USER mcpuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080')" || exit 1
# Set entrypoint and default command
ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["python", "server.py", "--config", "/app/config/config.yaml"]

114
Makefile Normal file
View File

@ -0,0 +1,114 @@
# ESXi MCP Server Makefile
# Provides convenient commands for Docker operations
# Variables
DOCKER_IMAGE = esxi-mcp-server
DOCKER_TAG = latest
CONTAINER_NAME = esxi-mcp-server
COMPOSE_FILE = docker-compose.yml
# Default target
.PHONY: help
help: ## Show this help message
@echo "ESXi MCP Server Docker Commands"
@echo "================================"
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
# Build commands
.PHONY: build
build: ## Build Docker image
docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
.PHONY: build-no-cache
build-no-cache: ## Build Docker image without cache
docker build --no-cache -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
# Run commands
.PHONY: run
run: ## Run container with docker-compose
docker-compose -f $(COMPOSE_FILE) up -d
.PHONY: run-logs
run-logs: ## Run container with docker-compose and show logs
docker-compose -f $(COMPOSE_FILE) up
.PHONY: stop
stop: ## Stop running containers
docker-compose -f $(COMPOSE_FILE) down
.PHONY: restart
restart: ## Restart containers
docker-compose -f $(COMPOSE_FILE) restart
# Development commands
.PHONY: logs
logs: ## Show container logs
docker-compose -f $(COMPOSE_FILE) logs -f
.PHONY: shell
shell: ## Open bash shell in running container
docker exec -it $(CONTAINER_NAME) bash
.PHONY: status
status: ## Show container status
docker-compose -f $(COMPOSE_FILE) ps
# Maintenance commands
.PHONY: clean
clean: ## Remove containers and volumes
docker-compose -f $(COMPOSE_FILE) down -v
docker rmi $(DOCKER_IMAGE):$(DOCKER_TAG) 2>/dev/null || true
.PHONY: clean-all
clean-all: ## Remove everything including images and volumes
docker-compose -f $(COMPOSE_FILE) down -v --rmi all
docker system prune -f
.PHONY: update
update: ## Update container (rebuild and restart)
make stop
make build
make run
# Setup commands
.PHONY: setup
setup: ## Initial setup - create directories and sample config
mkdir -p logs config
cp config.yaml.sample config/config.yaml || true
@echo "Setup complete! Please edit config/config.yaml with your vCenter details."
.PHONY: env-example
env-example: ## Create .env.example file
@echo "# VMware vCenter/ESXi Configuration" > .env.example
@echo "VCENTER_HOST=your-vcenter-ip-or-hostname" >> .env.example
@echo "VCENTER_USER=administrator@vsphere.local" >> .env.example
@echo "VCENTER_PASSWORD=your-password" >> .env.example
@echo "" >> .env.example
@echo "# Optional VMware Configuration" >> .env.example
@echo "VCENTER_DATACENTER=your-datacenter-name" >> .env.example
@echo "VCENTER_CLUSTER=your-cluster-name" >> .env.example
@echo "VCENTER_DATASTORE=your-datastore-name" >> .env.example
@echo "VCENTER_NETWORK=VM Network" >> .env.example
@echo "" >> .env.example
@echo "# Security Settings" >> .env.example
@echo "VCENTER_INSECURE=true" >> .env.example
@echo "MCP_API_KEY=your-api-key-here" >> .env.example
@echo "" >> .env.example
@echo "# Logging Configuration" >> .env.example
@echo "MCP_LOG_LEVEL=INFO" >> .env.example
@echo ".env.example file created!"
# Health check
.PHONY: health
health: ## Check container health
docker exec $(CONTAINER_NAME) python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080')" && echo "✅ Health check passed" || echo "❌ Health check failed"
# Quick start
.PHONY: quick-start
quick-start: ## Quick start with environment variables (requires .env file)
@echo "Starting with environment variables..."
@echo "Make sure you have created a .env file with your configuration!"
docker-compose -f $(COMPOSE_FILE) up -d
.PHONY: dev
dev: build run-logs ## Development mode: build and run with logs

286
README_DOCKER.md Normal file
View File

@ -0,0 +1,286 @@
# ESXi MCP Server - Docker Guide
This guide provides instructions for running the ESXi MCP Server using Docker and Docker Compose.
## Quick Start
### Prerequisites
- Docker 20.10+
- Docker Compose 2.0+
- Access to a VMware vCenter Server or ESXi host
### 1. Setup
```bash
# Clone the repository
git clone <repository-url>
cd esxi-mcp-server
# Create necessary directories and configuration
make setup
# Create environment variables file (optional)
make env-example
cp .env.example .env
```
### 2. Configuration
You have two options for configuration:
#### Option A: Configuration File (Recommended)
Edit `config/config.yaml`:
```yaml
vcenter_host: "your-vcenter-ip"
vcenter_user: "administrator@vsphere.local"
vcenter_password: "your-password"
datacenter: "your-datacenter"
cluster: "your-cluster"
datastore: "your-datastore"
network: "VM Network"
insecure: true
api_key: "your-api-key"
log_level: "INFO"
```
#### Option B: Environment Variables
Edit `.env` file:
```bash
VCENTER_HOST=your-vcenter-ip
VCENTER_USER=administrator@vsphere.local
VCENTER_PASSWORD=your-password
VCENTER_DATACENTER=your-datacenter
VCENTER_CLUSTER=your-cluster
VCENTER_DATASTORE=your-datastore
VCENTER_NETWORK=VM Network
VCENTER_INSECURE=true
MCP_API_KEY=your-api-key
MCP_LOG_LEVEL=INFO
```
### 3. Run the Server
```bash
# Build and run
make dev
# Or run in background
make run
# Check status
make status
# View logs
make logs
```
## Available Commands
Use `make help` to see all available commands:
```bash
make help
```
### Build Commands
- `make build` - Build Docker image
- `make build-no-cache` - Build without cache
### Run Commands
- `make run` - Run in background
- `make run-logs` - Run with logs
- `make stop` - Stop containers
- `make restart` - Restart containers
### Development Commands
- `make dev` - Development mode (build + run with logs)
- `make logs` - Show logs
- `make shell` - Open bash shell in container
- `make status` - Show container status
- `make health` - Check container health
### Maintenance Commands
- `make clean` - Remove containers and volumes
- `make clean-all` - Remove everything
- `make update` - Rebuild and restart
## Docker Architecture
### Multi-stage Build
The Dockerfile uses a multi-stage build process:
1. **Builder Stage**: Installs build dependencies and Python packages
2. **Production Stage**: Creates a minimal runtime image
### Security Features
- Runs as non-root user (`mcpuser`)
- Minimal base image (python:3.11-slim)
- Only necessary runtime dependencies
- Configurable resource limits
### Directory Structure
```
/app/
├── server.py # Main application
├── config.yaml.sample # Configuration template
├── docker-entrypoint.sh # Startup script
├── config/ # Configuration directory (mounted)
│ └── config.yaml # Runtime configuration
└── logs/ # Log directory (mounted)
└── vmware_mcp.log # Application logs
```
## Configuration Options
### Volume Mounts
- `./config.yaml:/app/config/config.yaml:ro` - Configuration file (read-only)
- `./logs:/app/logs` - Log directory
### Environment Variables
All configuration options can be set via environment variables:
| Variable | Description | Default |
|----------|-------------|---------|
| `VCENTER_HOST` | vCenter/ESXi hostname | Required |
| `VCENTER_USER` | Username | Required |
| `VCENTER_PASSWORD` | Password | Required |
| `VCENTER_DATACENTER` | Datacenter name | Auto-detect |
| `VCENTER_CLUSTER` | Cluster name | Auto-detect |
| `VCENTER_DATASTORE` | Datastore name | Auto-detect |
| `VCENTER_NETWORK` | Network name | VM Network |
| `VCENTER_INSECURE` | Skip SSL verification | true |
| `MCP_API_KEY` | API authentication key | None |
| `MCP_LOG_LEVEL` | Log level | INFO |
### Resource Limits
Default resource limits in docker-compose.yml:
- **Memory**: 512MB limit, 256MB reserved
- **CPU**: 0.5 cores limit, 0.25 cores reserved
## Health Checks
The container includes automatic health checks:
- **Interval**: 30 seconds
- **Timeout**: 10 seconds
- **Retries**: 3
- **Start Period**: 40 seconds
Check health manually:
```bash
make health
```
## Networking
The server exposes:
- **Port 8080**: HTTP API endpoint
- **Path `/sse`**: Server-Sent Events endpoint
- **Path `/sse/messages`**: JSON-RPC messages endpoint
## Troubleshooting
### Check Logs
```bash
make logs
```
### Check Container Status
```bash
make status
```
### Open Shell in Container
```bash
make shell
```
### Common Issues
1. **Configuration not found**: Ensure `config/config.yaml` exists or environment variables are set
2. **Permission denied**: Check that the `logs` directory is writable
3. **Connection failed**: Verify vCenter/ESXi connectivity and credentials
4. **Health check failed**: Check if the server is responding on port 8080
### Debug Mode
Run with debug logging:
```bash
# Set in .env file
MCP_LOG_LEVEL=DEBUG
# Or in config.yaml
log_level: "DEBUG"
```
## Production Deployment
### Security Recommendations
1. Use a dedicated user account for vCenter access
2. Enable API key authentication
3. Use valid SSL certificates (set `insecure: false`)
4. Limit container resources
5. Use Docker secrets for sensitive data
### High Availability
For production deployments, consider:
- Running multiple container instances
- Using a load balancer
- Implementing persistent storage for logs
- Setting up monitoring and alerting
## Examples
### Basic Usage
```bash
# Start the server
make run
# Check if it's working
curl http://localhost:8080/sse
```
### API Authentication
```bash
# With API key
curl -H "Authorization: Bearer your-api-key" http://localhost:8080/sse
```
### Development
```bash
# Development workflow
make build
make dev
# Make changes to code
# Rebuild and restart
make update
```

49
docker-compose.yml Normal file
View File

@ -0,0 +1,49 @@
version: '3.8'
services:
esxi-mcp-server:
build:
context: .
dockerfile: Dockerfile
container_name: esxi-mcp-server
restart: unless-stopped
ports:
- "8080:8080"
volumes:
# Mount configuration file
- ./config.yaml:/app/config/config.yaml:ro
# Mount logs directory
- ./logs:/app/logs
environment:
# Configuration can be overridden via environment variables
- VCENTER_HOST=${VCENTER_HOST:-}
- VCENTER_USER=${VCENTER_USER:-}
- VCENTER_PASSWORD=${VCENTER_PASSWORD:-}
- VCENTER_DATACENTER=${VCENTER_DATACENTER:-}
- VCENTER_CLUSTER=${VCENTER_CLUSTER:-}
- VCENTER_DATASTORE=${VCENTER_DATASTORE:-}
- VCENTER_NETWORK=${VCENTER_NETWORK:-VM Network}
- VCENTER_INSECURE=${VCENTER_INSECURE:-true}
- MCP_API_KEY=${MCP_API_KEY:-}
- MCP_LOG_LEVEL=${MCP_LOG_LEVEL:-INFO}
networks:
- mcp-network
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
networks:
mcp-network:
driver: bridge

93
docker-entrypoint.sh Normal file
View File

@ -0,0 +1,93 @@
#!/bin/bash
# Docker entrypoint script for ESXi MCP Server
set -e
# Function to wait for configuration file
wait_for_config() {
local config_file="/app/config/config.yaml"
local max_wait=30
local count=0
echo "Waiting for configuration file..."
while [ ! -f "$config_file" ] && [ $count -lt $max_wait ]; do
echo "Configuration file not found, waiting... ($count/$max_wait)"
sleep 2
count=$((count + 1))
done
if [ ! -f "$config_file" ]; then
echo "Warning: Configuration file not found. Using environment variables."
return 1
fi
echo "Configuration file found: $config_file"
return 0
}
# Function to validate required environment variables
validate_env() {
local required_vars=("VCENTER_HOST" "VCENTER_USER" "VCENTER_PASSWORD")
local missing_vars=()
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
missing_vars+=("$var")
fi
done
if [ ${#missing_vars[@]} -ne 0 ]; then
echo "Error: Missing required environment variables: ${missing_vars[*]}"
echo "Please set these variables or provide a configuration file."
exit 1
fi
}
# Function to create configuration from environment variables
create_config_from_env() {
local config_file="/app/config/config.yaml"
echo "Creating configuration from environment variables..."
cat > "$config_file" << EOF
vcenter_host: "${VCENTER_HOST}"
vcenter_user: "${VCENTER_USER}"
vcenter_password: "${VCENTER_PASSWORD}"
EOF
# Add optional configuration
[ -n "$VCENTER_DATACENTER" ] && echo "datacenter: \"${VCENTER_DATACENTER}\"" >> "$config_file"
[ -n "$VCENTER_CLUSTER" ] && echo "cluster: \"${VCENTER_CLUSTER}\"" >> "$config_file"
[ -n "$VCENTER_DATASTORE" ] && echo "datastore: \"${VCENTER_DATASTORE}\"" >> "$config_file"
[ -n "$VCENTER_NETWORK" ] && echo "network: \"${VCENTER_NETWORK}\"" >> "$config_file"
[ -n "$VCENTER_INSECURE" ] && echo "insecure: ${VCENTER_INSECURE}" >> "$config_file"
[ -n "$MCP_API_KEY" ] && echo "api_key: \"${MCP_API_KEY}\"" >> "$config_file"
[ -n "$MCP_LOG_LEVEL" ] && echo "log_level: \"${MCP_LOG_LEVEL}\"" >> "$config_file"
# Always set log file path
echo "log_file: \"/app/logs/vmware_mcp.log\"" >> "$config_file"
echo "Configuration file created successfully."
}
# Main execution
echo "Starting ESXi MCP Server..."
# Create logs directory if it doesn't exist
mkdir -p /app/logs
# Check if configuration file exists, if not try to create from environment
if ! wait_for_config; then
validate_env
create_config_from_env
fi
# Print configuration info (without sensitive data)
echo "Server starting with configuration:"
echo " Host: ${VCENTER_HOST:-'from config file'}"
echo " User: ${VCENTER_USER:-'from config file'}"
echo " Log Level: ${MCP_LOG_LEVEL:-INFO}"
echo " Port: 8080"
# Execute the main command
exec "$@"