fix(devops): потолок логов основного стека — journald, как у trade-in (#2761) #2762

Merged
bot-backend merged 1 commit from fix/2761-root-stack-logging into main 2026-08-06 22:29:27 +00:00
Collaborator

Проблема

У корневого docker-compose.prod.yml секции logging: не было вообще → дефолтный json-file без ротации, растёт до заполнения диска и умирает вместе с контейнером.

Опровергнутая предпосылка issue (главное)

Issue делит находку на «наше (~210 МБ)» и «вне репозитория — forgejo + glitchtip-worker, к владельцу». Проверка в issue искала отдельный compose-файл (git ls-files) и не нашла сервис внутри нашего:

docker inspect glitchtip-worker:
  project = gendesign
  file    = /opt/gendesign/docker-compose.prod.yml   ← ЭТОТ файл
  service = glitchtip-worker
/opt/gendesign/.env: COMPOSE_PROFILES=glitchtip      ← профиль активен, деплой его поднимает

glitchtip-worker — это 2.59 ГБ и 31.9 МБ/сутки, 81 % всего роста стека — и он чинится этим PR, а не эскалацией владельцу.

Вне репозитория остаётся только forgejo (10.3 ГБ, project=forgejo, /home/gendesign/forgejo/docker-compose.yml).

Замер (прод, 2026-08-06)

МБ/сутки = размер json-file ÷ возраст контейнера:

контейнер накоплено МБ/сутки
glitchtip-worker 2589 МБ 31.9
postgres 162 МБ 2.2
backend 0.3 МБ 1.9
worker 0.2 МБ 1.3
beat / caddy / forwarder / frontend <0.2 МБ 1.4 всего
site-finder (orphan, см. ниже) 34 МБ 0.4
redis / osrm / osrm-walk / glitchtip-web <3 МБ 0.2 всего
итого ~39

backend/worker/beat/caddy почти ничего не накапливают не потому, что мало пишут, а потому что deploy пересоздаёт их ~20 раз в сутки — то есть json-file их логи теряет, а не ограничивает.

Бюджет journald — посчитан, не предположен

Потолок взят не из документации, а из сообщения самого демона в журнале:

System Journal (/var/log/journal/...) is 2.2G, max 4.0G, 2.9G free.

(journald.conf пуст, все дефолты: 10 % от 145 ГБ = 14.5 ГБ, но капается 4 ГБ.)

  • системный поток: 2.2 ГБ / 103 суток ≈ 22 МБ/сутки
  • после правки: 22 + 39 + tradein (единицы) ≈ 65 МБ/сутки
  • глубина при 4 ГБ: 4096 / 65 ≈ 60 суток — требование «сутки-двое» перекрыто ×30

Укладывается. Дисковый эффект в плюс: 4 ГБ — потолок с самовытеснением, а сегодня glitchtip-worker растёт без потолка; плюс пересоздание удалит его json-file и разово освободит ~2.6 ГБ.

Проверено ДО мержа (прод, throwaway-контейнер)

docker run -d --log-driver=journald --log-opt tag=ponytail-2761-preflight alpine ...
docker rm -f <cid>;  docker inspect <cid> → No such object   (контейнера НЕТ)

journalctl -t ponytail-2761-preflight        → main-stack-journald-preflight-20260806T222227Z
journalctl CONTAINER_ID=<dead-short-id>      → та же строка

Запись пережила удаление контейнера и читается по метке и по ID мёртвого контейнера.

Ловушка из #2758 перепроверена — она ещё жива

Читал ровно теми правами, которыми будут читать:

id → uid=1000(gendesign) groups=gendesign,sudo,users,docker   ← НЕТ adm/systemd-journal
journalctl -t ponytail-2761-preflight   → -- No entries --
sudo -n true                            → sudo: a password is required

Владелец usermod -aG adm gendesign пока не выдавал. Рабочий обход (через docker-группу) записан в комментарии файла, вместе с ловушкой TZ=UTC из #2760. До выдачи прав правка не регрессия: docker logs работает как раньше.

Blast radius первого деплоя

Log-driver — свойство создания контейнера, поэтому compose up -d пересоздаст всё. Но backend/worker/beat/caddy/forwarder и так force-recreate'ятся каждым деплоем (deploy.yml:417-431). Инкремент именно этой правки:

  • postgres — ~10 с даунтайма (у deploy.yml уже есть такой путь на строке 407)
  • redis — брокер celery, graceful stop сохраняет RDB
  • osrm + osrm-walk — перезагрузка MLD-графа в RAM (backend не depends_on, деплой не блокируется)
  • frontend, glitchtip-web, glitchtip-worker

Разово. Мержится в окно без ночных прогонов (ближайший — 00:39 UTC).

Rollback: revert коммита → следующий деплой вернёт json-file. Данных не теряет, состояния нет.

Что НЕ сделано (осознанно)

  • Корневой docker-compose.yml не трогал. Issue называет оба файла, но этот — для локальной разработки, а в Docker Desktop (macOS/Windows) journald в VM нет → контейнеры просто не стартуют. Anchor туда = сломанный docker compose up у всех локально. Потолок для dev-логов, если понадобится, — отдельный json-file max-size.
  • forgejo не трогал — действительно чужой стек (10.3 ГБ, ~128 МБ/сутки). Это к владельцу, и это по-прежнему главный потребитель диска.
  • Orphan gendesign-site-finder-1 (34 МБ, работает 81 день): сервиса site-finder в compose-файле НЕТ — контейнер остался от прежней версии и up -d его не трогает. Логи так и останутся на json-file. Чинится --remove-orphans в deploy.yml, но это меняет поведение деплоя для всего project=gendesign — отдельная задача, не этот PR.
  • Уровень логирования не трогал — это app-код, не infra.

Test plan

  • docker compose -f docker-compose.prod.yml config — валиден; все 12 сервисов резолвятся в driver: journald, tag: {{.Name}} (проверено разбором вывода, а не глазами по anchor'у)
  • journald-драйвер проверен на проде до мержа (throwaway-контейнер, см. выше)
  • чтение проверено правами deploy-юзера (не root, не sudo)
  • после деплоя: все контейнеры Up, https://gendsgn.ru/ → 200
  • после деплоя: читается запись контейнера, которого больше нет (backend создаётся на строке 396 и уничтожается force-recreate'ом на 417 — проверка доступна в пределах одного деплоя)

Refs #2761, #2758, #2741

## Проблема У корневого `docker-compose.prod.yml` секции `logging:` не было **вообще** → дефолтный `json-file` без ротации, растёт до заполнения диска и умирает вместе с контейнером. ## Опровергнутая предпосылка issue (главное) Issue делит находку на «наше (~210 МБ)» и «вне репозитория — `forgejo` + `glitchtip-worker`, к владельцу». Проверка в issue искала отдельный compose-файл (`git ls-files`) и не нашла **сервис внутри нашего**: ``` docker inspect glitchtip-worker: project = gendesign file = /opt/gendesign/docker-compose.prod.yml ← ЭТОТ файл service = glitchtip-worker /opt/gendesign/.env: COMPOSE_PROFILES=glitchtip ← профиль активен, деплой его поднимает ``` `glitchtip-worker` — это **2.59 ГБ и 31.9 МБ/сутки, 81 % всего роста стека** — и он чинится этим PR, а не эскалацией владельцу. Вне репозитория остаётся **только** `forgejo` (10.3 ГБ, `project=forgejo`, `/home/gendesign/forgejo/docker-compose.yml`). ## Замер (прод, 2026-08-06) МБ/сутки = размер json-file ÷ возраст контейнера: | контейнер | накоплено | МБ/сутки | |---|---:|---:| | glitchtip-worker | 2589 МБ | **31.9** | | postgres | 162 МБ | 2.2 | | backend | 0.3 МБ | 1.9 | | worker | 0.2 МБ | 1.3 | | beat / caddy / forwarder / frontend | <0.2 МБ | 1.4 всего | | site-finder (orphan, см. ниже) | 34 МБ | 0.4 | | redis / osrm / osrm-walk / glitchtip-web | <3 МБ | 0.2 всего | | **итого** | | **~39** | backend/worker/beat/caddy почти ничего не накапливают не потому, что мало пишут, а потому что deploy пересоздаёт их ~20 раз в сутки — то есть json-file их логи теряет, а не ограничивает. ## Бюджет journald — посчитан, не предположен Потолок взят не из документации, а из сообщения самого демона в журнале: ``` System Journal (/var/log/journal/...) is 2.2G, max 4.0G, 2.9G free. ``` (`journald.conf` пуст, все дефолты: 10 % от 145 ГБ = 14.5 ГБ, но капается 4 ГБ.) - системный поток: 2.2 ГБ / 103 суток ≈ **22 МБ/сутки** - после правки: 22 + 39 + tradein (единицы) ≈ **65 МБ/сутки** - глубина при 4 ГБ: 4096 / 65 ≈ **60 суток** — требование «сутки-двое» перекрыто ×30 **Укладывается.** Дисковый эффект в плюс: 4 ГБ — потолок с самовытеснением, а сегодня glitchtip-worker растёт без потолка; плюс пересоздание удалит его json-file и разово освободит **~2.6 ГБ**. ## Проверено ДО мержа (прод, throwaway-контейнер) ``` docker run -d --log-driver=journald --log-opt tag=ponytail-2761-preflight alpine ... docker rm -f <cid>; docker inspect <cid> → No such object (контейнера НЕТ) journalctl -t ponytail-2761-preflight → main-stack-journald-preflight-20260806T222227Z journalctl CONTAINER_ID=<dead-short-id> → та же строка ``` Запись пережила удаление контейнера и читается по метке **и** по ID мёртвого контейнера. ## Ловушка из #2758 перепроверена — она ещё жива Читал ровно теми правами, которыми будут читать: ``` id → uid=1000(gendesign) groups=gendesign,sudo,users,docker ← НЕТ adm/systemd-journal journalctl -t ponytail-2761-preflight → -- No entries -- sudo -n true → sudo: a password is required ``` Владелец `usermod -aG adm gendesign` пока не выдавал. Рабочий обход (через docker-группу) записан в комментарии файла, вместе с ловушкой `TZ=UTC` из #2760. До выдачи прав правка не регрессия: `docker logs` работает как раньше. ## Blast radius первого деплоя Log-driver — свойство создания контейнера, поэтому `compose up -d` пересоздаст всё. Но backend/worker/beat/caddy/forwarder **и так** force-recreate'ятся каждым деплоем (deploy.yml:417-431). **Инкремент именно этой правки:** - `postgres` — ~10 с даунтайма (у deploy.yml уже есть такой путь на строке 407) - `redis` — брокер celery, graceful stop сохраняет RDB - `osrm` + `osrm-walk` — перезагрузка MLD-графа в RAM (backend не `depends_on`, деплой не блокируется) - `frontend`, `glitchtip-web`, `glitchtip-worker` Разово. Мержится в окно без ночных прогонов (ближайший — 00:39 UTC). **Rollback:** revert коммита → следующий деплой вернёт json-file. Данных не теряет, состояния нет. ## Что НЕ сделано (осознанно) - **Корневой `docker-compose.yml` не трогал.** Issue называет оба файла, но этот — для локальной разработки, а в Docker Desktop (macOS/Windows) journald в VM нет → контейнеры просто не стартуют. Anchor туда = сломанный `docker compose up` у всех локально. Потолок для dev-логов, если понадобится, — отдельный json-file `max-size`. - **`forgejo` не трогал** — действительно чужой стек (10.3 ГБ, ~128 МБ/сутки). Это к владельцу, и это по-прежнему главный потребитель диска. - **Orphan `gendesign-site-finder-1`** (34 МБ, работает 81 день): сервиса `site-finder` в compose-файле НЕТ — контейнер остался от прежней версии и `up -d` его не трогает. Логи так и останутся на json-file. Чинится `--remove-orphans` в deploy.yml, но это меняет поведение деплоя для всего project=gendesign — отдельная задача, не этот PR. - **Уровень логирования не трогал** — это app-код, не infra. ## Test plan - [x] `docker compose -f docker-compose.prod.yml config` — валиден; **все 12 сервисов** резолвятся в `driver: journald, tag: {{.Name}}` (проверено разбором вывода, а не глазами по anchor'у) - [x] journald-драйвер проверен на проде до мержа (throwaway-контейнер, см. выше) - [x] чтение проверено правами deploy-юзера (не root, не sudo) - [ ] после деплоя: все контейнеры Up, `https://gendsgn.ru/` → 200 - [ ] после деплоя: читается запись контейнера, которого больше нет (backend создаётся на строке 396 и уничтожается force-recreate'ом на 417 — проверка доступна в пределах одного деплоя) Refs #2761, #2758, #2741
bot-backend added 1 commit 2026-08-06 22:23:55 +00:00
fix(devops): потолок логов основного стека — journald, как у trade-in (#2761)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
4e2796f159
У корневого docker-compose.prod.yml не было секции logging ВООБЩЕ: дефолтный
json-file растёт без границ и живёт внутри контейнера. Ставим тот же anchor и
тот же драйвер, что уже стоят у trade-in (#2758) — один способ на оба стека.

Главное, что изменилось против постановки issue: glitchtip-worker (2.59 ГБ,
31.9 МБ/сутки — 81% всего роста стека) описан ИМЕННО В ЭТОМ ФАЙЛЕ
(project=gendesign, service=glitchtip-worker, профиль glitchtip активен в
/opt/gendesign/.env). В issue он числился «вне репозитория, к владельцу» —
проверка искала отдельный compose-файл и не нашла сервис внутри нашего.
Так что эта правка закрывает 2.59 из 13 ГБ, а не только «наши 210 МБ».

Вне репозитория остаётся только forgejo (10.3 ГБ, project=forgejo,
/home/gendesign/forgejo/docker-compose.yml) — там правит владелец.

Бюджет посчитан, не предположен: journald сам сообщает "System Journal ... is
2.2G, max 4.0G"; системный поток 22 МБ/сутки + наши 39 + tradein ≈ 65 МБ/сутки
→ ~60 суток глубины при 4G-потолке с самовытеснением. По диску эффект в плюс:
сегодня glitchtip-worker растёт без потолка, а пересоздание удалит его
json-file и разово освободит ~2.6 ГБ.

Корневой docker-compose.yml намеренно НЕ трогаем: он для локальной разработки,
а в Docker Desktop journald нет — контейнеры бы не стартовали.

Refs #2761, #2758, #2741
bot-backend merged commit 896243cf3f into main 2026-08-06 22:29:27 +00:00
bot-backend deleted branch fix/2761-root-stack-logging 2026-08-06 22:29:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#2762
No description provided.