Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Паттерны деплоя
deployment_patterns

Паттерны деплоя

Деплой MCP-серверов, Docker, SSE-транспорт, масштабирование и мониторинг.

Паттерны деплоя

Деплой MCP-сервера в продакшен требует больше, чем python server.py. В этой теме разберём контейнеризацию, SSE-транспорт, reverse proxy, масштабирование и мониторинг.

Цель деплоя: сервер доступен, надёжен, безопасен и наблюдаем.

#Docker-контейнеризация

Контейнер — стандартный способ упаковки MCP-сервера. Все зависимости включены, сервер работает одинаково на любом хосте.

FROM python:3.12-slim WORKDIR /app # Установка зависимостей COPY pyproject.toml poetry.lock ./ RUN pip install --no-cache-dir fastmcp pydantic aiohttp requests # Копирование кода сервера COPY server.py . # Порт для SSE-транспорта EXPOSE 8000 # Запуск сервера CMD ["python", "server.py"]

Сервер внутри контейнера запускается в SSE-режиме:

# server.py from fastmcp import FastMCP import logging logging.basicConfig(level=logging.INFO) mcp = FastMCP("ProductionServer") @mcp.tool() def get_metrics() -> str: """Возвращает метрики системы.""" return '{"cpu": 42.5, "memory": 67.3, "disk": 55.0}' if __name__ == "__main__": mcp.run(transport="sse", host="0.0.0.0", port=8000)

Запуск контейнера:

docker build -t mcp-server . docker run -d -p 8000:8000 --name mcp-instance mcp-server

Флаг -d запускает контейнер в фоне, -p 8000:8000 пробрасывает порт на хост.

#Docker Compose для разработки

Docker Compose упрощает запуск сервера с зависимостями (БД, Redis):

version: "3.8" services: mcp-server: build: . ports: - "8000:8000" environment: - DATABASE_URL=postgresql://user:pass@db:5432/mcp - REDIS_URL=redis://redis:6379/0 depends_on: - db - redis db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: pass POSTGRES_DB: mcp volumes: - pgdata:/var/lib/postgresql/data redis: image: redis:7-alpine volumes: pgdata:
docker compose up -d

Все сервисы запускаются одной командой. MCP-сервер подключается к БД и Redis по внутренним именам сервисов (db, redis).

#Reverse Proxy

В продакшене перед MCP-сервером ставят reverse proxy (nginx, Caddy). Proxy берёт на себя HTTPS, rate limiting, логирование.

#Nginx конфигурация

server { listen 443 ssl; server_name mcp.example.com; ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; # SSE требует специальных заголовков proxy_set_header Connection ''; proxy_buffering off; proxy_cache off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }

Ключевые настройки для SSE:

  • proxy_buffering off — отключает буферизацию, SSE-события доставляются немедленно
  • proxy_cache off — отключает кэширование, каждое соединение индивидуально
  • proxy_set_header Connection '' — поддерживает постоянное соединение

#Caddy (альтернатива)

Caddy проще — автоматически получает HTTPS-сертификаты:

mcp.example.com {
    reverse_proxy 127.0.0.1:8000
}

Caddy автоматически терминализирует HTTPS и проксирует запросы к MCP-серверу.

#Health Check

Добавьте эндпоинт проверки здоровья для мониторинга:

from fastmcp import FastMCP from fastmcp.server.file_route import file_route import uvicorn mcp = FastMCP("ProductionServer") # MCP-инструменты @mcp.tool() def get_status() -> str: """Возвращает статус сервера.""" return '{"status": "ok"}' # HTTP health check (отдельный от MCP) @file_route("/health") async def health_check(): """HTTP эндпоинт для проверки здоровья.""" from starlette.responses import JSONResponse return JSONResponse({"status": "ok", "version": "1.0.0"}) if __name__ == "__main__": # Для production используем uvicorn напрямую config = uvicorn.Config( "server:mcp.app", host="0.0.0.0", port=8000, log_level="info" ) server = uvicorn.Server(config) server.run()

Health check позволяет orchestrator (Kubernetes, Docker) проверить, жив ли сервер:

curl https://mcp.example.com/health # {"status": "ok", "version": "1.0.0"}

#Мониторинг

Структурированное логирование для продакшена:

import logging import json class JSONFormatter(logging.Formatter): """Форматирует логи в JSON для сбора в ELK/Datadog.""" def format(self, record): log_entry = { "timestamp": self.formatTime(record), "level": record.levelname, "message": record.getMessage(), "module": record.module, "function": record.funcName, "line": record.lineno, } if record.exc_info: log_entry["exception"] = self.formatException(record.exc_info) return json.dumps(log_entry, ensure_ascii=False) handler = logging.StreamHandler() handler.setFormatter(JSONFormatter()) logger = logging.getLogger() logger.addHandler(handler) logger.setLevel(logging.INFO)

Каждый лог — JSON-объект, который легко парсится системами мониторинга (ELK Stack, Datadog, Grafana Loki).

#Паттерн: мультитенантный сервер

Один MCP-сервер может обслуживать нескольких клиентов с изоляцией данных:

from fastmcp import FastMCP from fastmcp.server.context import Context mcp = FastMCP("MultiTenantServer") @mcp.tool() async def get_dashboard(ctx: Context) -> str: """Возвращает дашборд для текущего клиента.""" # В реальности: извлекаем tenant_id из контекста сессии tenant_id = extract_tenant_from_context(ctx) data = get_tenant_data(tenant_id) return json.dumps(data, indent=2, ensure_ascii=False) @mcp.resource("config://tenant") async def get_tenant_config(ctx: Context) -> str: """Конфигурация текущего клиента.""" tenant_id = extract_tenant_from_context(ctx) config = load_tenant_config(tenant_id) return json.dumps(config, indent=2)

Каждый клиент получает только свои данные. Сервер один — данные изолированы.

#Чеклист деплоя

  • SSE-транспорт, не stdio
  • Docker-контейнер с фиксированной версией Python
  • Reverse proxy с HTTPS
  • Health check эндпоинт
  • Структурированное логирование
  • Rate limiting на proxy
  • Мониторинг: latency, error rate, throughput
  • Бэкап данных (БД, конфиги)
  • CI/CD пайплайн для деплоя
  • Тесты проходят перед деплоем