control-api: every route now carries a mandatory access level (admin / agent / open) in a route table. All /api/v1/admin/* require the admin token; the write calls of validator-agent and prober (self-check, events, results, complete) require a separate static agent token; register, heartbeat and fetching the assignment stay open. Tokens come from env vars, are compared in constant time and never logged. An empty token leaves that level open with a startup warning (backward compatible). validator-agent / prober: apiclient sends the agent token only to control-api. admin-dashboard: login/password (from env) with a stateless HMAC session cookie, Origin-based CSRF check, per-IP brute-force throttle, HX-Redirect for htmx polls, logout in the sidebar; the dashboard calls control-api with the admin token. Login page layout fixed after review. Also: env plumbing in docker-compose/rxprod-compose/systemd/config examples, e2e script with token assertions, tests, docs (API, SETUP, USAGE, DASHBOARD, README), plan and review under docs/changes/, bin/ rebuilt with new SHA256SUMS. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
53 KiB
Подготовка стенда и первичная инициализация
Документ описывает, как подготовить конфигурацию и запустить стенд с нуля
— от чистой машины до работающего control-api, валидаторов и проберов.
Бинарники брать не обязательно из исходников: в репозитории уже лежат
готовые сборки для Linux x86_64 (bin/) — это самый быстрый путь к
развёртыванию, см. «Получение бинарников». Если
нужно просто быстро посмотреть систему в работе без реального OpenStack —
сразу переходите к разделу
«Быстрая проверка без OpenStack».
Ниже описано развёртывание как systemd-юнитами (по умолчанию), так и
Docker-контейнерами — оба пути равноправны и описаны исчерпывающе, см.
«Развёртывание в Docker».
Содержание
- Компоненты и роли машин
- Требования
- Получение бинарников
- Быстрая проверка без OpenStack (offline-режим)
- Подготовка конфигурации для реального стенда
- Развёртывание control-api
- Развёртывание validator-agent на ВМ-валидаторах
- Развёртывание prober на внешних площадках
- Развёртывание admin-dashboard
- Развёртывание в Docker
- Проверка после запуска
- Сетевые доступы
Компоненты и роли машин
| Компонент | Где запускается | Кол-во |
|---|---|---|
control-api |
Отдельная управляющая машина/ВМ с доступом к OpenStack API | 1 (без HA) |
validator-agent |
Каждая ВМ-валидатор в сервисном проекте облака | по числу валидаторов |
prober |
По одному на каждой из внешних тестовых площадок | 3 (по числу площадок) |
admin-dashboard |
Любая машина с сетевым доступом до control-api (опционально) |
0 или 1 |
control-api — единственный компонент с состоянием (SQLite). Валидаторы,
проберы и admin-dashboard не хранят локального состояния и полностью
управляются через опрос control-api (см. API.md,
DASHBOARD.md). admin-dashboard не обязателен — вся его
функциональность доступна и через curl напрямую по API.
Требования
- Целевые серверы (control-api, валидаторы, площадки) — Linux x86_64 (amd64). Для этой платформы в репозитории уже лежат готовые бинарники (см. ниже) — устанавливать Go на целевые машины не требуется.
- Go 1.22+ нужен только если вы пересобираете бинарники из исходников (проверено на Go 1.26) — например, для другой платформы (arm64, другая ОС) или после изменения кода.
- Для
control-apiв боевом режиме (openstack.mode: real) — учётная запись OpenStack с правами на чтение/изменение floating IP (Neutron) в сервисном проекте, и заранее выделенные (allocated) floating IP — инструмент их не создаёт, только привязывает/отвязывает существующие. - Для
validator-agentиprober— возможность отправлять ICMP echo (нужен root либо capabilityCAP_NET_RAW, см. юниты systemd). curl,sqlite3(опционально, для ручной инспекции БД) на машине с control-api пригодятся для диагностики.
Получение бинарников
Вариант A: готовые бинарники из репозитория (рекомендуется)
В директории bin/ репозитория уже лежат четыре готовых бинарника —
собирать их на целевых серверах не нужно, разворачивание сразу
начинается с копирования и запуска (раздел
«Развёртывание control-api» и далее).
bin/
├── control-api # ~11 МБ
├── validator-agent # ~7 МБ
├── prober # ~7 МБ
├── admin-dashboard # ~9 МБ (опционален, см. DASHBOARD.md)
└── SHA256SUMS
Характеристики сборки:
- Платформа:
GOOS=linux GOARCH=amd64. CGO_ENABLED=0— статическая линковка, без cgo (используется чистый Go-драйвер SQLitemodernc.org/sqlite). Дополнительные.so-библиотеки и конкретная версия glibc на целевом сервере не требуются.- Собрано флагами
-trimpath -ldflags="-s -w"(без путей сборки и отладочной информации — компактнее и без утечки информации о машине сборки).
Убедиться в отсутствии динамических зависимостей и целостности файлов после переноса на целевой сервер:
ldd bin/control-api # => "not a dynamic executable"
file bin/control-api # => ELF 64-bit LSB executable, x86-64, statically linked
# После scp/rsync на целевой сервер — проверить, что файлы не повреждены:
sha256sum -c bin/SHA256SUMS
Перенос на целевые серверы, например:
scp bin/control-api control-api-host:/tmp/
scp bin/validator-agent validator-host-01:/tmp/
scp bin/prober probe-site-1:/tmp/
scp bin/admin-dashboard dashboard-host:/tmp/ # опционально
Если целевая платформа отличается от linux/amd64 (например, ВМ на arm64) — готовые бинарники не подойдут, используйте вариант B.
Вариант B: сборка из исходников
Из корня репозитория:
export PATH=$PATH:/usr/local/go/bin # если go не в PATH
export CGO_ENABLED=0 GOOS=linux GOARCH=amd64 # поменяйте GOARCH для другой платформы
go build -trimpath -ldflags="-s -w" -o bin/control-api ./cmd/control-api
go build -trimpath -ldflags="-s -w" -o bin/validator-agent ./cmd/validator-agent
go build -trimpath -ldflags="-s -w" -o bin/prober ./cmd/prober
go build -trimpath -ldflags="-s -w" -o bin/admin-dashboard ./cmd/admin-dashboard
Каждый бинарник самодостаточен — скопируйте нужный файл на соответствующую машину (control-api → управляющая машина, validator-agent → каждый валидатор, prober → каждая площадка, admin-dashboard → опционально, любая машина с доступом до control-api).
Убедиться, что всё собирается и юнит-тесты проходят:
go build ./... && go test ./...
Пересобирайте из исходников и обновляйте bin/ с зафиксированными
SHA256SUMS, если меняли код — готовые бинарники в репозитории не
обновляются автоматически:
sha256sum bin/control-api bin/validator-agent bin/prober bin/admin-dashboard | sed 's#bin/##' > bin/SHA256SUMS
Быстрая проверка без OpenStack (offline-режим)
Прежде чем разворачивать реальный стенд, рекомендуется убедиться, что всё собирается и работает корректно на локальной машине — без облака и внешних площадок:
scripts/run-local-e2e.sh
Скрипт сам поднимает control-api (в режиме openstack.mode: mock),
одного validator-agent и трёх проберов как локальные процессы, прогоняет
один тестовый адрес через полный цикл проверки и печатает итоговый
результат. Подробности — в docs/LOCAL_E2E.md. Это же
хороший способ разобраться в поведении системы перед первым боевым
запуском.
Подготовка конфигурации для реального стенда
Все три компонента конфигурируются YAML-файлами. Шаблоны лежат в
configs/*.example.yaml — скопируйте их и заполните под ваш стенд.
1. control-api.yaml
cp configs/control-api.example.yaml /etc/cloud-ip-validator/control-api.yaml
Что обязательно нужно заполнить:
validators— список валидаторов, у каждогоvalidator_id(произвольное имя, должно совпадать сvalidator_idв конфиге соответствующегоvalidator-agent) иos_port_id— ID Neutron-порта основного сетевого интерфейса ВМ-валидатора (узнать:openstack port list --server <имя-ВМ>или в веб-консоли облака).sites— внешние площадки,site_id+index(число слотов не ограничено; в примере ниже — три).site_idдолжен совпадать сsite_idв конфиге соответствующегоprober.ip_addresses— список публичных IPv4-адресов на проверку, в порядке обработки. Адреса должны существовать в сервисном проекте как уже выделенные (allocated) floating IP — инструмент их не создаёт.openstack.mode: "real"и*_envполя — имена переменных окружения, из которых будут прочитаны реальные учётные данные (сами значения в этот файл не пишутся, см. следующий пункт).targetsиcheck_types— при необходимости смените набор целей для egress-проверок (по умолчанию — hub.docker.com, github.com, packages.ubuntu.com) или включитеssh(по умолчанию выключен).
validators,sites,targetsиcheck_typesчитаются из этого файла только один раз — при самом первом старте против пустой базы данных (bootstrap). После этого все последующие изменения этих четырёх секций вносятся через/api/v1/admin/config/*, а не правкой YAML — см. API.md и USAGE.md.ip_addresses— исключение, он остаётся YAML + аддитивным добавлением при каждом старте (плюсPOST /api/v1/admin/ipsдля управления очередью без рестарта).
2. Переменные окружения для OpenStack
Учётные данные передаются только через переменные окружения — никогда
через YAML. Есть два режима аутентификации, выбираются полем
openstack.auth_method в control-api.yaml:
auth_method: "token" (по умолчанию) — вы предоставляете уже
готовый, заранее scoped на нужный проект токен (например, полученный
через openstack --os-project-id=<id> token issue). Control-api
использует его как есть, ни на что не обменивает. Просто, но токен не
самообновляется: когда он истечёт, control-api начнёт получать ошибки
от OpenStack API, пока вы вручную не перевыпустите токен, не обновите
переменную и не перезапустите процесс.
install -m 0600 -o cloud-ip-validator -g cloud-ip-validator /dev/null /etc/cloud-ip-validator/control-api.env
cat >> /etc/cloud-ip-validator/control-api.env <<'EOF'
OS_AUTH_URL=https://keystone.example.com:5000/v3
OS_TOKEN=<токен администратора, уже scoped на сервисный проект>
OS_PROJECT_ID=<id сервисного проекта>
OS_REGION_NAME=<регион>
EOF
auth_method: "password" — вы предоставляете обычные логин/пароль;
control-api сам получает токен через Keystone и автоматически
переполучает новый при истечении текущего (весь срок жизни процесса, без
ручного вмешательства). Компромисс — в файле окружения лежит долгоживущий
пароль, а не токен.
install -m 0600 -o cloud-ip-validator -g cloud-ip-validator /dev/null /etc/cloud-ip-validator/control-api.env
cat >> /etc/cloud-ip-validator/control-api.env <<'EOF'
OS_AUTH_URL=https://keystone.example.com:5000/v3
OS_PROJECT_ID=<id сервисного проекта>
OS_REGION_NAME=<регион>
OS_USERNAME=<логин>
OS_USER_DOMAIN_NAME=<домен пользователя>
OS_PASSWORD=<пароль>
EOF
и в control-api.yaml: openstack.auth_method: "password".
Опционально в обоих режимах — OS_INTERFACE (public/internal/admin,
по умолчанию public): выбирает, какой из адресов Neutron в каталоге
сервисов использовать, если у эндпоинта их несколько.
Имена переменных должны совпадать с тем, что указано в
control-api.yaml в секции openstack (auth_url_env, token_env и
т.д.) — в шаблоне это ровно OS_AUTH_URL, OS_PROJECT_ID,
OS_REGION_NAME, OS_INTERFACE, и, в зависимости от режима, либо
OS_TOKEN, либо OS_USERNAME/OS_USER_DOMAIN_NAME/OS_PASSWORD — это
стандартные имена, принятые в python-openstackclient/RC-файлах, менять их
обычно не требуется.
3. validator-agent.yaml (свой на каждом валидаторе)
cp configs/validator-agent.example.yaml /etc/cloud-ip-validator/validator-agent.yaml
Обязательно поменять:
validator_id— должен совпадать с одним изvalidators[].validator_idв конфиге control-api.control_api_url— адрес, по которому эта ВМ достучится до control-api.
4. prober.yaml (свой на каждой площадке)
cp configs/prober.example.yaml /etc/cloud-ip-validator/prober.yaml
Обязательно поменять:
site_id— должен совпадать с одним изsites[].site_idв конфиге control-api (для трёх площадок — три разных файла сsite-1,site-2,site-3или как вы их назвали).control_api_url— адрес control-api, доступный с площадки (обычно через интернет — площадки внешние).
5. Аутентификация: токены и пароль дашборда
Доступ к API и дашборду защищается секретами из переменных окружения (в YAML значения не хранятся; в *.yaml — только имена
переменных, и менять их нужно редко). Токены генерируются случайными: openssl rand -hex 32. Схема доступа к методам —
API.md.
| Где | Переменная | Назначение |
|---|---|---|
| control-api | CONTROL_API_ADMIN_TOKEN |
токен администратора: закрывает /api/v1/admin/* |
| control-api | CONTROL_API_AGENT_TOKEN |
токен агентов: закрывает запись результатов валидаторов и проберов |
| validator-agent, prober | CONTROL_API_AGENT_TOKEN |
тот же токен агентов (отправляется как Bearer) |
| admin-dashboard | ADMIN_DASHBOARD_CONTROL_API_TOKEN |
токен администратора control-api (то же значение, что CONTROL_API_ADMIN_TOKEN) |
| admin-dashboard | ADMIN_DASHBOARD_USERNAME, ADMIN_DASHBOARD_PASSWORD |
логин и пароль единственного администратора дашборда |
| admin-dashboard | ADMIN_DASHBOARD_SESSION_SECRET |
ключ подписи cookie-сессии (случайная строка; без него — случайный на каждый запуск, сессии сбрасываются рестартом) |
- Пустое значение = защита выключена. Токен не задан — соответствующий уровень API открыт; логин/пароль не заданы — дашборд открыт. В обоих случаях в логе при старте — предупреждение. Это сделано для обратной совместимости; на реальном стенде задайте всё.
- systemd: добавьте переменные в
/etc/cloud-ip-validator/<компонент>.env(подключаетсяEnvironmentFile=, файлchmod 600). Docker: переменные из.env(см..env.example). - Ключи
auth.*и*_token_envв YAML меняют только имена переменных; время жизни сессии —auth.session_ttl_minutesдашборда (по умолчанию 480). - Токены и пароль передаются открытым текстом, если перед сервисами нет TLS: публикуйте API и дашборд через reverse-proxy с TLS.
Порядок включения без простоя (особенно когда валидаторы и пробер на других машинах):
- обновите бинарники всех компонентов — токены ещё не заданы, всё работает как раньше;
- задайте
CONTROL_API_AGENT_TOKENна валидаторах и проберах,ADMIN_DASHBOARD_*на дашборде и перезапустите их; - последним задайте
CONTROL_API_ADMIN_TOKENиCONTROL_API_AGENT_TOKENна control-api и перезапустите его. Если включить токен агентов на control-api раньше, чем он появится у валидатора или пробера, их результаты будут получать401и проверки не завершатся. Ротация токена — та же последовательность с новым значением.
Развёртывание control-api
useradd --system --no-create-home --shell /usr/sbin/nologin cloud-ip-validator
mkdir -p /var/lib/cloud-ip-validator /etc/cloud-ip-validator
chown cloud-ip-validator:cloud-ip-validator /var/lib/cloud-ip-validator
cp bin/control-api /usr/local/bin/control-api
cp deploy/systemd/control-api.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now control-api
Первичная инициализация базы данных происходит автоматически — при
первом старте control-api создаёт файл SQLite по пути database.path
из конфига (миграции схемы применяются один раз каждая, повторные запуски
— no-op). Отдельной команды "init db" не требуется.
При каждом старте control-api также:
- Bootstrap-once для
validators/sites/targets/check_types. YAML применяется только если соответствующая таблица в БД сейчас пуста — то есть только на самом первом старте против чистой базы. Как только в таблице появилась хотя бы одна строка (через этот bootstrap либо через/api/v1/admin/config/*, см. API.md), YAML для этой секции больше не перечитывается ни при одном последующем рестарте — источник истины переключается на БД. Это осознанное отличие от более ранних версий, гдеvalidatorsиз YAML переприменялись при каждом рестарте: теперь правки, сделанные через admin API (например, сменаos_port_idвалидатора), переживают рестарт вместо того, чтобы тихо откатываться. - Всегда аддитивно добавляет в очередь все адреса из
ip_addresses, которых там ещё нет (уже обработанные ранее адреса повторно не добавляются и не сбрасываются — см. USAGE.md). Это отдельный, не завязанный на bootstrap-once путь — не путайте сPOST /api/v1/admin/ips, который умеет то же самое (и ещё принудительный повтор уже проверенных адресов) без перезапуска.
Проверить, что процесс поднялся:
curl -s http://localhost:8080/healthz
# {"ok":true}
journalctl -u control-api -f
Развёртывание validator-agent на ВМ-валидаторах
Повторить на каждой ВМ-валидаторе:
cp bin/validator-agent /usr/local/bin/validator-agent
cp deploy/systemd/validator-agent.service /etc/systemd/system/
mkdir -p /etc/cloud-ip-validator
# скопировать сюда заполненный validator-agent.yaml с уникальным validator_id
systemctl daemon-reload
systemctl enable --now validator-agent
journalctl -u validator-agent -f
Юнит выдаёт процессу capability CAP_NET_RAW (без root) — она нужна для
отправки ICMP echo в рамках проверок.
Развёртывание prober на внешних площадках
Аналогично, на каждой из трёх площадок:
cp bin/prober /usr/local/bin/prober
cp deploy/systemd/prober.service /etc/systemd/system/
mkdir -p /etc/cloud-ip-validator
# скопировать сюда prober.yaml с уникальным site_id для этой площадки
systemctl daemon-reload
systemctl enable --now prober
journalctl -u prober -f
Развёртывание admin-dashboard
Опционально — вся его функциональность доступна и через curl напрямую
по API (см. API.md). На любой машине с сетевым доступом до
control-api:
cp bin/admin-dashboard /usr/local/bin/admin-dashboard
cp deploy/systemd/admin-dashboard.service /etc/systemd/system/
mkdir -p /etc/cloud-ip-validator
cp configs/admin-dashboard.example.yaml /etc/cloud-ip-validator/admin-dashboard.yaml
# отредактировать control_api.base_url под ваш стенд
systemctl daemon-reload
systemctl enable --now admin-dashboard
journalctl -u admin-dashboard -f
Открыть http://<admin-dashboard>:8090/ в браузере. Подробнее о
страницах и о том, что дашборд может (и не может) — в
DASHBOARD.md.
Развёртывание в Docker
Альтернатива всем systemd-разделам выше — те же четыре бинарника, но
упакованные в Docker-образы. Не обязательно выбирать одно или другое —
можно, например, гонять control-api под systemd, а prober на внешней
площадке — в контейнере; главное, чтобы они видели друг друга по сети (см.
«Сетевые доступы»).
Три способа запуска, по возрастанию гранулярности:
docker compose, один хост — быстрее всего увидеть всё в работе (mock-режим, без OpenStack).docker compose, несколько хостов — реальный стенд, тот же compose с профилями решает, какие сервисы поднимать на каждой машине.docker build/docker runпо одному компоненту — точечная отладка одного сервиса без всего compose-стека.
Требования для Docker-развёртывания
- Docker Engine и Compose plugin v2 (
docker compose version; отдельная утилитаdocker-composev1 не поддерживается — команды ниже используют синтаксисdocker compose ...). - Хост под управлением Docker — linux/amd64 рекомендуется. На другой
архитектуре (например, Apple Silicon Mac) обязательно указывать
--platform linux/amd64при сборке/запуске (вdocker-compose.ymlон уже прописан для каждого сервиса) — иначе получится образ, чей слой ОС собран под архитектуру хоста, а внутри лежит скопированный amd64-бинарник (см. ниже), и контейнер не запустится (exec format error). - Важное отличие от типичных Go-проектов: Dockerfile'ы здесь ничего не
компилируют.
deploy/docker/<компонент>/Dockerfile— это простоFROM alpine:3.20+COPY bin/<компонент> ...(иногда плюс шаблон конфига иdocker-entrypoint.sh— см. ниже). Собственно Go-сборка происходит заранее, отдельно от Docker (см. «Получение бинарников») — то есть передdocker build/docker compose buildв каталогеbin/уже должны лежать актуальные бинарники под linux/amd64: либо уже закоммиченные в репозитории (вариант A — тогда простоdocker compose up -d --buildсработает сразу), либо свежесобранные после правок кода (вариант B —go build ..., см. «Обновление образов после изменения кода»). - Все команды
docker build/docker compose buildзапускаются из каталогаdeploy/docker/(или с указанием контекста../..) — контекст сборки каждого сервиса вdocker-compose.ymlэто корень репозитория (context: ../..), поэтому на каждом хосте, где вы собираете образы локально, должен быть выкачан весь репозиторий (git clone), а не только каталогdeploy/docker/.
Вариант 1: docker compose, один хост (знакомство/dev, mock-режим)
Самый быстрый способ увидеть всю систему целиком работающей — без реального облака, все четыре сервиса на одной машине:
cd deploy/docker
cp .env.example .env
cp control-api/control-api.docker.example.yaml control-api/control-api.docker.yaml
docker compose up -d --build
Что при этом происходит:
docker-compose.yml(общие определения сервисов) объединяется сdocker-compose.override.yml— dev-надстройка, которуюdocker composeподхватывает автоматически, без явного-f(именно её в проде заменяют наdocker-compose.prod.yml, см. следующий раздел)..env(см..env.example) выставляетCOMPOSE_PROFILES=control-plane, dashboard,prober,validator— включены все четыре профиля/сервиса сразу, то есть весь стенд поднимается на одной машине. В реальном распределённом развёртывании на каждом хосте включают только нужные профили (см. вариант 2).control-api.docker.yaml, скопированный изcontrol-api.docker.example.yaml, — заранее заполненный конфиг сopenstack.mode: "mock"и тестовымиvalidator_01/site-1/тремя IP-заглушками (203.0.113.10-12) — совпадает с дефолтамиPROBER_SITE_ID/VALIDATOR_AGENT_VALIDATOR_IDв.env.example, так чтоprober/validator-agentсразу находят себя в конфиге control-api и успешно регистрируются. Реального OpenStack и учётных данных для этого режима не нужно.- Dev-надстройка пробрасывает порты наружу (
8080— control-api,8090— admin-dashboard) и добавляетdepends_on: control-api: condition: service_healthyдляadmin-dashboard/prober/validator-agent— они не пытаются зарегистрироваться раньше, чемcontrol-apiпройдёт свой healthcheck (GET /healthz, настроен в базовомdocker-compose.yml). -d— фоновый режим;--build— собрать образы изDockerfile, а не пытаться скачать несуществующие в реестре.
Проверить, что всё поднялось:
docker compose ps
curl -s http://localhost:8080/healthz
curl -s http://localhost:8080/api/v1/admin/status | python3 -m json.tool
Открыть дашборд в браузере: http://localhost:8090/.
Посмотреть логи (в т.ч. чтобы убедиться, что prober/validator-agent
успешно зарегистрировались):
docker compose logs -f control-api
docker compose logs -f prober validator-agent admin-dashboard
Остановить:
docker compose down # контейнеры + сеть; volume с БД (cloud-ip-validator-db) остаётся
docker compose down -v # то же самое + удалить volume с БД (полный сброс состояния)
Вариант 2: docker compose, реальный стенд по нескольким хостам
Та же пара файлов (docker-compose.yml + профили), но с
docker-compose.prod.yml вместо dev-надстройки и реальным конфигом
control-api вместо mock. docker-compose.prod.yml не публикует порты
наружу напрямую (стенд предполагается за reverse-proxy/файрволом) и не
использует depends_on (на разнесённом по хостам стенде профиль
control-plane может вообще отсутствовать в compose-вызове конкретного
хоста, и depends_on на неактивный профиль — ошибка конфигурации
docker compose); вместо этого у каждого сервиса restart: unless-stopped — prober/validator-agent при недоступном на старте
control-api просто падают и перезапускаются политикой рестарта, пока
control-api не станет доступен.
На каждом хосте — свой .env.prod (по образцу .env.prod.example) с
COMPOSE_PROFILES, задающим, какие сервисы именно этот хост поднимает:
| Хост | COMPOSE_PROFILES |
|---|---|
| Управляющая машина (control-api + опционально дашборд) | control-plane,dashboard |
| Внешняя площадка (prober) | prober |
| ВМ-валидатор (validator-agent) | validator |
| Всё на одной машине (как вариант 1, но прод-режим) | control-plane,dashboard,prober,validator |
На управляющей машине (control-plane/dashboard):
git clone <repo> && cd <repo>/deploy/docker # если ещё не склонировано
cp .env.prod.example .env.prod
# отредактировать .env.prod:
# COMPOSE_PROFILES=control-plane,dashboard
# CONTROL_API_CONFIG_PATH=/etc/cloud-ip-validator/control-api.yaml (реальный конфиг,
# заполненный по образцу configs/control-api.example.yaml — см.
# "Подготовка конфигурации для реального стенда" выше)
# OS_AUTH_URL / OS_PROJECT_ID / OS_REGION_NAME / OS_TOKEN (или пароль-режим) —
# те же переменные, что и для systemd-развёртывания, см. раздел 2 выше
# ADMIN_DASHBOARD_CONTROL_API_URL=http://control-api:8080 (тот же docker-сеть,
# менять не нужно, если admin-dashboard в том же compose-вызове)
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d --build
Конфиг control-api при этом монтируется файлом (путь из
CONTROL_API_CONFIG_PATH), а не генерируется из переменных окружения, как
у остальных трёх компонентов — у него в конфиге списки (validators,
sites, targets, ip_addresses), которые не выразить одной
переменной. Данные (control-api.db) живут в volume cloud-ip-validator-db
— переживают docker compose down (без -v) и пересоздание контейнера.
На каждой внешней площадке (prober):
git clone <repo> && cd <repo>/deploy/docker
cp .env.prod.example .env.prod
# COMPOSE_PROFILES=prober
# PROBER_SITE_ID=<site_id, уже зарегистрированный на control-api через
# PUT /api/v1/admin/config/sites/{index} — см. USAGE.md>
# PROBER_CONTROL_API_URL=<реальный, сетевой доступный адрес control-api,
# НЕ http://control-api:8080 — тот хост есть только внутри compose-сети
# самой управляющей машины>
docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env.prod up -d --build
На каждой ВМ-валидаторе (validator) — аналогично, с
COMPOSE_PROFILES=validator, VALIDATOR_AGENT_VALIDATOR_ID (должен
совпадать с validators[].validator_id в конфиге control-api) и
VALIDATOR_AGENT_CONTROL_API_URL.
Полный список переменных окружения для каждого сервиса, с дефолтами — см. таблицы в разделе «Вариант 3» ниже или прямо в
docker-compose.yml/.env.prod.example.
Вариант 3: docker build/docker run по одному компоненту
Для точечной пересборки/перезапуска одного сервиса напрямую, без compose
— например, обновить только prober на одной площадке, не трогая
остальной стенд. Все команды — из корня репозитория.
prober:
docker build --platform linux/amd64 -t cloud-ip-validator-prober -f deploy/docker/prober/Dockerfile .
docker run -d --platform linux/amd64 --cap-add NET_RAW --name prober \
-e PROBER_SITE_ID=<site_id> \
-e PROBER_CONTROL_API_URL=<http://control-api-host:port> \
cloud-ip-validator-prober
| Переменная | Обязательна | По умолчанию |
|---|---|---|
PROBER_SITE_ID |
да | — (должен быть зарегистрирован в control-api, PUT /api/v1/admin/config/sites/{index}) |
PROBER_CONTROL_API_URL |
да | — |
PROBER_POLL_INTERVAL_SECONDS |
нет | 5 |
PROBER_TCP_TIMEOUT_SECONDS |
нет | 5 |
PROBER_ICMP_TIMEOUT_SECONDS |
нет | 5 |
PROBER_ICMP_COUNT |
нет | 3 |
--cap-add NET_RAW обязателен — без него ICMP-проверки не заработают.
admin-dashboard:
docker build --platform linux/amd64 -t cloud-ip-validator-admin-dashboard -f deploy/docker/admin-dashboard/Dockerfile .
docker run -d --platform linux/amd64 -p 8090:8090 --name admin-dashboard \
-e ADMIN_DASHBOARD_CONTROL_API_URL=<http://control-api-host:port> \
cloud-ip-validator-admin-dashboard
| Переменная | Обязательна | По умолчанию |
|---|---|---|
ADMIN_DASHBOARD_CONTROL_API_URL |
да | — |
ADMIN_DASHBOARD_LISTEN_ADDR |
нет | :8090 |
ADMIN_DASHBOARD_CONTROL_API_TIMEOUT_SECONDS |
нет | 10 |
ADMIN_DASHBOARD_LAST_COMPLETED_COUNT |
нет | 20 |
ADMIN_DASHBOARD_POLL_INTERVAL_SECONDS |
нет | 5 |
control-api:
docker build --platform linux/amd64 -t cloud-ip-validator-control-api -f deploy/docker/control-api/Dockerfile .
docker run -d --platform linux/amd64 -p 8080:8080 --name control-api \
-v /path/to/control-api.yaml:/etc/cloud-ip-validator/control-api.yaml:ro \
-v cloud-ip-validator-db:/var/lib/cloud-ip-validator \
-e OS_AUTH_URL=<keystone_url> \
-e OS_PROJECT_ID=<project_id> \
-e OS_REGION_NAME=<region> \
-e OS_TOKEN=<token> \
cloud-ip-validator-control-api
В отличие от остальных трёх компонентов, у control-api конфиг не
генерируется из переменных окружения — он содержит списки
(validators/sites/targets/ip_addresses) и имена переменных для
OpenStack-креденшлов, которые проще смонтировать файлом (по образцу
configs/control-api.example.yaml, см.
«Подготовка конфигурации»). Сама
база данных — отдельным volume (cloud-ip-validator-db) для
персистентности между перезапусками контейнера. При auth_method: password вместо OS_TOKEN передайте OS_USERNAME,
OS_USER_DOMAIN_NAME, OS_PASSWORD; для openstack.mode: mock
переменные OpenStack не нужны вовсе.
validator-agent:
docker build --platform linux/amd64 -t cloud-ip-validator-validator-agent -f deploy/docker/validator-agent/Dockerfile .
docker run -d --platform linux/amd64 --cap-add NET_RAW --name validator-agent \
-e VALIDATOR_AGENT_VALIDATOR_ID=<validator_id> \
-e VALIDATOR_AGENT_CONTROL_API_URL=<http://control-api-host:port> \
cloud-ip-validator-validator-agent
| Переменная | Обязательна | По умолчанию |
|---|---|---|
VALIDATOR_AGENT_VALIDATOR_ID |
да | — (должен совпадать с validators[].validator_id в конфиге control-api) |
VALIDATOR_AGENT_CONTROL_API_URL |
да | — |
VALIDATOR_AGENT_POLL_INTERVAL_SECONDS |
нет | 5 |
VALIDATOR_AGENT_SELF_CHECK_TIMEOUT_SECONDS |
нет | 10 |
VALIDATOR_AGENT_HTTPS_TIMEOUT_SECONDS |
нет | 10 |
VALIDATOR_AGENT_ICMP_TIMEOUT_SECONDS |
нет | 5 |
VALIDATOR_AGENT_ICMP_COUNT |
нет | 3 |
VALIDATOR_AGENT_SSH_ENABLED |
нет | false |
VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS |
нет | 5 |
--cap-add NET_RAW обязателен для ICMP-проверок, как и у prober.
self_check.ip_echo_urls в переменные не вынесен — при отсутствии в
конфиге агент сам подставляет дефолт (api.ipify.org, ifconfig.me);
свой список задавайте через смонтированный конфиг вместо шаблона, если
нужно переопределить.
Обновление образов после изменения кода
Как уже сказано в требованиях — образы ничего не компилируют, а копируют
bin/<компонент>. После правок кода:
# 1. пересобрать бинарники (из корня репозитория)
export PATH=$PATH:/usr/local/go/bin
export CGO_ENABLED=0 GOOS=linux GOARCH=amd64
go build -trimpath -ldflags="-s -w" -o bin/control-api ./cmd/control-api
go build -trimpath -ldflags="-s -w" -o bin/validator-agent ./cmd/validator-agent
go build -trimpath -ldflags="-s -w" -o bin/prober ./cmd/prober
go build -trimpath -ldflags="-s -w" -o bin/admin-dashboard ./cmd/admin-dashboard
# 2. пересобрать и перезапустить образы — Docker сам заметит, что
# содержимое bin/ изменилось (COPY инвалидирует кэш слоя по хэшу
# файла), отдельный --no-cache не нужен
cd deploy/docker
docker compose up -d --build
Для варианта 3 (docker build/docker run вручную) — то же самое: шаг 1
не меняется, дальше docker build ... (та же команда, что и при первой
сборке) и docker rm -f <имя> && docker run ... (или docker restart,
если менялись только переменные окружения, а не сам бинарник/образ).
Диагностика Docker-развёртывания
- Контейнер сразу падает, в логах
exec format error— образ собран не под ту архитектуру: пересоберите с явным--platform linux/amd64(или, если хост реально не amd64 — соберите бинарники под нужную архитектуру,GOARCH=arm64и т.д., см. «Вариант B: сборка из исходников», и уберите--platform/platform:из образов). prober/validator-agent/admin-dashboardне стартуют, пишут... is required— не задана обязательная переменная окружения (PROBER_SITE_ID,VALIDATOR_AGENT_VALIDATOR_ID,ADMIN_DASHBOARD_CONTROL_API_URL) — см. таблицы выше/.env.prober/validator-agentв цикле рестартов — обычно означает, чтоcontrol-apiнедоступен по указанному URL, либоsite_id/validator_idне зарегистрирован в конфиге control-api. Смотритеdocker compose logs -f <сервис>— при ошибке регистрации процесс завершается с ненулевым кодом и в dev/prod оверлеях перезапускается политикойrestart.docker compose configругается наdepends_onдля неактивного профиля — это спецификаdocker-compose.override.yml(dev): он подходит только когда все четыре профиля включены на одном хосте. Для разнесённого по хостам стенда используйтеdocker-compose.prod.yml(в нёмdepends_onнет намеренно, см. вариант 2).- После
docker compose down -vпропали данные —-vудаляет и volume с базой control-api (cloud-ip-validator-db); без-vvolume сохраняется между запусками. - Общие команды диагностики:
docker compose ps,docker compose logs -f [сервис],docker compose exec control-api sh,curl -s http://localhost:8080/healthz.
Проверка после запуска
После того как control-api, все валидаторы и все три пробера запущены:
curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool
Ожидаемая картина сразу после старта: часть адресов в состоянии queued,
часть уже переходит в assigning_fip/awaiting_self_check/checking по
мере того, как освобождаются валидаторы. Через некоторое время появляются
записи в done/failed. Подробнее о том, как читать этот вывод и что
делать дальше — в USAGE.md.
Также стоит убедиться, что все валидаторы видны и не «зависли»:
curl -s http://<control-api>:8080/api/v1/admin/validators | python3 -m json.tool
Все зарегистрированные валидаторы должны рано или поздно оказываться в
состоянии idle (между заданиями) — если валидатор надолго застрял в
unreachable, проверьте сетевую связность до control-api и логи агента
(journalctl -u validator-agent).
Сетевые доступы
Минимально необходимая связность:
validator-agent→control-api: TCP, порт изserver.listen_addr(обычно 8080).prober(на каждой из 3 площадок) →control-api: тот же порт, обычно через интернет.prober→ адрес, который в данный момент проверяется (динамический, меняется по ходу работы очереди): TCP 22/80/443/8080 + ICMP — собственно и есть проверяемый трафик, его нельзя заранее ограничить одним IP.validator-agent→ интернет: HTTPS/ICMP до целей изtargetsконфига (по умолчанию hub.docker.com, github.com, packages.ubuntu.com) — именно через floating IP, который в данный момент привязан к валидатору.validator-agent→ внешние IP-echo сервисы изself_check.ip_echo_urls(по умолчаниюapi.ipify.org,ifconfig.me) — обязательно вне облака: это и есть механизм self-check (см. DIAGRAMS.md). Если валидатор не может достучаться ни до одного из этих адресов, self-check никогда не пройдёт и IP будет бесконечно возвращаться в очередь — см. USAGE.md.control-api→ OpenStack Keystone/Neutron API (OS_AUTH_URLи далее по каталогу сервисов).admin-dashboard→control-api: тот же порт (server.listen_addr), адрес задаётся вcontrol_api.base_urlконфига дашборда.- Оператор (браузер) →
admin-dashboard: порт изserver.listen_addrдашборда (по умолчанию 8090).
API control-api сейчас не аутентифицирован (см. предупреждение в начале
API.md) — то же самое верно и для admin-dashboard, который
это API оборачивает. Ограничивайте доступ к обоим портам на уровне
сети/firewall: к control-api — теми хостами, где реально работают
валидаторы, проберы и сам дашборд; к дашборду — теми, кому разрешено
администрировать стенд.