# Подготовка стенда и первичная инициализация Документ описывает, как подготовить конфигурацию и запустить стенд с нуля — от чистой машины до работающего control-api, валидаторов и проберов. Бинарники брать не обязательно из исходников: в репозитории уже лежат готовые сборки для Linux x86_64 (`bin/`) — это самый быстрый путь к развёртыванию, см. [«Получение бинарников»](#получение-бинарников). Если нужно просто быстро посмотреть систему в работе без реального OpenStack — сразу переходите к разделу [«Быстрая проверка без OpenStack»](#быстрая-проверка-без-openstack-offline-режим). Ниже описано развёртывание как systemd-юнитами (по умолчанию), так и Docker-контейнерами — оба пути равноправны и описаны исчерпывающе, см. [«Развёртывание в Docker»](#развёртывание-в-docker). ## Содержание - [Компоненты и роли машин](#компоненты-и-роли-машин) - [Требования](#требования) - [Получение бинарников](#получение-бинарников) - [Вариант A: готовые бинарники из репозитория (рекомендуется)](#вариант-a-готовые-бинарники-из-репозитория-рекомендуется) - [Вариант B: сборка из исходников](#вариант-b-сборка-из-исходников) - [Быстрая проверка без OpenStack (offline-режим)](#быстрая-проверка-без-openstack-offline-режим) - [Подготовка конфигурации для реального стенда](#подготовка-конфигурации-для-реального-стенда) - [Развёртывание control-api](#развёртывание-control-api) - [Развёртывание validator-agent на ВМ-валидаторах](#развёртывание-validator-agent-на-вм-валидаторах) - [Развёртывание prober на внешних площадках](#развёртывание-prober-на-внешних-площадках) - [Развёртывание admin-dashboard](#развёртывание-admin-dashboard) - [Развёртывание в 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-развёртывания) - [Проверка после запуска](#проверка-после-запуска) - [Сетевые доступы](#сетевые-доступы) ## Компоненты и роли машин | Компонент | Где запускается | Кол-во | |---|---|---| | `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](API.md), [DASHBOARD.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 либо capability `CAP_NET_RAW`, см. юниты systemd). - `curl`, `sqlite3` (опционально, для ручной инспекции БД) на машине с control-api пригодятся для диагностики. ## Получение бинарников ### Вариант A: готовые бинарники из репозитория (рекомендуется) В директории `bin/` репозитория уже лежат четыре готовых бинарника — собирать их на целевых серверах не нужно, разворачивание сразу начинается с копирования и запуска (раздел [«Развёртывание control-api»](#развёртывание-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-драйвер 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/ scp bin/admin-dashboard dashboard-host:/tmp/ # опционально ``` > Если целевая платформа отличается от linux/amd64 (например, ВМ на > arm64) — готовые бинарники не подойдут, используйте > [вариант B](#вариант-b-сборка-из-исходников). ### Вариант B: сборка из исходников Из корня репозитория: ```bash 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). Убедиться, что всё собирается и юнит-тесты проходят: ```bash go build ./... && go test ./... ``` Пересобирайте из исходников и обновляйте `bin/` с зафиксированными `SHA256SUMS`, если меняли код — готовые бинарники в репозитории **не обновляются автоматически**: ```bash sha256sum bin/control-api bin/validator-agent bin/prober bin/admin-dashboard | sed 's#bin/##' > bin/SHA256SUMS ``` ## Быстрая проверка без 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 <имя-ВМ>` или в веб-консоли облака). - **`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](API.md#управление-очередью-и-конфигурацией) и > [USAGE.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= token issue`). Control-api использует его как есть, ни на что не обменивает. Просто, но токен не самообновляется: когда он истечёт, `control-api` начнёт получать ошибки от OpenStack API, пока вы вручную не перевыпустите токен, не обновите переменную и не перезапустите процесс. ```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_TOKEN=<токен администратора, уже scoped на сервисный проект> OS_PROJECT_ID= OS_REGION_NAME=<регион> EOF ``` **`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= 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` (свой на каждом валидаторе) ```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, доступный с площадки (обычно через интернет — площадки внешние). ### 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` и проверки не завершатся. Ротация токена — та же последовательность с новым значением. ## Развёртывание 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` из конфига (миграции схемы применяются один раз каждая, повторные запуски — no-op). Отдельной команды "init db" не требуется. При каждом старте control-api также: 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`, который умеет то же самое (и ещё принудительный повтор уже проверенных адресов) без перезапуска. Проверить, что процесс поднялся: ```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 ``` ## Развёртывание 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://:8090/` в браузере. Подробнее о страницах и о том, что дашборд может (и не может) — в [DASHBOARD.md](DASHBOARD.md). ## Развёртывание в 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 && cd /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 && cd /deploy/docker cp .env.prod.example .env.prod # COMPOSE_PROFILES=prober # PROBER_SITE_ID= # 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= \ -e PROBER_CONTROL_API_URL= \ 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= \ 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= \ -e OS_PROJECT_ID= \ -e OS_REGION_NAME= \ -e OS_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= \ -e VALIDATOR_AGENT_CONTROL_API_URL= \ 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` и `self_check.methods` в переменные не вынесены — при отсутствии в конфиге агент сам подставляет дефолты (`api.ipify.org`, `ifconfig.me` и `methods: [ip_echo]`); свои значения задавайте через смонтированный конфиг вместо шаблона, если нужно переопределить. `methods` — способы самопроверки в порядке приоритета (`ip_echo`, `control_api`), достаточно подтверждения любым. Способ `control_api` спрашивает у control-api, с какого адреса он видит валидатора (`GET /agents/{id}/observed-ip`); при внешнем размещении control-api рекомендуется `[control_api, ip_echo]`. Ограничение: если control-api достижим из облака по внутренней сети, он увидит приватный адрес валидатора и этот способ всегда даст несовпадение — используйте `ip_echo`. ### Обновление образов после изменения кода Как уже сказано в требованиях — образы ничего не компилируют, а копируют `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`, если менялись только переменные окружения, а не сам бинарник/образ). Для массового обновления валидаторов (git-клон, сборка образа на хосте, замена контейнера, проверка регистрации) есть Ansible-сценарий: [`deploy/ansible/`](../deploy/ansible/README.md). ### Диагностика 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`. ## Проверка после запуска После того как control-api, все валидаторы и все три пробера запущены: ```bash curl -s http://: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://: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 способом `ip_echo` (при `self_check.methods` с `control_api` достаточно ещё и доступа к control-api по внешней сети; см. [DIAGRAMS.md](DIAGRAMS.md#2-поток-данных-от-валидатора-к-целевому-серверу-egress-проверка)). Если валидатор не может достучаться ни до одного из этих адресов, self-check никогда не пройдёт и IP будет бесконечно возвращаться в очередь — см. [USAGE.md](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](API.md)) — то же самое верно и для `admin-dashboard`, который это API оборачивает. Ограничивайте доступ к обоим портам на уровне сети/firewall: к control-api — теми хостами, где реально работают валидаторы, проберы и сам дашборд; к дашборду — теми, кому разрешено администрировать стенд.