mirror of
https://github.com/apache/superset.git
synced 2026-04-27 03:55:47 +00:00
docs(mcp): add Kubernetes deployment instructions to MCP service README (#36547)
This commit is contained in:
@@ -234,3 +234,561 @@ Note: Replace "admin" with your actual Superset username. These environment vari
|
|||||||
### 📍 Claude Desktop Config Location
|
### 📍 Claude Desktop Config Location
|
||||||
|
|
||||||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ☸️ Kubernetes Deployment
|
||||||
|
|
||||||
|
This section covers deploying the MCP service on Kubernetes for production environments. The MCP service runs as a separate deployment alongside Superset, connected via an API gateway or ingress controller.
|
||||||
|
|
||||||
|
### Architecture Overview
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
Client[MCP Client]
|
||||||
|
|
||||||
|
subgraph "Ingress / API Gateway"
|
||||||
|
Gateway[Ingress Controller<br/>Route based on URL path]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Superset Web Deployment"
|
||||||
|
WebApp[Superset App<br/>Gunicorn<br/>Port: 8088]
|
||||||
|
WebHPA[HPA<br/>Auto-scaling]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "MCP Service Deployment"
|
||||||
|
MCPApp[MCP Service<br/>FastMCP Server<br/>Port: 5008]
|
||||||
|
MCPHPA[HPA<br/>2-3 replicas]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Shared Dependencies"
|
||||||
|
DB[(PostgreSQL)]
|
||||||
|
Redis[(Redis)]
|
||||||
|
end
|
||||||
|
|
||||||
|
Client --> Gateway
|
||||||
|
Gateway -->|/superset/*| WebApp
|
||||||
|
Gateway -->|/mcp/*| MCPApp
|
||||||
|
|
||||||
|
WebHPA -.->|scales| WebApp
|
||||||
|
MCPHPA -.->|scales| MCPApp
|
||||||
|
|
||||||
|
WebApp --> DB
|
||||||
|
WebApp --> Redis
|
||||||
|
|
||||||
|
MCPApp --> DB
|
||||||
|
|
||||||
|
style Gateway fill:#9c27b0
|
||||||
|
style WebApp fill:#42a5f5
|
||||||
|
style MCPApp fill:#C76E00
|
||||||
|
style WebHPA fill:#66bb6a
|
||||||
|
style MCPHPA fill:#66bb6a
|
||||||
|
style DB fill:#4db6ac
|
||||||
|
style Redis fill:#ef5350
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client as MCP Client
|
||||||
|
participant Ingress as Ingress/Gateway
|
||||||
|
participant MCP as MCP Service
|
||||||
|
participant Auth as Authentication
|
||||||
|
participant Flask as Flask Context
|
||||||
|
participant Tools as MCP Tools
|
||||||
|
participant DB as PostgreSQL
|
||||||
|
|
||||||
|
Client->>Ingress: MCP Request (/mcp/*)
|
||||||
|
Ingress->>MCP: Route to MCP Service
|
||||||
|
MCP->>Auth: Validate credentials
|
||||||
|
Auth-->>MCP: User authenticated
|
||||||
|
|
||||||
|
MCP->>Flask: Establish Flask app context
|
||||||
|
Flask->>DB: Connect to database
|
||||||
|
DB-->>Flask: Connection established
|
||||||
|
|
||||||
|
MCP->>Tools: Execute tool (e.g., list_dashboards)
|
||||||
|
Tools->>DB: Query data
|
||||||
|
DB-->>Tools: Return data
|
||||||
|
Tools-->>MCP: Tool result
|
||||||
|
|
||||||
|
MCP-->>Ingress: Response
|
||||||
|
Ingress-->>Client: MCP Response
|
||||||
|
|
||||||
|
Note over Auth,Flask: Authentication Layer
|
||||||
|
Note over Tools: Tool Execution Layer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Kubernetes cluster (1.19+)
|
||||||
|
- Helm 3.x installed
|
||||||
|
- kubectl configured with cluster access
|
||||||
|
- PostgreSQL database (can use the bundled chart or external)
|
||||||
|
- Redis (optional, for caching and Celery)
|
||||||
|
|
||||||
|
### Option 1: Using the Official Superset Helm Chart
|
||||||
|
|
||||||
|
The simplest approach is to extend the existing Superset Helm chart to include the MCP service as a sidecar or separate deployment.
|
||||||
|
|
||||||
|
#### Step 1: Add the Superset Helm Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm repo add superset http://apache.github.io/superset/
|
||||||
|
helm repo update
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 2: Create a Custom Values File
|
||||||
|
|
||||||
|
Create `mcp-values.yaml` with MCP-specific configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# mcp-values.yaml
|
||||||
|
# Extend the Superset Helm chart to include MCP service
|
||||||
|
|
||||||
|
# Image configuration - ensure fastmcp extra is installed
|
||||||
|
image:
|
||||||
|
repository: apache/superset
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# MCP Service configuration via extraContainers
|
||||||
|
supersetNode:
|
||||||
|
extraContainers:
|
||||||
|
- name: mcp-service
|
||||||
|
image: "apache/superset:latest"
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-c"
|
||||||
|
- |
|
||||||
|
pip install fastmcp && \
|
||||||
|
superset mcp run --host 0.0.0.0 --port 5008
|
||||||
|
ports:
|
||||||
|
- name: mcp
|
||||||
|
containerPort: 5008
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: FLASK_APP
|
||||||
|
value: superset
|
||||||
|
- name: PYTHONPATH
|
||||||
|
value: /app/pythonpath
|
||||||
|
# MCP-specific environment variables
|
||||||
|
- name: MCP_DEV_USERNAME
|
||||||
|
value: "admin" # Override with your admin username
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: '{{ template "superset.fullname" . }}-env'
|
||||||
|
volumeMounts:
|
||||||
|
- name: superset-config
|
||||||
|
mountPath: /app/pythonpath
|
||||||
|
readOnly: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 5008
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 15
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 5008
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
# Superset configuration overrides for MCP
|
||||||
|
configOverrides:
|
||||||
|
mcp_config: |
|
||||||
|
# MCP Service Configuration
|
||||||
|
MCP_DEV_USERNAME = 'admin'
|
||||||
|
SUPERSET_WEBSERVER_ADDRESS = 'http://localhost:8088'
|
||||||
|
|
||||||
|
# WebDriver for screenshots (adjust based on your setup)
|
||||||
|
WEBDRIVER_BASEURL = 'http://localhost:8088/'
|
||||||
|
WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL
|
||||||
|
|
||||||
|
# Secret configuration
|
||||||
|
extraSecretEnv:
|
||||||
|
SUPERSET_SECRET_KEY: 'your-secret-key-here' # Use a strong secret!
|
||||||
|
|
||||||
|
# Database configuration (using bundled PostgreSQL)
|
||||||
|
postgresql:
|
||||||
|
enabled: true
|
||||||
|
auth:
|
||||||
|
username: superset
|
||||||
|
password: superset
|
||||||
|
database: superset
|
||||||
|
|
||||||
|
# Redis configuration
|
||||||
|
redis:
|
||||||
|
enabled: true
|
||||||
|
architecture: standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 3: Deploy with Helm
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create namespace
|
||||||
|
kubectl create namespace superset
|
||||||
|
|
||||||
|
# Install the chart
|
||||||
|
helm install superset superset/superset \
|
||||||
|
--namespace superset \
|
||||||
|
--values mcp-values.yaml \
|
||||||
|
--wait
|
||||||
|
|
||||||
|
# Verify deployment
|
||||||
|
kubectl get pods -n superset
|
||||||
|
kubectl get svc -n superset
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Dedicated MCP Service Deployment
|
||||||
|
|
||||||
|
For production environments requiring independent scaling and isolation, deploy the MCP service as a separate Kubernetes deployment.
|
||||||
|
|
||||||
|
#### Step 1: Create MCP Deployment Manifest
|
||||||
|
|
||||||
|
Create `mcp-deployment.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# mcp-deployment.yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: superset-mcp
|
||||||
|
namespace: superset
|
||||||
|
labels:
|
||||||
|
app: superset-mcp
|
||||||
|
component: mcp-service
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: superset-mcp
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: superset-mcp
|
||||||
|
component: mcp-service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mcp-service
|
||||||
|
image: apache/superset:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- "/bin/sh"
|
||||||
|
- "-c"
|
||||||
|
- |
|
||||||
|
pip install fastmcp && \
|
||||||
|
superset mcp run --host 0.0.0.0 --port 5008
|
||||||
|
ports:
|
||||||
|
- name: mcp
|
||||||
|
containerPort: 5008
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
- name: FLASK_APP
|
||||||
|
value: superset
|
||||||
|
- name: PYTHONPATH
|
||||||
|
value: /app/pythonpath
|
||||||
|
- name: MCP_DEV_USERNAME
|
||||||
|
value: "admin"
|
||||||
|
# Database connection (must match Superset's config)
|
||||||
|
- name: DATABASE_URI
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: superset-env
|
||||||
|
key: DATABASE_URI
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: superset-env
|
||||||
|
volumeMounts:
|
||||||
|
- name: superset-config
|
||||||
|
mountPath: /app/pythonpath
|
||||||
|
readOnly: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 512Mi
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 1Gi
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 5008
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 15
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 5008
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
startupProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 5008
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 30
|
||||||
|
periodSeconds: 5
|
||||||
|
volumes:
|
||||||
|
- name: superset-config
|
||||||
|
secret:
|
||||||
|
secretName: superset-config
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: superset-mcp
|
||||||
|
namespace: superset
|
||||||
|
labels:
|
||||||
|
app: superset-mcp
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
- port: 5008
|
||||||
|
targetPort: 5008
|
||||||
|
protocol: TCP
|
||||||
|
name: mcp
|
||||||
|
selector:
|
||||||
|
app: superset-mcp
|
||||||
|
---
|
||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: superset-mcp-hpa
|
||||||
|
namespace: superset
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: superset-mcp
|
||||||
|
minReplicas: 2
|
||||||
|
maxReplicas: 5
|
||||||
|
metrics:
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 70
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: memory
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 80
|
||||||
|
---
|
||||||
|
apiVersion: policy/v1
|
||||||
|
kind: PodDisruptionBudget
|
||||||
|
metadata:
|
||||||
|
name: superset-mcp-pdb
|
||||||
|
namespace: superset
|
||||||
|
spec:
|
||||||
|
minAvailable: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: superset-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 2: Create Ingress for Routing
|
||||||
|
|
||||||
|
Create `mcp-ingress.yaml` to route `/mcp/*` requests to the MCP service:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# mcp-ingress.yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: superset-ingress
|
||||||
|
namespace: superset
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
||||||
|
# Enable WebSocket support for MCP
|
||||||
|
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-set-header-upgrade: "$http_upgrade"
|
||||||
|
nginx.ingress.kubernetes.io/proxy-set-header-connection: "upgrade"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: superset.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
# Route MCP requests to MCP service
|
||||||
|
- path: /mcp
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: superset-mcp
|
||||||
|
port:
|
||||||
|
number: 5008
|
||||||
|
# Route all other requests to Superset
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: superset
|
||||||
|
port:
|
||||||
|
number: 8088
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- superset.example.com
|
||||||
|
secretName: superset-tls
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 3: Apply the Manifests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Apply the deployment
|
||||||
|
kubectl apply -f mcp-deployment.yaml
|
||||||
|
|
||||||
|
# Apply the ingress
|
||||||
|
kubectl apply -f mcp-ingress.yaml
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
kubectl get pods -n superset -l app=superset-mcp
|
||||||
|
kubectl get svc -n superset
|
||||||
|
kubectl get ingress -n superset
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Reference
|
||||||
|
|
||||||
|
#### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `MCP_DEV_USERNAME` | Superset username for MCP authentication | `admin` |
|
||||||
|
| `MCP_AUTH_ENABLED` | Enable/disable authentication | `true` |
|
||||||
|
| `MCP_JWT_PUBLIC_KEY` | JWT public key for token validation | - |
|
||||||
|
| `SUPERSET_WEBSERVER_ADDRESS` | Internal Superset URL | `http://localhost:8088` |
|
||||||
|
| `WEBDRIVER_BASEURL` | URL for screenshot generation | Same as webserver |
|
||||||
|
|
||||||
|
#### superset_config.py Options
|
||||||
|
|
||||||
|
```python
|
||||||
|
# MCP Service Configuration
|
||||||
|
MCP_DEV_USERNAME = 'admin' # Username for development/testing
|
||||||
|
MCP_AUTH_ENABLED = True # Enable authentication
|
||||||
|
MCP_JWT_PUBLIC_KEY = 'your-public-key' # For JWT token validation
|
||||||
|
|
||||||
|
# For production with JWT authentication
|
||||||
|
MCP_AUTH_FACTORY = 'your.custom.auth_factory'
|
||||||
|
MCP_USER_RESOLVER = 'your.custom.user_resolver'
|
||||||
|
|
||||||
|
# WebDriver for chart screenshots
|
||||||
|
WEBDRIVER_BASEURL = 'http://superset:8088/'
|
||||||
|
WEBDRIVER_TYPE = 'chrome'
|
||||||
|
WEBDRIVER_OPTION_ARGS = ['--headless', '--no-sandbox']
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Considerations
|
||||||
|
|
||||||
|
#### Security
|
||||||
|
|
||||||
|
1. **Authentication**: Configure proper JWT authentication for production:
|
||||||
|
```python
|
||||||
|
# superset_config.py
|
||||||
|
MCP_AUTH_ENABLED = True
|
||||||
|
MCP_JWT_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
|
||||||
|
Your RSA public key here
|
||||||
|
-----END PUBLIC KEY-----"""
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Network Policies**: Restrict MCP service network access:
|
||||||
|
```yaml
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: mcp-network-policy
|
||||||
|
namespace: superset
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: superset-mcp
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
name: ingress-nginx
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5008
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: postgresql
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 5432
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **TLS**: Always use TLS in production via ingress or service mesh.
|
||||||
|
|
||||||
|
#### Resource Allocation
|
||||||
|
|
||||||
|
Recommended resources for production:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 1Gi
|
||||||
|
limits:
|
||||||
|
cpu: 2000m
|
||||||
|
memory: 2Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Monitoring
|
||||||
|
|
||||||
|
Add Prometheus annotations for metrics scraping:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
prometheus.io/port: "5008"
|
||||||
|
prometheus.io/path: "/metrics"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
#### Check MCP Service Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl logs -n superset -l app=superset-mcp -f
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Verify Service Connectivity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Port-forward to test locally
|
||||||
|
kubectl port-forward -n superset svc/superset-mcp 5008:5008
|
||||||
|
|
||||||
|
# Test health endpoint
|
||||||
|
curl http://localhost:5008/health
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Common Issues
|
||||||
|
|
||||||
|
1. **Database Connection Errors**: Ensure the MCP service has the same database credentials as Superset
|
||||||
|
2. **Authentication Failures**: Verify `MCP_DEV_USERNAME` matches an existing Superset user
|
||||||
|
3. **Screenshot Generation Fails**: Check WebDriver configuration and ensure Chrome/Firefox is available in the container
|
||||||
|
|||||||
Reference in New Issue
Block a user