Как стать автором
Поиск
Написать публикацию
Обновить

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

Уровень сложностиСредний
Время на прочтение3 мин
Количество просмотров256

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

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

Мне нужно было автоматизировать процесс сбора спортивных данных (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 решают большую часть боли.

Теги:
Хабы:
+3
Комментарии0

Публикации

Ближайшие события