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>
111 lines
11 KiB
Markdown
111 lines
11 KiB
Markdown
# Доставка 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`).
|