Эксплуатация NATS: nats CLI, мониторинг, backup и обновление кластера

Администрирование NATS: nats CLI и contexts, endpoints /varz /jsz /connz, prometheus-nats-exporter и Grafana, nats-top, backup/restore стримов и rolling upgrade кластера — с продакшн-чеклистом серии

Это девятая, последняя статья серии «Погружение в NATS». Все предыдущие восемь разбирали, как NATS работает: subjects и Core (ст. 1), JetStream (ст. 2), классический кластер на routes и RAFT-группы JetStream (ст. 3), гео-топологии, надёжность и производительность, клиенты на Go и Java, accounts и безопасность. Всё это — про то, что система делает правильно, когда она работает. Эта статья — про то, как убедиться, что она действительно работает, и что делать, когда придётся её трогать руками: смотреть в неё, снимать метрики, разбирать состояние кластера по горячим следам, делать бэкап перед рискованной операцией и обновлять версию без остановки трафика.

Эксплуатация NATS: nats CLI, мониторинг, backup и обновление кластера

Это тот же вопрос, что закрывал последнюю статью серии про RabbitMQ («Эксплуатация RabbitMQ: мониторинг и продакшн-чеклист»): кластеризация и репликация из ст. 3 дают инфраструктуру для отказоустойчивости, но реально работающий production — это ещё и то, что вы видите происходящее до того, как оно станет инцидентом, и умеете откатиться, если что-то пошло не так. Эксплуатация закрывает цикл серии, и заканчивается статья сводным чеклистом по всем девяти статьям.

В статье

nats CLI и contexts

nats — единственный CLI, который использовался во всех восьми предыдущих статьях: nats stream add, nats consumer next, nats pub, nats stream cluster step-down (ст. 3). Здесь — то, что до сих пор оставалось за кадром: сам CLI как инструмент эксплуатации, а не только демонстрации.

Contexts — именованные профили подключения: сервер, учётные данные, TLS. Вместо того чтобы на каждую команду передавать --server, --creds, --tlscert, контекст сохраняет это один раз:

nats context add prod \
  --server tls://n1.prod.internal:4222,tls://n2.prod.internal:4222,tls://n3.prod.internal:4222 \
  --creds /etc/nats/prod.creds \
  --tlsca /etc/nats/ca.pem \
  --select

nats context ls
nats context select prod
nats context info

--select делает контекст текущим по умолчанию сразу при создании; nats context select переключает между уже сохранёнными; nats context info показывает, какой контекст активен и что в нём. Полезная деталь: у команды есть context previous — быстрый прыжок обратно на контекст, который был активен до последнего переключения, что удобно, когда вы туда-сюда прыгаете между prod и staging в одной сессии терминала. В демо-стендах серии это выглядело проще — один локальный контекст без TLS и credentials (nats context save local --server localhost:4222 --select), но именно contexts делают тот же CLI пригодным для эксплуатации многоузлового production-кластера с mTLS: команда nats stream info ORDERS работает одинаково что в деве, что в проде, различается только выбранный контекст.

Административные команды идут по той же логике «одна сущность — набор подкоманд», что и stream/consumer, которые уже встречались в серии: nats server info, nats server ls, nats server ping — базовая инвентаризация; nats stream ls/nats consumer ls — списки поверх текущего контекста без необходимости лезть в HTTP monitoring endpoints вручную.

nats server report — это сводки по конкретному аспекту состояния кластера, агрегированные по всем нодам сразу: nats server report jetstream, nats server report connections, nats server report accounts, nats server report gateways, nats server report health. В отличие от HTTP endpoints (следующий раздел), которые отдают состояние одной ноды, server report строит сводную таблицу по всему видимому кластеру — например, jetstream покажет по каждой ноде объём памяти/диска под JetStream, число streams и consumers, что удобно как разовая проверка «у нас всё ещё сбалансировано» без сравнения нескольких /jsz вручную.

Важная деталь эксплуатации: server report, как и другие команды семейства server (server ping, server watch), общаются с сервером через системные ($SYS) subjects, а не HTTP monitoring API. Это значит, что клиенту, от имени которого выполняется команда, нужны права на account $SYS — либо явный доступ (в отдельном account с JWT, ст. 8), либо запуск от имени встроенного system-пользователя. Без этого команда вернёт ошибку авторизации, а не пустой отчёт — если в вашем кластере включены accounts и разграничение прав (что для production рекомендовано), это стоит учитывать заранее, а не в момент инцидента, когда вам срочно нужен server report.

nats server report jetstream
# error: server request failed, ensure the account used has system privileges
# — типичная ошибка, если контекст не имеет доступа к $SYS

Мониторинговые endpoints

У nats-server есть встроенный HTTP monitoring API — набор JSON-эндпоинтов на отдельном порту (по умолчанию не включён; включается флагом -m <port>/--http_port <port>, в демо-стендах серии — 8222). В отличие от server report, который опрашивает кластер через $SYS и требует прав, эти endpoints не требуют аутентификации по умолчанию (в production их стоит закрывать сетевым доступом, а не полагаться на то, что «никто не найдёт порт») и отдают состояние ровно той ноды, к которой обратились напрямую.

Endpoint Что показывает
/varz Общая информация о процессе: версия, uptime, конфигурация, счётчики сообщений/байт (включая in_client_msgs/out_client_msgs/in_client_bytes/out_client_bytes — про них дальше), память, CPU
/jsz Состояние JetStream: meta-cluster (кто лидер, сколько нод), список streams и consumers с их RAFT-статусом, используемая память/диск
/connz Список активных клиентских соединений: адрес, подписки, скорость трафика на конкретное соединение
/routez Route-соединения Core-кластера (ст. 3) — с кем эта нода в full-mesh, состояние каждого route
/gatewayz Gateway-соединения supercluster (ст. 4) — какие удалённые кластеры видны через эту ноду
/leafz Leaf node соединения (ст. 4) — какие edge-площадки подключены как leaf к этой ноде
/healthz Простая проверка живости: {"status":"ok"} при HTTP 200 — то, что используется как gate в rolling upgrade (см. ниже)
graph TD N["nats-server
монитор :8222"] N --> V["/varz
процесс, конфиг, in/out msgs+bytes"] N --> J["/jsz
JetStream: meta-cluster, streams, consumers"] N --> C["/connz
клиентские соединения"] N --> R["/routez
Core-кластер: routes"] N --> G["/gatewayz
supercluster: gateways"] N --> L["/leafz
edge: leaf nodes"] N --> H["/healthz
ok/fail — gate для upgrade"] style J fill:#c9e4c5,stroke:#5b8a5e

graph TD
  N["nats-server
монитор :8222"] N --> V["/varz
процесс, конфиг, in/out msgs+bytes"] N --> J["/jsz
JetStream: meta-cluster, streams, consumers"] N --> C["/connz
клиентские соединения"] N --> R["/routez
Core-кластер: routes"] N --> G["/gatewayz
supercluster: gateways"] N --> L["/leafz
edge: leaf nodes"] N --> H["/healthz
ok/fail — gate для upgrade"] style J fill:#c9e4c5,stroke:#5b8a5e
Points of observation: какой endpoint отвечает за какой аспект состояния ноды

/jsz — главный endpoint для JetStream, и стоит разобрать его отдельно. По умолчанию он отдаёт агрегаты (сколько памяти/диска занято, сколько accounts используют JetStream, состояние meta-cluster), но с query-параметрами раскрывает детали:

curl -s "localhost:8222/jsz?streams=1&consumers=1" | jq

?streams=1 добавляет в ответ список streams с их состоянием (лидер, реплики, current/OFFLINE — ровно те же поля, что в выводе nats stream info из ст. 3, но в одном JSON-запросе по всем streams сразу, а не по одному через CLI). ?consumers=1 аналогично разворачивает consumers внутри каждого stream. Для кластерной ноды в /jsz также видно meta_cluster — тот же блок, что уже использовался в demo ст. 3 (curl -s localhost:8222/jsz | jq '.meta_cluster') для проверки, что meta-group сформирована и видит все ноды.

/connz тоже принимает полезные параметры: ?subs=1 добавляет список subject-подписок каждого соединения — полезно при разборе «кто вообще подписан на этот subject прямо сейчас», когда nats sub для проверки заводить не хочется (например, в проде, где лишняя подписка — это лишний шум в трафике).

Практическое правило. server reportnats top) дают агрегированный снимок через $SYS и требуют прав на системный account; server check (следующий пункт) — отдельный случай: это health-check, который ходит обычным клиентским соединением и $SYS не требует (проверено: nats server check jetstream работает без системных прав). HTTP endpoints дают сырой JSON по конкретной ноде тоже без прав, но требуют явного обхода всех нод кластера, если нужна полная картина, — и именно поэтому существует третий инструмент, который автоматизирует это для мониторинговых систем: prometheus-nats-exporter.

Метрики: Prometheus + Grafana

HTTP endpoints хороши для ручной проверки «что происходит прямо сейчас», но для алертов и графиков во времени нужен экспортёр, который регулярно ходит в monitoring API и отдаёт результат в формате Prometheus.

prometheus-nats-exporter

natsio/prometheus-nats-exporter — официальный экспортёр проекта NATS, отдельный процесс (не встроен в nats-server), который опрашивает monitoring endpoints одной или нескольких нод и публикует метрики на своём порту (по умолчанию 7777, путь /metrics):

prometheus-nats-exporter -varz -connz -routez -subz -jsz=streams -p 7777 http://nats:8222

Флаги соответствуют endpoints напрямую: -varz включает метрики из /varz, -connz — из /connz, -jsz — из /jsz. Значение -jsz — это список фильтров, а не «включить всё»: например -jsz=streams даёт метрики по streams, а полный набор задаётся перечислением -jsz=streams,consumers,accounts. Каждая метрика получает префикс gnatsd_ и постфикс с именем поля источника — например, поле connections из /varz превращается в метрику gnatsd_varz_connections.

Ключевые метрики

Трафик клиентов — метрики, добавленные в nats-server 2.12.9. Начиная с версии 2.12.9 /varz (и, соответственно, экспортёр) отдаёт отдельные счётчики трафика именно от/к обычным клиентам, в отличие от общих in_msgs/out_msgs/in_bytes/out_bytes, которые суммируют вообще весь трафик ноды — включая route, gateway и leaf-соединения между серверами:

Метрика Смысл
gnatsd_varz_in_client_msgs Сообщения, полученные именно от клиентов (не от других серверов кластера)
gnatsd_varz_out_client_msgs Сообщения, отправленные именно клиентам
gnatsd_varz_in_client_bytes Байты от клиентов
gnatsd_varz_out_client_bytes Байты клиентам

Разница практическая: на ноде с активным Core-кластером и репликацией JetStream общий in_msgs включает трафик RAFT-репликации и route-gossip между n1/n2/n3 — он растёт даже если ни один внешний клиент ничего не публикует. in_client_msgs отфильтровывает это и показывает именно то, что интересует при ответе на вопрос «сколько сообщений реально прислали приложения» — до 2.12.9 эту цифру нужно было реконструировать косвенно (например, через /connz суммированием по всем клиентским соединениям), теперь она есть напрямую.

Соединения и ресурсы:

Метрика Смысл
gnatsd_varz_connections Число активных клиентских соединений на ноде
gnatsd_varz_mem RSS процесса nats-server
gnatsd_varz_cpu Загрузка CPU процессом

JetStream:

Метрика Смысл
gnatsd_varz_jetstream_stats_memory Память, занятая JetStream (streams/consumers с storage: memory)
gnatsd_varz_jetstream_stats_storage Диск, занятый JetStream (storage: file)
gnatsd_varz_jetstream_stats_accounts Число accounts, использующих JetStream
gnatsd_varz_jetstream_stats_api_errors Счётчик ошибок JetStream API — рост этой метрики стоит сразу проверять: это либо баг в клиентском коде, либо клиент из ст. 2 бьётся об строгий режим валидации JetStream API, включённый по умолчанию с версии, где статья 2 это подробно разбирала

docker-compose.yml демо к этой статье поднимает nats-exporter с флагами -varz -connz -routez -subz -jsz=streams — Prometheus скрейпит его каждые 5 секунд, а Grafana строит по этим метрикам панели «Активных соединений», «Client msgs in/out (rate)», «Client bytes in/out (rate)» и «JetStream: memory / storage». Полная конфигурация — в README демо.

graph LR NS["nats-server
:8222 /varz /jsz /connz"] -->|HTTP polling| EXP["prometheus-nats-exporter
:7777 /metrics"] EXP -->|scrape каждые 5s| PROM["Prometheus
:9090"] PROM -->|PromQL| GRAF["Grafana
:3000 дашборд"] style EXP fill:#f9f3e3,stroke:#8b7355 style GRAF fill:#c9e4c5,stroke:#5b8a5e

graph LR
  NS["nats-server
:8222 /varz /jsz /connz"] -->|HTTP polling| EXP["prometheus-nats-exporter
:7777 /metrics"] EXP -->|scrape каждые 5s| PROM["Prometheus
:9090"] PROM -->|PromQL| GRAF["Grafana
:3000 дашборд"] style EXP fill:#f9f3e3,stroke:#8b7355 style GRAF fill:#c9e4c5,stroke:#5b8a5e
Путь метрики от nats-server до дашборда Grafana

Мост: vs rabbitmq_prometheus vs Kafka JMX

В статье про мониторинг RabbitMQ метрики шли по-другому: плагин rabbitmq_prometheus встроен прямо в брокер и отдаёт /metrics в формате Prometheus без отдельного процесса — включил плагин, получил endpoint. NATS устроен иначе: monitoring API у сервера свой, JSON-based, а перевод в формат Prometheus — обязанность отдельного внешнего процесса, prometheus-nats-exporter. Разница не техническая придирка, а разный операционный след: у RabbitMQ одной строкой в конфиге закрывается вся задача, у NATS нужно поднимать и следить за ещё одним процессом (или sidecar-контейнером, как в demo-стенде), у которого свои флаги, свой порт и свой жизненный цикл, отдельный от nats-server.

Kafka в этом сравнении — третий вариант: у брокера нет ни встроенного HTTP-endpoint для Prometheus (как у RabbitMQ), ни отдельного HTTP-based экспортёра уровня prometheus-nats-exporter в комплекте (как у NATS). Метрики Kafka экспонируются через JMX (Java Management Extensions) — стандартный механизм JVM-приложений, и чтобы превратить их в Prometheus-метрики, обычно ставят JMX-агент (jmx_exporter) как javaagent прямо в процесс брокера, либо отдельный JMX-to-Prometheus proxy рядом. Три брокера — три разных ответа на один вопрос «как метрики попадают в Prometheus»: встроенный HTTP endpoint (RabbitMQ), официальный внешний exporter поверх собственного monitoring API (NATS), JVM-агент поверх JMX (Kafka). При выборе брокера это тоже часть операционной цены, которую стоит учитывать наравне с моделью доставки — она обсуждалась в обзорной статье.

nats-top

Иногда метрик в Grafana мало — нужен интерактивный, top-подобный обзор прямо сейчас: какие клиенты подключены, кто генерирует больше всего трафика, как быстро растут pending-байты у конкретного медленного консьюмера. Для этого в экосистеме NATS есть два варианта, и стоит понимать разницу.

nats top <server-name> — встроенная подкоманда nats CLI, тот же бинарник, что использовался во всей серии для stream/consumer/context. Работает не через HTTP monitoring port, а через тот же механизм $SYS, что и server report, — поэтому принимает имя сервера (server_name из конфига), а не URL, и тоже требует прав на системный account:

nats top n1

Показывает живую, обновляемую таблицу соединений с сортировкой (по умолчанию — по cid, можно --sort по другому полю), опционально с подписками (--subs) и разрешением DNS для адресов клиентов (--lookup).

Отдельный пакет nats-io/nats-top — исторически самостоятельный инструмент, который ходит не через $SYS, а напрямую в HTTP monitoring endpoint (/connz и соседние), и поэтому не требует системных прав — только доступ к порту 8222. Функциональность nats-top с тех пор перенесена в nats CLI как nats top, но отдельный пакет по-прежнему существует и может быть удобнее в средах, где HTTP monitoring port открыт, а доступ к $SYS — нет (например, у оператора, который смотрит на чужой кластер только «снаружи», без административных credentials).

Практическое правило: если у вас уже есть $SYS-доступ через nats CLI (а в собственном кластере он обычно есть) — используйте nats top, он в том же бинарнике, что и всё остальное в этой статье. Если доступ ограничен только HTTP monitoring портом — отдельный nats-top.

Backup/restore

JetStream хранит данные на диске (storage: file, ст. 2) и реплицирует их по RAFT при --replicas > 1 (ст. 3) — но репликация защищает от падения ноды, а не от человеческой ошибки: nats stream purge по невнимательности, повреждение диска на всех репликах разом, миграция на новый кластер. Для этого нужен независимый снапшот, который можно унести в сторону.

nats stream backup создаёт снапшот stream целиком (сообщения плюс, по умолчанию, состояние consumers) через NATS-протокол — без прямого доступа к файловой системе ноды:

nats stream backup ORDERS ./orders-backup

Результат — каталог с двумя файлами: backup.json (метаданные — конфигурация stream, список consumers) и stream.tar.s2 (собственно данные, сжатые S2). Команда работает по сети, читая данные через тот же JetStream API, которым пользуются обычные клиенты, — это то же самое, что называется Snapshot API на уровне протокола; nats stream backup — просто CLI-обёртка над ним. У команды есть alias snapshot (nats stream snapshot делает то же самое) и флаг --check, который перед бэкапом проверяет здоровье stream.

Restore — обратная операция, с важным ограничением: stream с таким именем не должен существовать на целевом сервере (в отличие от backup, где stream, разумеется, обязан существовать):

nats stream restore ./orders-backup

CLI восстановит stream с тем же именем и конфигурацией, что была на момент бэкапа, включая все сообщения и их порядок (First Sequence/Last Sequence в stream info после restore совпадут с тем, что было до потери). При восстановлении на кластер можно переопределить размещение — --cluster, --tag, --replicas — например, поднять --replicas 3 даже если бэкап снимался с single-node stream, получив сразу отказоустойчивую конфигурацию. На одной ноде без кластера --replicas больше 1 закономерно завершится ошибкой (replicas > 1 not supported in non-clustered mode) — это ограничение кластеризации, а не самого backup/restore.

nats stream backup ORDERS ./orders-backup
nats stream rm ORDERS -f                      # имитация потери — purge/rm по ошибке
nats stream restore ./orders-backup
nats stream info ORDERS   # Messages/Sequence — как до потери

Практический сценарий backup/restore целиком воспроизведён в README демо: создаём stream, публикуем несколько сообщений, снимаем бэкап, удаляем stream, восстанавливаем из бэкапа и проверяем, что Messages и порядок сообщений совпадают с тем, что было до удаления — сценарий, который в этой статье выполнен и проверен вживую.

Мост от RabbitMQ и Kafka. Ни у RabbitMQ, ни у Kafka нет прямого аналога команды stream backup/restore на уровне протокола. В RabbitMQ снапшот кластера — это резервное копирование определений (rabbitmqctl export_definitions) плюс данных на файловой системе каждой ноды (Mnesia/Khepri-стор), то есть операция уровня файловой системы, а не сетевого API. В Kafka снапшот темы обычно строится вокруг механизмов уровня инфраструктуры — снапшот дисков брокеров или репликация в отдельный кластер (MirrorMaker) с последующим переключением, а не единой командой kafka-topics.sh backup. JetStream backup через NATS-протокол — не файловая, а сетевая операция: снапшот можно снять с любой машины, у которой есть доступ к кластеру, без SSH на конкретную ноду и без знания, где физически лежит store_dir.

Обновление кластера

Официальная процедура rolling upgrade кластера NATS — четыре шага, повторяемых на каждой ноде по очереди:

  1. Перевести ноду в lame duck mode — сервер перестаёт принимать новые соединения и начинает плавно, в течение настраиваемого периода (lame_duck_duration, по умолчанию 2 минуты, с grace-периодом ~10 секунд перед началом эвакуации), закрывать существующие клиентские соединения, давая клиентам время переподключиться к другим нодам кластера.
  2. Заменить бинарник или Docker-образ на новую версию.
  3. Перезапустить сервер.
  4. Дождаться, пока /healthz не начнёт отдавать HTTP 200 OK, и только после этого переходить к следующей ноде.
# Перевод ноды в lame duck mode (сигналом к процессу или через docker exec)
nats-server --signal ldm=<pid>

# Проверка готовности после рестарта — gate перед следующей нодой
curl -sf localhost:8222/healthz || echo "not ready yet"

Смысл lame duck mode — не резкое обрывание соединений (что вызвало бы одновременный “thundering herd” реконнектов, создающий пиковую нагрузку CPU на TLS handshake на оставшихся нодах), а управляемая, растянутая по времени эвакуация клиентов с одной ноды, пока кластер продолжает обслуживать трафик через остальные. Именно поэтому процедура одинакова для любой ноды — не нужно отдельно думать про лидера meta-group или лидера конкретного stream: RAFT сам переизберёт лидера, когда нода, на которой он был, уйдёт в lame duck и остановится, — то же самое поведение, что уже разбиралось в failover-сценарии ст. 3.

Для того stream, чья RAFT-группа сейчас возглавляется нодой, которую вы планируете обновлять следующей, есть управляемая альтернатива — не дожидаться, пока RAFT переизберёт лидера реактивно при остановке ноды, а попросить его уступить заранее:

nats stream cluster step-down ORDERS

Это та же команда, что уже использовалась в failover-сценарии ст. 3 (nats stream cluster step-down EVENTS) — синтаксис двухсловный, stream cluster step-down, а не cluster-step-down. Перед плановым обслуживанием ноды-лидера конкретного важного stream шаг-даун делает переключение предсказуемым и контролируемым — вы сами выбираете момент, а не ждёте, пока это сделает таймаут при остановке процесса.

sequenceDiagram participant Op as Оператор participant N1 as n1 participant N2 as n2 participant N3 as n3 Note over N1,N3: Кластер на старой версии, все ноды здоровы Op->>N1: signal ldm (lame duck mode) Note over N1: Новые соединения отклоняются,
клиенты плавно эвакуируются на n2/n3 Op->>N1: остановить, заменить образ, перезапустить Op->>N1: GET /healthz (poll) N1-->>Op: 200 OK (новая версия готова) Op->>N2: signal ldm (lame duck mode) Note over N2: Если n2 — лидер важного stream:
можно stream cluster step-down заранее Op->>N2: остановить, заменить образ, перезапустить Op->>N2: GET /healthz (poll) N2-->>Op: 200 OK Op->>N3: то же самое N3-->>Op: 200 OK Note over N1,N3: Кластер полностью на новой версии,
простоя для клиентов не было

sequenceDiagram
  participant Op as Оператор
  participant N1 as n1
  participant N2 as n2
  participant N3 as n3

  Note over N1,N3: Кластер на старой версии, все ноды здоровы

  Op->>N1: signal ldm (lame duck mode)
  Note over N1: Новые соединения отклоняются,
клиенты плавно эвакуируются на n2/n3 Op->>N1: остановить, заменить образ, перезапустить Op->>N1: GET /healthz (poll) N1-->>Op: 200 OK (новая версия готова) Op->>N2: signal ldm (lame duck mode) Note over N2: Если n2 — лидер важного stream:
можно stream cluster step-down заранее Op->>N2: остановить, заменить образ, перезапустить Op->>N2: GET /healthz (poll) N2-->>Op: 200 OK Op->>N3: то же самое N3-->>Op: 200 OK Note over N1,N3: Кластер полностью на новой версии,
простоя для клиентов не было
Rolling upgrade кластера из трёх нод: по одной, с lame duck mode и gate на /healthz

Совместимость версий внутри кластера во время апгрейда — обычно не проблема на соседних минорных версиях (например, обновление 2.11.x → 2.12.x): протокол RAFT и route-протокол спроектированы с обратной совместимостью в пределах поддерживаемого окна версий, и именно поэтому вся процедура строится вокруг «по одной ноде, дождаться healthy, следующая», а не «остановить всё сразу». Тем не менее в production стоит свериться с release notes конкретных версий перед апгрейдом — release notes могут явно предупреждать о breaking changes, требующих иного порядка (как строгий режим валидации JetStream API, добавленный ранее в серии, — не breaking change для рантайма, но меняющий поведение клиентов, писавших некорректные запросы, которые раньше молча проходили).

Продакшн-чеклист серии

Ниже — сводка того, что разобрано за девять статей серии, в виде проверяемых пунктов.

Топология и кластер (ст. 3–4)

  • Нечётное число нод/реплик — 3 или 5, никогда 2 или 4. Кворум RAFT — математика, а не настройка: 3 переживают падение 1, 5 — падение 2.
  • --replicas 3 (R3) для важных streams — по умолчанию --replicas 1 не даёт никакой отказоустойчивости данных, только транспортную связность через Core-кластер.
  • storage: file, не memory, для всего, что должно пережить рестарт процесса — memory быстрее, но исчезает при падении ноды.
  • Placement осмысленно ограничен тегами, если у кластера есть физическая топология (стойки, зоны доступности) — иначе реплики могут случайно оказаться в одной точке отказа.
  • Клиент знает все узлы кластера — список адресов, а не один. Один известный адрес превращает эту ноду в SPOF на стороне клиента, даже если кластер жив (тот же принцип, что в чеклисте RabbitMQ).

Лимиты и ресурсы (ст. 2, 5)

  • Лимиты аккаунтов и streams заданы явноmax_mem/max_file на уровне jetstream{}, MaxBytes/MaxMsgs/MaxAge на уровне конкретного stream. Без лимитов один растущий stream может исчерпать диск ноды целиком.
  • Retention policy осознанно выбрана для каждого stream — limits (лог), interest или workqueue (очередь), не оставлена в дефолте по инерции.
  • AckWait/MaxDeliver настроены под реальное время обработки — значения по умолчанию не всегда подходят для медленных consumers.

Безопасность (ст. 8)

  • TLS/mTLS включены между клиентами и сервером и между нодами кластера (cluster{tls{}}) — трафик RAFT-репликации и route-gossip по умолчанию не шифруется.
  • Accounts используются для мультитенантности, а не общий $G для всего — изоляция subject-namespace и JetStream-домена между приложениями/командами.
  • Decentralized JWT или nkeys вместо голых user/password там, где это оправдано масштабом — особенно для leaf nodes и edge, где сервер не обязан заранее знать всех клиентов.

Мониторинг и эксплуатация (эта статья)

  • -m 8222 включён на всех нодах, порт закрыт от внешнего мира сетевыми политиками (endpoints не аутентифицированы по умолчанию).
  • /jsz — обязательная точка контроля для любого кластера с JetStream: meta-cluster health, состояние streams/consumers по всем нодам.
  • prometheus-nats-exporter развёрнут и скрейпится со всех нод, не с одной — потеря ноды должна быть видна в Prometheus (up == 0), а не пропасть вместе с нодой.
  • Алерты настроены минимум по: gnatsd_varz_jetstream_stats_api_errors растёт; число живых нод в кластере упало ниже кворума; gnatsd_varz_connections резко просело (массовый реконнект); свободное место под store_dir приближается к лимиту.
  • $SYS-доступ настроен для операторских инструментов (server report, nats top) заранее, а не в момент инцидента, когда обнаруживается, что текущий контекст не имеет системных прав. (server check $SYS не требует — ходит обычным соединением.)
  • nats server check интегрирован во внешний health-check/алертинг (Nagios/Prometheus-совместимый вывод через --format) — не полагаться только на визуальный осмотр дашборда.

Backup и обновления (эта статья)

  • Регулярный nats stream backup для критичных streams, снапшоты хранятся вне кластера — репликация R3 защищает от падения ноды, но не от purge/rm по ошибке или потери всех реплик разом.
  • Restore проверен заранее, не в момент аварии — восстановление в тестовом окружении хотя бы раз, чтобы знать реальное время операции и то, что --replicas/--cluster/--tag действительно позволяют перенести stream на другую топологию.
  • Rolling upgrade только через lame duck mode, нода за нодой, с gate на /healthz — не массовый рестарт всех нод разом.
  • stream cluster step-down для критичных streams перед плановым обслуживанием ноды-лидера — управляемое переключение вместо реактивного.

Заключение серии

Девять статей — это путь от «что такое subject» до «как обновить кластер под нагрузкой, не потеряв ни одного сообщения». По пути были две константы, которые стоит вынести за скобки. Первая: NATS — не единая монолитная гарантия, а набор явно выбираемых уровней — Core транспорт без хранения, JetStream persistence с настраиваемой retention, R1/R3/R5 репликация с понятной ценой каждого шага. Вторая: почти для каждой возможности NATS был прямой мост к тому же самому в RabbitMQ или Kafka — не потому что системы одинаковые, а потому что задачи, которые решает messaging layer, одни и те же, и знакомая интуиция почти всегда переносится, если знать, куда именно она переносится иначе.

  1. NATS для тех, кто знает Kafka и RabbitMQ: subjects, Core и request/reply — модель subjects, pub/sub, queue groups, request/reply; Core NATS как at-most-once транспорт без хранения.
  2. JetStream: как NATS получает persistence, streams и гарантии доставки — streams, retention policies, pull/push consumers, ack и redelivery, дедупликация, KV и Object store.
  3. Классический кластер NATS: routes, RAFT и надёжный failover — два уровня кластеризации, meta-group и per-stream RAFT, кворум и failover вживую.
  4. Геораспределённый NATS: superclusters, gateways, leaf nodes и mirror-стримы — supercluster через gateways, leaf nodes для edge, асинхронная репликация streams через mirror/source.
  5. Надёжность и производительность NATS: гарантии, репликация и tuning — матрица гарантий, влияние R3 на latency, slow consumer и flow control, измерение через nats bench.
  6. NATS из Go: nats.go, Core и JetStream паттерны — подключение и устойчивость, Core pub/sub, JetStream, KV на практике.
  7. NATS из Java: jnats, Core и JetStream паттерны — те же паттерны на jnats, зеркально к Go-статье.
  8. Безопасность и multi-tenancy в NATS: accounts, JWT, nkeys и TLS — accounts как изоляция, exports/imports, три модели аутентификации, TLS/mTLS.
  9. Эксплуатация NATS: nats CLI, мониторинг, backup и обновление кластера (эта статья) — nats CLI и contexts, monitoring endpoints, Prometheus/Grafana, nats-top, backup/restore, rolling upgrade, сводный чеклист.

Demo-стенд к этой статье — khorost-tech/digital-cookbook → nats/06-observability: один узел nats:2.12 с включённым JetStream и monitoring-портом, prometheus-nats-exporter, Prometheus, Grafana с готовым дашбордом и сценарием backup/restore stream. Все стенды серии — в nats/ репозитория digital-cookbook, каждый поднимается одной командой docker compose up -d.

Документация

Обсуждение в Telegram

Присоединиться →

Комментарии