# Доставка validator-agent на валидаторы (Ansible) Сценарий запускается с jump-хоста и на каждой ВМ-валидаторе: обновляет git-клон в `/opt/cloud-ip-validator`, **собирает образ на самом хосте**, останавливает и удаляет старый контейнер `cloud-ip-validator-validator-agent` и поднимает на его месте новый. Параметры запуска агента вынесены в env-файл. Порядок безопасен: сначала проверки, обновление кода и сборка образа, и только потом замена контейнера. Если что-то упало до замены, старый контейнер продолжает работать. Простой валидатора — секунды (`stop` + `rm` + `run`). ## Требования | Где | Что | |---|---| | jump-хост (Debian 13) | `apt install ansible-core`; SSH-доступ по ключу ко всем валидаторам | | валидаторы | Docker, git, клон репозитория в `/opt/cloud-ip-validator`, `sudo` без пароля для SSH-пользователя, доступ к Gitea и Docker Hub (`alpine:3.20`), архитектура x86_64 | Только модули `ansible.builtin`: Python Docker SDK и дополнительные коллекции на валидаторах не нужны. Go на валидаторах не нужен: образ копирует закоммиченный `bin/validator-agent` (его сумма проверяется по `bin/SHA256SUMS`). ## Подготовка (один раз) ```bash cd deploy/ansible cp env/validator-agent.env.example env/validator-agent.env chmod 600 env/validator-agent.env $EDITOR env/validator-agent.env # адрес control-api, токен, способы самопроверки, таймауты ansible validators -m ping # проверка связи (20 хостов: validator-1 ... validator-20) ``` - В `inventory/hosts.yml` 20 хостов `validator-1 … validator-20` с адресами (`ansible_host`) и переменной `validator_id` (`vkiplab-v1 … vkiplab-v20`, как в control-api). Соответствие `validator-N` → `vkiplab-vN` задано по порядку номеров; **сценарий сверяет `validator_id` с тем, что записано в работающем контейнере на хосте, и при расхождении останавливается до замены контейнера**. `VALIDATOR_AGENT_VALIDATOR_ID` в env-файл писать не нужно: сценарий подставляет его из inventory. - Отпечатки хостов запоминаются при первом подключении (`StrictHostKeyChecking=accept-new` в `ansible.cfg`); сменившийся отпечаток известного хоста — ошибка. - SSH: пользователь `debian` и ключ `~/.ssh/vk_cloud_priv.key` на jump-хосте (права 0600) — одинаковые на всех хостах; меняются в `inventory/group_vars/validators.yml` (`ansible_user`, `ansible_ssh_private_key_file`). Для docker и записи env-файла сценарий повышает права через `sudo`. - Токен в env-файле можно зашифровать: `ansible-vault encrypt env/validator-agent.env`, запускать с `--ask-vault-pass`. Рабочий `env/validator-agent.env` в git не попадает (`.gitignore`). ## Запуск ```bash cd deploy/ansible ansible-playbook playbooks/deploy-validator-agent.yml --limit vkiplab-v1 # канарейка: один валидатор ansible-playbook playbooks/deploy-validator-agent.yml # все валидаторы волнами ansible-playbook playbooks/deploy-validator-agent.yml --check # только проверки (preflight), без изменений ``` Волны по умолчанию: 1 хост, затем 4, затем все остальные (`deploy_serial: [1, 4, "100%"]`). Любой сбой в волне останавливает прогон: следующая волна не начнётся. Все сразу: `-e '{"deploy_serial": ["100%"]}'`. Что выкатывается — `deploy_ref` (по умолчанию `main`): ветка, тег или коммит. Выкатить нужно **запушенный** коммит: валидаторы берут код из репозитория, а не с jump-хоста. После правок кода сначала пересоберите `bin/validator-agent` (см. [SETUP.md](../../docs/SETUP.md#обновление-образов-после-изменения-кода)) и закоммитьте его вместе с `bin/SHA256SUMS`. ### Откат ```bash ansible-playbook playbooks/deploy-validator-agent.yml -e deploy_ref=<предыдущий коммит или тег> ``` Для тега или коммита клон переходит в detached HEAD; следующий запуск с `deploy_ref=main` возвращает его на ветку. ## Что делает сценарий на каждом хосте 1. **preflight** — env-файл на jump-хосте существует и в нём задан `VALIDATOR_AGENT_CONTROL_API_URL` (адрес-пример `example.com` не принимается); на валидаторе отвечает Docker, есть git и клон, архитектура x86_64; `validator_id` из inventory совпадает с `validator_id` работающего контейнера. Показывает текущий контейнер. 2. **git** — `fetch`, затем клон сбрасывается на `deploy_ref` (`checkout --force`). Через `git`, а не модуль `git`: учётные данные, уже настроенные в клоне, не трогаются. Локальные правки отслеживаемых файлов в клоне будут сброшены; неотслеживаемые и игнорируемые (`.env.*`) — нет. 3. **build** — проверка `bin/validator-agent` по `bin/SHA256SUMS`; `docker build --platform linux/amd64` с контекстом в корне репозитория, образ получает метку ревизии (`cloud-ip-validator-validator-agent:<хеш>`) и `latest`. 4. **replace** — env-файл копируется в `/opt/cloud-ip-validator/deploy/docker/.env.validator` (0600; путь закрыт `.gitignore`), затем `docker stop` → `docker rm -f` → `docker run -d --restart unless-stopped --cap-add NET_RAW --env-file …`. 5. **verify** — ждёт строку `registered` в логе агента (регистрация в control-api), проверяет, что контейнер запущен, без перезапусков и на только что собранном образе. При неудаче хост падает с последними строками лога, следующие волны не стартуют. Затем остаются 3 последних образа с метками ревизий (`keep_images`), остальные и «висячие» удаляются. Все шаги можно запускать по тегам: `--tags preflight|git|build|replace|verify`. ## Параметры **`env/validator-agent.env`** — запуск агента (читает `deploy/docker/validator-agent/docker-entrypoint.sh`): `VALIDATOR_AGENT_CONTROL_API_URL`, `CONTROL_API_AGENT_TOKEN`, `VALIDATOR_AGENT_SELF_CHECK_METHODS`, `VALIDATOR_AGENT_SELF_CHECK_TIMEOUT_SECONDS`, `VALIDATOR_AGENT_POLL_INTERVAL_SECONDS`, `VALIDATOR_AGENT_HTTPS_TIMEOUT_SECONDS`, `VALIDATOR_AGENT_ICMP_TIMEOUT_SECONDS`, `VALIDATOR_AGENT_ICMP_COUNT`, `VALIDATOR_AGENT_SSH_ENABLED`, `VALIDATOR_AGENT_SSH_TIMEOUT_SECONDS`. Смена только env-файла не требует изменения кода: достаточно запустить сценарий (контейнер пересоздаётся с новыми значениями). **`inventory/group_vars/validators.yml`** — доставка: `repo_dir`, `repo_remote`, `deploy_ref`, `git_user` (пользователь, у которого в клоне настроен доступ к репозиторию; пусто — SSH-пользователь), `image_name`, `container_name`, `platform`, `keep_images`, `restart_policy`, `capabilities`, `log_max_size`, `log_max_file`, `stop_timeout`, `verify_retries`, `verify_delay`, `local_env_file`. Любой параметр переопределяется ключом `-e`. ## Разбор сбоев - **«Нет env-файла» / «не задан VALIDATOR_AGENT_CONTROL_API_URL»** — см. «Подготовка». - **«в запущенном контейнере validator_id=…, а в inventory …»** — соответствие `validator-N` и `validator_id` в `inventory/hosts.yml` неверно для этого хоста: исправьте inventory (контейнер при этом не тронут). - **Контейнер не прошёл проверку** — в сообщении последние строки лога. `registration failed` — control-api недоступен с валидатора или `validator_id` не заведён в control-api. Контейнер остаётся на хосте для разбора (`docker logs`). - **`dubious ownership`, `could not read Username`** — клон принадлежит другому пользователю или учётные данные git есть у другого: задайте `git_user`. - **`bin/validator-agent` не совпадает с суммой** — в репозитории устарел `bin/SHA256SUMS`: пересоберите бинарник и обновите сумму. - **Сборка падает на `FROM alpine:3.20` / `apk add`** — с валидатора нет доступа к Docker Hub / репозиториям Alpine. - **Старый образ другого имени остаётся** — сценарий чистит только образы `image_name`; образ с прежним именем удалите вручную (`docker rmi`).