第10讲:MCP Server 生产化部署

发布时间:2026/9/25 12:10:20

第10讲:MCP Server 生产化部署 前九讲我们完成了 MCP Server 的开发、集成和编排。但所有这些都在本地开发环境中运行。这一讲我们要把 MCP Server部署到生产环境——让它稳定、安全、可监控地对外提供服务。一、生产环境的挑战维度开发环境生产环境传输方式stdio本地进程SSE网络服务认证无API Key / OAuth并发单用户多用户并发可靠性挂了重启高可用、自动恢复监控print 调试日志、指标、告警部署命令行启动Docker / K8s二、从 stdio 迁移到 SSEstdio 模式只适合本地开发。生产环境需要 SSEServer-Sent Events模式让 MCP Server 作为一个独立的 HTTP 服务运行。2.1 SSE Server 实现# sse_mcp_server.py import asyncio import json import uuid from datetime import datetime from mcp.server import Server from mcp.server.sse import SseServerTransport from starlette.applications import Starlette from starlette.routing import Route, Mount from starlette.middleware import Middleware from starlette.middleware.base import BaseHTTPMiddleware from starlette.responses import JSONResponse import uvicorn # 导入之前的 Server 逻辑 from db_mcp_server import server as db_server class AuthMiddleware(BaseHTTPMiddleware): 认证中间件 def __init__(self, app, api_keys: list[str]): super().__init__(app) self.api_keys api_keys async def dispatch(self, request, call_next): # SSE 连接端点不需要认证 if request.url.path /sse: return await call_next(request) # 其他端点需要 API Key api_key request.headers.get(X-API-Key, ) if api_key not in self.api_keys: return JSONResponse( {error: Unauthorized}, status_code401 ) return await call_next(request) class RateLimitMiddleware(BaseHTTPMiddleware): 限流中间件 def __init__(self, app, max_requests: int 100, window_seconds: int 60): super().__init__(app) self.max_requests max_requests self.window_seconds window_seconds self.requests {} async def dispatch(self, request, call_next): client_ip request.client.host now datetime.now().timestamp() # 清理过期记录 if client_ip in self.requests: self.requests[client_ip] [ t for t in self.requests[client_ip] if now - t self.window_seconds ] # 检查限流 if client_ip in self.requests and len(self.requests[client_ip]) self.max_requests: return JSONResponse( {error: Rate limit exceeded}, status_code429, headers{Retry-After: str(self.window_seconds)} ) # 记录请求 if client_ip not in self.requests: self.requests[client_ip] [] self.requests[client_ip].append(now) return await call_next(request) # 创建 SSE 传输 sse_transport SseServerTransport(/messages) # 创建 Starlette 应用 async def handle_sse(request): async with sse_transport.connect_sse( request.scope, request.receive, request._send ) as sessions: await db_server.run( sessions, db_server.create_initialization_options() ) async def handle_messages(request): await sse_transport.handle_post_message( request.scope, request.receive, request._send ) # 健康检查端点 async def health_check(request): return JSONResponse({ status: healthy, timestamp: datetime.now().isoformat(), version: 1.0.0, uptime: ... # 可以从启动时间计算 }) # 指标端点 async def metrics(request): return JSONResponse({ connections: len(sse_transport._sessions) if hasattr(sse_transport, _sessions) else 0, tools_count: len(db_server._tools) if hasattr(db_server, _tools) else 0 }) # 路由配置 routes [ Route(/sse, endpointhandle_sse), Route(/messages, endpointhandle_messages, methods[POST]), Route(/health, endpointhealth_check), Route(/metrics, endpointmetrics), ] # 中间件配置 middleware [ Middleware(AuthMiddleware, api_keys[sk-prod-xxx, sk-staging-xxx]), Middleware(RateLimitMiddleware, max_requests100, window_seconds60), ] app Starlette( routesroutes, middlewaremiddleware, on_startup[lambda: print( MCP Server 已启动)], on_shutdown[lambda: print( MCP Server 已关闭)] ) if __name__ __main__: uvicorn.run( app, host0.0.0.0, port8000, workers4, # 多 worker 提高并发 log_levelinfo )2.2 SSE Client 连接# sse_client.py import asyncio from mcp import ClientSession from mcp.client.sse import sse_client async def connect_to_production(): 连接到生产环境的 MCP Server server_url http://localhost:8000/sse headers {X-API-Key: sk-prod-xxx} async with sse_client(server_url, headersheaders) as streams: async with ClientSession(streams[0], streams[1]) as session: await session.initialize() # 获取工具列表 tools await session.list_tools() print(f已连接发现 {len(tools.tools)} 个工具) # 调用工具 result await session.call_tool(query_database, { sql: SELECT * FROM employees LIMIT 5 }) print(result.content[0].text) if __name__ __main__: asyncio.run(connect_to_production())三、Docker 容器化部署3.1 Dockerfile# Dockerfile FROM python:3.11-slim LABEL maintaineryour-teamcompany.com LABEL descriptionMCP Database Server - Production # 设置环境变量 ENV PYTHONUNBUFFERED1 \ PYTHONDONTWRITEBYTECODE1 \ PIP_NO_CACHE_DIR1 # 安装系统依赖 RUN apt-get update apt-get install -y --no-install-recommends \ gcc \ rm -rf /var/lib/apt/lists/* # 创建工作目录 WORKDIR /app # 复制依赖文件 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 创建非 root 用户 RUN useradd -m -u 1000 mcp chown -R mcp:mcp /app USER mcp # 暴露端口 EXPOSE 8000 # 健康检查 HEALTHCHECK --interval30s --timeout10s --start-period5s --retries3 \ CMD curl -f http://localhost:8000/health || exit 1 # 启动命令 CMD [uvicorn, sse_mcp_server:app, --host, 0.0.0.0, --port, 8000, --workers, 4]3.2 docker-compose.yml# docker-compose.yml version: 3.8 services: mcp-db-server: build: context: . dockerfile: Dockerfile container_name: mcp-db-server ports: - 8000:8000 environment: - DB_TYPE${DB_TYPE:-sqlite} - DB_DATABASE/data/database.db - API_KEYS${API_KEYS:-sk-default} - LOG_LEVELINFO - MAX_REQUESTS_PER_MINUTE100 volumes: - ./data:/data - ./logs:/app/logs restart: unless-stopped networks: - mcp-network deploy: resources: limits: cpus: 1 memory: 512M reservations: cpus: 0.5 memory: 256M mcp-fs-server: build: context: . dockerfile: Dockerfile.fs container_name: mcp-fs-server ports: - 8001:8000 environment: - FS_ROOT/data/sandbox - API_KEYS${API_KEYS:-sk-default} volumes: - ./sandbox:/data/sandbox restart: unless-stopped networks: - mcp-network nginx: image: nginx:alpine container_name: mcp-gateway ports: - 443:443 volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./ssl:/etc/nginx/ssl depends_on: - mcp-db-server - mcp-fs-server networks: - mcp-network networks: mcp-network: driver: bridge3.3 Nginx 反向代理配置# nginx.conf events { worker_connections 1024; } http { upstream mcp_db_server { server mcp-db-server:8000; } upstream mcp_fs_server { server mcp-fs-server:8000; } server { listen 443 ssl; server_name mcp.example.com; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; # 数据库 Server location /db/ { proxy_pass http://mcp_db_server/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_buffering off; proxy_cache off; # SSE 支持 proxy_set_header Connection ; proxy_http_version 1.1; chunked_transfer_encoding off; } # 文件系统 Server location /fs/ { proxy_pass http://mcp_fs_server/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_buffering off; proxy_cache off; proxy_http_version 1.1; chunked_transfer_encoding off; } # 健康检查 location /health { proxy_pass http://mcp_db_server/health; } } }四、日志与监控4.1 结构化日志# logging_config.py import json import logging import sys from datetime import datetime class StructuredFormatter(logging.Formatter): 结构化日志格式化器 def format(self, record): log_entry { timestamp: datetime.utcnow().isoformat() Z, level: record.levelname, logger: record.name, message: record.getMessage(), } # 添加额外字段 if hasattr(record, session_id): log_entry[session_id] record.session_id if hasattr(record, tool_name): log_entry[tool] record.tool_name if hasattr(record, duration_ms): log_entry[duration_ms] record.duration_ms # 添加异常信息 if record.exc_info: log_entry[exception] self.formatException(record.exc_info) return json.dumps(log_entry, ensure_asciiFalse) def setup_logging(): 配置日志 handler logging.StreamHandler(sys.stdout) handler.setFormatter(StructuredFormatter()) root_logger logging.getLogger() root_logger.addHandler(handler) root_logger.setLevel(logging.INFO) # 文件日志 file_handler logging.FileHandler(/app/logs/mcp_server.log) file_handler.setFormatter(StructuredFormatter()) root_logger.addHandler(file_handler) return root_logger4.2 Prometheus 指标# metrics.py from prometheus_client import Counter, Histogram, Gauge, generate_latest from starlette.responses import Response import time # 定义指标 TOOL_CALLS_TOTAL Counter( mcp_tool_calls_total, Total number of tool calls, [server, tool, status] ) TOOL_CALL_DURATION Histogram( mcp_tool_call_duration_seconds, Duration of tool calls, [server, tool], buckets(0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 30.0) ) ACTIVE_CONNECTIONS Gauge( mcp_active_connections, Number of active MCP connections ) TOOL_ERRORS_TOTAL Counter( mcp_tool_errors_total, Total number of tool errors, [server, tool, error_type] ) class MetricsMiddleware: 指标采集中间件 async def __call__(self, request, call_next): start time.time() response await call_next(request) duration time.time() - start # 记录指标 if request.url.path /sse: ACTIVE_CONNECTIONS.inc() return response # 指标端点 async def metrics_endpoint(request): return Response( contentgenerate_latest(), media_typetext/plain )4.3 告警配置# alerts.yml (Prometheus AlertManager 配置) groups: - name: mcp_alerts rules: - alert: HighErrorRate expr: rate(mcp_tool_errors_total[5m]) 0.1 for: 5m labels: severity: critical annotations: summary: MCP Server 错误率过高 description: 过去5分钟错误率超过10% - alert: HighLatency expr: histogram_quantile(0.95, rate(mcp_tool_call_duration_seconds_bucket[5m])) 5 for: 5m labels: severity: warning annotations: summary: MCP Server 响应缓慢 description: P95 延迟超过5秒 - alert: LowConnections expr: mcp_active_connections 1 for: 1m labels: severity: critical annotations: summary: MCP Server 无活跃连接 description: 没有客户端连接到 MCP Server五、生产部署 Checklist5.1 安全加固# security.py import os from typing import Optional class SecurityConfig: 安全配置 # API Key 管理 API_KEYS os.environ.get(API_KEYS, ).split(,) # TLS/SSL SSL_CERT os.environ.get(SSL_CERT, ) SSL_KEY os.environ.get(SSL_KEY, ) # IP 白名单 ALLOWED_IPS os.environ.get(ALLOWED_IPS, 0.0.0.0/0).split(,) # 请求大小限制 MAX_REQUEST_SIZE 10 * 1024 * 1024 # 10MB # 超时设置 TOOL_TIMEOUT 30 # 秒 CONNECTION_TIMEOUT 60 # 秒 def validate_api_key(api_key: str) - bool: 验证 API Key return api_key in SecurityConfig.API_KEYS def validate_ip(client_ip: str) - bool: 验证客户端 IP import ipaddress for allowed in SecurityConfig.ALLOWED_IPS: if client_ip in ipaddress.ip_network(allowed): return True return False5.2 部署前检查清单## 部署前检查清单 ### 安全 - [ ] API Key 已设置且强度足够 - [ ] TLS/SSL 证书已配置 - [ ] IP 白名单已配置如需要 - [ ] 数据库使用只读账号 - [ ] 文件系统沙箱目录权限正确 ### 可靠性 - [ ] 健康检查端点正常工作 - [ ] 超时设置合理 - [ ] 重试机制已实现 - [ ] 资源限制已配置CPU/内存 ### 可观测性 - [ ] 结构化日志已配置 - [ ] Prometheus 指标已暴露 - [ ] 告警规则已配置 - [ ] 日志持久化存储已配置 ### 部署 - [ ] Docker 镜像构建成功 - [ ] docker-compose 配置正确 - [ ] Nginx 反向代理配置正确 - [ ] 端口映射正确 - [ ] 数据卷挂载正确 ### 测试 - [ ] SSE 连接测试通过 - [ ] 工具调用测试通过 - [ ] 认证测试通过 - [ ] 限流测试通过 - [ ] 压力测试通过六、生产环境运维6.1 启动脚本#!/bin/bash # deploy.sh - 生产部署脚本 set -e echo 开始部署 MCP Server... # 1. 构建 Docker 镜像 echo 构建 Docker 镜像... docker build -t mcp-server:latest . # 2. 备份旧数据 echo 备份数据... BACKUP_DIR./backups/$(date %Y%m%d_%H%M%S) mkdir -p $BACKUP_DIR cp -r ./data/* $BACKUP_DIR/ 2/dev/null || true # 3. 启动服务 echo ▶️ 启动服务... docker-compose up -d # 4. 等待服务就绪 echo ⏳ 等待服务就绪... for i in {1..30}; do if curl -sf http://localhost:8000/health /dev/null 21; then echo ✅ 服务已就绪 break fi sleep 1 done # 5. 运行健康检查 echo 运行健康检查... python scripts/health_check.py echo ✅ 部署完成6.2 滚动更新# docker-compose.prod.yml version: 3.8 services: mcp-server: image: registry.example.com/mcp-server:${VERSION} deploy: replicas: 3 update_config: parallelism: 1 delay: 10s order: start-first restart_policy: condition: any delay: 5s max_attempts: 3七、性能优化7.1 连接池优化# connection_pool_prod.py from concurrent.futures import ThreadPoolExecutor import asyncio class ProductionConnectionPool: 生产环境连接池 def __init__(self, min_size5, max_size50): self.min_size min_size self.max_size max_size self._pool ThreadPoolExecutor(max_workersmax_size) self._connections [] async def get_connection(self): 获取连接异步 loop asyncio.get_event_loop() return await loop.run_in_executor( self._pool, self._create_connection ) def _create_connection(self): 创建数据库连接同步 import sqlite3 conn sqlite3.connect(production.db) conn.execute(PRAGMA journal_modeWAL) conn.execute(PRAGMA synchronousNORMAL) conn.execute(PRAGMA cache_size-64000) # 64MB 缓存 return conn7.2 查询缓存# cache.py import hashlib import json from datetime import datetime, timedelta import redis class QueryCache: 查询结果缓存 def __init__(self, redis_urlredis://localhost:6379/0): self.redis redis.from_url(redis_url) self.ttl 300 # 5 分钟 def _make_key(self, sql: str) - str: 生成缓存键 return fquery:{hashlib.md5(sql.encode()).hexdigest()} def get(self, sql: str): 获取缓存 key self._make_key(sql) data self.redis.get(key) if data: return json.loads(data) return None def set(self, sql: str, result: str): 设置缓存 key self._make_key(sql) self.redis.setex(key, self.ttl, result) def invalidate(self, pattern: str *): 清除缓存 for key in self.redis.scan_iter(fquery:{pattern}): self.redis.delete(key)八、课后作业实现灰度发布让新版 MCP Server 只对部分用户生效逐步放量。添加请求追踪为每个请求生成 Trace ID贯穿所有日志和指标方便排查问题。挑战题实现一个 MCP Server 的“控制面板”——Web 页面展示实时连接数、QPS、错误率、延迟分布并支持动态调整限流阈值。九、专栏总结10 讲回顾讲次核心内容产出第1讲MCP 协议基础理解 MCP 的价值第2讲协议核心原理Transport/Tool/Resource第3讲手写 MCP Server第一个可运行的 Server第4讲MCP Client 开发连接、发现、调用第5讲数据库 MCP Server安全查询数据库第6讲文件系统 MCP Server安全读写文件第7讲API 网关 MCP Server调用外部 API第8讲MCP Agent 集成Agent 自主使用工具第9讲多 Server 编排协同完成复杂任务第10讲生产化部署安全、稳定、可监控你现在掌握的技能✅ 理解 MCP 协议的设计哲学和工作原理✅ 能手写 MCP Server 暴露任意工具✅ 能开发 MCP Client 连接和调用工具✅ 能对接数据库、文件系统、外部 API✅ 能将 MCP 集成到 Agent 中✅ 能编排多个 MCP Server 协同工作✅ 能安全地将 MCP Server 部署到生产环境下一步可以做什么贡献社区把你写的 MCP Server 开源到 GitHub深入框架学习 LangChain MCP、AutoGen MCP 等集成方案垂直场景针对客服、运维、数据分析等场景定制 MCP Server协议演进关注 MCP 协议的更新和新特性 开发之余处理 Base64、JWT 解析、JSON 格式化、Crontab 计算、PDF 合并压缩这些碎片需求我常用一个纯前端本地工具箱zz365.top子页 PDF 大师PDF 大师 - zz365工具箱。所有计算在浏览器完成文件不上传服务器关页即清。免费、无登录、无广告适合开发者当常驻标签页。
延伸阅读

更多相关文章

2026/9/23 6:21:38

如何用AlwaysOnTop彻底解决Windows窗口管理的三大痛点?

如何用AlwaysOnTop彻底解决Windows窗口管理的三大痛点? 【免费下载链接】AlwaysOnTop Make a Windows application always run on top 项目地址: https://gitcode.com/gh_mirrors/al/AlwaysOnTop 你是否经常在多个窗口间来回切换,为了查看参考文档…

2026/9/21 3:32:03

ArcGIS基础工具条核心功能解析:从导航到测量的GIS入门指南

1. 从“找工具”到“用工具”:ArcGIS基础工具条的核心价值 刚接触ArcGIS Desktop的朋友,十有八九都经历过这个阶段:软件界面一打开,左边是内容列表,右边是地图窗口,中间是各种图标按钮,看着功能…

2026/9/21 14:29:38

MySQL数据库表约束详解与最佳实践

1. MySQL表约束的核心价值解析在数据库设计领域,表约束就像交通规则对于城市道路系统一样不可或缺。我处理过太多因为约束缺失导致的数据灾难案例——从重复的会员注册信息到订单金额出现负值,这些看似简单的错误往往需要数小时的紧急修复。MySQL作为最流…

2026/9/25 12:08:05

工业时序大模型:让AI真正读懂工厂暗数据

1. 项目概述:当大模型真正“看懂”工厂里的每一秒数据流ManuDrive不是又一个挂在PPT上的AI概念,而是我去年在一家汽车零部件厂的产线调试现场,亲眼看着它把三台停机27小时的压铸机重新拉回满负荷运转的真实工具。它不生成诗歌、不写周报、不画…

2026/9/25 12:08:05

天津短视频代拍运营公司推荐:有实力的服务商合作实力参考

现在越来越多天津实体企业布局短视频线上获客,不少工厂在运营过程中都会遇到这类问题:没有专业内容创作团队,自己拍的内容播放不少但没咨询,找售后完善的短视频代拍运营企业合作,却不知道该怎么筛选靠谱机构。不少企业…

2026/9/25 12:03:05

Atlas 300V 24G部署YOLOv5s实战:从环境搭建到推理优化全记录

去年底接了一个产线上的缺陷检测项目,老机台本来跑的是传统视觉算法,客户要求换成深度学习的检测模型,专门盯产品表面的划痕和脏污。我们在选型阶段纠结过一阵,最后定了 Atlas 300V 24G 这张卡,在上面部署 YOLOv5s。整…

2026/9/24 20:24:47

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/25 0:02:35

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:02:35

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:02:35

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑