fix(deploy): гейт деплоя ПТИЦЫ — мёртвый worker/beat/frontend больше не уезжает зелёным #3334

Merged
bot-backend merged 2 commits from fix/3324-ptica-deploy-gate into main 2026-09-02 12:17:33 +00:00
Collaborator

Refs #3324 (закрывает часть «гейт деплоя»; периметр-трио остаётся в issue — НЕ auto-close). Аудит 01-02.09, линза devops. Единственной проверкой был curl backend /health; образец дисциплины лежал в соседнем deploy-tradein.yml.

Healthcheck'и (compose)

  • worker: celery inspect ping -d celery@$$(hostname) — адресно в ЭТОТ узел: без -d ответил бы любой воркер на брокере. 60s/30s/retries 5 (3 минуты флапа Redis не приговаривают исправный worker), start_period 90s. Stderr пробы виден в .State.Health.Log.
  • beat: beat на inspect ping не отвечает — проба по mtime celerybeat-schedule* в /tmp (< 10 мин). Порог: sync_every=180s + поминутные zombie-cleanup → живой beat обновляет файл ~каждые 3 мин, 10 мин = 3× запас.

Все три пробы прогнаны на живом проде до мержа: worker pong rc=0, beat rc=0, фронт HTTP 200, /usr/bin/hostname в контейнере есть.

Гейт (deploy.yml, после существующего curl; порядок «миграции → код» не тронут)

  1. wait_healthy по docker inspect для backend/worker/beat/frontend (240s); command_timeout: 30m на SSH-шаге (дефолт 10m убил бы диагноз в worst-case ~17 мин);
  2. HTTP-проба фронта с хоста (2xx/3xx);
  3. сверка «образ→контейнер» — следующее звено после check-latest-image-revision.sh; worker исключается, если guard #3029 его намеренно не пересоздавал;
  4. worker без health-конфига (старый, переживший guard): «стабильный running» + неизменность RestartCount за 15 с — crash-loop не проскакивает;
  5. провал печатает state/health/RestartCount/логи; итог health_rc/frontend_rc/image_rc → rc + явный exit; все вызовы || var=$? в той же строке.

Цена

Обычный деплой: ~+60-100 с. Сломанный worker уходит в unhealthy за ~390 с — гейт красный раньше по таймауту 240 с (в логе будет «starting» + логи контейнера).

Прод-приёмка после мержа (план)

  • зелёный контроль: сам деплой этого PR идёт через новый гейт — 4× healthy, HTTP 200, rc=0;
  • красный по worker: rm /app/.venv/bin/celery в контейнере → unhealthy → dispatch-деплой красный с диагнозом; восстановить --force-recreate --no-deps worker;
  • красный по образам: откатить beat на прошлый тег → «beat ОТСТАЛ».
Refs #3324 (закрывает часть «гейт деплоя»; периметр-трио остаётся в issue — НЕ auto-close). Аудит 01-02.09, линза devops. Единственной проверкой был `curl backend /health`; образец дисциплины лежал в соседнем deploy-tradein.yml. ## Healthcheck'и (compose) - **worker**: `celery inspect ping -d celery@$$(hostname)` — адресно в ЭТОТ узел: без `-d` ответил бы любой воркер на брокере. 60s/30s/retries 5 (3 минуты флапа Redis не приговаривают исправный worker), start_period 90s. Stderr пробы виден в `.State.Health.Log`. - **beat**: beat на `inspect ping` не отвечает — проба по mtime `celerybeat-schedule*` в /tmp (< 10 мин). Порог: `sync_every=180s` + поминутные zombie-cleanup → живой beat обновляет файл ~каждые 3 мин, 10 мин = 3× запас. **Все три пробы прогнаны на живом проде до мержа**: worker `pong` rc=0, beat rc=0, фронт HTTP 200, `/usr/bin/hostname` в контейнере есть. ## Гейт (deploy.yml, после существующего curl; порядок «миграции → код» не тронут) 1. `wait_healthy` по `docker inspect` для backend/worker/beat/frontend (240s); `command_timeout: 30m` на SSH-шаге (дефолт 10m убил бы диагноз в worst-case ~17 мин); 2. HTTP-проба фронта с хоста (2xx/3xx); 3. сверка «образ→контейнер» — следующее звено после `check-latest-image-revision.sh`; worker исключается, если guard #3029 его намеренно не пересоздавал; 4. worker без health-конфига (старый, переживший guard): «стабильный running» + неизменность `RestartCount` за 15 с — crash-loop не проскакивает; 5. провал печатает state/health/RestartCount/логи; итог `health_rc/frontend_rc/image_rc → rc` + явный exit; все вызовы `|| var=$?` в той же строке. ## Цена Обычный деплой: ~+60-100 с. Сломанный worker уходит в unhealthy за ~390 с — гейт красный раньше по таймауту 240 с (в логе будет «starting» + логи контейнера). ## Прод-приёмка после мержа (план) - зелёный контроль: сам деплой этого PR идёт через новый гейт — 4× healthy, HTTP 200, `rc=0`; - красный по worker: `rm /app/.venv/bin/celery` в контейнере → unhealthy → dispatch-деплой красный с диагнозом; восстановить `--force-recreate --no-deps worker`; - красный по образам: откатить beat на прошлый тег → «beat ОТСТАЛ».
bot-backend added 1 commit 2026-09-02 11:50:19 +00:00
fix(deploy): гейт ПТИЦЫ читает health worker/beat/frontend, а не только curl backend (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m40s
13c4420d1f
Деплой ПТИЦЫ проверял ровно один признак — `curl backend /health`. Worker и beat
не имели healthcheck'а в compose вообще, у frontend была TCP-only проба, которую
деплой не читал. Crash-loop воркера, вставший beat и фронт с 500 уезжали зелёным
деплоем: признак «прод жив» отсутствовал в старом состоянии ровно так же, как в
новом.

compose: worker — `celery inspect ping -d celery@$(hostname)` (адресно в ЭТОТ узел,
без -d ответил бы любой воркер на брокере); beat — свежесть shelve-файла
расписания (на inspect ping beat не отвечает; поминутная beat-задача гарантирует
обновление mtime не реже ~3 мин при sync_every=180 с, порог 10 мин = 3× запас).
Фиктивной `true`-пробы нет: она повторяла бы State.Running.

deploy.yml: после подъёма — ожидание healthy для backend/worker/beat/frontend
через docker inspect, HTTP-статус фронта (TCP мало), сверка running-образа с
локально скачанным $IMAGE_TAG (приём #2679 из deploy-tradein). Каждая проверка
при провале печатает контейнер, статус, healthcheck-лог и хвост логов; итог —
явный rc в логе и exit им же. Порядок «миграции до подъёма кода» не тронут,
`up -d --wait` не используется намеренно (подъём разбит на несколько up).
Light1YT added 1 commit 2026-09-02 11:58:48 +00:00
fix(deploy): правки ревью гейта ПТИЦЫ — таймаут сессии, crash-loop, множественный id (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m35s
1b1977efa3
command_timeout: 30m — дефолт appleboy/ssh-action 10m короче worst-case гейта
(~17 мин), сессию убило бы посреди диагноза и авария читалась бы обрывом связи.

Деградация «нет health-конфига» требовала лишь running дважды: crash-loop с
временем жизни больше паузы проходил как стабильный (обе проверки видят running,
просто это разные жизни контейнера). Теперь сверяется RestartCount до/после окна
15 с; окно учитывается в счётчике ожидания, иначе таймаут 240 с растянулся бы на
~24 мин и упёрся в command_timeout.

cid(): `ps -aq` возвращает несколько id при залежавшемся exited-контейнере →
docker inspect падает → пустой статус → ложный красный. tail -n1.

worker healthcheck: убран `2>&1` (глушил причину, которую деплой печатает из
.State.Health.Log), retries 3→5 — при interval 60s тройка промахов = 3 минуты,
столько длится обычный флап Redis, а из unhealthy контейнер сам не выходит.
bot-backend merged commit d8b7de2cf6 into main 2026-09-02 12:17:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3334
No description provided.