Последовательное обновление серверов Ansible: serial и проверка готовности
Playbook перезапускает приложение на всех хостах группы. Даже если каждая машина возвращается за несколько секунд, одновременный restart оставляет сервис без работающих экземпляров. Serial позволяет проходить play партиями: закончить весь набор задач на первой части хостов и только затем перейти к следующей.
Но размер партии отвечает лишь за количество одновременно обрабатываемых машин. Для безопасного продолжения нужно ещё подтвердить, что обновлённый экземпляр обслуживает нужную версию. Запущенный процесс и готовый сервис — разные наблюдения.
Определим условия учебного стенда
Предположим, на хостах app уже установлен systemd-сервис demo-app. Он запускается из /opt/demo-app/current, а release-каталог /opt/demo-app/releases/2026-10-29.1 заранее доставлен на каждый сервер. Локальный HTTP endpoint /health отвечает JSON со status=ok и version=2026-10-29.1. Перед использованием адаптируйте эти пути и контракт ответа к своему приложению.
- name: Switch application one host at a time
hosts: app
become: true
gather_facts: false
serial: 1
any_errors_fatal: true
vars:
release_version: "2026-10-29.1"
tasks:
- name: Check installed release directory
ansible.builtin.stat:
path: "/opt/demo-app/releases/{{ release_version }}"
register: release_dir
- name: Require a prepared release
ansible.builtin.assert:
that:
- release_dir.stat.isdir | default(false)
- name: Switch current symlink
ansible.builtin.file:
src: "/opt/demo-app/releases/{{ release_version }}"
dest: /opt/demo-app/current
state: link
notify: Restart application
- name: Apply the pending restart before health check
ansible.builtin.meta: flush_handlers
- name: Wait for the expected version
ansible.builtin.uri:
url: http://127.0.0.1:8080/health
return_content: true
status_code: 200
register: health
until: >
(health.status | default(0)) == 200 and
(health.json.status | default('')) == 'ok' and
(health.json.version | default('')) == release_version
retries: 12
delay: 5
handlers:
- name: Restart application
ansible.builtin.systemd_service:
name: demo-app
state: restarted
После переключения ссылки handler выполняется до HTTP-проверки. Без flush_handlers отложенный restart мог бы произойти позже, а readiness успела бы увидеть старый процесс. Проверка версии не позволяет принять старый успешный /health за готовность нового релиза.
Почему failure должен останавливать следующие партии
В примере any_errors_fatal требует прекратить play после неустранённой ошибки. Если первый экземпляр не прошёл readiness, продолжать переключение остальных опасно: можно последовательно потерять все рабочие серверы. Не добавляйте ignore_errors к этой проверке только ради завершения playbook.
Размер партии serial выбирают по ёмкости оставшихся экземпляров. Значение 1 удобно для первого стенда, но даже оно не гарантирует доступность: на двух серверах один оставшийся должен выдержать весь трафик. Forks ограничивает число работников Ansible и не заменяет такую границу партии.
Что происходит с трафиком
Playbook проверяет локальный процесс на управляемом хосте: uri выполняется там, поэтому 127.0.0.1 относится к серверу приложения. Это не проверка внешнего пути через балансировщик. В рабочем процессе часто нужно сначала вывести сервер из распределения трафика и дождаться завершения активных запросов, затем обновить, проверить и вернуть.
Команды отключения и включения зависят от конкретного балансировщика, поэтому их нельзя заменить универсальным echo. Разместите эти реальные действия внутри той же партии. Возвращать экземпляр следует только после успешной readiness; при ошибке сохраните его отключённым до решения оператора.
Повторный запуск и откат
Когда ссылка уже указывает на нужную версию, file не должен уведомлять handler повторно, но readiness всё равно проверяется. Если приложение уже остановлено, одного отсутствующего уведомления недостаточно для восстановления: добавьте отдельную задачу state: started, если это часть принятого контракта роли.
Для отката потребуется существующий каталог предыдущего релиза и совместимость данных. Смена ссылки не отменяет миграцию базы. Прежде чем автоматизировать обратный переход, проверьте, может ли старая версия работать с текущей схемой и файлами.
На стенде намеренно верните неправильную version из первого хоста. Дальнейшие серверы не должны обновляться. Затем исправьте readiness и повторите запуск. Эта отрицательная проверка подтверждает главный смысл serial: следующий экземпляр получает обновление после проверенного результата предыдущего, а не просто после паузы.
Ещё по теме
- Ansible --tags пропускает нужные задачи: include_tasks и import_tasks
- Ansible Vault и no_log: где секреты остаются после расшифровки
Продолжить изучение
Хотите собрать настройку серверов в повторяемый процесс? Inventory, роли, шаблоны и обновление сервисов входят в программу «Ansible: автоматизация инфраструктуры».
Продолжить на практике
- Ansible: автоматизация инфраструктуры ↗
Настраивайте серверы воспроизводимо: inventory, playbooks, роли, шаблоны и секреты. Соберите автоматизацию для нескольких окружений, проверьте повторный запуск и обновляйте сервисы управляемо.
