Как я автоматизировал деплой аналитической платформы для спортивных данных на базе нестабильного API

от автора

Первые сутки сервис падал каждый час, но сейчас система выдерживает пиковые запросы без даунтайма.

Исходная задача

Мне нужно было автоматизировать процесс сбора спортивных данных (NFL, NBA, UFC) с dingerodds для дальнейшего анализа и обучения моделей. Источник выбран из-за:

  • доступного REST API (пример запроса ниже)

  • свежих коэффициентов и статистики

  • наличия исторических данных

GET /api/v1/events/upcoming?market=moneyline&sport=baseball Authorization: Bearer <token>

Но оказалось, что API отваливается под минимальной нагрузкой и плохо обрабатывает батчи (особенно GET /events/history).


Проблемы

  1. Rate-limits не задокументированы, но явно действуют: после ~60 запросов в минуту — 429.

  2. API отдает 502/504 на пиковых батчах.

  3. Нет webhook или pub/sub, всё надо опрашивать вручную.

  4. Нестабильный ответ JSON — иногда odds приходят как null, иногда как {}.


Архитектура решения

Обёртка над API (Python, asyncio, httpx)

import httpx from tenacity import retry, stop_after_attempt, wait_exponential  @retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1)) async def fetch(url: str, headers: dict):     async with httpx.AsyncClient(timeout=10) as client:         response = await client.get(url, headers=headers)         response.raise_for_status()         return response.json()
  • Circuit Breaker через кастомную реализацию с fallback

  • Поддержка конкурентных запросов (asyncio.gather)

  • Встроенные прокси-пулы с ротацией IP

  • В случае fail → log в Loki, push alert в Telegram

CI/CD pipeline (GitLab)

stages:   - build   - test   - deploy  build:   stage: build   script:     - docker build -t registry/app:$CI_COMMIT_SHA .     - docker push registry/app:$CI_COMMIT_SHA  deploy:   stage: deploy   script:     - helm upgrade --install dingerodds-chart ./chart \         --set image.tag=$CI_COMMIT_SHA
  • Helm + custom values для окружений (dev, prod)

  • Secrets через sealed-secrets

  • Логика деплоя: если main — обновляем prod, иначе dev

Kubernetes (Yandex Cloud)

  • 1 Deployment: api-fetcher — воркер, который забирает данные с dingerodds

  • 1 CronJob: retrain-models — переобучение моделей каждые 6 часов

  • MinIO: хранилище parquet-файлов (s3://sports-data/{sport}/{date}.parquet)

  • Horizontal Pod Autoscaler: масштабируем api-fetcher по CPU > 70%

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler spec:   scaleTargetRef:     kind: Deployment     name: api-fetcher   minReplicas: 1   maxReplicas: 5   metrics:     - type: Resource       resource:         name: cpu         target:           type: Utilization           averageUtilization: 70

ETL: Dask + Parquet

  • После каждого батча сохраняем данные в parquet с разделением по sport и timestamp

  • Используем fastparquet как backend

  • Dask позволяет агрегировать большие объемы без перегрузки памяти

import dask.dataframe as dd  df = dd.from_pandas(pandas_df, npartitions=4) df.to_parquet("s3://sports-data/nfl/2025-07-24/", engine="fastparquet")

Мониторинг

  • Prometheus: метрики по количеству успешных и проваленных запросов

  • Loki + Grafana: логи по статус-кодам, времени ответа

  • Alertmanager: шлёт уведомления в Telegram, если API не отвечает > 2 минут

Результаты

  • Система в проде уже 14 дней — ни одного критического отказа

  • Обновление данных каждые 5 минут

  • ML-модели обучаются с актуальными и полными датасетами

  • Аптайм > 99.95% (до внедрения был ~85%)

  • Время от запроса до появления данных в parquet — < 2 мин

Планы на будущее

  • Перейти на pub/sub модель (если dingerodds даст такую возможность)

  • Интегрировать Redis для кеширования популярных выборок

  • Сделать delta-lake поверх MinIO + добавить версионирование данных

  • Использовать Kafka для push-событий, если появится мульти-источник

Выводы

Работа с нестабильным API — это почти всегда про защиту от самого источника.

Если бы я просто подключил dingerodds без буферов, ретраев и лимитов — прод упал бы в первые часы.

Обёртка + CI/CD + отказоустойчивая архитектура на Kubernetes решают большую часть боли.


ссылка на оригинал статьи https://habr.com/ru/articles/931050/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *