Ansible: рестарт контейнеров переведён на handlers

- caddyproxy, netdata, authelia, bifrost, remembos: конфиг-задачи нотифаят
  handler "Restart application" вместо безусловного или условного рестарта
- ansible.cfg: force_handlers, иначе упавший прогон теряет notify и рестарт,
  применяющий правку конфига, не случится уже никогда
- authelia рестартит только authelia_app, чтобы не сбрасывать сессии в redis
This commit is contained in:
av
2026-07-24 21:05:27 +03:00
parent 1cf660b4fd
commit 9c156df0c7
10 changed files with 75 additions and 40 deletions
+5
View File
@@ -2,3 +2,8 @@
host_key_checking = True
vault_password_file = ./ansible-vault-password-file
roles_path = ./galaxy.roles
# Выполнять handlers даже если play упал: иначе упавший прогон теряет
# накопленные notify, а на следующем конфиг уже не changed — и рестарт,
# применяющий правку, не случится никогда.
force_handlers = True
+1 -1
View File
@@ -21,7 +21,6 @@ _(sec-ревью)_ — три независимых security-ревью на м
- [Убрать группу `docker` у сервисных пользователей](service-users-docker-group.md) — `docker` = root на хосте; ~все app-аккаунты фактически root, изоляция «юзер на сервис» обнуляется _(sec-ревью)_
- [Изолировать/запаролить сессионный redis Authelia](authelia-redis-isolation.md) — стор сессий SSO в общей сети без пароля; запись в него = hijack SSO всех сервисов _(sec-ревью)_
- [Алерты на проблемные контейнеры](container-alerts.md) — wakapi крутился в restart-loop несколько дней незамеченным; healthcheck в compose + алерты Netdata
- [Рестарт контейнеров через handlers](ansible-handlers-restart.md) — `state: restarted` выполняется безусловно на каждом прогоне (лишний downtime), нет ни одного handler _(ансибл-ревью)_
## Средний
@@ -36,6 +35,7 @@ _(sec-ревью)_ — три независимых security-ревью на м
- [Gitea runner on-demand в Yandex Cloud](gitea-runner-on-demand.md) — раннер активен только во время сборки; webhook→Cloud Function стартует ВМ, probe/decide гасят по idle; экономия ~95%
- [Синхронизация общих Ansible-ролей](shared-roles-sync.md) — `owner`/`eget`/`secrets` дублируются между репозиториями и дрейфуют; канон в ansible-shared + rsync-таски invoke
- [Уведомления о размере приложений и диске](disk-usage-alerts.md) — алерты на заполнение диска и аномальный рост data-томов; сервер ресурсно ограничен _(tududi)_
- [Handlers рестарта для оставшихся приложений](handlers-remaining-apps.md) — apprise/dashboard/transcriber/miniflux монтируют конфиг в контейнер, но рестарта нет вовсе; правка конфига не подхватывается
- [Composable-роль `backup`](ansible-backup-role.md) — бэкап — самый чистый шов для extraction (одинаков у всех, различается только список targets) _(ансибл-ревью)_
- [Точечные фиксы идемпотентности и баг имени play](ansible-quick-fixes.md) — быстрые правки: имя play в wanderer, `changed_when` в netdata/eget, backup-targets через template _(ансибл-ревью)_
-20
View File
@@ -1,20 +0,0 @@
# Рестарт контейнеров через handlers, а не безусловно
**Приоритет:** высокий
Ни в одном плейбуке нет `handlers:`. Вместо этого задача `state: restarted`
выполняется **всегда** — рестартит контейнер на каждом прогоне даже без
изменений (`playbook-caddyproxy.yml:106`, `playbook-netdata.yml:143`,
`playbook-authelia.yml:92`): не идемпотентно, лишний downtime. В
`playbook-gitea.yml` рестарта нет вовсе — несогласованность. Канонический
паттерн: шаблон конфига `notify`-ит handler, который делает
`docker_compose_v2: state: restarted` только при реальном изменении. Внедряется
инкрементально, по одному сервису. Заодно убрать мёртвый
`docker_compose_file_result` в `playbook-memos.yml:76` (регистрируется, нигде не
используется — задумывался под `when`/`notify`).
Топ-приоритет ансибл-ревью. Полный контекст:
[docs/drafts/ansible-review.md](../drafts/ansible-review.md) §3.
Связано: playbook-caddyproxy.yml, playbook-netdata.yml, playbook-authelia.yml,
playbook-gitea.yml, playbook-memos.yml.
+30
View File
@@ -0,0 +1,30 @@
# Handlers рестарта для оставшихся приложений
**Приоритет:** средний
Паттерн `handlers` + `notify` внедрён в caddyproxy, netdata, authelia, bifrost,
remembos (см. задачу `ansible-handlers-restart`, закрыта). Остались плейбуки, где
конфиг bind-моунтится в контейнер, но рестарта нет **вообще** — правка конфига без
изменения docker-compose.yml не подхватывается до следующего пересоздания
контейнера:
- `playbook-apprise.yml``server.cfg``config_dir:/config`.
- `playbook-dashboard.yml``config/*.yaml``config_dir:/app/config`.
- `playbook-transcriber.yml``config.toml` bind-моунтится **отдельным файлом**
(`{{ config_file }}:/config/config.toml:ro`). Худший случай: `template`
перезаписывает файл с новым inode, и контейнер продолжает видеть старый до
рестарта.
- `playbook-miniflux.yml` — файлы секретов роли `secrets``secrets_dir:/secrets:ro`,
читаются через `*_FILE` при старте.
Заодно проверить `playbook-goaccess.yml`: конфиг там не монтируется, но
`Dockerfile`/`entrypoint.sh` меняются без изменения compose-файла — надо
убедиться, что `build: always` действительно пересоздаёт контейнер.
Отдельный кандидат: caddy сейчас перезапускается целиком, хотя умеет
`caddy reload` — применение Caddyfile без разрыва соединений на реверс-прокси,
через который идёт весь трафик. В `playbook-caddyproxy.yml` для этого уже лежит
закомментированный блок с `docker_compose_v2_exec`.
Связано: playbook-apprise.yml, playbook-dashboard.yml, playbook-transcriber.yml,
playbook-miniflux.yml, playbook-goaccess.yml, playbook-caddyproxy.yml.
+10 -1
View File
@@ -46,6 +46,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0600"
notify: "Restart application"
- name: "Copy configuration file"
ansible.builtin.template:
@@ -54,6 +55,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0600"
notify: "Restart application"
- name: "Copy gobackup config"
ansible.builtin.template:
@@ -87,9 +89,16 @@
tags:
- run-app
- name: "Restart application with docker compose"
handlers:
# Приложение читает конфиги только при старте, а `state: present` не
# пересоздаёт контейнер, если docker-compose.yml не изменился, — поэтому
# правку конфига применяет только явный рестарт.
- name: "Restart application"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
# Только сам authelia: рестарт redis сбросил бы сессии всех сервисов.
services:
- "authelia_app"
state: "restarted"
tags:
- run-app
+6 -9
View File
@@ -42,7 +42,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0600"
register: config_file_result
notify: "Restart application"
- name: "Copy docker compose file"
ansible.builtin.template:
@@ -51,7 +51,6 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
register: docker_compose_file_result
- name: "Run application with docker compose"
community.docker.docker_compose_v2:
@@ -61,15 +60,13 @@
tags:
- run-app
# Bifrost читает config.json только при старте. Если изменился лишь конфиг
# (а compose — нет), `state: present` не пересоздаёт контейнер, и правки
# (ротация ключа, смена модели/правил) не подхватятся — нужен явный рестарт.
- name: "Restart docker compose services if config changed but not docker-compose.yml"
handlers:
# Приложение читает конфиги только при старте, а `state: present` не
# пересоздаёт контейнер, если docker-compose.yml не изменился,поэтому
# правку конфига применяет только явный рестарт.
- name: "Restart application"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
state: "restarted"
when:
- config_file_result.changed
- not docker_compose_file_result.changed
tags:
- run-app
+6 -1
View File
@@ -74,6 +74,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
notify: "Restart application"
- name: "Copy docker compose file"
ansible.builtin.template:
@@ -99,7 +100,11 @@
# tags:
# - run-app
- name: "Restart application with docker compose"
handlers:
# Приложение читает конфиги только при старте, а `state: present` не
# пересоздаёт контейнер, если docker-compose.yml не изменился, — поэтому
# правку конфига применяет только явный рестарт.
- name: "Restart application"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
state: "restarted"
-1
View File
@@ -69,7 +69,6 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
register: docker_compose_file_result
- name: "Run application with docker compose"
community.docker.docker_compose_v2:
+11 -1
View File
@@ -44,6 +44,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
notify: "Restart application"
- name: "Find all go.d plugin config files"
ansible.builtin.find:
@@ -60,6 +61,7 @@
group: "{{ app_user }}"
mode: "0640"
loop: "{{ go_d_source_files.files }}"
notify: "Restart application"
- name: "Find existing go.d config files on server"
ansible.builtin.find:
@@ -73,6 +75,7 @@
state: absent
loop: "{{ go_d_existing_files.files }}"
when: (item.path | basename) not in (go_d_source_files.files | map(attribute='path') | map('basename') | list)
notify: "Restart application"
- name: "Find all health.d config files"
ansible.builtin.find:
@@ -89,6 +92,7 @@
group: "{{ app_user }}"
mode: "0640"
loop: "{{ health_d_source_files.files }}"
notify: "Restart application"
- name: "Find existing health.d config files on server"
ansible.builtin.find:
@@ -102,6 +106,7 @@
state: absent
loop: "{{ health_d_existing_files.files }}"
when: (item.path | basename) not in (health_d_source_files.files | map(attribute='path') | map('basename') | list)
notify: "Restart application"
- name: "Copy health alarm notify config"
ansible.builtin.template:
@@ -110,6 +115,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
notify: "Restart application"
- name: "Grab docker group id."
ansible.builtin.shell:
@@ -136,7 +142,11 @@
tags:
- run-app
- name: "Restart application with docker compose"
handlers:
# Приложение читает конфиги только при старте, а `state: present` не
# пересоздаёт контейнер, если docker-compose.yml не изменился, — поэтому
# правку конфига применяет только явный рестарт.
- name: "Restart application"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
state: "restarted"
+6 -6
View File
@@ -41,7 +41,7 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
register: config_file_result
notify: "Restart application"
- name: "Copy docker compose file"
ansible.builtin.template:
@@ -50,7 +50,6 @@
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: "0640"
register: docker_compose_file_result
- name: 'Login to Yandex Container Registry'
community.docker.docker_login:
@@ -66,12 +65,13 @@
tags:
- run-app
- name: "Restart docker compose services if config changed but not docker-compose.yml"
handlers:
# Приложение читает конфиги только при старте, а `state: present` не
# пересоздаёт контейнер, если docker-compose.yml не изменился, — поэтому
# правку конфига применяет только явный рестарт.
- name: "Restart application"
community.docker.docker_compose_v2:
project_src: "{{ base_dir }}"
state: "restarted"
when:
- config_file_result.changed
- not docker_compose_file_result.changed
tags:
- run-app