2026-08-21 07:34:45 +03:00
# Подготовка стенда и первичная инициализация
2026-08-21 08:11:58 +03:00
Документ описывает, как подготовить конфигурацию и запустить стенд с нуля
— от чистой машины до работающего control-api, валидаторов и проберов.
Бинарники брать не обязательно из исходников: в репозитории уже лежат
готовые сборки для Linux x86_64 (`bin/` ) — это самый быстрый путь к
развёртыванию, см. [«Получение бинарников» ](#получение-бинарников ). Если
нужно просто быстро посмотреть систему в работе без реального OpenStack —
сразу переходите к разделу
2026-08-21 07:34:45 +03:00
[«Быстрая проверка без OpenStack» ](#быстрая-проверка-без-openstack-offline-режим ).
2026-09-23 10:23:17 +03:00
Ниже описано развёртывание как systemd-юнитами (по умолчанию), так и
Docker-контейнерами — оба пути равноправны и описаны исчерпывающе, см.
[«Развёртывание в Docker» ](#развёртывание-в-docker ).
2026-08-21 07:34:45 +03:00
## Содержание
- [Компоненты и роли машин ](#компоненты-и-роли-машин )
- [Требования ](#требования )
2026-08-21 08:11:58 +03:00
- [Получение бинарников ](#получение-бинарников )
- [Вариант A: готовые бинарники из репозитория (рекомендуется) ](#вариант-a-готовые-бинарники-из-репозитория-рекомендуется )
- [Вариант B: сборка из исходников ](#вариант-b-сборка-из-исходников )
2026-08-21 07:34:45 +03:00
- [Быстрая проверка без OpenStack (offline-режим) ](#быстрая-проверка-без-openstack-offline-режим )
- [Подготовка конфигурации для реального стенда ](#подготовка-конфигурации-для-реального-стенда )
- [Развёртывание control-api ](#развёртывание-control-api )
- [Развёртывание validator-agent на ВМ-валидаторах ](#развёртывание-validator-agent-на-вм-валидаторах )
- [Развёртывание prober на внешних площадках ](#развёртывание-prober-на-внешних-площадках )
2026-08-23 20:39:22 +03:00
- [Развёртывание admin-dashboard ](#развёртывание-admin-dashboard )
2026-09-23 10:23:17 +03:00
- [Развёртывание в Docker ](#развёртывание-в-docker )
- [Требования для Docker-развёртывания ](#требования-для-docker-развёртывания )
- [Вариант 1: docker compose, один хост (знакомство/dev, mock-режим) ](#вариант-1-docker-compose-один-хост-знакомстводev-mock-режим )
- [Вариант 2: docker compose, реальный стенд по нескольким хостам ](#вариант-2-docker-compose-реальный-стенд-по-нескольким-хостам )
- [Вариант 3: docker build/docker run по одному компоненту ](#вариант-3-docker-builddocker-run-по-одному-компоненту )
- [Обновление образов после изменения кода ](#обновление-образов-после-изменения-кода )
- [Диагностика Docker-развёртывания ](#диагностика-docker-развёртывания )
2026-08-21 07:34:45 +03:00
- [Проверка после запуска ](#проверка-после-запуска )
- [Сетевые доступы ](#сетевые-доступы )
## Компоненты и роли машин
| Компонент | Где запускается | Кол-во |
|---|---|---|
| `control-api` | Отдельная управляющая машина/ВМ с доступом к OpenStack API | 1 (без HA) |
| `validator-agent` | Каждая ВМ-валидатор в сервисном проекте облака | по числу валидаторов |
| `prober` | По одному на каждой из внешних тестовых площадок | 3 (по числу площадок) |
2026-08-23 20:39:22 +03:00
| `admin-dashboard` | Любая машина с сетевым доступом до `control-api` (опционально) | 0 или 1 |
2026-08-21 07:34:45 +03:00
2026-08-23 20:39:22 +03:00
`control-api` — единственный компонент с состоянием (SQLite). Валидаторы,
проберы и `admin-dashboard` не хранят локального состояния и полностью
управляются через опрос control-api (см. [API.md ](API.md ),
[DASHBOARD.md ](DASHBOARD.md )). `admin-dashboard` не обязателен — вся его
функциональность доступна и через `curl` напрямую по API.
2026-08-21 07:34:45 +03:00
## Требования
2026-08-21 08:11:58 +03:00
- Целевые серверы (control-api, валидаторы, площадки) — **Linux
x86_64 (amd64)**. Для этой платформы в репозитории уже лежат готовые
бинарники (см. ниже) — устанавливать Go на целевые машины не требуется.
- **Go 1.22+** нужен только если вы пересобираете бинарники из
исходников (проверено на Go 1.26) — например, для другой платформы
(arm64, другая ОС) или после изменения кода.
2026-08-21 07:34:45 +03:00
- Для `control-api` в боевом режиме (`openstack.mode: real` ) — учётная
запись OpenStack с правами на чтение/изменение floating IP (Neutron)
в сервисном проекте, и заранее выделенные (allocated) floating IP —
инструмент их **не создаёт** , только привязывает/отвязывает
существующие.
- Для `validator-agent` и `prober` — возможность отправлять ICMP echo
(нужен root либо capability `CAP_NET_RAW` , см. юниты systemd).
- `curl` , `sqlite3` (опционально, для ручной инспекции БД) на машине с
control-api пригодятся для диагностики.
2026-08-21 08:11:58 +03:00
## Получение бинарников
### Вариант A: готовые бинарники из репозитория (рекомендуется)
2026-08-23 20:39:22 +03:00
В директории `bin/` репозитория уже лежат четыре готовых бинарника —
2026-08-21 08:11:58 +03:00
собирать их на целевых серверах не нужно, разворачивание сразу
начинается с копирования и запуска (раздел
[«Развёртывание control-api» ](#развёртывание-control-api ) и далее).
```
bin/
├── control-api # ~11 МБ
├── validator-agent # ~7 МБ
├── prober # ~7 МБ
2026-08-23 20:39:22 +03:00
├── admin-dashboard # ~9 МБ (опционален, см. DASHBOARD.md)
2026-08-21 08:11:58 +03:00
└── SHA256SUMS
```
Характеристики сборки:
- Платформа: `GOOS=linux GOARCH=amd64` .
- `CGO_ENABLED=0` — статическая линковка, без cgo (используется чистый
Go-драйвер SQLite `modernc.org/sqlite` ). Дополнительные `.so` -библиотеки
и конкретная версия glibc на целевом сервере **не требуются** .
- Собрано флагами `-trimpath -ldflags="-s -w"` (без путей сборки и
отладочной информации — компактнее и без утечки информации о машине
сборки).
Убедиться в отсутствии динамических зависимостей и целостности файлов
после переноса на целевой сервер:
```bash
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
```
Перенос на целевые серверы, например:
```bash
scp bin/control-api control-api-host:/tmp/
scp bin/validator-agent validator-host-01:/tmp/
scp bin/prober probe-site-1:/tmp/
2026-08-23 20:39:22 +03:00
scp bin/admin-dashboard dashboard-host:/tmp/ # опционально
2026-08-21 08:11:58 +03:00
```
> Если целевая платформа отличается от linux/amd64 (например, ВМ на
> arm64) — готовые бинарники не подойдут, используйте
> [вариант B](#вариант-b-сборка-из-исходников).
### Вариант B: сборка из исходников
2026-08-21 07:34:45 +03:00
Из корня репозитория:
```bash
export PATH = $PATH :/usr/local/go/bin # если go не в PATH
2026-08-21 08:11:58 +03:00
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
2026-08-23 20:39:22 +03:00
go build -trimpath -ldflags= "-s -w" -o bin/admin-dashboard ./cmd/admin-dashboard
2026-08-21 07:34:45 +03:00
```
Каждый бинарник самодостаточен — скопируйте нужный файл на
соответствующую машину (control-api → управляющая машина, validator-agent
2026-08-23 20:39:22 +03:00
→ каждый валидатор, prober → каждая площадка, admin-dashboard →
опционально, любая машина с доступом до control-api).
2026-08-21 07:34:45 +03:00
Убедиться, что всё собирается и юнит-тесты проходят:
```bash
go build ./... && go test ./...
```
2026-08-21 08:11:58 +03:00
Пересобирайте из исходников и обновляйте `bin/` с зафиксированными
`SHA256SUMS` , если меняли код — готовые бинарники в репозитории **не
обновляются автоматически**:
```bash
2026-08-23 20:39:22 +03:00
sha256sum bin/control-api bin/validator-agent bin/prober bin/admin-dashboard | sed 's#bin/##' > bin/SHA256SUMS
2026-08-21 08:11:58 +03:00
```
2026-08-21 07:34:45 +03:00
## Быстрая проверка без OpenStack (offline-режим)
Прежде чем разворачивать реальный стенд, рекомендуется убедиться, что всё
собирается и работает корректно на локальной машине — без облака и
внешних площадок:
```bash
scripts/run-local-e2e.sh
```
Скрипт сам поднимает control-api (в режиме `openstack.mode: mock` ),
одного validator-agent и трёх проберов как локальные процессы, прогоняет
один тестовый адрес через полный цикл проверки и печатает итоговый
результат. Подробности — в [docs/LOCAL_E2E.md ](LOCAL_E2E.md ). Это же
хороший способ разобраться в поведении системы перед первым боевым
запуском.
## Подготовка конфигурации для реального стенда
Все три компонента конфигурируются YAML-файлами. Шаблоны лежат в
`configs/*.example.yaml` — скопируйте их и заполните под ваш стенд.
### 1. `control-api.yaml`
```bash
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 <имя-ВМ>` или в веб-консоли облака).
2026-08-26 20:47:54 +03:00
- **`sites` ** — внешние площадки, `site_id` + `index` (число слотов не
ограничено; в примере ниже — три). `site_id` должен совпадать с
`site_id` в конфиге соответствующего `prober` .
2026-08-21 07:34:45 +03:00
- **`ip_addresses` ** — список публичных IPv4-адресов на проверку, **в
порядке обработки**. Адреса должны существовать в сервисном проекте как
уже выделенные (allocated) floating IP — инструмент их не создаёт.
- **`openstack.mode: "real"` ** и `*_env` поля — имена переменных
окружения, из которых будут прочитаны реальные учётные данные (сами
значения в этот файл **не пишутся** , см. следующий пункт).
- **`targets` ** и ** `check_types` ** — при необходимости смените набор
целей для egress-проверок (по умолчанию — hub.docker.com, github.com,
packages.ubuntu.com) или включите `ssh` (по умолчанию выключен).
2026-08-23 20:39:22 +03:00
> `validators`, `sites`, `targets` и `check_types` читаются из этого файла
> только один раз — при самом первом старте против пустой базы данных
> (bootstrap). После этого все последующие изменения этих четырёх секций
> вносятся через `/api/v1/admin/config/*`, а не правкой YAML — см.
> [API.md](API.md#управление-очередью-и-конфигурацией) и
> [USAGE.md](USAGE.md#управление-валидаторами). `ip_addresses` — исключение,
> он остаётся YAML + аддитивным добавлением при каждом старте (плюс
> `POST /api/v1/admin/ips` для управления очередью без рестарта).
2026-08-21 07:34:45 +03:00
### 2. Переменные окружения для OpenStack
Учётные данные передаются **только** через переменные окружения — никогда
2026-08-21 09:58:08 +03:00
через YAML. Есть два режима аутентификации, выбираются полем
`openstack.auth_method` в `control-api.yaml` :
** `auth_method: "token"` (по умолчанию)** — вы предоставляете уже
готовый, заранее scoped на нужный проект токен (например, полученный
через `openstack --os-project-id=<id> token issue` ). Control-api
использует его как есть, ни на что не обменивает. Просто, но токен не
самообновляется: когда он истечёт, `control-api` начнёт получать ошибки
от OpenStack API, пока вы вручную не перевыпустите токен, не обновите
переменную и не перезапустите процесс.
2026-08-21 07:34:45 +03:00
```bash
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
2026-08-21 09:58:08 +03:00
OS_TOKEN=<токен администратора, уже scoped на сервисный проект>
2026-08-21 07:34:45 +03:00
OS_PROJECT_ID=<id сервисного проекта>
OS_REGION_NAME=<регион>
EOF
```
2026-08-21 09:58:08 +03:00
** `auth_method: "password"` ** — вы предоставляете обычные логин/пароль;
control-api сам получает токен через Keystone и автоматически
переполучает новый при истечении текущего (весь срок жизни процесса, без
ручного вмешательства). Компромисс — в файле окружения лежит долгоживущий
пароль, а не токен.
```bash
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 в каталоге
сервисов использовать, если у эндпоинта их несколько.
2026-08-21 07:34:45 +03:00
Имена переменных должны совпадать с тем, что указано в
`control-api.yaml` в секции `openstack` (`auth_url_env` , `token_env` и
2026-08-21 09:58:08 +03:00
т.д.) — в шаблоне это ровно `OS_AUTH_URL` , `OS_PROJECT_ID` ,
`OS_REGION_NAME` , `OS_INTERFACE` , и, в зависимости от режима, либо
`OS_TOKEN` , либо `OS_USERNAME` /`OS_USER_DOMAIN_NAME` /`OS_PASSWORD` — это
стандартные имена, принятые в python-openstackclient/RC-файлах, менять их
обычно не требуется.
2026-08-21 07:34:45 +03:00
### 3. `validator-agent.yaml` (свой на каждом валидаторе)
```bash
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` (свой на каждой площадке)
```bash
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, доступный с площадки (обычно
через интернет — площадки внешние).
2026-10-01 11:35:24 +03:00
### 5. Аутентификация: токены и пароль дашборда
Доступ к API и дашборду защищается секретами из переменных окружения (в YAML значения не хранятся; в `*.yaml` — только *имена*
переменных, и менять их нужно редко). Токены генерируются случайными: `openssl rand -hex 32` . Схема доступа к методам —
[API.md ](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.
**Порядок включения без простоя** (особенно когда валидаторы и пробер на других машинах):
1. обновите бинарники всех компонентов — токены ещё не заданы, всё работает как раньше;
2. задайте `CONTROL_API_AGENT_TOKEN` на валидаторах и проберах, `ADMIN_DASHBOARD_*` на дашборде и перезапустите их;
3. **последним** задайте `CONTROL_API_ADMIN_TOKEN` и `CONTROL_API_AGENT_TOKEN` на control-api и перезапустите его.
Если включить токен агентов на control-api раньше, чем он появится у валидатора или пробера, их результаты будут получать `401` и проверки не завершатся.
Ротация токена — та же последовательность с новым значением.
2026-08-21 07:34:45 +03:00
## Развёртывание control-api
```bash
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`
2026-08-23 20:39:22 +03:00
из конфига (миграции схемы применяются один раз каждая, повторные запуски
— no-op). Отдельной команды "init db" не требуется.
2026-08-21 07:34:45 +03:00
При каждом старте control-api также:
2026-08-23 20:39:22 +03:00
1. **Bootstrap-once для `validators`/`sites`/`targets`/`check_types`.**
YAML применяется **только если соответствующая таблица в БД сейчас
пуста** — то есть только на самом первом старте против чистой базы.
Как только в таблице появилась хотя бы одна строка (через этот
bootstrap либо через `/api/v1/admin/config/*` , см.
[API.md ](API.md#управление-очередью-и-конфигурацией )), YAML для этой
секции больше не перечитывается ни при одном последующем рестарте —
источник истины переключается на БД. Это осознанное отличие от более
ранних версий, где `validators` из YAML переприменялись при каждом
рестарте: теперь правки, сделанные через admin API (например, смена
`os_port_id` валидатора), переживают рестарт вместо того, чтобы
тихо откатываться.
2. **Всегда аддитивно** добавляет в очередь все адреса из
`ip_addresses` , которых там ещё нет (уже обработанные ранее адреса
повторно не добавляются и не сбрасываются — см.
[USAGE.md ](USAGE.md#добавление-новых-ip-в-очередь )). Это отдельный,
не завязанный на bootstrap-once путь — не путайте с
`POST /api/v1/admin/ips` , который умеет то же самое (и ещё
принудительный повтор уже проверенных адресов) без перезапуска.
2026-08-21 07:34:45 +03:00
Проверить, что процесс поднялся:
```bash
curl -s http://localhost:8080/healthz
# {"ok":true}
journalctl -u control-api -f
```
## Развёртывание validator-agent на ВМ-валидаторах
Повторить на каждой ВМ-валидаторе:
```bash
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 на внешних площадках
Аналогично, на каждой из трёх площадок:
```bash
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
```
2026-08-23 20:39:22 +03:00
## Развёртывание admin-dashboard
Опционально — вся его функциональность доступна и через `curl` напрямую
по API (см. [API.md ](API.md )). На любой машине с сетевым доступом до
`control-api` :
```bash
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 ](DASHBOARD.md ).
2026-09-23 10:23:17 +03:00
## Развёртывание в Docker
Альтернатива всем systemd-разделам выше — те же четыре бинарника, но
упакованные в Docker-образы. Не обязательно выбирать одно или другое —
можно, например, гонять `control-api` под systemd, а `prober` на внешней
площадке — в контейнере; главное, чтобы они видели друг друга по сети (см.
[«Сетевые доступы» ](#сетевые-доступы )).
Три способа запуска, по возрастанию гранулярности:
1. ** `docker compose` , один хост** — быстрее всего увидеть всё в работе
(mock-режим, без OpenStack).
2. ** `docker compose` , несколько хостов** — реальный стенд, тот же compose
с профилями решает, какие сервисы поднимать на каждой машине.
3. ** `docker build` /`docker run` по одному компоненту** — точечная отладка
одного сервиса без всего compose-стека.
### Требования для Docker-развёртывания
- Docker Engine и Compose plugin v2 (`docker compose version` ; отдельная
утилита `docker-compose` v1 не поддерживается — команды ниже используют
синтаксис `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-режим)
Самый быстрый способ увидеть всю систему целиком работающей — без
реального облака, все четыре сервиса на одной машине:
```bash
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`, а не
пытаться скачать несуществующие в реестре.
Проверить, что всё поднялось:
` ``bash
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`
успешно зарегистрировались):
` ``bash
docker compose logs -f control-api
docker compose logs -f prober validator-agent admin-dashboard
` ``
Остановить:
` ``bash
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`):
` ``bash
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`):
` ``bash
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»](#вариант-3-docker-builddocker-run-по-одному-компоненту)
> ниже или прямо в ` docker-compose.yml`/` .env.prod.example`.
### Вариант 3: docker build/docker run по одному компоненту
Для точечной пересборки/перезапуска одного сервиса напрямую, без compose
— например, обновить только ` prober` на одной площадке, не трогая
остальной стенд. Все команды — из корня репозитория.
**prober:**
` ``bash
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:**
` ``bash
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:**
` ``bash
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:**
` ``bash
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/<компонент>`. После правок кода:
` ``bash
# 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: сборка из исходников»](#вариант-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](#вариант-2-docker-compose-реальный-стенд-по-нескольким-хостам)).
- **После ` docker compose down -v` пропали данные** — ` -v` удаляет и volume
с базой control-api (` cloud-ip-validator-db`); без ` -v` volume
сохраняется между запусками.
- Общие команды диагностики: ` docker compose ps`, ` docker compose logs -f
[сервис]`, ` docker compose exec control-api sh`, ` curl -s
http://localhost:8080/healthz`.
2026-08-21 07:34:45 +03:00
## Проверка после запуска
После того как control-api, все валидаторы и все три пробера запущены:
` ``bash
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](USAGE.md).
Также стоит убедиться, что все валидаторы видны и не «зависли»:
` ``bash
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, который в данный момент привязан к валидатору.
2026-08-21 11:04:49 +03:00
- ` validator-agent` → внешние IP-echo сервисы из ` self_check.ip_echo_urls`
(по умолчанию ` api.ipify.org`, ` ifconfig.me`) — **обязательно вне
облака**: это и есть механизм self-check (см.
[DIAGRAMS.md](DIAGRAMS.md#2-поток-данных-от-валидатора-к-целевому-серверу-egress-проверка)).
Если валидатор не может достучаться ни до одного из этих адресов,
self-check никогда не пройдёт и IP будет бесконечно возвращаться в
очередь — см. [USAGE.md](USAGE.md#частые-проблемы-и-что-с-ними-делать).
2026-08-21 07:34:45 +03:00
- ` control-api` → OpenStack Keystone/Neutron API (` OS_AUTH_URL` и далее по
каталогу сервисов).
2026-08-23 20:39:22 +03:00
- ` admin-dashboard` → ` control-api`: тот же порт (` server.listen_addr`),
адрес задаётся в ` control_api.base_url` конфига дашборда.
- Оператор (браузер) → ` admin-dashboard`: порт из ` server.listen_addr`
дашборда (по умолчанию 8090).
2026-08-21 07:34:45 +03:00
API control-api сейчас не аутентифицирован (см. предупреждение в начале
2026-08-23 20:39:22 +03:00
[API.md](API.md)) — то же самое верно и для ` admin-dashboard`, который
это API оборачивает. Ограничивайте доступ к обоим портам на уровне
сети/firewall: к control-api — теми хостами, где реально работают
валидаторы, проберы и сам дашборд; к дашборду — теми, кому разрешено
администрировать стенд.