ЧАСТЬ IV: МАСШТАБИРОВАНИЕ

Production Deployment

Глава 21. Production Deployment

"Локально работает — это только начало пути."


21.1. От ноутбука до облака

Три уровня зрелости

Уровень 1: Ноутбук

python app.py
# Работает только у вас

Уровень 2: Сервер

ssh user@server
python app.py &
# Работает пока сервер не перезагрузится

Уровень 3: Production

docker build → k8s deploy → auto-scale → monitoring
# Работает 24/7, масштабируется, самовосстанавливается

21.2. Docker — упаковка приложения

Зачем Docker

Проблема: "У меня работает"

Developer: "У меня на Python 3.11 работает"
Server: *Python 3.8* — ошибка!

Решение: Docker

  • Упаковываем приложение + зависимости + окружение
  • Работает одинаково везде

Dockerfile для AI-приложения

# Базовый образ
FROM python:3.11-slim

# Рабочая директория
WORKDIR /app

# Копируем зависимости
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Копируем код
COPY . .

# Порт
EXPOSE 8000

# Запуск
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

requirements.txt

fastapi==0.104.1
uvicorn==0.24.0
openai==1.3.0
langchain==0.0.350
pydantic==2.5.0
python-dotenv==1.0.0

Сборка и запуск

# Сборка образа
docker build -t my-ai-app .

# Запуск контейнера
docker run -d \
  -p 8000:8000 \
  -e OPENAI_API_KEY=$OPENAI_API_KEY \
  --name ai-app \
  my-ai-app

# Проверка
curl http://localhost:8000/health

Docker Compose для нескольких сервисов

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Запуск:

docker-compose up -d

21.3. Kubernetes — оркестрация в масштабе

Когда нужен Kubernetes

Docker достаточно если:

  • 1 сервер
  • Низкая нагрузка
  • Простое приложение

Kubernetes нужен если:

  • Много серверов
  • Auto-scaling
  • High availability (99.9%+)
  • Микросервисы

Основные концепции K8s

Pod — наименьшая единица (1+ контейнеров) Deployment — управление репликами Pods Service — сетевой доступ к Pods Ingress — HTTP маршрутизация

Deployment manifest

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ai-app
spec:
  replicas: 3  # 3 копии приложения
  selector:
    matchLabels:
      app: ai-app
  template:
    metadata:
      labels:
        app: ai-app
    spec:
      containers:
      - name: ai-app
        image: my-ai-app:latest
        ports:
        - containerPort: 8000
        env:
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: api-keys
              key: openai
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"

Service для доступа

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ai-app-service
spec:
  selector:
    app: ai-app
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer

Применение

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

# Проверка
kubectl get pods
kubectl get services

Auto-scaling

# hpa.yaml (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ai-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ai-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

Автоматически масштабирует от 2 до 10 pods в зависимости от CPU.


21.4. Cloud Providers

AWS vs GCP vs Azure

СервисAWSGCPAzure
ComputeEC2Compute EngineVirtual Machines
ContainersECS/EKSGKEAKS
ServerlessLambdaCloud FunctionsFunctions
StorageS3Cloud StorageBlob Storage
DatabaseRDSCloud SQLSQL Database
AI ServicesSageMakerVertex AIAzure AI

AWS Lambda для AI (Serverless)

Идея: Код запускается только при запросе. Оплата за выполнение.

# lambda_function.py
import json
import openai
import os

openai.api_key = os.environ['OPENAI_API_KEY']

def lambda_handler(event, context):
    """AWS Lambda handler"""

    # Получаем запрос
    body = json.loads(event['body'])
    prompt = body.get('prompt', '')

    # Вызываем OpenAI
    response = openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}]
    )

    # Возвращаем результат
    return {
        'statusCode': 200,
        'body': json.dumps({
            'response': response.choices[0].message.content
        })
    }

Деплой:

# Упаковать
zip function.zip lambda_function.py

# Загрузить в AWS Lambda
aws lambda create-function \
  --function-name ai-function \
  --runtime python3.11 \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://function.zip

Преимущества:

  • Не нужно управлять серверами
  • Автоматическое масштабирование
  • Оплата только за использование

Недостатки:

  • Cold start (первый запрос медленнее)
  • Лимиты на время выполнения (15 минут AWS)

GCP Cloud Run (Container-based serverless)

# Деплой Docker образа
gcloud run deploy ai-app \
  --image gcr.io/my-project/ai-app \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars OPENAI_API_KEY=$OPENAI_API_KEY

Получаете URL: https://ai-app-xxx.run.app

Автоматически масштабируется от 0 до 1000+ экземпляров.


21.5. CI/CD для AI-приложений

Что такое CI/CD

CI (Continuous Integration):

  • Автоматические тесты при каждом commit
  • Сборка Docker образа
  • Проверка качества кода

CD (Continuous Deployment):

  • Автоматический деплой при успешных тестах
  • Rolling updates (постепенное обновление)
  • Rollback при ошибках

GitHub Actions pipeline

# .github/workflows/deploy.yml
name: Deploy AI App

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest

      - name: Run tests
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: pytest tests/

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Build Docker image
        run: docker build -t my-ai-app:${{ github.sha }} .

      - name: Push to registry
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker tag my-ai-app:${{ github.sha }} username/my-ai-app:latest
          docker push username/my-ai-app:latest

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/ai-app ai-app=username/my-ai-app:latest

Что происходит:

  1. Push в main → запускаются тесты
  2. Тесты прошли → сборка Docker образа
  3. Образ загружен → деплой в K8s
  4. Всё автоматически!

21.6. Стратегии деплоя

Rolling Update (по умолчанию в K8s)

Old version: [Pod1] [Pod2] [Pod3]
             [Pod1] [Pod2] [New1]  ← Заменяем по одному
             [Pod1] [New1] [New2]
             [New1] [New2] [New3]  ← Все обновлены

Преимущества: Нет downtime Недостатки: Два версии работают одновременно

Blue-Green Deployment

Blue (old):  [Pod1] [Pod2] [Pod3] ← Пользователи здесь
Green (new): [Pod4] [Pod5] [Pod6] ← Запущено параллельно

Switch traffic → Green

Blue (old):  [Pod1] [Pod2] [Pod3] ← Можно удалить
Green (new): [Pod4] [Pod5] [Pod6] ← Теперь пользователи здесь

Преимущества: Быстрый rollback Недостатки: 2x ресурсов на момент деплоя

Canary Deployment

Old: [Pod1] [Pod2] [Pod3] ← 90% трафика
New: [Pod4]                ← 10% трафика (тест)

Если OK:
Old: [Pod1] [Pod2]         ← 50% трафика
New: [Pod3] [Pod4]         ← 50% трафика

Если OK:
New: [Pod1] [Pod2] [Pod3] [Pod4] ← 100% трафика

Преимущества: Постепенное тестирование на реальных пользователях Недостатки: Сложнее настроить


21.7. Environment management

Development → Staging → Production

Development (dev):
- Ваш ноутбук
- Быстрые итерации
- Можно ломать

Staging (stage):
- Копия production
- Тестирование перед релизом
- Интеграционные тесты

Production (prod):
- Реальные пользователи
- Высокая доступность
- Нельзя ломать!

Управление конфигурацией

# config.py
import os
from enum import Enum

class Environment(Enum):
    DEVELOPMENT = "dev"
    STAGING = "stage"
    PRODUCTION = "prod"

class Config:
    ENV = os.getenv("ENV", "dev")

    # Общие настройки
    APP_NAME = "AI App"

    # Специфичные для окружения
    if ENV == Environment.DEVELOPMENT.value:
        DEBUG = True
        DATABASE_URL = "sqlite:///dev.db"
        OPENAI_MODEL = "gpt-3.5-turbo"  # Дешевле для dev
        LOG_LEVEL = "DEBUG"

    elif ENV == Environment.STAGING.value:
        DEBUG = True
        DATABASE_URL = os.getenv("STAGING_DB_URL")
        OPENAI_MODEL = "gpt-4"
        LOG_LEVEL = "INFO"

    else:  # Production
        DEBUG = False
        DATABASE_URL = os.getenv("PROD_DB_URL")
        OPENAI_MODEL = "gpt-4"
        LOG_LEVEL = "WARNING"
        SENTRY_DSN = os.getenv("SENTRY_DSN")  # Error tracking

21.8. Secrets management

Никогда не коммитьте секреты!

Плохо:

OPENAI_API_KEY = "sk-abc123..."  # В коде!

Хорошо:

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

Environment variables

.env файл (локально):

# .env
OPENAI_API_KEY=sk-abc123...
DATABASE_URL=postgresql://...
# В коде
from dotenv import load_dotenv
load_dotenv()

api_key = os.getenv("OPENAI_API_KEY")

ВАЖНО: Добавьте .env в .gitignore!

Kubernetes Secrets

# Создать secret
kubectl create secret generic api-keys \
  --from-literal=openai=sk-abc123...

# Использовать в deployment
env:
- name: OPENAI_API_KEY
  valueFrom:
    secretKeyRef:
      name: api-keys
      key: openai

AWS Secrets Manager / GCP Secret Manager

from google.cloud import secretmanager

client = secretmanager.SecretManagerServiceClient()
name = "projects/my-project/secrets/openai-key/versions/latest"
response = client.access_secret_version(request={"name": name})
api_key = response.payload.data.decode("UTF-8")

Ключевые выводы главы

Docker: Упаковывает приложение + зависимости

Kubernetes: Оркестрация, auto-scaling, high availability

Cloud: AWS/GCP/Azure для hosting, serverless для простоты

CI/CD: Автоматические тесты и деплой (GitHub Actions)

Deployment strategies: Rolling, Blue-Green, Canary

Environments: dev → staging → production

Secrets: НИКОГДА не коммитьте, используйте env vars или secret managers

Infrastructure as Code: Описывайте инфраструктуру в файлах (YAML)


Следующая глава: Performance optimization — скорость и масштабирование