golang

Как я выбирал стек для SaaS-мониторинга сайтов

  • четверг, 9 апреля 2026 г. в 00:00:09
https://habr.com/ru/articles/1021000/

Я написал сервис мониторинга сайтов. Проверяю доступность из 10 точек мира, шлю алерты в Telegram, Slack, на почту. Есть бесплатный план, платные тарифы, status-страницы, SSL-мониторинг, серверный агент — всё как у больших.

Дашборд с мониторингом конкретного сайта.
Дашборд с мониторингом конкретного сайта.

Тут не будет универсальных советов «как выбрать стек для стартапа». Просто мой опыт: что взял, почему, и что бы поменял.

Бэкенд: FastAPI

Выбирал между Django, Flask и FastAPI. Django отпал сразу — мне не нужен его ORM (я хотел async), не нужен встроенный шаблонизатор (фронт на Vue), не нужен admin panel (написал свой). Django — это как купить грузовик, чтобы ездить за хлебом.

Flask — ок, но голый. Роутинг есть, а структуры нет. Каждый Flask-проект, который я видел после полугода разработки, превращается в кашу.

FastAPI сел идеально:

  • Async из коробки. У меня checker делает сотни HTTP-запросов параллельно — без async это смерть.

  • WebSocket поддержка. Dashboard обновляется в реалтайме через Redis pub/sub.

  • Dependency Injection через Depends() — вместо глобальных переменных.

  • Автодокументация на /api/docs. Swagger генерится сам из тайпхинтов. Я ничего не писал.

Вот как выглядит типичный эндпоинт:

@router.get("/sites")
async def list_sites(
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    sites = await db.execute(
        select(Site).where(Site.user_id == current_user.id)
    )
    return sites.scalars().all()

Чистый async, типизированный, без магии. Pydantic валидирует всё, что приходит и уходит. Если кто-то пришлёт {"url": 123} вместо строки — FastAPI вернёт 422 без единой строчки моего кода.

База: PostgreSQL + TimescaleDB

Чекер проверяет сайты каждые 1-10 минут. Каждая проверка — строка в таблице: timestamp, статус-код, время ответа, регион, IP. За месяц на 20 мониторах это ~860 000 строк. За год — 10 миллионов.

Обычный PostgreSQL справится. Но запросы «покажи среднее время ответа за последние 30 дней с группировкой по часам» будут тормозить.

TimescaleDB — расширение для PostgreSQL. Ставишь, делаешь SELECT create_hypertable('checks', 'time') — и таблица автоматически партиционируется по времени. Запросы по временным диапазонам летают. При этом это всё тот же PostgreSQL — тот же pg_dump, тот же Alembic, те же миграции.

  • Почему не MongoDB: мне нужны JOIN'ы. Сайт → проверки → инциденты → пост-мортемы → пользователь → подписка. Это реляционные данные. Запихивать их в документы — мучение.

  • Почему не ClickHouse: оверкилл. У меня не петабайты логов, а миллионы строк. TimescaleDB хватает с запасом, и он не требует отдельной инфраструктуры.

Docker-образ — timescale/timescaledb:latest-pg15. Пул — 20 соединений с overflow до 30:

engine = create_async_engine(
    DATABASE_URL,
    pool_size=20,
    max_overflow=10,
    pool_pre_ping=True,
)

Фронтенд: Vue 3

React я знаю. Vue тоже знаю. Выбрал Vue.

Причина тупая: мне быстрее на нём писать. Composition API во Vue 3 — это почти тот же React hooks, но без boilerplate. Не нужен useEffect с массивами зависимостей. watch() делает то, что написано — следит за реактивной переменной.

Стейт-менеджмент — Pinia. Это как Vuex, только без мутаций и без commit('SET_SOMETHING', payload). Просто стор с реактивными свойствами. Пишешь store.user = data — и всё.

Билд — Vite. Холодный старт dev-сервера за 300мс. После webpack это как пересесть с трамвая на мотоцикл.

Полный package.json:

{
  "dependencies": {
    "vue": "^3.4.21",
    "vue-router": "^4.3.0",
    "pinia": "^2.1.7",
    "vue-i18n": "^9.14.5",
    "apexcharts": "^3.54.0",
    "@simplewebauthn/browser": "^13.3.0"
  },
  "devDependencies": {
    "vite": "^5.2.0",
    "@vitejs/plugin-vue": "^5.0.4"
  }
}

6 зависимостей в production. Шесть. Никаких axios (fetch хватает), никаких lodash (не нужен), никаких UI-фреймворков (свой CSS, ~20KB). Dashboard грузится за секунду.

Очередь задач: Celery + Redis

У меня 12 фоновых задач, которые крутятся по расписанию:

  • Проверка сайтов — каждую минуту

  • SSL-сертификаты — раз в сутки

  • Safe Browsing (Google) — каждые 6 часов

  • DNS-записи — каждый час

  • Heartbeat-мониторы — каждую минуту

  • Отправка отчётов — ежедневно

  • Скриншоты упавших сайтов — по событию

  • ...ещё пять штук

Celery + Redis — скучный выбор. И это хорошо. Драматик (Dramatiq) модный, но экосистема меньше. RQ простой, но нет Beat (планировщика). Celery тяжёлый, с кучей настроек — зато работает. Пять лет на проде у тысяч компаний.

celery = Celery(
    "valpero",
    broker=REDIS_URL,
    include=[
        "app.tasks.checker",
        "app.tasks.notifier",
        "app.tasks.billing",
        "app.tasks.screenshot_task",
        # ...ещё 8 модулей
    ],
)

Redis заодно работает как pub/sub для реалтайм-обновлений дашборда. Один Redis — два применения.

Агент: Go

Серверный агент собирает метрики: CPU, RAM, диск, сеть, процессы, Docker-контейнеры. Раз в 30 секунд шлёт JSON на бэкенд.

Написал на Go. Не на Python. Вот почему:

  1. Размер. Go-бинарник — 5.6 MB. Один файл, без зависимостей. Скачал, chmod +x, запустил. Python-агент — это Python runtime (~30 MB) + pip + venv + зависимости + psutil. На дешёвом VPS с 1 GB RAM это больно.

  2. Кросс-компиляция. GOOS=linux GOARCH=arm64 go build — и готов бинарник для ARM. Без Docker, без CI, без танцев. Поддерживаю amd64 и arm64 одной командой.

  3. gopsutil. Библиотека для сбора системных метрик. Один import — и есть CPU, RAM, диск, температура, процессы. На Python есть psutil, но тогда нужен Python на целевой машине.

Агент ставится одной командой:

curl -sSL https://valpero.com/agent/install.sh | sudo bash -s -- --token YOUR_TOKEN

Скачивает бинарник, создаёт systemd-сервис, всё. Без Python, без npm, без ничего. То как и где генерится токен не расскажу, может пока что.

Десктоп: pywebview + rumps

Нужны были нативные приложения для Windows и macOS. Не Electron — он жрёт 200 MB RAM ради обёртки над веб-страницей.

  • Windows — pywebview. Открывает Valpero-дашборд в нативном WebView2 (Chromium). По сути обёртка, но весит мало и запускается быстро. Билд через PyInstaller в .exe.

  • macOS — rumps. Иконка в меню-баре, показывает статус мониторов. Зелёная — всё ок, красная — что-то упало. Клик — попап с деталями. Без браузера, чисто нативный виджет.

Почему два разных подхода? На маке menubar-виджет полезнее, чем окно. На винде нет menubar, зато есть трей — но полноценное окно удобнее.

Хостинг: Docker + дешёвые VPS

Infrastructure — Valpero

Основной сервер — Hetzner, Хельсинки, ~€4/мес. FastAPI, PostgreSQL, Redis, Celery — всё в Docker Compose.

Probe-ноды для мониторинга из разных регионов — Ferrum от WorkTitans по €1.20/мес за штуку. 1 vCore, 1 GB RAM, 15 GB NVMe. Есть в Исландии, Италии, Японии, Бразилии, Новой Зеландии, Казахстане. Нода — один Python-скрипт, ~200 строк. Подключается к центру по WebSocket, получает задания, отправляет результаты.

Итого: €4 (core) + 6 × €1.20 (пробы) = €11.20/мес за 10 точек мониторинга на четырёх континентах. Ещё 4 ноды у меня на существующих серверах.

Что бы поменял

Год назад я бы ответил иначе на пару вопросов.

  • Celery → может Dramatiq или Taskiq. Celery работает, но конфигурация — это ад. Документация огромная и запутанная. Celery Beat хранит расписание в файле (по умолчанию). Если воркер умирает криво то расписание может сломаться. Для нового проекта я бы посмотрел на Taskiq он async-native и проще.

  • Один репозиторий. Бэкенд, фронт, агент, десктоп-приложения, лендинг — всё в одном репо. Для соло-разработчика это ок. Но git log превращается в кашу. Уже вынес агент в отдельный репозиторий, как и виджеты.

  • CSS без фреймворка. Написал ~20KB CSS руками. Для дашборда это нормально, но для лендинга с 6 языковыми версиями — боль. Каждый визуальный баг надо фиксить в 6 файлах. Tailwind бы упростил жизнь, вопрос — не поздно ли еще?

Бэкенд и базу менять бы не стал. FastAPI + PostgreSQL + TimescaleDB — попадание в десятку.

Полный стек

Слой

Технология

Бэкенд

FastAPI + Uvicorn

ORM

SQLAlchemy (async)

База

PostgreSQL + TimescaleDB

Кэш/брокер

Redis

Очередь

Celery + Beat

Фронтенд

Vue 3 + Pinia + Vue Router

Билд

Vite

i18n

vue-i18n (6 языков)

Агент

Go + gopsutil

Скриншоты

Playwright (Chromium)

Десктоп Win

pywebview

Десктоп Mac

rumps

Платежи

Stripe

Алерты

Email, Telegram, Slack, Discord, SMS, WhatsApp, Webhook

Деплой

Docker Compose + Nginx

Хостинг

Hetzner + WorkTitans Ferrum

Общая стоимость инфраструктуры: €11.20/мес. Python 3.11. Один разработчик. Много времени.


Если делаете SaaS и выбираете стек — не парьтесь слишком долго. Берите то, на чём быстро пишете. Стек не определит успех. Продукт — определит. Мой мониторит сайты на valpero.com.
Как то так. Хостинг на Хетзере поэтому возможно понадобится VPN или bypasser DPI,ну и большей статей позже.