Стадия жизненного цикла модуля: Общедоступная версия

У модуля есть требования для установки

Обзор

Мониторинг сайтов — система blackbox-мониторинга, встроенная в DOP. Позволяет проверять доступность веб-сайтов, TCP-сервисов и DNS-записей из внешних точек наблюдения (зон).

Агент устанавливается на сервер за пределами кластера, получает конфигурацию из DOP, выполняет проверки и отправляет метрики обратно в DOP. Результаты доступны на дашбордах и через систему алертов.

Поддерживаемые типы проверок: HTTP, TCP/TLS, DNS, Ping (ICMP).

Быстрый старт

  1. Убедитесь, что функция мониторинга сайтов включена (Admin → Settings → web_monitoring_enabled)
  2. Создайте зону: Admin → Zones → New Zone (укажите имя, например world)
  3. Создайте агента: Admin → Agents → New Agent (укажите имя и зону)
  4. Скопируйте токен агента из карточки
  5. Установите агент на внешний сервер (см. раздел Установка агента)
  6. Перейдите в проект: Project → Веб-мониторинг → Добавить сайт
  7. Укажите домен, выберите зоны, добавьте пробу через preset «HTTPS Check», сохраните

Через 1–2 минуты на дашбордах появятся данные о доступности.

Основные понятия

Зоны

Зона — географическая или сетевая точка наблюдения. Каждый агент принадлежит одной зоне. Сайт можно проверять из нескольких зон одновременно для независимой оценки доступности.

Создание зон: Admin → Zones → New Zone.

Сайты

Сайт — объект мониторинга: доменное имя или IP-адрес. Принадлежит проекту. Каждый сайт содержит одну или несколько проб и общие настройки (период, ping, зоны, лейблы).

Пробы и чеки

Проба — один запрос определённого типа (HTTP, TCP или DNS) к сайту. Каждая проба содержит один или несколько чеков — конкретных условий проверки (например, HTTP status = 200 или время ответа < 5s).

Агент выполняет все чеки при каждом срабатывании пробы и отправляет метрику gogomonia_test (1 = pass, 0 = fail) для каждого чека.

Многошаговый HTTP-сценарий (multihttp) — проба из нескольких HTTP-запросов, выполняемых по порядку: например, вход → профиль → выход. Шаги делят cookie-сессию, могут извлекать переменные из ответов (JSON path, regex, заголовок, cookie, CSS-селектор) и использовать их в следующих шагах как ${name}; у каждого шага свои чеки. См. Многошаговые HTTP-сценарии.

Лейблы

Лейблы — произвольные Prometheus-совместимые key=value пары, добавляемые к метрикам. Используются для группировки, фильтрации и управления алертингом.

Лейблы задаются на трёх уровнях:

  • Сайт (site_labels) — добавляются ко всем метрикам сайта
  • Сайт (check_labels) — добавляются ко всем проверкам сайта
  • Чек (check_labels) — per-check лейблы, переопределяют site-level

Управление алертингом: лейбл alert=yes (по умолчанию) включает алерты, alert=no — отключает для конкретного чека.

Формат ключей: латинские буквы, цифры, подчёркивания; начинается с буквы или подчёркивания (/^[a-zA-Z_][a-zA-Z0-9_]*$/).

Типы проверок

Каждая проба (HTTP/TCP/DNS) содержит один или несколько чеков. Агент выполняет все чеки при каждом срабатывании пробы и отправляет метрику gogomonia_test (1 = pass, 0 = fail) для каждого чека.

Все pattern-поля (bodyMatch, contentType, answerMatch, location, headers.match, cookies.match) используют формат /regex/ (Go RE2-совместимый). Добавление ! после закрывающего слэша (/pattern/!) инвертирует результат (NOT match). Значение без обрамляющих слэшей сравнивается на полное совпадение как обычная строка.

HTTP-чеки

status — Проверка HTTP status code

Проверяет, что код ответа входит в список допустимых. Используется для контроля корректности ответа сервера.

  • Поле: список кодов через запятую
  • Формат: целые числа 100–599 или regex в слэшах
  • Примеры: 200 · 200, 301, 302 · /2[0-9]{2}/ (любой 2xx)

responseTime — Максимальное время ответа

Проверяет, что запрос выполнился быстрее порога. Чек срабатывает в реальном времени: если ответ не пришёл за max, тест помечается как fail ещё до завершения запроса.

  • Поле: max — максимальная длительность
  • Формат: число + единица: ms, s, m, h
  • Примеры: 5s · 1500ms · 1m

bodyMatch — Regex-проверка тела ответа

Проверяет, что тело HTTP-ответа содержит (или не содержит) указанный паттерн. Полезно для проверки, что страница содержит ожидаемый контент и не показывает ошибку.

  • Поле: regex
  • Формат: /regex/ или /regex/! (NOT match)
  • Примеры: /OK/ · /Welcome to/ · /error|maintenance/! (не должно содержать)

bodySize — Размер тела ответа

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

  • Поля: min — минимальный размер (опционально), max — максимальный размер (опционально). Требуется хотя бы одно из min или max.
  • Формат: число + единица: b, Kb, Mb, Gb
  • Примеры: min 100b · max 10Mb · min 1Kb max 5Mb

contentType — Проверка Content-Type заголовка

Проверяет, что заголовок Content-Type в ответе соответствует ожидаемому. Используется для контроля, что сервер возвращает правильный тип контента, а не страницу ошибки.

  • Поле: regex
  • Формат: /regex/ или /regex/!
  • Примеры: /text\/html/ · /application\/json/ · /image\/.*/

location — Проверка заголовка Location (редиректы)

Проверяет, что заголовок Location в ответе (при 3xx редиректах) соответствует паттерну. Полезно для проверки, что редирект ведёт на правильный URL.

  • Поле: regex или точный URL
  • Формат: /regex/, /regex/! или URL, сравниваемый на полное совпадение
  • Примеры: /https:\/\/example\.com\// · /\/login$/ · https://example.com/

При сравнении URL порт, подразумеваемый схемой, игнорируется: https://example.com:443/ и https://example.com/ считаются одним и тем же адресом, в обе стороны. Любой другой порт сравнивается как написан. На формат /regex/ это не распространяется — он матчится с заголовком в том виде, в каком тот пришёл.

sslBasicConstraintsValid — Проверка TLS-сертификата

Комплексная проверка TLS-сертификата: корректность BasicConstraints (CA-сертификаты должны иметь BasicConstraintsValid=true), срок действия leaf-сертификата (NotBefore / NotAfter), соответствие имени хоста (exact и wildcard *.example.com).

  • Поля: нет (включается как boolean)

certValidDays — Срок действия сертификата

Проверяет, что TLS-сертификат не истечёт в ближайшие N дней. Используется для заблаговременного обнаружения истекающих сертификатов.

  • Поле: min — минимум дней до истечения
  • Формат: положительное целое число
  • Примеры: 30 (предупредить за 30 дней) · 7 · 90

sslValidDays — Срок действия сертификата (альтернатива)

Аналогичен certValidDays. Нельзя использовать одновременно с certValidDays.

  • Поле: min — минимум дней до истечения
  • Формат: положительное целое число

headers — Проверка HTTP-заголовков ответа

Проверяет, что заголовки HTTP-ответа содержат указанные значения. Каждая пара: имя заголовка + regex для значения. Все пары должны совпасть (AND-логика).

  • Поля: пары name / match
  • name: имя заголовка (case-insensitive), например Content-Type, X-Request-Id
  • match: regex /pattern/ или /pattern/!
  • Примеры: name X-Request-Id match /^[a-f0-9-]+$/ · name Cache-Control match /no-cache/!

cookies — Проверка cookies в ответе

Проверяет, что cookies HTTP-ответа содержат указанные значения. Каждая пара: имя cookie + regex для значения. Все пары должны совпасть (AND-логика).

  • Поля: пары name / match
  • name: имя cookie, например session_id, csrf_token
  • match: regex /pattern/ или /pattern/!
  • Примеры: name session_id match /^[a-f0-9]{32}$/ · name lang match /^(en|ru)$/

jsonPath — Проверка JSON-тела

Проверяет условия над значениями JSON-тела ответа. Каждое условие: JSONPath-выражение, условие и (кроме exists) ожидаемое значение. Все условия должны выполниться (AND-логика). Доступна для HTTP-проб и для шагов многошаговых сценариев.

  • Поля: тройки expression / condition / value
  • expression: JSONPath, например $.status, $.items[0].id, $.user.roles[*]
  • condition: exists, equals, notEquals, contains, notContains, startsWith, endsWith, matches (regex), gt, gte, lt, lte (числовые)
  • value: ожидаемое значение; может ссылаться на переменную сценария как ${name}
  • Примеры: $.status equals ok · $.items exists · $.balance gt 0 · $.user.id equals ${user_id}

Многошаговые HTTP-сценарии

Проба multihttp выполняет несколько HTTP-запросов по порядку как одну пробу. Добавляется через меню «Добавить пробу» (тип «Многошаговый HTTP-сценарий») в обоих режимах формы и нужна для потоков, которые имеют смысл только как последовательность: войти и открыть защищённую страницу, создать объект и прочитать его, пройти оформление заказа. В простом режиме доступны интервал, таймаут и общие cookies сценария, а у каждого шага — имя, метод, схема, URL, тело (для POST/PUT/PATCH), переменные и простые чеки (status, responseTime, сертификат); расширенный режим добавляет порт и таймаут шага, следование редиректам, заголовки запроса, Basic Auth, отключение HTTP/2, логирование значений переменных и остальные HTTP-чеки, включая jsonPath, — то же разделение, что и в форме HTTP-пробы.

Настройки сценария:

  • интервал — от 30s до 5m (сценарий запускается не чаще раза в интервал; диапазон шире, чем 60s у одиночной пробы, потому что сценарий делает несколько последовательных запросов)
  • таймаут — дедлайн всего сценария (по умолчанию интервал минус 5s)
  • общие cookies — сохранять cookies между шагами (включено по умолчанию)
  • логировать значения переменных — писать извлечённые значения в логи агента (выключено по умолчанию: логируются только имена)
  • отключить HTTP/2 для HTTPS — выполнять все шаги по HTTP/1.1; настройка общая для сценария, потому что шаги делят один HTTP-клиент

У каждого шага есть имя, URL (абсолютный — со своими схемой, хостом и портом — или путь, достраиваемый по хосту сайта), схема и порт для URL-пути, метод, тело, заголовки, Basic Auth, таймаут, политика редиректов, переменные и чеки:

  • URL отправляется как написан: указывайте его уже закодированным, вместе с query-параметрами (/search?q=hello%20world). Переменные подставляются как ${name} (дословно) или ${name|urlencode} (percent-encoding по RFC 3986 — кодируется всё, кроме A-Za-z0-9-._~, пробел → %20, — годится и для значения параметра, и для сегмента пути): /me?sid=${sid|urlencode}. Любой другой фильтр отвергается при сохранении сайта.
  • Схема и порт достраивают URL-путь (/api/login → https://<хост сайта>:8443/api/login) — ровно как у HTTP-пробы; новый шаг в форме начинается с HTTPS. Шаг, сохранённый без схемы (через API или старой формой), достраивается схемой из request сайта, а без неё — http; форма показывает это эффективное значение. Абсолютный URL несёт свои схему и порт: оба поля показывают их и заблокированы, а scheme/port рядом с абсолютным url через API — ошибка валидации.
  • Шаблоны [[ rndStr ]] и [[ .Site ]] работают в URL, значениях заголовков и Basic Auth шага так же, как в запросе HTTP-пробы — подставляются один раз при загрузке конфига агентом; тело отправляется как написано.
  • Переменные извлекаются из ответа шага: jsonPath ($.access_token), regex (первая группа захвата или всё совпадение), header (значение заголовка), cookie (значение cookie) или cssSelector (текст элемента или его атрибут). До 10 на шаг. Переменная, которую не удалось извлечь, проваливает шаг.
  • ${name} — ссылки на переменные предыдущих шагов допустимы в URL, значениях заголовков, теле и в значениях чеков; значение чека может ссылаться и на переменную своего шага (переменные извлекаются до выполнения чеков). Ввод ${ в одном из этих полей редактора предлагает переменные, доступные этому шагу.
  • Basic Auth задаётся на шаг (к шагам без своего продолжают применяться настройки запроса сайта); явный заголовок Authorization — например Bearer ${token} — имеет приоритет, как и в HTTP-пробе.
  • Чеки — описанные выше HTTP-чеки плюс jsonPath; до 10 на шаг. Новый шаг начинается с status и responseTime, как HTTP-проба; каждый тип чека добавляется в шаг один раз (уже добавленный тип пропадает из меню «Проверка»). Уровень алертинга настраивается на сценарии, а не на отдельном чеке шага.

Сценарий останавливается на первом проваленном шаге: ошибка транспорта, неудачная экстракция или провал любого чека (включая responseTime и проверки сертификата). Оставшиеся шаги помечаются пропущенными, все их чеки проваливаются.

Алертинг работает по сценарию, а не по чекам шагов: один алерт GogomoniaScenarioFailed на пробу с серьёзностью из селектора «Уведомления» пробы и списком упавших шагов и проверок. Поэтому у чеков шагов нет настроек уведомлений (checkLabels для них не принимаются); алерт по отдельной проверке GogomoniaCheckFailed относится только к одиночным пробам.

Метрики несут лейбл step: серии по шагам (gogomonia_test{step="login"}, gogomonia_http_response_time_ms_sum{step="login"}, …) плюс серии сценария (gogomonia_probe_request_succeeded{step=""}, gogomonia_multihttp_runs_total, gogomonia_multihttp_duration_ms_sum, gogomonia_multihttp_steps_total{result}). Страница сайта показывает каждый шаг с его чеками и длительность сценария; в дашборде «Сайт веб-мониторинга» раскрывающийся блок «Многошаговые сценарии» даёт на каждый сценарий сайта строку из трёх графиков — статус сценария (одна линия: ok / failed / request failed / ambiguous), длительность прогона и время ответа шагов; статусы отдельных шагов и их чеков — в блоках «Checks (overall)» и «Checks (per server)». Ссылка из алерта GogomoniaScenarioFailed открывает этот дашборд с одним упавшим сценарием.

Для многошаговых сценариев нужны агенты версии 0.3.0 и новее; более старые агенты такие пробы пропускают.

TCP/TLS-чеки

connected — Проверка TCP-соединения

Проверяет, что TCP-соединение (и TLS-хэндшейк, если TLS включён) установлено успешно. Базовый чек доступности TCP-сервиса.

  • Поля: нет (включается как boolean)

responseTime — Время установки соединения

Аналогичен HTTP responseTime, но измеряет время установки TCP-соединения (и TLS-хэндшейка при наличии).

  • Поле: max — максимальная длительность
  • Примеры: 5s · 2s

sslBasicConstraintsValid — Проверка TLS-сертификата

Аналогичен HTTP sslBasicConstraintsValid. Проверяет сертификат TCP/TLS соединения.

  • Поля: нет (включается как boolean)

certValidDays — Срок действия TLS-сертификата

Аналогичен HTTP certValidDays. Проверяет сертификат TCP/TLS соединения.

  • Поле: min — дней до истечения
  • Примеры: 30 · 7

sslValidDays — Срок действия TLS-сертификата (альтернатива)

Аналогичен certValidDays. Нельзя использовать одновременно с certValidDays.

DNS-чеки

gotAnswer — Получен непустой DNS-ответ

Проверяет, что DNS-запрос вернул хотя бы одну запись. Базовый чек — домен резолвится.

  • Поля: нет (включается как boolean)

answerMatch — Regex-проверка DNS-ответа

Проверяет, что хотя бы одна запись в DNS-ответе соответствует паттерну. Ответ содержит полную строку записи (например example.com. 300 IN A 1.2.3.4).

  • Поле: regex
  • Формат: /regex/ или /regex/!
  • Примеры: /1\.2\.3\.4/ · /IN A/ · /IN MX.*mail\./

responseTime — Время DNS-запроса

Аналогичен HTTP responseTime, но измеряет время DNS-запроса.

  • Поле: max — максимальная длительность
  • Примеры: 2s · 500ms

Ping (ICMP)

Ping включается флагом ping: true на уровне сайта. Работает полностью независимо от проб — не требует ни проб, ни чеков. Встроенный тест ping.maxRtt проходит, если RTT < 500ms (порог захардкожен).

Настройка мониторинга

Создание зон

Зона — географическая или сетевая точка наблюдения. Каждый агент принадлежит одной зоне.

Создание: Admin → Zones → New Zone. Укажите имя зоны.

Создание сайтов

Путь: Project → Веб-мониторинг → Добавить сайт.

Два режима формы

Форма создания/редактирования сайта работает в двух режимах.

Простой режим (по умолчанию для новых сайтов):

  • Показаны основные поля: host, period, ping, зоны
  • Добавление проб через presets (готовые шаблоны)
  • Доступные чеки ограничены (см. таблицу ниже)
  • TCP: упрощённый переключатель TLS

Расширенный режим (переключатель «Расширенный режим» внизу формы):

  • Все дополнительные поля: hosts, probe_all_resolved_ips, site_labels, check_labels, request_defaults
  • Полный набор типов чеков
  • Группировка чеков
  • Per-probe лейблы и period
  • TCP: полная настройка TLS (insecureSkipVerify, serverName)

Переключение из расширенного в простой режим блокируется, если в форме есть данные, доступные только в расширенном режиме. Для существующих сайтов режим выбирается автоматически на основе сохранённых настроек.

Доступные чеки по режимам

Тип пробы Простой режим Только расширенный
HTTP status, responseTime, certificate (sslBasicConstraintsValid + certValidDays) bodyMatch, bodySize, contentType, location, headers, cookies, jsonPath
TCP connected, responseTime, certificate —
DNS gotAnswer, responseTime answerMatch
Многошаговый HTTP status, responseTime, сертификат (по шагам) bodyMatch, bodySize, contentType, location, headers, cookies, jsonPath; порт и таймаут шага, редиректы, логирование переменных

Группировка чеков

В расширенном режиме доступен переключатель «Группировать условия в проверки».

Без группировки (по умолчанию): каждый чек — отдельная проверка с собственным результатом (pass/fail) и собственным алертом.

Каждый тип проверки добавляется один раз: уже добавленный тип (отдельной проверкой или внутри группы) пропадает из меню «Проверка» пробы или шага, а когда заняты все типы, кнопка блокируется. В HTTP-пробе вторая проверка того же типа — например два bodyMatch с разными паттернами и уровнями — добавляется отдельной группой при включённой группировке.

С группировкой: несколько условий (status, bodyMatch, responseTime и т.д.) объединяются в одну логическую проверку с общим именем и общим алертом. Группе назначается уровень severity.

Пример: группа «https-health» может включать status=200 + bodyMatch="/OK/" + responseTime max=5s. При нарушении любого условия срабатывает один общий алерт.

Группировка включается автоматически, если сайт уже содержит чеки с несколькими условиями в одном config.

Параметры сайта

Параметр Описание Режим
name Отображаемое имя, уникальное в проекте оба
host Домен или IP для мониторинга оба
period Интервал проверок каждого агента. Формат: \d+(s|m), max 60s. Пример: 20s, 1m оба
ping Включить ICMP ping (true/false) оба
zones Из каких зон проверять. Если не указан — проверяется из всех зон оба
hosts Массив конкретных IP вместо DNS-резолва расш.
probe_all_resolved_ips Проверять все разрешённые IP, а не только первый расш.
site_labels Prometheus-лейблы ко всем метрикам сайта расш.
check_labels Лейблы ко всем проверкам сайта расш.
request_defaults HTTP defaults: scheme, timeout, headers расш.
probes Массив проб с чеками оба

Распределение агентов внутри интервала

Каждый агент зоны выполняет пробу раз в интервал, но агенты одной зоны не ходят на сайт одновременно: платформа нумерует зарегистрированных агентов зоны (в порядке создания, независимо от того, онлайн они сейчас или нет) и агент k из N начинает пробу на k × интервал / N секунд позже первого. При интервале 60 с и трёх агентах сайт получает запрос каждые 20 с, при двух — каждые 30 с; один агент работает как раньше. Форма сайта показывает под интервалом расчёт по выбранным зонам. Разнос действует на HTTP-, TCP- и многошаговые пробы; DNS-пробы и ping от него не зависят.

Слоты считаются по системным часам, поэтому часы агентов одной зоны должны быть синхронизированы (NTP). Агент сравнивает своё время с временем платформы при каждом опросе конфигурации: расхождение попадает в метрику gogomonia_agent_clock_skew_seconds, при расхождении больше 5 с агент пишет предупреждение в лог. Разнос требует агентов версии 0.6.0 и новее; более старые агенты продолжают выполнять пробы в прежние моменты.

Presets

Presets — готовые шаблоны проб, доступные при создании сайта. Позволяют добавить типовую конфигурацию одним нажатием.

Preset Тип пробы Создаваемые чеки
HTTPS Check HTTP GET (https) status 200, sslBasicConstraintsValid, certValidDays min=30
TCP/TLS TCP с TLS connected, certValidDays min=30
DNS DNS A-запрос gotAnswer
HTTP Redirect HTTP GET status 301/302, location

Запуск вручную («Протестировать»)

У каждой сохранённой пробы на странице сайта есть кнопка Протестировать. Она выполняет пробу немедленно на выбранных агентах — любом доступном, всех агентах одной зоны, всех агентах всех зон или одном конкретном агенте (в списке — все живые агенты зон сайта в виде «Агент <зона> / <имя>») — и показывает результат в модальном окне. Проба выполняется по каждому IP-адресу хоста (все A-записи, как у traceroute) независимо от опции сайта «Проверять все IP»; у сайта со своим списком адресов — по одному IP на запись.

Во вкладке агента каждый проверенный IP — строка: статус, IP и в скобках хост, из которого он получен, время прогона; она разворачивает подробности: отправленный запрос (GET https://…; чувствительные заголовки вроде Authorization и API-ключей замаскированы, значения cookie показываются первыми и последними четырьмя символами — sid=abcd…wxyz, а значения, взятые из хранилища секретов, показываются ссылкой ${secret:<путь>#<ключ>}, из которой пришли), ответ (200 HTTP/2.0, заголовки, тело до 50 КБ; если шаг шёл по редиректам — конечный URL и таблица хопов: статус, откуда, куда, какие cookie выставил хоп; у TCP-пробы — установленное соединение и его время, у DNS-пробы строка начинается с запрошенного хоста и указывает резолвер), извлечённые переменные и ожидаемое/фактическое значение каждого чека (у bodyMatch «получено» описывает страницу: заголовок, размер, тип и первый видимый текст).

Для многошаговых сценариев каждый шаг — отдельная карточка с заголовком «Шаг N из M» и именем шага; она показывает время ответа рядом со статусом, а после — состояние сценария: все накопленные переменные и cookie-jar по всем хостам в виде host: name=value — то, с чем стартует следующий шаг.

Кнопка Логи на странице сайта открывает записи агентов по его пробам. Проваленный запрос или шаг сценария оставляет три вида записей уровня error, каждая читается сама по себе: запись запроса — её сообщение называет первый проваленный тест и причину (check 'logged_in/http.bodyMatch' failed: expected /Deckhouse UI/, got page "Deckhouse" (3522 bytes, text/html) at https://dex.example.com/auth after 4 redirects (+1 more failed test)), а поля несут запрос и ответ с замаскированными секретами (Cookie: _oauth2_proxy=<redacted>, Authorization: Bearer <redacted>), probe.url (как запрошено), при редиректах probe.final_url и probe.redirects (по хопу на строку: 302 https://a → https://b; set-cookie: session=<redacted>), probe.redirect_count, probe.response.summary, checks.failed и tests.failed.

Если ответа не было вовсе (таймаут, отказ соединения), сообщение называет URL, на котором запрос прервался — редирект-хоп помечен (after redirect) и повторён в probe.final_url, — а списков тестов нет: проверять было нечего.

Далее по записи на каждый проваленный тест (test '…' failed: expected …, got … с test.expect, test.actual — у bodyMatch сводка страницы с фрагментом текста — и тем же контекстом ответа); у сценариев — сводка прогона multihttp scenario failed со статусами всех шагов, probe.failed_step и probe.failure.

Та же кнопка есть у каждой пробы в форме создания и редактирования сайта: проба выполняется в том виде, в каком она сейчас заполнена, без сохранения формы — можно править конфигурацию, запускать, смотреть результат и снова править. Форма при этом должна быть валидной (ошибки показываются в окне запуска).

Такие запуски — только по запросу: они не создают метрик и алертов, результат удаляется при закрытии окна. Участвуют агенты, выходившие на связь за последние 90 секунд; HTTP-пробу (одиночную и сценарий) выполняют агенты версии 0.7.0 и новее — более старые отдают трейс со значениями секретов и cookie в открытом виде, поэтому не предлагаются.

Traceroute

Кнопка Traceroute в шапке сайта строит сетевой маршрут от агентов до сайта: выберите, где выполнить (любой доступный агент, все агенты одной зоны, все агенты всех зон или один конкретный агент, умеющий трассировать), протокол и режим — и каждый агент покажет путь до каждого IPv4-адреса хостов сайта (все A-записи, резолв выполняется на самом агенте без кэша).

  • Протокол: ICMP echo (как traceroute -I) или TCP — соединения на порт с ограниченным TTL, как tcptraceroute: пробы идут тем же путём и через те же фаерволы, что и трафик проверок. Порт подставляется из проб сайта (явный порт пробы, иначе 80/443 по схеме) и его можно изменить.
  • Режим: Стандарт — 30 хопов, 3 пробы на хоп, 1 с на хоп; Тщательный — 30 хопов, 5 проб на хоп, 3 с на хоп. Пробы одного хопа идут параллельно, хопы — последовательно до максимума, без ранней остановки на молчащих хопах.
  • Результат: по вкладке на агента (зона / имя агента, с точкой по худшему из его результатов и спиннером, пока он не ответил), а во вкладке — по строке на цель: трассируемый IP с хостом, из которого он получен, в скобках, длительность и достигнута ли цель, — и под ней компактная таблица хопов: номер, IP ответившего узла, его обратное DNS-имя и RTT каждой пробы (потерянная проба — красная *; если хоп потерял часть проб, рядом показано потеряно/отправлено). Стрелка перед целью сворачивает её таблицу. Цель зелёная, если достигнута без потерь; жёлтая (с потерями), если достигнута, но пробы терялись — на самой цели или частично на промежуточном хопе (хопы, не отвечающие вовсе, — это маршрутизаторы, не отправляющие ICMP-ошибки, потерями они не считаются); красная, если не достигнута за лимит хопов. Иконка буфера обмена у цели помещает в буфер вывод в формате классического traceroute.

Как читать результат

Та же легенда открывается по ссылке Как читать результат под вкладками агентов.

  • Строка — один хоп, маршрутизатор на пути к цели; # — его порядковый номер. На каждый хоп агент отправляет несколько проб (3 или 5 в зависимости от режима); в колонке RTT — время ответа каждой из них, Хост — обратное DNS-имя адреса (пусто, если записи нет).
  • * вместо времени — проба не получила ответа за таймаут режима (1 с или 3 с).
  • Серая строка без IP, все пробы * — хоп не ответил ни на одну пробу. Как правило, это маршрутизатор, который вообще не отправляет ICMP Time Exceeded (политика оператора, MPLS-ядро, облачный шлюз); это не проблема, пока отвечают следующие хопы, и потерей это не считается.
  • Подсвеченная строка с 1/3 — хоп ответил не на все пробы: потеряно/отправлено. На промежуточном хопе это обычно ограничение частоты ICMP-ответов на самом роутере, а не потери трафика — реальному трафику ответ роутера не нужен. Потери на самой цели или на всех хопах начиная с какого-то — проблема пути.
  • Зелёная жирная последняя строка — цель ответила сама, маршрут построен до конца.
  • Статусы цели: ок — достигнута, все пробы ответили; с потерями — достигнута, но часть проб потеряна на цели или на промежуточном хопе; ошибка — цель не достигнута за N хопов: после последнего ответившего маршрутизатора никто не ответил, то есть цель не отвечает на ICMP echo или её закрывает файрвол (попробуйте TCP-режим на порту сервиса); ошибка запуска — трассировка не выполнилась (нет прав на raw-сокет, IPv6-адрес, ошибка агента), причина — в строке цели.
  • У сайта с несколькими A-записями — по строке на адрес; адреса могут размещаться в разных местах, поэтому их маршруты и статусы различаются.

Трассируется только IPv4: IPv6-хост показывается с пометкой и пропускается. Как и для «Протестировать», нужны агенты, выходившие на связь за последние 90 секунд, версии 0.4.0 и новее; агент должен работать от root или с CAP_NET_RAW (systemd-unit установщика это обеспечивает), иначе каждая цель будет отмечена ошибкой. Результат удаляется при закрытии окна.

Маршрут, где не ответил ни один хоп (* * * на всех строках), — это то, что отдаёт сеть, а не сбой агента: многие облачные сети и NAT-шлюзы не пропускают ICMP Time Exceeded от маршрутизаторов, и системный traceroute с хоста агента показывает те же звёздочки. В такой сети используйте TCP-режим — сама цель отвечает на connect, поэтому её достижимость и RTT видны даже при полностью молчащих промежуточных хопах. Для сравнения запустите на хосте агента системный traceroute -T -p 443 <host>: он идёт тем же путём, что и агент.

Что учесть с точки зрения безопасности

Разовый запуск и traceroute уходят с агентов мониторинга, а не из браузера, и куда именно — ничем не ограничено:

  • агенты зоны общие для всех проектов платформы, поэтому запуск достаёт всё, что достают они: внутренние сети площадки, где установлены агенты, loopback хоста агента, сервис метаданных облака, в котором он работает;
  • оператор видит ответ на этот запрос, до 50 КБ тела, а TCP-traceroute показывает, открыт ли порт;
  • редиректы отслеживаются на любой хост, поэтому цель запуска не обязательно та, для которой его начали.

Запуск требует права править сайты проекта (роли user и admin, AI-ассистент от имени такого пользователя) — то же право, которое и так позволяет отправить запрос конфигурацией куда угодно. Выдавайте эти роли соответственно и размещайте агентов в сетевом сегменте, доступность которого соответствует тому, что позволено видеть проектам платформы. Роль viewer может только смотреть результат чужого запуска. Платформа намеренно не ограничивает адреса запуска: это принятая часть модели доверия к этим ролям, а границей служит сетевой сегмент, в котором стоят агенты.

Запуск из формы сайта выполняет пробу в том виде, как она заполнена, без сохранения — и не оставляет записи в журнале аудита: аудит ведётся только по сохранённым конфигурациям. Такой запуск может ссылаться на секреты проекта так же, как сохранённый сайт (их читает то же право на редактирование), поэтому факт использования секрета в запуске, который так и не был сохранён, потом не проследить; AI-ассистенту ссылки на секреты в составленном им самим запуске запрещены — только в сохранённых сайтах.

Секреты

Пароли, токены и API-ключи, которые нужны проверкам (basic auth, заголовок Authorization и другие заголовки, тела запросов, шаги сценариев, ожидаемые значения проверок), не обязательно хранить в платформе: в любом строковом поле конфигурации сайта можно указать ссылку на секрет, которую backend разрешает из хранилища секретов, совместимого с HashiCorp Vault, — Deckhouse Stronghold или HashiCorp Vault — только в момент передачи конфигурации агентам. В базе данных платформы, публичном API, журнале аудита и выгрузках webmon-cli остаётся ссылка, а не значение.

${secret:<путь>#<ключ>}
  • путь — путь к секрету KV v2 внутри единственного KV-хранилища, настроенного для платформы (ui.secretsStore.kv.mount, по умолчанию secret); ключ — ключ внутри этого секрета. Сегменты пути могут содержать буквы, цифры, _ . -.
  • Относительный путь (${secret:app/prod#token}) замкнут на проект: он разрешается в <mount>/<pathPrefix>/<имя space>/<имя проекта>/app/prod (pathPrefix по умолчанию dop/web-monitoring). Относительные ссылки доступны любому проекту без дополнительных прав. Переименование space или проекта меняет этот префикс — перенесите секреты соответственно.
  • Абсолютный путь начинается с / (${secret:/team-a/prod/db#password}) и разрешается в <mount>/team-a/prod/db. Абсолютные ссылки разрешены только под префиксами, которые администратор платформы перечислил в настройках проекта (Настройки → Веб-мониторинг → Разрешённые абсолютные пути секретов, по одному полю на префикс, например /team-a/; / разрешает всё хранилище; раздел показывается только при настроенном хранилище секретов). Совпадение проверяется по границе сегмента: /team-a/ разрешает /team-a/prod/db, но не /team-abc/x. Пустой список (по умолчанию) запрещает абсолютные пути.
  • Имя secret зарезервировано под этот синтаксис и не может использоваться как переменная сценария.
  • Ссылки допустимы в полях запроса и в ожидаемых значениях проверок. В метках метрик (siteLabels, checkLabels), именах и экстракторах переменных они запрещены: подставленное там значение стало бы меткой всех серий пробы и было бы видно каждому, кто может читать метрики проекта.

Кто может прочитать секрет. Ссылка разрешается в запрос, который отправляют агенты, поэтому любой, кто может править сайты проекта (роли user и admin, API-токен с доступом к веб-мониторингу, AI-ассистент от имени такого пользователя), может направить проверку на подконтрольный себе хост и получить значение там. Право править сайты равносильно праву читать все секреты, на которые проект может ссылаться: выдавайте роль соответственно и держите список разрешённых абсолютных путей как можно уже. Роль viewer не может ни править сайты, ни запускать разовые проверки, ни получать каталог секретов. Чего платформа не показывает никогда — это само значение: в базе, публичном API, журнале аудита, выгрузках webmon-cli, логах агентов и трейсах разовых запусков на его месте стоит ссылка.

В форме сайта ввод ${ в поле, принимающем ссылки (путь и URL запроса, заголовки, тело запроса, basic auth, ожидаемые значения проверок), открывает список того, что туда можно подставить: внутри шага сценария — переменные, извлечённые предыдущими шагами, как ${имя}; при настроенном хранилище секретов — секреты под префиксом проекта и под разрешёнными абсолютными префиксами, как ${secret:путь#ключ} (только пути и имена ключей, никогда значения). Продолжайте печатать, чтобы сузить список; выбор — стрелками и Enter или кликом. Если хранилище не разрешает платформе листинг префикса, список об этом сообщает — путь вводится вручную.

При сохранении сайта каждая новая ссылка читается из хранилища: несуществующий путь или ключ, путь, который платформе запрещено читать, абсолютный путь вне списка проекта или недоступное хранилище — ошибки валидации, конфигурация не сохраняется. Редактирование сайта без изменения его ссылок к хранилищу не обращается.

В рантайме backend читает значения, когда формирует конфигурацию для агентов, и передаёт их рядом с ней; агент подставляет их в сам запрос, поэтому значение, похожее на шаблон или ссылку на переменную, уходит как данные:

  • проба выполняется только тогда, когда у каждой её ссылки есть значение: секрет никогда не заменяется заглушкой, поэтому проба без значения не попадает в конфигурацию, которую выполняет агент, — остальные пробы сайта и его ping продолжают работать. На странице сайта указаны ссылка и причина, а для проекта срабатывает алерт GogomoniaSiteRejected;
  • если хранилище недоступно, проект остаётся на своей конфигурации, а агенты продолжают проверять его со значениями последнего успешного формирования; на остальную зону это не влияет. Проба, значений для которой у агента нет вовсе (он перезапустился при недоступном хранилище или ссылка новая), не выполняется до ответа хранилища; это покрывает платформенный алерт WebMonitoringSecretsStoreUnavailable — проект такую проблему не починит. Агенты старше 0.8.2 не подставляют ссылки сами: им эндпоинт состояния отвечает 503, и они остаются на текущей конфигурации до ответа хранилища;
  • если отдельная ссылка перестала разрешаться (секрет удалён или перенесён, изменилась политика, сузили список проекта), использующие её пробы не выполняются, а на странице сайта указана ссылка; остальной сайт продолжает мониториться. Агенту старше 0.8.2 вместо этого не отдаётся сайт целиком — он не умеет пропускать отдельную пробу;
  • ротация секретов подхватывается автоматически: раз в 5 минут backend сравнивает версии KV используемых секретов с последними увиденными и при изменении повышает версию конфигурации, чтобы агенты перечитали её. Разовые запуски (Протестировать) разрешают ссылки в момент, когда агент забирает задание; ссылка, которую в этот момент разрешить нельзя, завершает запуск ошибкой с её указанием.

Те же ссылки можно использовать в conf.yaml, который синхронизируется через webmon-cli (см. Управление конфигурацией из Git): API проверяет их при синхронизации так же, как форма, а выгрузки возвращают ссылки, а не значения.

Существующие учётные данные в открытом виде автоматически не мигрируются: положите значение в хранилище и замените его в конфигурации сайта ссылкой.

Настройка платформы

Порядок настройки с Deckhouse Stronghold в том же кластере; отличия для HashiCorp Vault — в конце раздела. Полное описание параметров ui.secretsStore — в справочнике конфигурации модуля. В примерах используются KV-хранилище dop-secrets, префикс web-monitoring и политика dop-web-monitoring.

1. Доступ администратора к Stronghold. Команды выполняются CLI d8 stronghold (совместим с vault, читает VAULT_ADDR и VAULT_TOKEN). Администратор из management.administrators входит через метод oidc_deckhouse (d8 stronghold login -method=oidc -path=oidc_deckhouse). В режиме management.mode: Automatic root-токен хранится в Secret stronghold-keys пространства имён d8-stronghold (ключ rootToken); он подходит для первичной настройки из консоли — не выводите его на экран:

export VAULT_ADDR=https://stronghold.<publicDomainTemplate>   # адрес из Ingress модуля stronghold
export VAULT_TOKEN="$(kubectl -n d8-stronghold get secret stronghold-keys -o jsonpath='{.data.rootToken}' | base64 -d)"
d8 stronghold status                                            # Initialized true, Sealed false
d8 stronghold auth list                                         # kubernetes_local/ настроен модулем

2. Адрес хранилища. Backend проверяет TLS-сертификат по системному набору CA и по имени хоста. Если API на порту 8200 отдаёт сертификат Ingress (выписан на публичное имя), укажите публичный адрес https://stronghold.<publicDomainTemplate> без caCert. Внутренний адрес https://stronghold.d8-stronghold.svc:8200 подходит, только если сертификат выписан на это имя; его CA указывается в ui.secretsStore.caCert. Проверка сертификата из кластера:

kubectl -n d8-observability-platform run tls-check --rm -i --restart=Never --image=alpine/openssl -- \
  s_client -connect stronghold.d8-stronghold.svc:8200 -servername stronghold.d8-stronghold.svc </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -ext subjectAltName

3. KV v2 и раскладка путей. Платформа работает с одним KV v2 (ui.secretsStore.kv.mount). Секреты проектов лежат под <mount>/<pathPrefix>/<space>/<проект>/... и доступны проектам по относительным ссылкам. Для секретов, общих для нескольких проектов, заведите отдельный префикс (например shared/): проекты ссылаются на него абсолютными путями, разрешёнными в настройках проекта.

d8 stronghold secrets list                                          # существующие хранилища
d8 stronghold secrets enable -path=dop-secrets -version=2 kv        # если KV v2 ещё нет

4. Политика платформы. data — чтение значений при формировании конфигурации агентов; metadata — отслеживание ротации (current_version) и список секретов в форме сайта (list; без него пути вводятся вручную). Политика должна покрывать pathPrefix и каждый абсолютный префикс, который планируется разрешать проектам:

# dop-web-monitoring.hcl
path "dop-secrets/data/web-monitoring/*"     { capabilities = ["read"] }
path "dop-secrets/metadata/web-monitoring/*" { capabilities = ["read", "list"] }
path "dop-secrets/data/shared/*"             { capabilities = ["read"] }
path "dop-secrets/metadata/shared/*"         { capabilities = ["read", "list"] }
d8 stronghold policy write dop-web-monitoring dop-web-monitoring.hcl

5. Аутентификация платформы. Поддерживаются два способа.

Вариант А — метод Kubernetes (auth.method: Kubernetes, по умолчанию). Backend входит в хранилище projected-токеном ServiceAccount backend-secrets пространства имён d8-observability-platform (создаётся модулем, RBAC не требуется) и получает токен хранилища с политикой роли; токен продлевается автоматически. Роль в методе kubernetes_local:

d8 stronghold write auth/kubernetes_local/role/dop-web-monitoring \
  bound_service_account_names=backend-secrets \
  bound_service_account_namespaces=d8-observability-platform \
  policies=dop-web-monitoring ttl=1h

Проверка роли и политики до включения интеграции — токеном того же ServiceAccount:

JWT=$(kubectl -n d8-observability-platform create token backend-secrets --duration=10m)
d8 stronghold write -field=token auth/kubernetes_local/login role=dop-web-monitoring jwt="$JWT" > /tmp/dop.token
VAULT_TOKEN=$(cat /tmp/dop.token) d8 stronghold kv list -mount=dop-secrets web-monitoring/     # список или пустой ответ, но не 403
rm /tmp/dop.token

Вариант Б — статический токен (auth.method: Token). Backend обращается к хранилищу токеном из Kubernetes Secret (имя — auth.token.secretName, по умолчанию secrets-store-token; ключ token). Вход в хранилище не выполняется, ServiceAccount backend-secrets не используется. Выпустите orphan-токен с политикой платформы через роль метода token и положите его в Secret:

d8 stronghold write auth/token/roles/dop-web-monitoring \
  allowed_policies=dop-web-monitoring orphan=true renewable=true token_type=service
d8 stronghold token create -role=dop-web-monitoring -policy=dop-web-monitoring \
  -display-name=dop-web-monitoring -ttl=768h -field=token \
  | kubectl -n d8-observability-platform create secret generic secrets-store-token --save-config --from-file=token=/dev/stdin

Срок жизни токена ограничен max_lease_ttl метода token (по умолчанию 768 часов). Платформа токен не продлевает: до истечения срока выпустите новый токен, обновите Secret и перезапустите backend и backend-scheduler — значение Secret передаётся в переменную окружения при старте пода. После перезапуска отзовите прежний токен (d8 stronghold token revoke -accessor <accessor>; accessor и display_name токенов показывает d8 stronghold token lookup -accessor по списку d8 stronghold list auth/token/accessors).

d8 stronghold token create -role=dop-web-monitoring -policy=dop-web-monitoring \
  -display-name=dop-web-monitoring -ttl=768h -field=token \
  | kubectl -n d8-observability-platform create secret generic secrets-store-token --from-file=token=/dev/stdin \
      --dry-run=client -o yaml | kubectl apply -f -
kubectl -n d8-observability-platform rollout restart deployment/backend deployment/backend-scheduler

Истёкший или отозванный токен хранилище не отличает от запрета политикой и отвечает 403: ни одна ссылка на секрет не разрешается, пробы с секретами не выполняются, на странице сайта показывается причина access denied, а сайты с новыми ссылками не сохраняются до замены токена.

6. Включение в модуле. mount и pathPrefix должны совпадать с политикой; секция auth — по выбранному варианту:

# kubectl edit moduleconfig observability-platform
spec:
  settings:
    ui:
      secretsStore:
        enabled: true
        address: https://stronghold.<publicDomainTemplate>   # см. шаг 2
        auth:
          method: Kubernetes            # вариант А
          kubernetes:
            path: kubernetes_local
            role: dop-web-monitoring
          # method: Token               # вариант Б
          # token:
          #   secretName: secrets-store-token
        kv:
          mount: dop-secrets
          pathPrefix: web-monitoring

Deckhouse перекатывает backend и backend-scheduler: поды получают переменные SECRETS_STORE_* из ConfigMap backend-envs, в варианте А — ServiceAccount backend-secrets и projected-токен в /var/run/secrets/secrets-store/token, в варианте Б — переменную SECRETS_STORE_TOKEN из Secret. Интеграция готова, когда ввод ${ в поле пароля формы сайта предлагает ссылки ${secret:…} (а для проекта, у которого секретов ещё нет, — заметку с его префиксом dop-secrets/web-monitoring/<space>/<проект>).

7. Секрет и ссылка на него. Секрет кладётся под префикс проекта, в конфигурации сайта указывается ссылка; при сохранении backend читает каждую новую ссылку из хранилища:

d8 stronghold kv put -mount=dop-secrets web-monitoring/<space>/<проект>/httpbin user=monitor password='...'
d8 stronghold kv put -mount=dop-secrets shared/api token='...'      # для абсолютной ссылки ${secret:/shared/api#token}
request:
  basicAuth:
    username: ${secret:httpbin#user}          # относительно префикса проекта
    password: ${secret:httpbin#password}
  headers:
    - name: Authorization
      value: Bearer ${secret:/shared/api#token}   # абсолютный путь: нужен префикс /shared/ в настройках проекта

Абсолютные пути разрешает администратор платформы в настройках проекта (Настройки → Веб-мониторинг → Разрешённые абсолютные пути секретов, например /shared/; администраторы проекта видят список только для чтения). Ссылку на несуществующий ключ, запрещённый политикой путь или абсолютный путь вне списка форма и API отклоняют с указанием ссылки.

8. Ротация и отказы. Новая версия секрета (kv put) подхватывается автоматически: backend-scheduler раз в 5 минут сравнивает current_version используемых секретов и при изменении повышает версию конфигурации, агенты перечитывают её на следующем опросе; независимо от этого агент раз в 10 минут запрашивает полную конфигурацию и применяет её, если содержимое изменилось.

Если секрет удалён или доступ к нему отозван, использующие его пробы перестают выполняться, а на странице сайта указана ссылка — секрет никогда не заменяется заглушкой. Если хранилище недоступно, агенты продолжают проверки с уже имеющимися значениями, пробы без значений не выполняются, а сайты с новыми ссылками не сохраняются до восстановления хранилища (агентам старше 0.8.2 на весь запрос состояния отвечают 503, и они остаются на прежней конфигурации). Значения секретов в пути URL не защищаются: агент записывает строку запроса в журнал проверки и маскирует только заголовки Authorization, Cookie и подобные — ссылки следует использовать в basicAuth, заголовках и телах запросов, а не в path.

Типичные ошибки: 403 при чтении — политика не покрывает путь (data/) или его метаданные (metadata/), либо истёк или отозван статический токен; 404 — нет такого пути или ключа (в политике KV v2 путь содержит сегмент data/, в командах kv — нет); login failed with HTTP 403 в логах backend — роль метода Kubernetes не привязана к backend-secrets/d8-observability-platform, у роли задан audience, отличный от ui.secretsStore.auth.kubernetes.audience, либо хранилищу не хватает прав на TokenReview API (для Vault в кластере — ClusterRole system:auth-delegator у его ServiceAccount); certificate verify failed — адрес не совпадает с именами в сертификате (шаг 2).

HashiCorp Vault. Отличия: KV-хранилище должно быть версии 2 и называться так, как указано в политике и в ui.secretsStore.kv.mount; метод Kubernetes включается и настраивается вручную, путь метода — kubernetes (ui.secretsStore.auth.kubernetes.path: kubernetes); в ui.secretsStore.address указывается адрес Vault (внутри кластера — http(s)://<service>.<namespace>.svc:8200). Статический токен выпускается так же, командами vault:

vault secrets enable -version=2 -path=dop-secrets kv
vault auth enable kubernetes
# Vault внутри кластера: токен пода backend проверяется через TokenReview API от имени
# ServiceAccount самого Vault — ему нужна ClusterRole system:auth-delegator:
#   kubectl create clusterrolebinding vault-auth-delegator \
#     --clusterrole=system:auth-delegator --serviceaccount=<namespace Vault>:<ServiceAccount Vault>
vault write auth/kubernetes/config kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
# Vault вне кластера: укажите ему адрес кластера и либо выдайте reviewer-токен
# (token_reviewer_jwt), либо включите ui.secretsStore.auth.kubernetes.grantTokenReview: true,
# чтобы ServiceAccount платформы мог сам вызывать TokenReview API.
vault policy write dop-web-monitoring dop-web-monitoring.hcl
vault write auth/kubernetes/role/dop-web-monitoring \
  bound_service_account_names=backend-secrets \
  bound_service_account_namespaces=d8-observability-platform \
  policies=dop-web-monitoring ttl=1h

Если у роли указан явный audience, то же значение задаётся в ui.secretsStore.auth.kubernetes.audience. ui.secretsStore.namespace передаёт заголовок X-Vault-Namespace для пространств имён Stronghold или Vault Enterprise; в остальных случаях он остаётся пустым. Собственный CA хранилища указывается в ui.secretsStore.caCert.

Что агенты сообщают о конфигурации

Платформа проверяет сайт при сохранении, но последнее слово за агентом: выражения переменных сценария и проверок он компилирует своими движками (Go RE2, ojg, cascadia), эффективные таймауты считает сам и значения секретов держит у себя. Сайт или проба, которые он отверг, не дают метрик вовсе, поэтому ни один алерт проверок по ним не сработает — вердикт каждого агента показывается на странице сайта и выносится бейджем в список сайтов:

  • сайт отвергнут целиком — конфигурация не прошла валидацию агента (выражение, которое он не компилирует, таймаут выше посчитанного им предела, ключ, неизвестный этой версии агента). Этот агент не мониторит сайт вообще;
  • проба не выполняется — у её ссылки ${secret:...} нет значения: ссылка не разрешается или хранилище секретов не удалось прочитать. Остальные пробы сайта и его ping продолжают работать;
  • проба выполняется со значениями секретов прежней конфигурации — хранилище секретов недоступно, и агент продолжает проверки с тем, что у него уже есть.

Агент применяет сохранённую конфигурацию и отчитывается в течение минуты, поэтому вердикт появляется вскоре после сохранения; обновите страницу сайта, чтобы его увидеть. Отвергнутая конфигурация дополнительно поднимает проектный алерт GogomoniaSiteRejected, а недоступное хранилище секретов — платформенный WebMonitoringSecretsStoreUnavailable: владелец проекта хранилище не починит. Агенты старше 0.8.2 вердикты не присылают: для них отвергнутый сайт виден только в их журнале и в gogomonia_agent_config_problem_sites.

Требования к проектам

Для работы мониторинга сайтов у проекта должен быть API-токен с правом «Метрики: запись» (write_metrics). Этот токен создаётся автоматически при создании проекта.

Если токен удалён — в разделе Веб-мониторинг отображается предупреждение, а агент не получает конфигурацию для этого проекта.

Установка агента

Требования

  • Linux (amd64)
  • Сетевой доступ до DOP API (api.<domain>)

Ручная установка

  1. Создать агента в Admin UI: Admin → Agents → New Agent (указать имя и зону)
  2. Скопировать токен из карточки агента
  3. На целевом сервере:
# Скачать бинарь
curl -o /usr/local/bin/gogomonia-agent \
  "https://update.<domain>/gogomonia-agent/storage/latest/linux/amd64/gogomonia-agent"
chmod +x /usr/local/bin/gogomonia-agent

# Конфиг
mkdir -p /usr/local/gogomonia-agent/etc
cat > /usr/local/gogomonia-agent/etc/config.yaml <<EOF
dop_api_url: "https://api.<domain>"
EOF

# Токен
echo -n "<TOKEN>" > /usr/local/gogomonia-agent/agent.token

# Запуск
/usr/local/bin/gogomonia-agent

Конфигурация агента

Файл config.yaml:

dop_api_url: "https://api.dop.example.com"
proxy:
  url: ""          # HTTP proxy (опционально)
  no_verify_tls: false
poll_interval: 60s        # опрос изменений конфигурации
force_poll_interval: 10m  # полный запрос стейта: подхватывает изменения без смены версии, не короче poll_interval

Переменные окружения

Переменная Описание По умолчанию
GOGOMONIA_CONFIG_PATH Путь к config.yaml /usr/local/gogomonia-agent/etc/config.yaml
GOGOMONIA_TOKEN_PATH Путь к файлу с токеном /usr/local/gogomonia-agent/agent.token
GOGOMONIA_UUID_PATH Путь к файлу UUID /usr/local/gogomonia-agent/agent.uuid
GOGOMONIA_PUSH_URL Override для remote write URL (из state API)
GOGOMONIA_POLL_INTERVAL Период опроса изменений конфигурации 60s
GOGOMONIA_FORCE_POLL_INTERVAL Период полного запроса стейта (не короче GOGOMONIA_POLL_INTERVAL) 10m

Управление конфигурацией из Git

Утилита converter позволяет хранить конфигурацию сайтов в git-репозитории в декларативном YAML-формате с поддержкой Go-шаблонов и синхронизировать её с DOP через API. Это GitOps-подход: validate на merge request, sync на merge в master.

Защита от изменений через UI

По умолчанию все сайты в DOP редактируются через UI — это касается и тех, что создаются webmon-cli. Если хочется явно зафиксировать в git, что конкретный сайт управляется только через API (чтобы оператор случайно не поправил его в UI и не получил, как следствие, расхождение между git и DOP), задайте в YAML флаг uiEditable: false. Доступно начиная с webmon-cli v0.5.1:

defaults:
  uiEditable: false       # все сайты в файле — только через API
targets:
  - name: managed-site
    host: managed.example.com
    # uiEditable наследуется из defaults → false

  - name: editable-site
    host: editable.example.com
    uiEditable: true      # точечное исключение: оставить редактирование в UI

Семантика:

  • uiEditable: true — поведение по умолчанию: сайт можно редактировать в UI и через API.
  • uiEditable: false — в UI у сайта появляется бейдж «Только через API», кнопка редактирования становится неактивной; удаление (и из UI, и из API) и обновление через API по-прежнему доступны.
  • Поле отсутствует в YAML — webmon-cli ничего не передаёт серверу, сервер сохраняет значение, заданное ранее (для новых сайтов — true).
  • Приоритет: targets[].uiEditable → defaults.uiEditable → значение, ранее сохранённое в DOP.

Подробнее о поле в API-контракте — в разделе API → Параметры сайтов.

Скачивание

Бинарь: webmon-cli.

curl -o /usr/local/bin/webmon-cli \
  "https://update.<domain>/webmon-cli/storage/latest/linux/amd64/webmon-cli"
chmod +x /usr/local/bin/webmon-cli

Команды

Первый аргумент — команда: sync или validate. Параметры задаются только переменными окружения.

sync — синхронизация конфигов в DOP

Создаёт, обновляет и (при DELETE_ORPHANS=true) удаляет сайты в DOP по содержимому YAML-файлов.

export DOP_API_URL="https://api.<domain>/api/v1/observability"
export CONFIGS_DIR=/path/to/configs
export DOP_WEB_MON_TOKEN_MYPROJECT="<API-token>"
webmon-cli sync

validate — локальная валидация

Проверяет конфиги без применения: формат полей, допустимые ключи, deprecated поля, дупликаты. Загружает список зон из API для проверки.

export DOP_API_URL="https://api.<domain>/api/v1/observability"
export CONFIGS_DIR=/path/to/configs
webmon-cli validate

Важно: DOP_API_URL обязателен и для validate (загрузка зон). Токен для validate не требуется.

Глобальные переменные окружения

Переменная Описание По умолчанию
DOP_API_URL URL backend API (обязательно, валидный URL) —
CONFIGS_DIR Путь к дереву конфигов .
LOG_LEVEL Уровень логов (zap): debug, info, warn, error info
COMMON_TEMPLATES Каталоги с общими .tpl-шаблонами, через запятую —
DRY_RUN План без применения (true / 1 / yes) false
DELETE_ORPHANS Удалять сайты в DOP, отсутствующие в конфигах false
SKIP_DUPLICATES Пропускать дубликаты вместо ошибки false

Маппинг проектов и токены

Для каждого каталога проекта задаются переменные окружения. Суффикс формируется из имени папки: все символы кроме букв и цифр заменяются на _, результат приводится к верхнему регистру.

Примеры: папка myapp → MYAPP, папка my-client → MY_CLIENT.

Переменная Описание Для sync
DOP_WEB_MON_TOKEN_{FOLDER} API-токен с правом «Мониторинг сайтов» обязательно
DOP_WEB_MON_SPACE_{FOLDER} Override имени рабочего пространства по умолчанию = имя папки
DOP_WEB_MON_PROJECT_{FOLDER} Override имени проекта по умолчанию = имя папки

Структура репозитория

{team-repo}/
  .helpers/helpers.tpl       # общие Go-шаблоны (Sprig)
  {project}/conf.yaml        # конфигурация сайтов
  .gitlab-ci.yml
  • Проект = любая папка, содержащая *.yaml файлы
  • Шаблоны .tpl загружаются из COMMON_TEMPLATES и .helpers/ каталогов по пути вверх от папки проекта
  • Шаблоны используют синтаксис Go text/template с библиотекой Sprig
  • Конфиги рендерятся как шаблоны перед парсингом YAML

Формат conf.yaml

# Лейблы уровня юнита — добавляются ко всем сайтам
siteLabels:                      # опционально, map[string]string
  env: production
checkLabels:                     # опционально, map[string]string
  alert: "yes"

# Значения по умолчанию для всех сайтов
defaults:                        # опционально
  ping: "true"                   # "true"/"yes" → включить ping
  period: "20s"                  # формат: \d+(s|m), max 60s
  uiEditable: false              # опционально, bool: false = в UI сайт API-only
  request:                       # defaults для HTTP-запросов
    scheme: https                # http / https
    timeout: 10s                 # формат: \d+(ms|s|m), max 300s
    headers:                     # массив {name, value}
      - name: User-Agent
        value: "monitoring/1.0"

# Список целей мониторинга (обязателен, минимум 1)
targets:
  - name: example-main           # ОБЯЗАТЕЛЬНО: уникальное имя сайта
    host: example.com            # ОБЯЗАТЕЛЬНО: домен или IP
    hosts:                       # опционально: фиксированные IP вместо DNS
      - 93.184.216.34
    probeAllResolvedIPs: "true"  # опционально: проверять все IP при DNS resolve
    period: 30s                  # override периода
    ping: true                   # override ping
    uiEditable: true             # опционально: override defaults.uiEditable
    siteLabels:                  # merge с unit-level
      env: staging
    checkLabels:                 # merge с unit-level
      alert: "no"
    request:                     # override request defaults
      scheme: https
      timeout: 5s
    probes:                      # >= 1 проба, если ping=false
      - name: https-main         # ОБЯЗАТЕЛЬНО: уникальное, без пробелов/кавычек/скобок/пайпов
        period: 30s              # опционально: override периода
        siteLabels:              # опционально: merge с site-level
          component: api
        checkLabels:             # опционально: merge с site-level
          alert: "yes"
        request:                 # полная HTTP-конфигурация
          scheme: https
          path: /healthz
          method: GET            # GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
          port: 443              # 1-65535
          timeout: 5s            # max 300s
          headers:
            - name: Accept
              value: application/json
          basicAuth:
            username: user
            password: pass
          data: '{"key":"value"}'
          disableHttp2ForHttps: "true"   # строка в кавычках: "true" или "false"
        checks:                  # ОБЯЗАТЕЛЬНО: >= 1 чек
          - name: status-ok      # ОБЯЗАТЕЛЬНО: уникальное, без пробелов/кавычек/скобок/пайпов
            status: [200, 301]
          - name: response-time
            responseTime:
              max: 5s
          - name: body-check
            bodyMatch: "/OK/"
          - name: size-check
            bodySize:
              min: 100b
              max: 10Mb
          - name: content-type
            contentType: "/text\\/html/"
          - name: ssl-check
            sslBasicConstraintsValid: true
          - name: cert-expiry
            certValidDays:
              min: 30
            checkLabels:
              alert: "no"

Legacy-формат: вместо targets можно использовать sites с ключами site (вместо host/name) и servers (вместо hosts). Оба формата поддерживаются, но targets — рекомендуемый. Совмещение site + host или servers + hosts в одной записи вызовет ошибку.

TCP-проба (вместо request):

        tcp:
          port: 443
          timeout: 5s
          tls:
            insecureSkipVerify: false
            serverName: example.com

DNS-проба (вместо request):

        dns:
          type: A                # A, AAAA, CNAME, MX, NS, TXT, SOA, SRV, PTR, CAA
          query: example.com     # ОБЯЗАТЕЛЬНО: домен для запроса
          timeout: 2s

Многошаговый HTTP-сценарий (multihttp: вместо request; у самой пробы checks нет, чеки живут в шагах). Шаги выполняются по порядку с общим cookie jar; ${name} — переменная, извлечённая предыдущим шагом. Логин за Dex (OIDC) и открытие защищённой страницы:

      - name: dex-login
        period: 60s                    # 30s..5m; таймаут сценария по умолчанию = period − 5s
        multihttp:
          timeout: 50s                 # опционально, дедлайн всех шагов вместе, <= period
          cookies: "true"              # опционально, общие cookies между шагами (по умолчанию true)
          logVariables: "false"        # опционально, логировать значения переменных, не только имена (по умолчанию false)
          disableHttp2ForHttps: "false" # опционально, HTTP/1.1 для всех шагов (один клиент на сценарий)
          steps:
            - name: open_console       # 1. консоль -> цепочка редиректов -> страница Dex «выберите способ входа»
              url: /?from=console      # путь → хост сайта; абсолютные http(s):// URL тоже допустимы; отправляется как есть
              followRedirects: "true"
              variables:
                - name: login_link     # href коннектора «Email», несёт одноразовый state
                  type: cssSelector    # jsonPath | regex | header | cookie | cssSelector
                  expression: 'a[href^="/auth/local"]'
                  attribute: href      # только для cssSelector: атрибут вместо текста элемента
              checks:
                - name: chooser
                  status: [200]
                  bodyMatch: "/select a login method/"
            - name: open_login_form    # 2. форма логина; её action несёт state запроса
              url: "https://dex.example.com${login_link}"
              followRedirects: "true"
              variables:
                - name: login_action
                  type: cssSelector
                  expression: form.login-form
                  attribute: action
              checks:
                - name: form
                  status: [200]
            - name: submit             # 3. POST учётных данных -> callback ставит сессионную cookie -> главная
              url: "https://dex.example.com${login_action}"
              method: POST
              headers:
                - name: Content-Type
                  value: application/x-www-form-urlencoded
              data: "login=monitor%40example.com&password=secret"
              timeout: 20s             # опционально, таймаут шага <= таймаута сценария
              followRedirects: "true"
              checks:
                - name: logged_in
                  status: [200]
                  bodyMatch: "/Deckhouse UI/"
                  responseTime:
                    max: 15s
            - name: protected_page     # 4. открывается с сессионной cookie, без редиректа на Dex
              url: "/?state=${login_link|urlencode}"  # переменная закодирована для query
              basicAuth:               # опционально, учётные данные только этого шага
                username: monitor
                password: "${secret:app/prod#password}"
              checks:
                - name: authorized
                  status: [200]

Поля шага: name (обязательно, уникально в пробе), url (обязательно; отправляется как написан — уже закодированным и с query-параметрами внутри, отдельного списка query нет), scheme (http/https) и port (1..65535), достраивающие URL-путь прежде request сайта (схема по умолчанию http; рядом с абсолютным url оба отвергаются), method (по умолчанию GET), headers (список {name, value}), basicAuth ({username, password} шага; явный заголовок Authorization имеет приоритет), data (тело запроса), timeout (если шаг его не задаёт — request.timeout сайта, иначе 10s; форма показывает это эффективное значение подсказкой в поле), followRedirects, variables (до 10 на шаг), checks (до 10 на шаг; те же чеки, что у HTTP-проб, плюс jsonPath; checkLabels на чеках шагов недопустимы — лейблы алертинга сценария задаются на пробе).

${name} допустимо в url, значениях headers, data и в значениях чеков; ${name|urlencode} подставляет значение закодированным (RFC 3986), любой другой фильтр — ошибка валидации. Сценарий останавливается на первом проваленном шаге; остальные шаги считаются проваленными. Нужны агенты 0.3.0 и новее; basicAuth, scheme, port шага, шаблоны [[ ]] в шагах, disableHttp2ForHttps и ${name|urlencode} требуют агентов 0.5.0 и новее (более старые агенты такие сценарии не получают).

Правила валидации:

  • name и host — обязательные поля каждой цели
  • Если ping: false и probes пусто — ошибка
  • Тип пробы определяется наличием ключа: tcp: → tcp, dns: → dns, multihttp: → multihttp, иначе http
  • multihttp: 1..10 шагов с уникальными именами, хотя бы один чек суммарно по шагам, без checks на уровне пробы; period 30s..5m; timeout <= period, timeout шага <= timeout сценария; ${name} может ссылаться только на переменные предыдущих шагов (в значениях чеков — и своего шага), единственный фильтр — urlencode; выражения переменных regex, jsonPath, cssSelector и jsonPath чеков должны компилироваться — иначе агент отверг бы весь сайт; steps[].query отвергается (unknown key "query": put the parameters into url and encode variable values with ${name|urlencode}); type переменной — один из jsonPath, regex, header, cookie, cssSelector (attribute только с cssSelector)
  • Зоны определяются из siteLabels.zone; пустые зоны = все зоны
  • certVerify / sslVerify — deprecated, вызывают предупреждение (используйте sslBasicConstraintsValid)
  • Неизвестные ключи на любом уровне — ошибка парсинга (отклоняются YAML-парсером)
  • request на уровне сайта/defaults допускает только scheme, timeout, headers. Ключи проб (port, method, path и др.) задаются только в probe-level request
  • certValidDays и sslValidDays — нельзя использовать одновременно
  • bodySize поддерживает min и/или max
  • responseTime поддерживает только max (не min); формат max: \d+(ms|s|m) (без h)
  • Эффективный period (override пробы → сайт → unit-default) должен быть >= responseTime.max каждого чека

CI/CD интеграция

Converter предназначен для использования в GitLab CI: validate на MR, sync на merge в master.

stages:
  - validate
  - apply

variables:
  DOP_CONVERTER_URL: "https://update.${DOP_BASE_DOMAIN}/webmon-cli/storage/${DOP_CONVERTER_VERSION}/linux/amd64/webmon-cli"

.download_converter: &download_converter
  image: alpine:${DOP_WEB_MONITORING_ALPINE_VERSION}
  before_script:
    - apk add --no-cache curl
    - curl -sSL -o webmon-cli "${DOP_CONVERTER_URL}"
    - chmod +x webmon-cli

validate:
  <<: *download_converter
  stage: validate
  variables:
    DOP_API_URL: "${DOP_API_URL}"
    CONFIGS_DIR: "."
  script:
    - ./webmon-cli validate

apply:
  <<: *download_converter
  stage: apply
  variables:
    DOP_API_URL: "${DOP_API_URL}"
    CONFIGS_DIR: "."
  script:
    - ./webmon-cli sync
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

Переменные CI/CD:

Переменная Описание Где задать
DOP_WEB_MON_TOKEN_{FOLDER} API-токен для каталога проекта Settings → CI/CD → Variables (masked)
DOP_API_URL URL API бэкенда Settings → CI/CD → Variables
DOP_BASE_DOMAIN Базовый домен для скачивания converter Group-level Variables
DOP_CONVERTER_VERSION Версия converter (например 0.4.1 или latest) Group-level Variables
DOP_WEB_MONITORING_ALPINE_VERSION Версия образа Alpine для CI-джоб Group-level Variables

Важно: подставьте {FOLDER} в соответствии с именем каталога проекта после санитизации. Папка myapp → DOP_WEB_MON_TOKEN_MYAPP.

Метрики

Имена метрик используют технический префикс gogomonia_ — это внутреннее имя компонента, сохранённое для обратной совместимости.

Probe-метрики (per-project tenant)

Метрика Тип Описание
gogomonia_test gauge Результат check/test (1=pass, 0=fail)
gogomonia_probe_request_succeeded gauge Probe request успешен (1/0)
gogomonia_http_requests_sent counter HTTP запросы отправлены
gogomonia_http_responses_received counter HTTP ответы получены
gogomonia_http_response_time_ms_sum counter Сумма HTTP response time (ms)
gogomonia_http_body_size_bytes counter Сумма размера HTTP тел
gogomonia_http_requests_in_flight gauge HTTP запросы в процессе
gogomonia_tcp_requests_sent counter TCP запросы
gogomonia_tcp_responses_received counter TCP успешные соединения
gogomonia_tcp_response_time_ms_sum counter TCP response time
gogomonia_tcp_requests_in_flight gauge TCP в процессе
gogomonia_dns_requests_sent counter DNS запросы
gogomonia_dns_responses_received counter DNS ответы
gogomonia_dns_response_time_ms_sum counter DNS response time
gogomonia_dns_requests_in_flight gauge DNS в процессе
gogomonia_ping_rtt_ms gauge Ping RTT (ms)
gogomonia_ping_requests_sent counter Ping запросы отправлены
gogomonia_ping_responses_received counter Ping ответы получены
gogomonia_site_resolved gauge DNS resolve статус сайта (1=ok, 0=fail)
gogomonia_site_config_problem gauge Проба, которую агент не выполняет так, как она настроена, или выполняет с устаревшими значениями секретов; лейблы site, probe, kind (см. Что агенты сообщают о конфигурации)
gogomonia_site_resolve_tries_sum counter Попытки DNS resolve
gogomonia_site_resolve_success_sum counter Успешные DNS resolve
gogomonia_server_ips gauge IP-адреса, разрешённые для сайта

Лейблы: instance, zone, project, site, server, probe, check, test

Agent selfmon-метрики

Метрика Тип Описание
gogomonia_agent_info gauge Информация об агенте (всегда 1)
gogomonia_agent_up gauge Heartbeat (1 = жив)
gogomonia_agent_start_time_seconds gauge Unix timestamp старта
gogomonia_agent_config_version gauge Версия применённого конфига
gogomonia_agent_config_sites_count gauge Кол-во сайтов в конфиге
gogomonia_agent_config_probes_count gauge Кол-во проб
gogomonia_agent_config_checks_count gauge Кол-во чеков
gogomonia_agent_config_problem_sites gauge Пробы применённого конфига, которые агент не выполняет так, как они настроены, по kind
gogomonia_agent_config_last_fetch_seconds gauge Время последнего получения конфига
gogomonia_agent_config_fetch_errors_total counter Ошибки получения конфига
gogomonia_agent_clock_skew_seconds gauge Часы агента минус часы платформы (по заголовку Date последнего ответа /state), секунды
gogomonia_agent_checks_ok gauge Кол-во чеков со статусом OK
gogomonia_agent_checks_failed gauge Кол-во чеков со статусом FAIL
gogomonia_agent_checks_no_data gauge Кол-во чеков без данных
gogomonia_agent_remote_write_errors_total counter Ошибки remote write
gogomonia_agent_remote_write_samples_total counter Отправлено samples
gogomonia_agent_remote_write_queue_length gauge Размер буфера remote write
gogomonia_agent_dns_resolve_errors_total counter Ошибки DNS resolve
gogomonia_agent_http_request_errors_total counter Ошибки HTTP запросов
gogomonia_agent_tcp_request_errors_total counter Ошибки TCP запросов
gogomonia_agent_dns_query_errors_total counter Ошибки DNS запросов
gogomonia_agent_log_push_queue_length gauge Длина очереди отправки логов
gogomonia_agent_log_push_errors_total counter Ошибки отправки логов

Лейблы: instance, zone, uuid, hostname, version

Дашборды

При включении мониторинга сайтов в проекте автоматически появляются дашборды:

  • Overall Status — сводная таблица всех сайтов проекта: availability, response times, статус чеков
  • Zone Status — детализация по зонам: per-zone метрики, сравнение доступности между зонами
  • Site by Instance — drill-down по сайту: per-server/per-instance метрики, scatter-plot времени ответа, блок «Многошаговые сценарии» (статус, длительность и время ответа шагов каждого сценария)

Дополнительные системные дашборды (Go Runtime, Logs) доступны для мониторинга здоровья самих агентов.