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

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

@potapov_me

Платформа

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

Контент

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

Компания

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

Аккаунт

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

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

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Деплой и продакшен-настройки
deployment

Деплой и продакшен-настройки

Gunicorn + uvloop, nginx reverse proxy, Docker, CI/CD, логирование, health checks, Prometheus.

Деплой и продакшен-настройки

Научитесь развёртывать aiohttp-приложения в продакшене с использованием Gunicorn, nginx, Docker и CI/CD

#Почему не web.run_app()?

web.run_app() предназначен для разработки, а не для продакшена:

  • Один процесс (не использует все ядра CPU)
  • Нет graceful shutdown
  • Нет перезапуска при ошибках
  • Нет ограничения запросов

Для продакшена используйте Gunicorn или uvicorn.

#Gunicorn с aiohttp

#Установка

pip install gunicorn

#Запуск приложения

# Базовый запуск gunicorn -w 4 -k aiohttp.GunicornWebWorker app.main:create_app # С настройками gunicorn app.main:create_app \ -w 4 \ -k aiohttp.GunicornWebWorker \ --bind 0.0.0.0:8000 \ --timeout 120 \ --keep-alive 5 \ --access-logfile logs/access.log \ --error-logfile logs/error.log \ --log-level info \ --pid pidfile.pid \ --daemon

#Параметры Gunicorn

ПараметрОписаниеРекомендация
-wКоличество воркеров2-4 × CPU ядра
-kКласс воркераaiohttp.GunicornWebWorker
--bindХост:порт0.0.0.0:8000
--timeoutТаймаут воркера (сек)120
--keep-aliveKeep-alive (сек)5
--max-requestsПерезапуск после N запросов1000
--max-requests-jitterРандомизация перезапуска50-100

#Конфигурационный файл

# gunicorn.conf.py import multiprocessing bind = '0.0.0.0:8000' workers = multiprocessing.cpu_count() * 2 + 1 worker_class = 'aiohttp.GunicornWebWorker' timeout = 120 keepalive = 5 # Логирование accesslog = 'logs/access.log' errorlog = 'logs/error.log' loglevel = 'info' # Перезапуск воркеров max_requests = 1000 max_requests_jitter = 100 # Graceful shutdown timeout_graceful = 30 # Preload (экономия памяти) preload_app = True

Запуск:

gunicorn --config gunicorn.conf.py app.main:create_app

#Nginx как reverse proxy

#Зачем нужен nginx?

  • SSL/TLS терминирование
  • Статические файлы
  • Rate limiting
  • Балансировка нагрузки
  • Кэширование ответов

#Конфигурация nginx

# /etc/nginx/sites-available/task_manager upstream task_manager { server 127.0.0.1:8000; server 127.0.0.1:8001; server 127.0.0.1:8002; } server { listen 80; server_name api.example.com; # Редирект на HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.example.com; # SSL сертификаты (Let's Encrypt) ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem; # SSL настройки ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # Логирование access_log /var/log/nginx/task_manager_access.log; error_log /var/log/nginx/task_manager_error.log; # Размеры client_max_body_size 10M; # Таймауты proxy_connect_timeout 60s; proxy_send_timeout 120s; proxy_read_timeout 120s; # API запросы location / { proxy_pass http://task_manager; 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; # WebSocket поддержка proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Буферы proxy_buffering off; proxy_buffer_size 4k; proxy_buffers 8 4k; } # Статические файлы location /static/ { alias /var/www/task_manager/static/; expires 30d; add_header Cache-Control "public, immutable"; } # Health check location /health { proxy_pass http://task_manager; access_log off; } # Rate limiting location /api/ { limit_req zone=api_limit burst=20 nodelay; proxy_pass http://task_manager; } } # Rate limiting зона http { limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; }

#Docker

#Dockerfile

# Dockerfile FROM python:3.11-slim # Переменные окружения ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 # Рабочая директория WORKDIR /app # Системные зависимости RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libpq-dev \ && rm -rf /var/lib/apt/lists/* # Установка зависимостей Python COPY pyproject.toml poetry.lock* ./ RUN pip install --no-cache-dir poetry && \ poetry config virtualenvs.create false && \ poetry install --only main --no-interaction # Копирование кода COPY . . # Пользователь (не root!) RUN useradd --create-home --shell /bin/bash app && \ chown -R app:app /app USER app # Порт EXPOSE 8000 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" # Запуск CMD ["gunicorn", "--config", "gunicorn.conf.py", "app.main:create_app"]

#docker-compose.yml

# docker-compose.yml version: '3.8' services: api: build: . ports: - "8000:8000" environment: - DATABASE_URL=postgresql://postgres:secret@db:5432/task_manager - REDIS_URL=redis://redis:6379 - JWT_SECRET=${JWT_SECRET} - DEBUG=false depends_on: db: condition: service_healthy redis: condition: service_started restart: unless-stopped networks: - app_network db: image: postgres:15-alpine environment: - POSTGRES_DB=task_manager - POSTGRES_USER=postgres - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - app_network redis: image: redis:7-alpine volumes: - redis_data:/data restart: unless-stopped networks: - app_network nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./ssl:/etc/ssl:ro depends_on: - api restart: unless-stopped networks: - app_network volumes: postgres_data: redis_data: networks: app_network: driver: bridge

#Запуск

# Сборка и запуск docker-compose up -d --build # Просмотр логов docker-compose logs -f api # Остановка docker-compose down # Остановка с удалением volumes docker-compose down -v

#Переменные окружения

#.env.example

# .env.example HOST=0.0.0.0 PORT=8000 DATABASE_URL=postgresql://user:password@localhost:5432/database DB_MIN_SIZE=5 DB_MAX_SIZE=20 REDIS_URL=redis://localhost:6379 JWT_SECRET=change-me-in-production JWT_ALGORITHM=HS256 DEBUG=false LOG_LEVEL=info SENTRY_DSN=https://...@sentry.io/...

#Загрузка переменных

# app/config.py import os from dotenv import load_dotenv load_dotenv() # Загрузка из .env class Settings: debug: bool = os.getenv('DEBUG', 'false').lower() == 'true' database_url: str = os.getenv('DATABASE_URL') jwt_secret: str = os.getenv('JWT_SECRET') # ... settings = Settings()

#Логирование

#Конфигурация logging

# app/logging_config.py import logging import sys LOGGING_CONFIG = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'default': { 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', }, 'json': { '()': 'pythonjsonlogger.jsonlogger.JsonFormatter', 'format': '%(asctime)s %(name)s %(levelname)s %(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'default', 'stream': sys.stdout, }, 'file': { 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'default', 'filename': 'logs/app.log', 'maxBytes': 10485760, # 10MB 'backupCount': 5, }, }, 'root': { 'level': 'INFO', 'handlers': ['console', 'file'], }, 'loggers': { 'aiohttp': { 'level': 'INFO', 'propagate': False, }, 'asyncpg': { 'level': 'WARNING', 'propagate': False, }, }, }

#Структурированное логирование (JSON)

pip install python-json-logger
import logging from pythonjsonlogger import jsonlogger logger = logging.getLogger(__name__) handler = logging.StreamHandler() handler.setFormatter(jsonlogger.JsonFormatter()) logger.addHandler(handler) logger.info('User logged in', extra={ 'user_id': 123, 'ip': '192.168.1.1', 'action': 'login' }) # {"asctime": "...", "user_id": 123, "ip": "192.168.1.1", "action": "login"}

#CI/CD

#GitLab CI

# .gitlab-ci.yml stages: - test - build - deploy variables: POSTGRES_DB: task_manager_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres test: stage: test image: python:3.11 services: - postgres:15 before_script: - pip install poetry - poetry install script: - poetry run pytest --cov=app --cov-fail-under=80 coverage: '/TOTAL.*\s+(\d+%)/' build: stage: build image: docker:24 services: - docker:24-dind before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA only: - main deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh script: - ssh -o StrictHostKeyChecking=no user@server " cd /app/task_manager && docker-compose pull && docker-compose up -d --build " only: - main

#Мониторинг

#Health check endpoint

# app/handlers/health.py from aiohttp import web import asyncio async def health_check(request): """Проверка здоровья приложения""" checks = { 'status': 'healthy', 'database': 'unknown', 'redis': 'unknown' } # Проверка БД try: async with request.app['db'].acquire() as conn: await conn.fetchval('SELECT 1') checks['database'] = 'ok' except Exception: checks['database'] = 'error' checks['status'] = 'unhealthy' # Проверка Redis try: redis = request.app.get('redis') if redis: await redis.ping() checks['redis'] = 'ok' except Exception: checks['redis'] = 'error' status = 200 if checks['status'] == 'healthy' else 503 return web.json_response(checks, status=status)

#Prometheus метрики

pip install aiohttp-prometheus
from aiohttp_prometheus import PrometheusMiddleware, MetricsHandler app = web.Application() app.middlewares.append(PrometheusMiddleware()) app.router.add_get('/metrics', MetricsHandler)

#Проверка понимания

Убедитесь, что вы понимаете:

  1. Почему не использовать web.run_app() в продакшене?
  2. Как настроить Gunicorn для aiohttp?
  3. Зачем нужен nginx перед приложением?
  4. Как организовать Docker-контейнеризацию?

Переходите к вопросам для закрепления.

Далее: Rate limiting, кэширование, оптимизация