Add an exhaustive Docker deployment walkthrough to docs/SETUP.md

deploy/docker/RUN.txt only documented docker build/run per component with
no explanation of docker-compose, multi-host profiles, or how the images
relate to bin/*. Adds a full "Развёртывание в Docker" section to SETUP.md
covering requirements, single-host and multi-host docker-compose flows,
per-component docker build/run (absorbing RUN.txt's content with more
context), rebuilding images after code changes, and troubleshooting.
README.md now points here as the primary source, with RUN.txt kept as a
quick command reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ayurishchevandClaude Sonnet 5 committed 2026-09-23 10:23:17 +03:00
1 parent 582b44f314
commit 3b6b2d04c9
2 files changed
+361 -4

No files matched your search

+8 -4
View File
@@ -20,13 +20,13 @@ HTTP API control-api.
| Документ | Для чего |
|---|---|
| [docs/SETUP.md](docs/SETUP.md) | Развёртывание из готовых бинарников (`bin/`) или сборка из исходников, конфигурация, первый запуск стенда — с нуля |
| [docs/SETUP.md](docs/SETUP.md) | Развёртывание из готовых бинарников (`bin/`) или сборка из исходников, конфигурация, первый запуск стенда — с нуля, включая исчерпывающую инструкцию по Docker/docker-compose |
| [docs/USAGE.md](docs/USAGE.md) | Повседневная работа: постановка адресов в очередь, наблюдение за статусом, разбор результатов |
| [docs/API.md](docs/API.md) | Спецификация HTTP API control-api и примеры запросов (curl) |
| [docs/DASHBOARD.md](docs/DASHBOARD.md) | Браузерная админ-панель (`admin-dashboard`) — то же самое API, но графически |
| [docs/DIAGRAMS.md](docs/DIAGRAMS.md) | Диаграммы потоков данных: control plane, поток проверки до целевого сервера, поток телеметрии |
| [docs/LOCAL_E2E.md](docs/LOCAL_E2E.md) | Полностью офлайн-прогон всей системы одним скриптом — без реального облака и интернета |
| [deploy/docker/RUN.txt](deploy/docker/RUN.txt) | Сборка и запуск каждого компонента в Docker: команды `docker build`/`docker run`, переменные окружения |
| [deploy/docker/RUN.txt](deploy/docker/RUN.txt) | Краткая шпаргалка команд `docker build`/`docker run` по компонентам (без пояснений) — подробный пошаговый разбор, включая docker-compose, см. в [docs/SETUP.md](docs/SETUP.md#развёртывание-в-docker) |
| [docs/CONTROL_DATA_PLANE.html](docs/CONTROL_DATA_PLANE.html) | Презентационные схемы control plane и data plane для микросервисного (docker-compose) деплоя — открыть в браузере |
## Быстрый старт (60 секунд, без OpenStack)
@@ -61,8 +61,12 @@ curl -s http://<control-api>:8080/api/v1/admin/status | python3 -m json.tool
## Развёртывание в Docker
Все четыре компонента можно собрать и запустить как отдельные Docker-образы
вместо systemd-юнитов — Dockerfile'ы лежат в `deploy/docker/<компонент>/`,
полные команды сборки/запуска и список переменных окружения — в
вместо systemd-юнитов — Dockerfile'ы лежат в `deploy/docker/<компонент>/`.
Исчерпывающая пошаговая инструкция (docker-compose для одного хоста и для
распределённого по нескольким хостам стенда, точечный `docker build`/`docker
run` по компоненту, обновление образов, диагностика) —
[docs/SETUP.md → «Развёртывание в Docker»](docs/SETUP.md#развёртывание-в-docker);
краткая шпаргалка тех же команд — в
[deploy/docker/RUN.txt](deploy/docker/RUN.txt).
- `prober`, `validator-agent`, `admin-dashboard` — конфиг генерируется
+353
View File
@@ -8,6 +8,9 @@
нужно просто быстро посмотреть систему в работе без реального OpenStack —
сразу переходите к разделу
[«Быстрая проверка без OpenStack»](#быстрая-проверка-без-openstack-offline-режим).
Ниже описано развёртывание как systemd-юнитами (по умолчанию), так и
Docker-контейнерами — оба пути равноправны и описаны исчерпывающе, см.
[«Развёртывание в Docker»](#развёртывание-в-docker).
## Содержание
@@ -22,6 +25,13 @@
- [Развёртывание 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-развёртывания)
- [Проверка после запуска](#проверка-после-запуска)
- [Сетевые доступы](#сетевые-доступы)
@@ -380,6 +390,349 @@ journalctl -u admin-dashboard -f
страницах и о том, что дашборд может (и не может) — в
[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 <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`.
## Проверка после запуска
После того как control-api, все валидаторы и все три пробера запущены: