diff --git a/deploy/ansible/README.md b/deploy/ansible/README.md index d405154..24382e3 100644 --- a/deploy/ansible/README.md +++ b/deploy/ansible/README.md @@ -1,7 +1,7 @@ # Доставка validator-agent на валидаторы (Ansible) Сценарий запускается с jump-хоста и на каждой ВМ-валидаторе: обновляет git-клон в `/opt/cloud-ip-validator`, -**собирает образ на самом хосте**, останавливает и удаляет старый контейнер `cloud-ip-validator-validator-agent` +**собирает образ на самом хосте**, останавливает и удаляет старый контейнер `validator-agent` (образ — `cloud-ip-validator-validator-agent`) и поднимает на его месте новый. Параметры запуска агента вынесены в env-файл. Порядок безопасен: сначала проверки, обновление кода и сборка образа, и только потом замена контейнера. Если что-то @@ -66,8 +66,9 @@ ansible-playbook playbooks/deploy-validator-agent.yml -e deploy_ref=<преды 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`: + с `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` с контекстом в корне репозитория, @@ -100,8 +101,10 @@ ansible-playbook playbooks/deploy-validator-agent.yml -e deploy_ref=<преды неверно для этого хоста: исправьте inventory (контейнер при этом не тронут). - **Контейнер не прошёл проверку** — в сообщении последние строки лога. `registration failed` — control-api недоступен с валидатора или `validator_id` не заведён в control-api. Контейнер остаётся на хосте для разбора (`docker logs`). -- **`dubious ownership`, `could not read Username`** — клон принадлежит другому пользователю или учётные данные git есть у другого: - задайте `git_user`. +- **`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`). diff --git a/deploy/ansible/inventory/group_vars/validators.yml b/deploy/ansible/inventory/group_vars/validators.yml index 92870b0..e77468d 100644 --- a/deploy/ansible/inventory/group_vars/validators.yml +++ b/deploy/ansible/inventory/group_vars/validators.yml @@ -15,13 +15,15 @@ repo_dir: /opt/cloud-ip-validator repo_remote: origin # Ветка, тег или коммит, который нужно выкатить (откат: -e deploy_ref=<коммит>). deploy_ref: main -# Пользователь, у которого в клоне настроен доступ к репозиторию (git fetch). -# Пусто — git работает от SSH-пользователя без sudo. -git_user: "" +# Пользователь, от имени которого выполняется git в клоне (через sudo): владелец +# клона. На валидаторах клон принадлежит root, репозиторий читается без учётных +# данных. Пусто — git работает от SSH-пользователя без sudo. +git_user: root # --- образ и контейнер -------------------------------------------------- image_name: cloud-ip-validator-validator-agent -container_name: cloud-ip-validator-validator-agent +# Имя контейнера на валидаторах (имя образа — другое, см. image_name). +container_name: validator-agent platform: linux/amd64 dockerfile: deploy/docker/validator-agent/Dockerfile # Сколько образов с метками ревизий хранить (кроме latest); старые удаляются. diff --git a/deploy/ansible/roles/validator_agent/tasks/preflight.yml b/deploy/ansible/roles/validator_agent/tasks/preflight.yml index 443bf51..e29b90a 100644 --- a/deploy/ansible/roles/validator_agent/tasks/preflight.yml +++ b/deploy/ansible/roles/validator_agent/tasks/preflight.yml @@ -85,6 +85,34 @@ failed_when: false check_mode: false +# Защита от второго агента: если на хосте уже есть другой контейнер этого +# агента (по имени или по образу), сценарий остановится, а не запустит +# рядом ещё один с тем же validator_id. +- name: List containers on the validator + ansible.builtin.command: docker ps -a --format {% raw %}'{{.Names}}|{{.Image}}'{% endraw %} + register: all_containers + changed_when: false + check_mode: false + +- name: Check that there is no other agent container + ansible.builtin.assert: + that: (other_agents | from_json) | length == 0 + fail_msg: >- + {{ inventory_hostname }}: найден другой контейнер агента: {{ (other_agents | from_json) | join(', ') }}. + Сценарий заменяет только контейнер {{ container_name }}. Проверьте container_name в + inventory/group_vars/validators.yml или удалите лишний контейнер вручную. + quiet: true + vars: + other_agents: >- + {%- set found = [] -%} + {%- for line in all_containers.stdout_lines -%} + {%- set row = line.split('|') -%} + {%- if row[0] != container_name and ('validator-agent' in row[0] or row[1] == image_name or row[1].startswith(image_name ~ ':')) -%} + {%- set _ = found.append(row[0] ~ ' (' ~ row[1] ~ ')') -%} + {%- endif -%} + {%- endfor -%} + {{- found | to_json -}} + # validator_id работающего контейнера — эталон: если он отличается от # inventory, заменять контейнер нельзя (агент зарегистрировался бы под чужим # именем, адреса привязывались бы к порту другой ВМ). Выводится только он,