Files
ayurishchevandClaude Sonnet 5.5 cf4a883363 ansible: fix container name and git user, refuse to start a second agent
The real container on the validators is named validator-agent (the image
is cloud-ip-validator-validator-agent); the playbook used the image name
as the container name, so it would have started a second agent next to
the old one with the same validator_id. The clone on the validators is
owned by root, so git must run as root (git_user), otherwise fetch fails
with "cannot open .git/FETCH_HEAD: Permission denied".

Preflight now stops when the host has another container of this agent
(by name or image) besides container_name.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-02 10:01:05 +03:00

111 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Доставка validator-agent на валидаторы (Ansible)
Сценарий запускается с jump-хоста и на каждой ВМ-валидаторе: обновляет git-клон в `/opt/cloud-ip-validator`,
**собирает образ на самом хосте**, останавливает и удаляет старый контейнер `validator-agent` (образ — `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` работающего контейнера; на хосте нет другого контейнера этого агента (по имени или образу) — иначе рядом со старым
запустился бы второй с тем же `validator_id`. Показывает текущий контейнер.
2. **git** — от имени `git_user` (на валидаторах `root`: клон принадлежит ему) `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`).
- **`Permission denied` на `.git/FETCH_HEAD`, `dubious ownership`, `could not read Username`** — git запущен не от владельца клона или
учётные данные есть у другого пользователя: задайте `git_user` (на валидаторах — `root`).
- **«найден другой контейнер агента»** — на хосте есть контейнер с похожим именем или образом, не совпадающий с `container_name`:
проверьте имя (`docker ps -a`) и при необходимости удалите лишний контейнер вручную.
- **`bin/validator-agent` не совпадает с суммой** — в репозитории устарел `bin/SHA256SUMS`: пересоберите бинарник и обновите сумму.
- **Сборка падает на `FROM alpine:3.20` / `apk add`** — с валидатора нет доступа к Docker Hub / репозиториям Alpine.
- **Старый образ другого имени остаётся** — сценарий чистит только образы `image_name`; образ с прежним именем удалите вручную (`docker rmi`).