fix(tradein/devops): логи в journald — переживают пересоздание контейнера (#2741)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped

json-file пишет в /var/lib/docker/containers/<id>/ — каталог удаляется вместе
с контейнером. При ~20 деплоях в сутки окно жизни лога редко больше часа, а
скрейперы работают ночью: к утреннему разбору логов заведомо нет. За один день
это трижды сорвало разбор (#2695 — 1600 отказов Авито, #2698, #2676).

journald отдаёт stdout/stderr в /var/log/journal на хосте: запись переживает
`docker rm` и адресуется по времени (--since/--until), а не «сколько осталось
от последнего рестарта». Глубина ограничена journald глобально (SystemMaxUse,
дефолт 4G), а не 60 МБ на сервис, обнуляемыми деплоем.

Проверено на проде до правки: контейнер с --log-driver=journald + --rm, после
его удаления запись читается по -t и по CONTAINER_NAME. Замеры там же:
/var/log/journal = 2.3G за 103 дня (~22 МБ/сутки), tradein добавит единицы-
десятки МБ/сутки (5.8k карточек за сутки по scrape_runs, ~153 Б/строку) —
вытеснения не будет неделями при требуемых «сутки-двое».

`docker logs <c>` работает как раньше (демон читает журнал сам). Прямой
journalctl под deploy-юзером не работает — он в docker, но не в adm; рабочий
docker-однострочник и разовая команда владельцу записаны в комментарии.
This commit is contained in:
bot-backend 2026-08-07 02:19:11 +05:00
parent 79f7b8fff3
commit 62f64f36cb

View file

@ -23,13 +23,47 @@
# ПЕРЕСОЗДАСТ ВСЕ tradein-контейнеры, включая scraper — прервёт бегущий sweep
# (stop_grace_period 120s даёт unit'у до-checkpoint'иться). Допустимо, разово.
#
# logging: json-file с ротацией (20m × 3 = ≤60M на сервис). До этого драйвер по
# умолчанию рос без границ. Общий anchor ниже.
# logging: journald (#2741). Было json-file 20m × 3 — оно решало только размер,
# и ценой того, что лог ЖИВЁТ В КОНТЕЙНЕРЕ: `docker rm` уносит его целиком.
# Деплоев ~20/сутки, скрейперы работают ночью, разбор идёт утром → окно жизни
# лога почти никогда не покрывает интересное (#2695, #2698, #2676 — три разбора
# подряд уперлись в «логов уже нет»).
#
# journald: демон отдаёт stdout/stderr в /var/log/journal (persistent, на хосте),
# запись переживает пересоздание контейнера и читается ПО ВРЕМЕНИ, а не «сколько
# осталось от последнего рестарта».
#
# КАК ЧИТАТЬ (проверено на проде 2026-08-06):
# docker logs tradein-scraper # как и раньше: только текущий контейнер
# # история через пересоздания — журнал принадлежит root, а deploy-юзер
# # gendesign состоит в docker, но НЕ в adm/systemd-journal, и sudo просит пароль,
# # поэтому голый `journalctl` у него выдаёт «No entries». Рабочий однострочник:
# docker run --rm -v /:/host:ro alpine chroot /host \
# journalctl -t tradein-scraper --utc --since "2026-08-07 00:30" --until "01:30"
# # то же по метке контейнера: CONTAINER_NAME=tradein-scraper (-o cat -f для tail -f)
# Владельцу стоит разово выдать `sudo usermod -aG adm gendesign` — после этого
# journalctl работает напрямую, без docker-обёртки (host-config, не этот файл).
#
# Ротация: журнал общий на хост, режется самим journald по размеру (SystemMaxUse)
# и НЕ обнуляется пересозданием контейнера — то есть глубина истории меряется
# сутками, а не «сколько прошло с последнего деплоя» (это и был баг #2741/#2715:
# 60 МБ json-file при флуде прокручивались за минуты, а деплой обнулял и их).
# Жёсткий потолок по возрасту (MaxRetentionSec) — при желании host drop-in.
# Замер 2026-08-06: /var/log/journal =
# 2.3G за 103 дня (~22 МБ/сутки системных) при дефолтном потолке SystemMaxUse=4G;
# tradein добавляет единицы-десятки МБ/сутки (5.8k карточек за сутки по
# scrape_runs, ~153 Б/строку) → до вытеснения старого ещё недели, требуемые
# «сутки-двое» покрыты с запасом. Дисковый риск нулевой: 4G — это ПОТОЛОК, при
# его достижении journald сам удаляет старейшее.
# Ceiling: journald рейт-лимитит (дефолт 10000 сообщений / 30s на сервис) — при
# превышении в журнал попадёт «Suppressed N messages». Июльский флуд postgres
# (40 МБ за 6 часов ≈ 12 строк/с) от лимита в ~25 раз ниже, но если такое
# появится — это host-config (drop-in journald.conf.d), не этот файл.
# tag: имя контейнера вместо ID — SYSLOG_IDENTIFIER стабилен между пересозданиями.
x-logging: &default-logging
driver: json-file
driver: journald
options:
max-size: "20m"
max-file: "3"
tag: "{{.Name}}"
services:
browser: