The "Scan Floating IP" button failed with a client timeout: the project now holds ~6.4k floating IPs and the scan listed them all in one unpaginated, timeout-less Neutron request on the HTTP request context. openstack: ListFreeFloatingIPs reads marker-based pages (fields= keeps them small) with per-page retry/backoff on transport errors, 5xx and 429, and every request now has a timeout (also ends hangs inside the orchestrator tick). orchestrator: the scan is a single-flight background job on the process context with progress (clearing/listing/enqueuing/done/error), dry_run, full discovery before anything is enqueued, then SubmitIPs in chunks of 500 in ascending IP order; a failed read leaves the queue untouched. The auto-cycle gets a "scanning" phase that polls the job, so the control loop and autoCycleMu are never held across OpenStack/DB work; it recovers after a restart and waits for (instead of adopting) a scan started by someone else. db: migration 0009 (indexes), paged ListIPsPage/ListRegistryPage, GROUP BY counters, EXISTS completion check, set-based ClearAllIPs. API: POST /admin/ips/scan -> 202 (dry_run, wait), GET /admin/ips/scan, paging and filters on /admin/ips and /admin/registry (bare arrays without limit), results_by_overall in /admin/status. dashboard: scan progress panel and dry-run button, paginated /ips and /registry with server-side filters, Overview on counters and capped lists with progress/ETA, "select all N by filter", hx-params fix for per-row buttons, real counts in confirmations. Also: docs (API, USAGE, DASHBOARD, README), plan and review under docs/changes/, bin/ rebuilt with new SHA256SUMS. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
60 KiB
Работа со стендом
Этот документ — для оператора, который уже развернул стенд (см. SETUP.md) и теперь использует его в повседневной работе: добавляет адреса на проверку, следит за очередью, разбирается в результатах и реагирует на проблемы. Прямые вызовы API описаны в API.md — здесь мы используем их только как инструмент, не углубляясь в протокол.
Токен в примерах. Если на стенде включена аутентификация (SETUP.md), к вызовам
/api/v1/admin/*в примерахcurlниже нужно добавлять заголовок-H "Authorization: Bearer $ADMIN_TOKEN"(значениеCONTROL_API_ADMIN_TOKEN); для краткости он опущен. Дашборд запрашивает логин и пароль.
Содержание
- Как устроена работа с системой
- Добавление новых IP в очередь
- Сканирование Floating IP из OpenStack
- Автоматический цикл проверок
- Наблюдение за очередью
- Значения полей IP
- Как читать итоговый результат (pass/partial/fail)
- Просмотр деталей и истории по конкретному адресу
- Реестр адресов и глубина истории
- Управление валидаторами
- Управление площадками (проберами)
- Управление типами проверок пробера
- Управление целями проверки
- Повторная проверка адреса
- Принудительная остановка проверки
- Удаление адресов из очереди
- Пауза перед self-check (fip_settle_seconds)
- Частые проблемы и что с ними делать
Как устроена работа с системой
Оператор не взаимодействует с валидаторами и проберами напрямую — вся
работа идёт через control-api. Цикл жизни одного IP-адреса:
- Адрес встаёт в очередь (
queued). - Control-api сам находит свободный валидатор, привязывает адрес к нему как Floating IP.
- Валидатор проверяет, что действительно вышел в интернет именно через этот адрес (self-check), затем прогоняет исходящие проверки (HTTPS, ICMP, опционально SSH до заданных внешних целей).
- Одновременно три внешние площадки проверяют, что этот адрес доступен снаружи (входящие TCP-подключения на 22/80/443/8080 и ICMP) — это ловит блокировки/чёрные списки на конкретных внешних сетях.
- Как только все источники (валидатор + 3 площадки) отчитались — или истекло время ожидания — control-api подводит итог и освобождает адрес (отвязывает Floating IP).
Всё это происходит автоматически, без участия оператора. Задача оператора — положить адреса в очередь и снять с них результат.
Добавление новых IP в очередь
Основной способ — API, без перезапуска процесса:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
{"added": ["203.0.113.10", "203.0.113.11"], "requeued": [], "reordered": [], "skipped_in_progress": []}
Адреса обрабатываются в порядке, в котором перечислены в addresses —
именно в этом порядке они и встанут в очередь друг за другом. Метод
идемпотентен относительно уже идущих проверок: адрес, который сейчас
активно проверяется, в ответе окажется в skipped_in_progress и не будет
тронут (см. «Повторная проверка адреса»
ниже — тот же метод форсирует перепроверку уже завершённых адресов).
Также по-прежнему можно добавить адреса через ip_addresses в
/etc/cloud-ip-validator/control-api.yaml и перезапустить control-api —
при каждом старте control-api доливает в очередь только новые адреса из
этого списка (уже обработанные ранее не сбрасываются и повторно не
проверяются). Держать control-api.yaml под версионным контролем (git)
по-прежнему полезно как журнал того, что изначально ставилось в очередь
при разворачивании стенда — но для повседневного добавления адресов проще
и быстрее пользоваться API выше.
Сканирование Floating IP из OpenStack
Вместо того чтобы перечислять адреса вручную, можно попросить control-api самому найти их в облаке:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/scan # 202: сканирование запущено в фоне
curl -s http://<control-api>:8080/api/v1/admin/ips/scan # ход и результат
Сканируются все Floating IP текущего проекта OpenStack, но в очередь ставятся только свободные — те, что не привязаны сейчас ни к одному порту (это и есть пул адресов, ожидающих проверки перед повторной выдачей). Уже привязанные к чему-то Floating IP игнорируются. Внутри вызов делает то же самое, что и обычное добавление — новые адреса встают в очередь, уже завершённые перезапускаются, активно проверяемые не трогаются (см. выше).
Сколько адресов — не важно. Сканирование работает в фоне и читает список из OpenStack
страницами (по 200 адресов, с повторами при обрывах), поэтому подходит и для проекта с
тысячами Floating IP: на стенде с 6441 адресом чтение занимает около 1,5–2 минут. Сначала
обнаруживаются все адреса, и только потом они ставятся в очередь (кусками по 500, в порядке
возрастания IP); сразу после этого начинаются проверки. Если чтение сорвалось даже после повторов,
в очередь не попадает ничего — очередь остаётся как была, а на панели виден error и причина.
Одновременно идёт одно сканирование: повторное нажатие присоединяется к текущему.
В admin-dashboard — кнопка «Сканировать Floating IP» на странице /ips: она сразу отвечает, а под
кнопкой появляется панель прогресса (читаются страницы → ставятся в очередь → готово: прочитано
страниц, найдено, свободных, добавлено, время), по окончании таблица обновляется сама. Рядом —
«Пробное сканирование»: оно проходит все страницы и показывает, сколько свободных адресов нашлось,
не меняя очередь (удобно проверить, что облако отвечает и сколько адресов будет поставлено).
Параметры чтения (control-api.yaml): openstack.list_page_size (200), openstack.request_timeout_seconds
(60 — таймаут одного запроса к OpenStack), openstack.list_page_retries (5), orchestrator.fip_scan_timeout_seconds
(1800 — предел всего сканирования).
Если хочется, чтобы сканирование происходило само по расписанию, а не
только по запросу — задайте orchestrator.fip_scan_interval_seconds
(в секундах) в control-api.yaml; 0 (по умолчанию) оставляет только
ручной запуск через ручку/кнопку выше. Пока включён
автоматический цикл, это периодическое
сканирование не выполняется — цикл сам управляет очередью.
Сколько займут проверки. Один адрес занимает около 50 секунд на валидаторе (из них 30 с — пауза
fip_settle_seconds). Поэтому очередь из 6440 адресов — примерно 18 часов на 5 валидаторах, 9 часов на 10, 4,5 часа на 20. Ускорить можно числом валидаторов и (осторожно)fip_settle_seconds; на странице «Обзор» виден прогресс «Готово D из T» и оценка оставшегося времени.
Автоматический цикл проверок
Опциональный режим, который сам повторяет то, что оператор делает руками: по умолчанию выключен, включается и настраивается администратором. Один цикл — это пять шагов:
- Очередь очищается целиком — то же, что кнопка «Очистить всё» (см. «Удаление адресов из очереди»). История в реестре при этом сохраняется.
- Control-api находит все свободные Floating IP и ставит их в очередь — то же, что «Сканировать Floating IP» (см. выше). Шаги 1–2 выполняются одним фоновым заданием, поэтому долгое чтение тысяч адресов не блокирует работу оркестратора (назначение валидаторов, лизинги, heartbeat).
- Проверки запускаются сами — как для любого адреса в очереди.
- Цикл ждёт, пока все адреса очереди дойдут до конечного состояния
(
done,failedилиoccupied). К этому моменту результат каждого адреса уже записан в реестр. - Выдерживается пауза
interval_seconds, после чего цикл начинается заново с шага 1. Пауза отсчитывается от завершения предыдущего цикла, а не от его начала.
Параметры
| Параметр | По умолчанию | Смысл |
|---|---|---|
interval_seconds |
3600 (1 час) |
Пауза между циклами. Не меньше 60: слишком частые сканы нагружают API OpenStack. |
max_run_seconds |
0 (без лимита) |
Сколько максимум ждать на шаге 4 (отсчёт — от конца сканирования). По истечении цикл фиксирует timeout и переходит к паузе — защита от зависания (нет свободных валидаторов, недоступна площадка). Очередь при этом не трогается: следующий цикл её очистит, а до тех пор видно, что именно не дошло до конца. При тысячах адресов оставьте 0 (или задайте больше расчётного времени: 6440 адресов — часы). |
Параметры хранятся в базе и меняются на лету, без перезапуска; в control-api.yaml
ничего задавать не нужно. Новый interval_seconds применяется к паузе
со следующего цикла — уже идущая пауза досчитывается по старому значению.
Управление
Через API (подробности — в API.md):
# задать параметры (любое из полей можно опустить)
curl -s -X PUT http://<control-api>:8080/api/v1/admin/auto-cycle \
-d '{"interval_seconds": 7200, "max_run_seconds": 1800}'
# включить: первый цикл начнётся сразу
curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/start
# выключить
curl -s -X POST http://<control-api>:8080/api/v1/admin/auto-cycle/stop
# посмотреть состояние
curl -s http://<control-api>:8080/api/v1/admin/auto-cycle
В admin-dashboard — панель «Автоматический цикл» на странице /settings: поля
«Интервал между циклами» и «Максимальная длительность проверки» (в минутах),
кнопки «Включить»/«Выключить». Пока автоцикл включён, на странице /overview
в блоке статистики показывается индикатор «Автоцикл активен» с фазой и временем
следующего запуска.
Фазы и результат последнего цикла
phase показывает, что происходит сейчас: idle (автоцикл выключен или ещё не
стартовал), scanning (шаги 1–2: очистка очереди и чтение/постановка Floating IP),
running (идут проверки — шаги 3–4) и waiting (пауза между циклами,
шаг 5; время следующего запуска — next_run_at). Результат последнего цикла
(last_outcome):
| Значение | Что произошло |
|---|---|
completed |
Все адреса дошли до конечного состояния; runs_total растёт на 1. |
no_free_ips |
Сканирование не нашло свободных Floating IP — ждать нечего, цикл сразу ушёл в паузу. |
timeout |
Проверки не уложились в max_run_seconds. |
error |
Не удалось очистить очередь или просканировать облако; причина — в last_error. Повтор — через interval_seconds. |
stopped |
Автоцикл выключили в момент, когда шёл цикл. Выключение в паузе предыдущий результат не затирает. |
Что важно знать
- Выключение не прерывает проверки, которые уже идут: они закончатся и попадут
в реестр, остановится только повторение. Если выключить цикл во время фазы
scanning, сканирование отменяется (уже поставленные в очередь куски остаются). - Автоцикл владеет очередью: каждый цикл начинается с её полной очистки, поэтому адреса, добавленные вручную, будут удалены (их история в реестре остаётся). Ручные «Очистить всё» и «Сканировать Floating IP» во время цикла не ломают его: если очередь опустела, цикл считается завершённым.
- Состояние хранится в базе и переживает перезапуск control-api: идущий цикл
продолжит ждать, а пауза — досчитается до прежнего
next_run_at. - События цикла (
auto_cycle_started,auto_cycle_completed,auto_cycle_timeout,auto_cycle_error,auto_cycle_stopped) пишутся в журнал событий вместе сqueue_clearedиfip_scan. - Если в момент старта цикла уже идёт чужое сканирование (ручное, пробное или по расписанию), цикл дожидается его окончания и запускает собственное (с очисткой очереди) — присоединяться к чужому нельзя: оно могло ничего не поставить в очередь.
- Если сканирование завершилось ошибкой, очередь уже очищена (шаг 1) и остаётся пустой до следующего
цикла (
interval_seconds); исход цикла —errorс причиной вlast_error. - Цикл на тысячах адресов длится часы; пауза
interval_secondsотсчитывается после его завершения. - В реальном OpenStack отвязка Floating IP после очистки очереди может
отразиться с задержкой; если скан сразу после неё не увидел свободных адресов,
цикл завершится с
no_free_ipsи повторится черезinterval_seconds.
Наблюдение за очередью
Общая сводка:
curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool
{
"total_ips": 25,
"ips_by_state": {"queued": 10, "awaiting_self_check": 1, "checking": 3, "done": 10, "failed": 1},
"total_validators": 4
}
ips_by_state — сколько адресов в каждом состоянии прямо сейчас. Если
хотите наблюдать за прогрессом в реальном времени:
watch -n 2 'curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool'
Полный список всех адресов со всеми полями:
curl -s http://<control-api>:8080/api/v1/admin/ips | python3 -m json.tool
Только финальные результаты (уже готовые адреса), с помощью jq:
curl -s http://<control-api>:8080/api/v1/admin/ips \
| jq '[.[] | select(.State=="done" or .State=="failed") | {IPAddress, State, OverallResult}]'
В admin-dashboard то же самое — страница /overview, с поиском по IP и
фильтром по статусу (pass/partial/fail/cancelled) над обеими
таблицами сразу (см. DASHBOARD.md).
Значения полей IP
| Поле | Значение |
|---|---|
IPAddress |
Проверяемый адрес |
Sequence |
Позиция в очереди (порядок постановки — из конфига при первом старте либо из последнего вызова POST /api/v1/admin/ips) |
State |
Текущий этап: queued, assigning_fip, awaiting_self_check, checking, aggregating, done, failed, occupied (см. ниже) |
OwnerValidatorID |
Какой валидатор сейчас (или последним) занимался этим адресом |
FIPID |
Идентификатор Floating IP в OpenStack, к которому привязан адрес (пусто, если ещё/уже не привязан) |
AttemptNumber |
Номер попытки — растёт при каждом requeue (сбой привязки, сбой self-check, реклейм по таймауту) |
RetryCount |
Сколько раз адрес уже переставлялся в очередь заново |
EgressComplete |
Валидатор закончил исходящие проверки |
OverallResult |
Итог: pass, partial, fail, cancelled (принудительно остановлена, см. «Принудительная остановка проверки»), либо пусто, пока проверка не завершена |
AssignedAt / AggregatedAt / FIPReleasedAt |
Метки времени соответствующих этапов |
Завершённость входящих проверок по каждой конкретной площадке в этом списке не отображается (площадок теперь может быть сколько угодно, а не фиксированные три) — детали по конкретной площадке смотрите в массиве
checksответаGET /api/v1/admin/ips/{ip}(Source: "inbound-site-N").
Как читать итоговый результат (pass/partial/fail)
pass— прошли все проверки: все исходящие, плюс все площадки по всем портам и ICMP — если площадки вообще настроены (sitesв конфиге control-api может быть пустым, см. «Управление площадками» — тогда учитываются только исходящие). Адрес можно считать пригодным к повторной выдаче.partial— часть проверок прошла, часть — нет (например, площадка site-2 не смогла достучаться по 8080/tcp, но остальное в порядке). Означает частичную деградацию — например, адрес заблокирован в отдельном сегменте сети/у отдельного провайдера. Требует решения оператора: годится ли адрес для данного случая использования.fail— либо ни одна проверка не прошла, либо адрес вообще не дошёл до стадии проверок (например, self-check не подтвердился — трафик валидатора не пошёл через назначенный FIP — и попытки исчерпались). Смотритеeventsпо этому адресу (см. ниже), чтобы понять, на каком шаге и почему.cancelled— проверку остановил оператор черезPOST /api/v1/admin/ips/{ip}/cancel(Stateпри этом —failed), а не система по итогам проверок. Отличать от обычногоfailполезно, чтобы не путать «адрес не прошёл проверку» с «проверку прервали вручную».
Отдельно от OverallResult стоит состояние State: "occupied" —
облако живое, и список адресов, переданный как «свободные» (из конфига
или через POST /api/v1/admin/ips), мог с тех пор разойтись с
реальностью, либо адрес мог быть передан на проверку по ошибке уже
занятым. Если при попытке привязки Floating IP control-api видит, что тот
уже привязан к чужому порту, адрес переводится в occupied до начала
цикла проверки — OverallResult при этом остаётся пустым, это не fail:
fail означает «проверка стартовала и не прошла», occupied — «проверка
не стартовала, адрес занят кем-то другим». В events по адресу
появляется строка fip_occupied. Автоматических повторных попыток нет
(Neutron сам не освобождает адрес) — верните адрес в работу вручную через
POST /api/v1/admin/ips, когда убедитесь, что конфликт в облаке
разрешился; в дашборде для таких адресов также показывается кнопка
«Перепроверить» вместо «Отменить».
Отсутствие ответа от источника (площадка не прислала результат до
истечения checking_window_seconds) засчитывается как провал — это
управляется настройкой aggregation.missing_counts_as_fail в конфиге
control-api (по умолчанию включено).
Просмотр деталей и истории по конкретному адресу
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
Ответ содержит три части:
ip— те же поля, что и в спискеadmin/ips, но для одного адреса;checks— все отдельные проверки текущей попытки: кто проверял (Source:egressилиinbound-site-N), что именно (CheckType,Target), результат (Success), задержка (LatencyMS), и человекочитаемая деталь (Detail, например текст ошибки при отказе);events— журнал аудита по этому адресу в хронологическом порядке (регистрация, привязка FIP, self-check, агрегация и т.д.) — полезен, чтобы восстановить точную последовательность событий при разборе инцидента.
Пример: почему адрес получил fail?
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 \
| jq '.checks[] | select(.Success==false)'
Это — только текущая попытка. Полная история адреса за всё время, включая предыдущие попытки и даже периоды, когда адрес не стоял в очереди вовсе, смотрится через реестр — см. следующий раздел.
Реестр адресов и глубина истории
GET /api/v1/admin/ips/{ip} (и /ips/{ip} в дашборде) показывает только
текущую попытку проверки. Но если адрес удалили из очереди и позже
добавили заново, это уже новая попытка — а куда девается история старой?
Она не пропадает: control-api ведёт отдельный реестр (ip_registry) —
запись обо всех адресах, когда-либо поставленных на проверку, вместе с
полной накопленной историей проверок по каждому, независимо от того,
удалялся ли адрес из очереди и добавлялся ли повторно.
# все адреса, когда-либо ставившиеся на проверку, с краткой сводкой
curl -s http://<control-api>:8080/api/v1/admin/registry | python3 -m json.tool
# полная история проверок одного адреса, по всем циклам, не только текущему
curl -s http://<control-api>:8080/api/v1/admin/registry/203.0.113.10 | python3 -m json.tool
В admin-dashboard — страницы /registry (список) и /registry/{ip}
(история конкретного адреса), со ссылкой туда со страницы /ips/{ip}.
На /registry — тот же поиск по IP и фильтр по статусу, что и на
/overview, плюс он отражается в адресной строке (?q=&status=), так что
отфильтрованную ссылку можно сохранить или переслать.
Глубина хранения. Чтобы история не росла бесконечно на адресах,
которые перепроверяют очень часто, можно ограничить, сколько последних
циклов проверки хранить на каждый адрес — history_retention_cycles на
странице /settings (или PUT /api/v1/admin/config/orchestrator, см.
API.md).
0 (по умолчанию) — хранить без ограничения. Ограничение действует только
на глубину детальной истории проверок; сама запись в реестре (что адрес
существует, когда впервые встречен, сколько всего было циклов) не
удаляется никогда.
Управление валидаторами
Список валидаторов и их текущее состояние:
curl -s http://<control-api>:8080/api/v1/admin/validators | python3 -m json.tool
Состояния валидатора: unregistered (в конфиге есть, агент ещё не
подключался), idle (свободен, готов взять адрес), assigned/checking
(занят), unreachable (пропустил heartbeat дольше
orchestrator.heartbeat_timeout_seconds).
Добавление нового валидатора (без перезапуска control-api):
- Поднимите новую ВМ в сервисном проекте облака, узнайте её Neutron
port_id. - Зарегистрируйте валидатора через API:
curl -s -X POST http://<control-api>:8080/api/v1/admin/config/validators \ -d '{"validator_id": "validator_05", "os_port_id": "port-abc123"}' - Разверните и запустите
validator-agentна новой ВМ с тем жеvalidator_idв его конфиге (см. SETUP.md).
Сменить os_port_id уже существующего валидатора (например, после
пересоздания ВМ) — PUT /api/v1/admin/config/validators/{id} с телом
{"os_port_id": "новый-port-id"}.
Полный список зарегистрированных валидаторов — GET /api/v1/admin/config/validators (в отличие от GET /api/v1/admin/validators, отдаёт snake_case и без текущего IP —
только конфигурационные поля).
Вывод валидатора из эксплуатации: остановите на нём
validator-agent (systemctl stop validator-agent). Он перестанет
получать новые задания после того, как закончит текущее (если оно было);
если он был убит посреди работы — control-api сам заберёт у него
незавершённый адрес обратно в очередь по истечении
orchestrator.lease_ttl_seconds. Удалять регистрацию валидатора не
обязательно — просто выключенный агент не будет ничего забирать. Если всё
же нужно убрать валидатора из системы совсем:
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/validators/validator_05
Возвращает 409, если валидатор прямо сейчас владеет каким-то IP —
дождитесь освобождения (или принудительно остановите его проверку, см.
«Принудительная остановка проверки»)
перед удалением.
Правки через
validators[]вcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть хотя бы один валидатор, YAML для этой секции игнорируется при всех последующих рестартах (см. SETUP.md). Для стенда, который уже хоть раз запускался, используйте API выше.
Управление площадками (проберами)
Входящие (inbound/prober) проверки полностью опциональны, а число
площадок не ограничено. Список sites в конфиге control-api и есть
переключатель: пусто — inbound-проверки выключены целиком, итоговый
результат считается только по исходящим (egress) проверкам, и агрегация
не ждёт вообще ни одного пробера. Указано N слотов — ждём именно их.
Явного отдельного флага "включить/выключить" нет — самого списка sites
достаточно.
Чтобы добавить площадку (без перезапуска control-api) — назначьте
site_id любому свободному слоту (index — любое целое >= 1, слотов
может быть сколько угодно) через API:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/sites/1 \
-d '{"site_id": "site-1"}'
и разверните на площадке prober с тем же site_id. Чтобы отключить
конкретную площадку — освободите слот:
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/config/sites/1
процесс prober на ней можно не останавливать (он просто перестанет
получать назначения — POST /api/v1/probers/register для отвязанного
site_id начнёт отвечать 400). Текущее распределение слотов — GET /api/v1/admin/config/sites.
Правки через
sites[]вcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть хотя бы одна площадка, YAML для этой секции игнорируется при всех последующих рестартах. Для стенда, который уже хоть раз запускался, используйте API выше.
Состояния площадки
По аналогии с валидаторами (см. «Управление
валидаторами» выше), у каждой площадки есть
состояние подключения — видно и в GET /api/v1/admin/config/sites
(поля hostname/state/last_heartbeat_at), и на странице /sites
дашборда бейджем:
unregistered— слот сконфигурирован, но процессproberна этой площадке ещё ни разу не подключался (не вызывалPOST /api/v1/probers/register).idle— площадка на связи:proberзарегистрирован и присылает heartbeat (POST /api/v1/probers/{site_id}/heartbeat) на каждом опросе.unreachable— площадка пропустила heartbeat дольшеorchestrator.heartbeat_timeout_seconds(тот же параметр, что и для валидаторов) — вероятно, процессproberупал или потерял сеть до control-api.
В отличие от валидатора, у площадки нет состояний assigned/checking
— пробер не привязан к одному IP, а на каждом опросе обрабатывает сразу
весь активный набор.
Управление типами проверок пробера
Список TCP-портов и флаг ICMP, которые prober проверяет на каждой
настроенной площадке — единый глобальный набор, общий для всех площадок
сразу (не то же самое, что список sites выше: sites решает, сколько
точек его применяют, а этот набор — что именно они проверяют).
Посмотреть текущий набор:
curl -s http://<control-api>:8080/api/v1/admin/config/inbound-checks | python3 -m json.tool
Изменить набор портов и/или ICMP (без перезапуска control-api — новое значение сразу видно и следующему опросу пробера, и уже идущей агрегации):
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [22, 80, 443, 8080], "icmp": true}'
Порты должны быть в диапазоне 1..65535 и не повторяться — иначе 400.
Пустой список портов вместе с "icmp": false — штатный способ временно
отключить inbound-проверки целиком, не трогая список площадок:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/inbound-checks \
-d '{"ports": [], "icmp": false}'
То же самое — на странице /settings дашборда, второй формой рядом с
паузой перед self-check (см. ниже): текстовое поле с портами через запятую
и чекбокс ICMP.
Порты 22 и 443 в этом списке трактуются особо: помимо базового
TCP-connect (tcp-22/tcp-443) пробер дополнительно выполняет настоящий
обмен SSH-банером (ssh) и настоящий TLS-хендшейк (tls-443) —
голого открытого TCP-порта недостаточно, чтобы считать SSH/HTTPS
рабочими. Отдельного переключателя для этих доп.проверок нет: они
включаются и выключаются вместе с самим портом в ports. Если на порту
22/443 у площадки на самом деле слушает что-то, кроме SSH/HTTPS,
доп.проверка будет закономерно проваливаться — заведите для такого сервиса
другой порт.
Правки через
orchestrator.inbound_checksвcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — как только в БД есть эта настройка (а она появляется сразу же при первом старте, значение по умолчанию — из YAML), YAML для этой секции игнорируется при всех последующих рестартах. Для стенда, который уже хоть раз запускался, используйте API выше.
Управление целями проверки
Набор egress-целей (targets) и типов проверок (check_types,
привязывающих тип — https/icmp/ssh — к одной или нескольким группам
целей) управляется через API так же, как валидаторы и площадки —
изменения подхватываются немедленно, следующим же назначением от
оркестратора, без перезапуска.
Посмотреть текущий набор:
curl -s http://<control-api>:8080/api/v1/admin/config/targets | python3 -m json.tool
curl -s http://<control-api>:8080/api/v1/admin/config/check-types | python3 -m json.tool
Создать/заменить группу целей и включить тип проверки, ссылающийся на неё:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/targets/web \
-d '{"targets": ["https://hub.docker.com", "https://github.com"]}'
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": true, "targets": ["web"]}'
Отключить тип проверки, не удаляя его (значения целей сохраняются):
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/check-types/ssh \
-d '{"enabled": false, "targets": ["web"]}'
Удалить группу целей можно только если на неё не ссылается ни один
check_type (иначе — 409); удалить сам check_type можно в любой
момент (DELETE /api/v1/admin/config/check-types/{name}).
Важное следствие принятого компромисса: если конфигурация меняется
ровно в момент, когда чей-то IP уже находится в checking (self-check
уже пройден, проверки уже назначены агенту), агрегация этой конкретной
попытки посчитает уже изменённую конфигурацию, а не ту, что была на
момент выдачи задания. На практике это узкое окно в несколько секунд;
деградирует безопасно — через aggregation.missing_counts_as_fail худший
исход для одной попытки — partial вместо pass, самоисправляется на
следующей попытке (в том числе через принудительный повтор, см. ниже).
Правки через
targets/check_typesвcontrol-api.yamlтоже поддерживаются, но только как bootstrap пустой базы данных при самом первом старте — см. примечание в разделах выше.
Повторная проверка адреса
Если адрес завершился с failed или partial, а вы хотите перепроверить
его ещё раз (например, после устранения блокировки на стороне сети) —
отправьте его тем же методом, что используется для постановки новых
адресов в очередь:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips \
-d '{"addresses": ["203.0.113.10"]}'
{"added": [], "requeued": ["203.0.113.10"], "reordered": [], "skipped_in_progress": []}
Адрес в requeued означает, что он был в терминальном состоянии
(done/failed) и его перезапустили: AttemptNumber увеличился,
RetryCount обнулён, предыдущий OverallResult сброшен, адрес снова
queued и будет обработан на общих основаниях. Никакой особой обработки
для уже проверенных адресов не требуется — тот же вызов безопасно
принимает список из новых и уже проверенных адресов одновременно;
единственное, что метод не сделает — не запустит вторую параллельную
проверку адреса, который прямо сейчас уже проверяется (такой адрес
вернётся в skipped_in_progress, см.
«Добавление новых IP в очередь»).
Принудительная остановка проверки
Если проверка адреса зависла дольше ожидаемого либо просто больше не
актуальна, не дожидайтесь истечения checking_window_seconds —
остановите её сразу:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/203.0.113.10/cancel
Работает из любого состояния, кроме уже терминального (done/failed
вернут 409 — отменять нечего). Если на момент отмены был привязан
Floating IP — он отвязывается (best-effort, как и при обычном завершении
проверки), владевший валидатор освобождается и снова становится idle.
Итог записывается как OverallResult: "cancelled" (в State: "failed"),
и виден в истории адреса наравне с обычными результатами:
curl -s http://<control-api>:8080/api/v1/admin/ips/203.0.113.10 | python3 -m json.tool
Чтобы позже всё же проверить этот адрес — используйте «Повторную проверку адреса» выше, она одинаково работает и для отменённых, и для обычно завершённых адресов.
Удаление адресов из очереди
Отличие от отмены (cancel) выше: удаление безвозвратно убирает адрес
из очереди. Cancel переводит адрес в failed/cancelled и сохраняет
запись как историю — её видно в очереди и в деталях адреса. Delete
физически стирает строку ip_queue: адрес полностью исчезает из
/ips//ips/{ip}, восстановить именно эту строку нельзя. Накопленная
история проверок при этом не теряется — она остаётся в
реестре (/registry/{ip}) и видна
там даже после удаления адреса из очереди. Если нужно просто остановить
зависшую проверку, но сохранить её результат прямо в очереди —
используйте
«Принудительную остановку проверки»
выше, а не удаление.
Удалить один адрес (работает из любого состояния, включая активно проверяемое — Floating IP при этом отвязывается, а владевший валидатор освобождается, точно так же, как при cancel):
curl -s -X DELETE http://<control-api>:8080/api/v1/admin/ips/203.0.113.10
Удалить список адресов одним вызовом (неизвестные адреса просто
попадают в not_found, не считаются ошибкой):
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/delete \
-d '{"addresses": ["203.0.113.10", "203.0.113.11"]}'
Полностью очистить очередь — самая опасная операция, удаляет вообще всё, включая адреса, которые прямо сейчас проверяются:
curl -s -X POST http://<control-api>:8080/api/v1/admin/ips/clear
В admin-dashboard то же самое доступно на странице /ips: чекбоксы у
каждой строки + кнопка «Удалить выбранные» для точечного/массового
удаления, кнопка «Удалить» в каждой строке, и отдельная кнопка «Очистить
всё» — каждая с подтверждением, явно предупреждающим о необратимости
(см. DASHBOARD.md).
Пауза перед self-check (fip_settle_seconds)
Как только Floating IP привязывается к валидатору, control-api по умолчанию сразу же позволяет агенту начать self-check — а data plane OpenStack может не успеть в этот же момент реально начать пропускать трафик через только что привязанный адрес, из-за чего self-check ложно проваливается по причине, не связанной с самой привязкой. Если это наблюдается на вашем стенде, задайте паузу между привязкой FIP и началом self-check:
curl -s -X PUT http://<control-api>:8080/api/v1/admin/config/orchestrator \
-d '{"fip_settle_seconds": 5}'
То же самое — на странице /settings дашборда. 0 (по умолчанию) — без
паузы. Пока пауза не истекла, адрес уже в состоянии
awaiting_self_check, но GET /assignment агенту продолжает отдавать
204 (агент просто ждёт следующего опроса, доработок на его стороне не
требуется); в дашборде на /ips такой адрес в это время помечен бейджем
«прогрев FIP» вместо обычного статуса.
Значение обязано оставлять запас внутри лизинга адреса:
fip_settle_seconds + self_check_timeout_seconds должно быть меньше
orchestrator.lease_ttl_seconds — иначе пауза плюс сам self-check не
влезут в лизинг, адрес не успеет пройти self-check до истечения
lease_ttl_seconds и будет вечно возвращаться в очередь через
sweepExpiredLeases. Попытка задать такое значение отклоняется 400, не
обрезается молча.
Частые проблемы и что с ними делать
Валидатор долго висит в unreachable.
Проверьте сетевую связность ВМ-валидатора до control-api (порт из
server.listen_addr) и что процесс validator-agent вообще запущен
(systemctl status validator-agent, journalctl -u validator-agent).
Адрес не выходит из awaiting_self_check (статус не меняется вообще).
Если это длится всего несколько секунд и на стенде настроена
пауза перед self-check
(fip_settle_seconds) — это ожидаемое поведение, не сбой: адрес
сознательно придерживается, прежде чем агенту разрешат начать проверку.
Проблема — если статус не меняется значительно дольше этой паузы. Тогда
дело обычно в self-check: он запрашивает внешние (вне облака) сервисы из
self_check.ip_echo_urls в конфиге валидатора (по умолчанию
api.ipify.org, ifconfig.me) — если у ВМ-валидатора нет исходящего
доступа в интернет к этим адресам, запрос не проходит вообще, и агент
даже не может сообщить результат control-api (ни успешный, ни
неуспешный) — тогда статус реально зависает до истечения
orchestrator.lease_ttl_seconds, после чего адрес возвращается в
queued и цикл повторяется. Проверьте связность до
self_check.ip_echo_urls прямо с ВМ-валидатора (curl -s https://api.ipify.org) и логи journalctl -u validator-agent на предмет
ip echo request failed.
Адрес постоянно проваливает self-check (не зависает, а именно
возвращается в очередь снова и снова).
Смотрите events по адресу (GET /api/v1/admin/ips/{ip}) — в детали
события self_check_result будет указан обнаруженный исходящий адрес.
Если он не совпадает с ожидаемым — вероятно, на ВМ-валидаторе есть другой
маршрут наружу (не через назначенный Floating IP), либо привязка FIP на
стороне OpenStack не применилась. Проверьте вручную в OpenStack
(openstack floating ip show <адрес>), что port_id совпадает с портом
валидатора. Обратите внимание: адрес для сравнения обязан быть вне
облака (см. self_check.ip_echo_urls) — запрос к чему-либо внутри
проекта (в том числе к самому control-api, если он в той же внутренней
сети) покажет приватный адрес валидатора независимо от того, правильно
ли привязан FIP, и всегда будет давать ложный провал.
Площадка (site-N) никогда не отчитывается по конкретному IP.
Сперва проверьте статус самой площадки — GET /api/v1/admin/config/sites, поле state. unregistered или
unreachable означает, что prober на этой площадке вообще не на связи
с control-api (см. «Состояния площадки» выше) —
проверьте, что процесс запущен и его site_id в конфиге совпадает с
site_id в конфиге control-api, и что площадка имеет сетевой доступ до
control-api. Если статус idle (площадка на связи), а конкретный IP
всё равно не получает отметку о завершении — проверьте отдельно
связность до проверяемого адреса (входящий трафик на 22/80/443/8080 +
ICMP — это отдельная связность от связи с control-api, см.
SETUP.md).
Много адресов зависло в checking дольше ожидаемого.
Это нормально, если ещё не истёк orchestrator.checking_window_seconds —
агрегация ждёт либо полного набора ответов, либо истечения окна. Если
адрес завис заметно дольше окна — проверьте, что фоновый цикл control-api
вообще работает (смотрите journalctl -u control-api на предмет ошибок
в sweep checking window).
Нужно посмотреть на данные "из первых рук", в обход API.
control-api использует SQLite, файл — по пути database.path из
конфига. Можно (только для чтения, на той же машине, где крутится
control-api) открыть его sqlite3 в режиме WAL — это безопасно для
чтения параллельно с работающим процессом:
sqlite3 /var/lib/cloud-ip-validator/control-api.db \
"select ip_address, state, overall_result from ip_queue order by sequence"
Не редактируйте эту базу вручную во время работы control-api — это может рассинхронизировать состояние с реальными привязками Floating IP в OpenStack.