Netpulse_SasS/deploy
byrsapty a0eb2ff5f1
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Пакування: образи, повний стек, TLS, CI, процедура бекапу
Два образи замість пʼяти: серверні команди їдуть з одного модуля й
мусять ходити в схему БД однією версією, а зонд лишається окремо —
у чужій мережі йому не місце з DSN і ключами шифрування.

Стек піднімає БД, кеш, міграції, API, колектор і Caddy з автоматичним
TLS. Міграції окремою службою, бо API піднімається в кількох
примірниках і гонка за схему нікому не потрібна.

deploy/README.md описує бекап як три речі: дамп, DEK і JWT-ключ.
Без DEK дамп не відновлюється — у БД лише шифротекст.

Дорогою виправлено .gitignore: голі "netpulse-agent" і
"netpulse-server" ігнорували ще й каталоги cmd/ з кодом команд.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 12:47:54 +03:00
..
.env.example Пакування: образи, повний стек, TLS, CI, процедура бекапу 2026-08-25 12:47:54 +03:00
Caddyfile Пакування: образи, повний стек, TLS, CI, процедура бекапу 2026-08-25 12:47:54 +03:00
Dockerfile.agent Пакування: образи, повний стек, TLS, CI, процедура бекапу 2026-08-25 12:47:54 +03:00
Dockerfile.server Пакування: образи, повний стек, TLS, CI, процедура бекапу 2026-08-25 12:47:54 +03:00
README.md Пакування: образи, повний стек, TLS, CI, процедура бекапу 2026-08-25 12:47:54 +03:00

Розгортання NetPulse

Один хост, docker compose, автоматичний TLS. Такого розгортання вистачає до кількох тисяч хостів на моніторингу; розносити служби по машинах має сенс тоді, коли впирається БД, а не застосунок.

Що з чого складається

Служба Роль Порт
db PostgreSQL 16 + TimescaleDB — усі дані
cache DragonflyDB — черги й тимчасові стани
migrate накочування схеми; відпрацьовує і зупиняється
api HTTP API + вшитий інтерфейс 8080
collector gRPC-колектор, до якого підключаються зонди 9443
proxy Caddy: сертифікати, HTTPS, проксі до двох служб 80/443/9443

Назовні дивиться лише proxy. api і collector портів не публікують: до них ходять через нього.

Перший запуск

git clone <репозиторій> netpulse && cd netpulse
cp deploy/.env.example .env

Заповнити .env. Три значення обовʼязкові й генеруються так:

openssl rand -base64 24                 # POSTGRES_PASSWORD
echo "np1=$(openssl rand -hex 32)"      # NETPULSE_DEK
openssl rand -base64 48                 # NETPULSE_JWT_SECRET

Далі:

docker compose up -d --build
docker compose run --rm api netpulse-user \
  -tenant default -login admin -role owner -name "Адміністратор"

Пароль команда спитає інтерактивно — щоб він не осів в історії оболонки й у списку процесів.

Інтерфейс — на https://<NETPULSE_DOMAIN>.

Підключення зонда

В інтерфейсі: Зонди → Додати зонд. Видане запрошення (np_enr_…) одноразове — після реєстрації воно згоряє.

На машині, де стоятиме зонд:

docker run -d --name netpulse-agent --restart unless-stopped \
  --cap-add NET_RAW \
  -v netpulse-agent:/var/lib/netpulse \
  netpulse/agent:dev \
  -server netpulse.example.com:9443 \
  -enroll np_enr_… \
  -name "Зонд у Львові" \
  -modules icmp,snmp,topology,ncm

Зонд обміняє запрошення на постійний токен і збереже його в /var/lib/netpulse/agent.json (права 0600). Наступні запуски токена вже не потребують — том із посвідченням має пережити перестворення контейнера, інакше кожен старт вимагатиме нового запрошення.

Усі зʼєднання зонда вихідні: у мережі клієнта не треба відкривати жодного порту.

Бекап

Три речі, і всі три обовʼязкові:

  1. Базаусе, крім секретів у відкритому вигляді.
  2. NETPULSE_DEK — без нього паролі SSH і SNMP-community з бекапу не розшифрувати. У БД лежить лише шифротекст.
  3. NETPULSE_JWT_SECRET — без нього після відновлення всі активні сесії відваляться. Не смертельно, але користувачі помітять.
docker compose exec -T db \
  pg_dump -U netpulse -d netpulse -Fc --no-owner \
  > netpulse-$(date +%F).dump

Формат -Fc (custom), а не простий SQL: він стискається і дозволяє відновлювати вибірково.

Ключі зберігати окремо від дампа — інакше сенс шифрування секретів зникає: той, хто дістав бекап, дістав і ключ до нього.

Автоматично, щодня

15 3 * * * cd /opt/netpulse && docker compose exec -T db pg_dump -U netpulse -d netpulse -Fc --no-owner > /var/backups/netpulse-$(date +\%F).dump && find /var/backups -name 'netpulse-*.dump' -mtime +30 -delete

Відновлення

TimescaleDB вимагає рамки навколо відновлення: без неї фонові процеси агрегації втручаються в наливання даних і дамп лягає пошкодженим.

docker compose stop api collector

docker compose exec -T db psql -U netpulse -d postgres -c \
  'DROP DATABASE IF EXISTS netpulse; CREATE DATABASE netpulse;'

docker compose exec -T db psql -U netpulse -d netpulse -c \
  'CREATE EXTENSION IF NOT EXISTS timescaledb;'

docker compose exec -T db psql -U netpulse -d netpulse -c \
  'SELECT timescaledb_pre_restore();'

docker compose exec -T db pg_restore -U netpulse -d netpulse --no-owner \
  < netpulse-2026-08-25.dump

docker compose exec -T db psql -U netpulse -d netpulse -c \
  'SELECT timescaledb_post_restore();'

docker compose up -d api collector

У .env має лежати той самий NETPULSE_DEK, що й на момент дампа. Інакше застосунок підніметься, але кожна спроба скористатись збереженим паролем поверне помилку розшифрування — і виглядатиме це як зламані креденшели, а не як втрачений ключ.

Перевірка після відновлення:

docker compose exec -T db psql -U netpulse -d netpulse -c \
  'SELECT count(*) FROM core.devices;'
curl -sf https://<NETPULSE_DOMAIN>/healthz && echo OK

Оновлення

git pull
docker compose up -d --build

migrate відпрацює першим і не дасть піднятись API, якщо схема не накотилась. Міграції йдуть по одній у транзакції; уже застосований файл зі зміненою контрольною сумою зупиняє весь запуск — це захист від мовчазного розходження схеми з кодом.

Відкат схеми не передбачений: зворотні міграції на даних телеметрії коштують дорожче, ніж відновлення з бекапу.

Зміна ключа шифрування

Ключі перелічуються через кому, новий — першим:

NETPULSE_DEK=np2=<новий hex>,np1=<старий hex>

Нові секрети шифруються першим ключем, старі читаються своїм. Прибирати старий ключ можна лише після того, як усі секрети перезаписані.

Чому саме так

Два образи, а не пʼять. api, collector, migrate, netpulse-user і netpulse-secret — з одного модуля, з половиною спільного коду. Один образ гарантує, що API і колектор ходять у схему БД однією версією; окремі образи дають їм можливість розʼїхатись саме там, де це найдорожче.

Зонд — окремо: він їде в чужу мережу, і DSN, ключі шифрування та команди заведення користувачів не повинні бути в тому образі навіть як невикористані файли.

Міграції окремою службою. API піднімається в кількох примірниках; накочування схеми зі старту означало б гонку між ними.

TLS на проксі, а не в застосунку. Прострочений сертифікат на системі, яка сама має повідомляти про проблеми, — найгірший спосіб дізнатись про проблему. Caddy оновлює його сам.