Деплой
Ansible-плейбук, порядок ролей, секреты, сервисные хуки.
Деплой
Прод-окружение разворачивается через Ansible на одном хосте. Все сервисы — Docker-контейнеры в общей сети ng_metrics.
Главный плейбук
ansible/playbooks/prod/ng-metrics.yaml — единая точка деплоя.
cd ansible
poetry install
poetry run ansible-playbook -i inventory/prod playbooks/prod/ng-metrics.yamlПри запуске спросит GitHub-токен (для скачивания приватных репозиториев и контейнеров).
Порядок ролей
| Роль | Что делает | Статус |
|---|---|---|
infisical | Получает все секреты по проекту и окружению | активна |
common | Настройка хоста, swap, Docker, базовые пакеты | активна |
supabase | Поднимает Supabase (PostgreSQL, Auth, Storage), деплоит Edge Functions, заливает бренд-ассеты | активна |
system | Создаёт Docker-сеть ng_metrics, поднимает Redis, конфигурирует Traefik (HTTPS + reverse proxy) | активна |
ng-metrics-backend | Два контейнера: web (Django + gunicorn на :7000) и worker (celery -A ng_metrics worker). Воркер использует тот же образ; entrypoint очищен, чтобы не запускать миграции и gunicorn. Concurrency задаётся переменной celery_worker_concurrency (default 2). | активна |
ng-metrics-frontend | Next.js на :3000 | активна |
ng-metrics-bot | Telegram-бот на :8080 | отключена |
chatwoot | Платформа поддержки | отключена |
В файле ng-metrics.yaml обе роли закомментированы из-за ограничений Telegram в РФ. Для возврата — раскомментировать соответствующие блоки и повторить деплой.
Секреты — Infisical
Все секреты живут в Infisical (self-hosted). Структура:
| Проект | Окружение |
|---|---|
ng-metrics | prod / staging |
Ключевые переменные (без значений — чтобы не утекли в git):
| Группа | Переменные |
|---|---|
| Supabase | SUPABASE_URL, SUPABASE_EXTERNAL_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_JWT_SECRET |
| Postgres | POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT |
| Resend | RESEND_API_KEY, RESEND_FROM_EMAIL, RESEND_WEBHOOK_SECRET |
| Telegram | TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET |
| Chatwoot | CHATWOOT_URL, CHATWOOT_API_KEY, CHATWOOT_WEBHOOK_SECRET |
| Django | SECRET_KEY, ALLOWED_HOSTS, DASHBOARD_APP_PUBLIC_URL |
| Redis / Celery | REDIS_HOST, REDIS_PORT, REDIS_PASSWORD. URL'ы для брокера (DB 3) и result-backend (DB 4) собираются автоматически в settings/production.py. |
Локально секреты лежат в .env.local каждого сервиса (есть .env.example как шаблон).
Redis — разделение по базам
Один Redis-инстанс, пять баз:
| DB | Сервис | Что хранит |
|---|---|---|
| 0 | Garden | Redis Stream для уведомлений в Telegram, кэш |
| 1 | Chatwoot | Очереди, sidekiq |
| 2 | Bot | FSM-состояния Aiogram, сессии Chatwoot |
| 3 | Garden / Celery | Broker для фоновых задач (отправка уведомлений) |
| 4 | Garden / Celery | Result backend |
DB 3 и 4 добавлены в апреле 2026 вместе с Celery-воркером.
Reverse proxy и HTTPS
Перед всеми сервисами — Traefik (HTTPS terminator + reverse proxy + автоматические сертификаты Let's Encrypt). С апреля 2026 он заменил связку Caddy + Nginx — теперь это единая точка входа.
internet → Traefik (:80 → :443)
├─ knotta.ru → frontend:3000
├─ api-knotta.ru → backend:7000
└─ service-knotta.ru → supabase-kong:8000Traefik сам получает сертификаты Let's Encrypt по HTTP-01 challenge. Конфигурация:
- Статическая —
ansible/roles/system/templates/traefik.yaml.j2(entry points, ACME-резолвер, providers). - Динамическая (роутеры и сервисы) —
ansible/roles/system/templates/traefik-dynamic.yaml.j2. - Хранилище ACME-сертификатов —
volumes/traefik/acme.jsonна хосте.
Все сервисы за Traefik подключаются через статические маршруты в динамическом конфиге (file provider, без Docker labels).
Edge Functions
Деплой Supabase Edge Functions — внутри роли supabase:
supabase functions deploy send-email
supabase functions deploy auth-sync
supabase functions deploy set-roleЭто делается роль автоматически через supabase CLI.
Важно: после деплоя нужно проверить, что в Supabase Studio (Authentication → Settings → Hooks) URL хука send-email указывает на актуальную Edge Function.
Миграции БД
Миграции применяются автоматически при деплое ng-metrics-backend — таска роли запускает python manage.py migrate внутри контейнера до старта DRF-сервера.
Не запускайте миграции вручную в проде — это сломает контейнерную жизнь backend'а. Если миграция большая — добавьте её в roadmap отдельной задачей и применяйте в техническое окно.
Healthchecks
| Сервис | Healthcheck URL |
|---|---|
| Frontend | GET / (200) |
| Backend | GET /api/schema/ (200) |
| Bot | GET /health (200) |
| Supabase | GET /rest/v1/ с anon-ключом (200) |
Traefik и Docker используют их для restart-логики и для проверки live-ness апстримов.
Откат деплоя
# Назад к предыдущему контейнеру
poetry run ansible-playbook \
-i inventory/prod \
playbooks/prod/rollback.yaml \
--extra-vars "service=ng-metrics-backend version=v1.4.0"История версий — теги Docker-образов (создаются по тегам git).
Настройка нового окружения
- Создать инвентарь в
ansible/inventory/<env>/. - Положить
group_vars/all/vault.yaml(зашифрован через ansible-vault). - В Infisical создать соответствующее окружение и заполнить переменные.
- В Supabase создать проект, прокинуть
SUPABASE_*в Infisical. - Запустить плейбук на новом хосте.
- После старта — создать суперпользователя:
docker exec -it ng-metrics-backend python manage.py createsuperuserCI/CD
CI лежит в каждом репозитории отдельно (.github/workflows/):
- Frontend — линт, проверка типов, тесты, билд.
- Backend — линт, mypy, pytest.
- Bot — линт, mypy.
CD на текущем этапе ручной — деплой запускается с локальной машины DevOps через Ansible. В roadmap — переход на push-driven деплой через GitHub Actions + self-hosted runner.
Журнал инцидентов
Инциденты и крупные изменения деплоя описываются в Истории изменений. Полные технические детали последнего крупного релиза — в docs/modification-april-2026/system-design.md.