diff --git a/family/how-to/kraken-watchtower.md b/family/how-to/kraken-watchtower.md index c79deb3b..fa05504f 100755 --- a/family/how-to/kraken-watchtower.md +++ b/family/how-to/kraken-watchtower.md @@ -1,5 +1,5 @@ --- -updated: '2026-09-20' +updated: '2026-09-27' title: Watchtower — Kraken created: '2026-09-20' type: tech @@ -10,16 +10,21 @@ tags: - updates - infra - kraken + - pitfall related: - '[[family/how-to/arr-stack-kraken]]' - '[[personal/tech/watchtower-docker-updates]]' - '[[personal/tech/jellyfin-db-schema-mismatch]]' - '[[personal/tech/docker-data-root-mount-race]]' + - '[[personal/tech/kraken-docker-startup-order]]' --- # Watchtower на Kraken (RPi5 / OMV / arm64) -> **Создано:** 2026-09-20. **Статус:** ✅ ЗАВЕРШЕНО — версия 1.22.2 (форк nickfedor), label проставлены на 7 контейнеров, проверено прогоном `scanned=7 failed=0`. -> **Связанные:** [[family/how-to/arr-stack-kraken]], [[personal/tech/watchtower-docker-updates]] (реюзабельная теория), [[personal/tech/jellyfin-db-schema-mismatch]], [[personal/tech/docker-data-root-mount-race]] +> **Создано:** 2026-09-20. **Обновлено:** 2026-09-27. +> 🟡 **Статус 2026-09-27:** фикс 2026-09-20 **не был применён** (контейнер не пересоздан, +> крутился `containrrr/watchtower` без флагов). **Пересоздан 2026-09-27** на `nickfedor/watchtower:1.22.2` +> + критичные контейнеры исключены меткой `enable=false`. Подробно — § «ОПРОВЕРГНУТО» ниже. +> **Связанные:** [[family/how-to/arr-stack-kraken]], [[personal/tech/watchtower-docker-updates]] (реюзабельная теория), [[personal/tech/jellyfin-db-schema-mismatch]], [[personal/tech/docker-data-root-mount-race]], [[personal/tech/kraken-docker-startup-order]] ## ✅ РЕШЕНИЕ (2026-09-20): какие контейнеры обновляются автоматом @@ -155,14 +160,102 @@ done - `radarr-config-preupgrade.tar.gz` (58M), `sonarr-config-preupgrade.tar.gz` (20M), `prowlarr-config-preupgrade.tar.gz` (3.9M) - `prowlarr.db.bak`, `watchtower-compose.bak`, `networks.txt`, 4× `-inspect.json`, 3× compose-файла -## Что дальше (открытые пункты) +## 🔴 ОПРОВЕРГНУТО 2026-09-27: фикс 2026-09-20 НЕ БЫЛ ПРИМЕНЁН + +**Запись «Фикс ВЫПОЛНЕН 2026-09-20» выше — неверна.** Проверка 2026-09-27 показала: + +``` +$ docker inspect watchtower --format '{{.Config.Image}} | Created:{{.Created}}' +containrrr/watchtower | Created:2026-05-12T15:10:55Z +$ docker inspect watchtower --format '{{.Config.Cmd}}' +[] ← БЕЗ ФЛАГОВ +``` + +**Что произошло:** compose-файл действительно был исправлен 2026-09-20 (образ `nickfedor/watchtower:1.22.2`, +флаги `--interval 86400 --cleanup --label-enable`), но **контейнер не был пересоздан**. Создан он +2026-05-12 и с тех пор не менялся. `docker compose up -d watchtower` **не отработал** — +либо не выполнился, либо выполнился не в той директории. + +**Почему проверка это не поймала:** прогон, записанный как доказательство (`scanned=7 failed=0`), +шёл через `docker run --rm ... nickfedor/watchtower:1.22.2`. Это **временный контейнер из нужного +образа** — он подтверждает, что образ рабочий, но **ничего не говорит о работающем контейнере**. +Боевой `watchtower` продолжал крутить `containrrr/watchtower` без `--label-enable`. + +🔴 **Урок (общий):** `docker run --rm <образ>` проверяет **образ**, а не **работающий контейнер**. +Проверять надо `docker inspect` самого контейнера — образ, `Cmd`, дату создания. Если дата создания +старше правки compose-файла, правка не применена. + +🔴 **Урок (общий):** «поправил compose» ≠ «применил». Compose-файл — декларация; пока не выполнен +`docker compose up -d`, работающая конфигурация не меняется. Симптом-маркер: метка/флаг виден в файле, +но не в `docker inspect`. + +### Что реально сделано 2026-09-27 + +1. Бэкап: `/home/kraken/_backup-compose-20260927/` (`mattermost.yml`, `balda.yml`, `obsidian-mcp.yml`, `watchtower-active.yml`). +2. Метка `com.centurylinklabs.watchtower.enable: "false"` добавлена в compose четырёх критичных сервисов: + `mm-postgres`, `mattermost` (`/home/kraken/mattermost/docker-compose.yml`), + `balda-mm` (`/home/kraken/balda/docker-compose.yml`), + `obsidian-mcp` (`/home/kraken/obsidian-mcp/docker-compose.yml`). +3. **Watchtower пересоздан** из `/srv/.../docker/watchtower/` — впервые с 2026-05-12: + ```bash + cd /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/docker/watchtower + sudo docker compose up -d + # Image: nickfedor/watchtower:1.22.2 + # Cmd: [--interval 86400 --cleanup --label-enable] + ``` +4. Контейнеры пересозданы (`docker compose up -d` в каждом проекте) — **метка применяется только + при создании контейнера**. + +### 🔴 `docker update --label-add` НЕ СУЩЕСТВУЕТ + +Метки (`labels`) в Docker **нельзя изменить у работающего контейнера**. Попытка: +```bash +docker update --label-add com.centurylinklabs.watchtower.enable=false mm-postgres +# → unknown flag: --label-add +``` +Единственный путь — правка compose + пересоздание. Это важно: старая идея «проставить метку +командой без пересоздания» **нереализуема**. + +### Результат (проверено 2026-09-27) + +| Контейнер | Метка watchtower.enable | Статус | +|---|---|---| +| `mm-postgres` | `false` | running, healthy | +| `mattermost` | `false` | running, healthy | +| `balda-mm` | `false` | running | +| `obsidian-mcp` | `false` | running | + +Watchtower: `nickfedor/watchtower:1.22.2`, `--interval 86400 --cleanup --label-enable`, +`Next scheduled run: ... in 23 hours 59 minutes 59 seconds`. + +### Следствие: Watchtower УБИВАЛ контейнеры + +Старый Watchtower без `--label-enable` лез во **все** контейнеры и гасил их SIGTERM→SIGKILL. +На `mm-postgres` это выглядело как: +``` +16:25:42 container kill signal=2 ← SIGTERM +16:25:54 container kill signal=9 ← SIGKILL (пауза 11.6 с = таймаут docker stop) +16:25:58 container die exitCode=137, execDuration=70 +``` +`exitCode=137` = 128 + 9 (SIGKILL). **Не OOM** — `dmesg`/`journalctl -k` пусты, RAM available 2540 МБ. +Разбор: [[personal/tech/kraken-docker-startup-order]]. + +⚠️ `restart: unless-stopped` **не защищает** — Watchtower работает вне политик рестарта: он сам +останавливает контейнер, чтобы пересоздать его с новым образом. + +### Что дальше (открытые пункты) | # | Пункт | Статус | |---|---|---| | 1 | `Allowed Hosts is not configured` warning в Radarr/Sonarr/Prowlarr — ограничить, какие hostname могут обращаться | ⏳ отдельная задача | | 2 | Home Assistant на Kraken — `Exited (128)` ~2 месяца, не поднимался (см. [[family/how-to/kraken-access]]) | ⏳ отдельная задача | +| 3 | `/home/kraken/watchtower/` — директории нет; метка старого контейнера ссылалась на удалённый файл. **Проверить, нужна ли чистка метаданных** | ⏳ | +| 4 | `/home/kraken/_backup-compose-20260927/` — удалить после проверки Alex | ⏳ | +| 5 | ⚠️ Проверить поведение при **ребуте хоста** — соблюдается ли порядок между проектами при загрузке | ⏳ | -✅ Закрыто 2026-09-20: label проставлены, решение по Jellyfin принято (`latest`), первый прогон с label прошёл (`scanned=7 failed=0`). +✅ Закрыто 2026-09-20 (частично): label проставлены на 7 контейнеров. +✅ Закрыто 2026-09-27: Watchtower **реально** пересоздан, критичные контейнеры исключены меткой. +🔴 Открыто: правка 2026-09-20 не была применена — **проверять `docker inspect`, а не файл**. ## Теория (реюзабельно) diff --git a/personal/tech/kraken-docker-startup-order.md b/personal/tech/kraken-docker-startup-order.md index d8d1e2df..c9b9f784 100755 --- a/personal/tech/kraken-docker-startup-order.md +++ b/personal/tech/kraken-docker-startup-order.md @@ -4,7 +4,7 @@ created: '2026-09-27' updated: '2026-09-27' type: tech namespace: personal -status: awaiting-verification +status: resolved tags: - kraken - docker @@ -17,6 +17,8 @@ tags: - balda related: - '[[personal/tech/kraken-host-resource-limits.md]]' + - '[[personal/tech/watchtower-docker-updates]]' + - '[[family/how-to/kraken-watchtower]]' - '[[personal/projects/balda/balda-kraken-mm-deploy.md]]' - '[[personal/plans/balda-mm-endtoend-plan.md]]' --- @@ -29,6 +31,15 @@ related: > > **Найдены ДВЕ независимые причины.** Они не связаны между собой и лечатся по-разному. > См. `personal/tech/kraken-host-resource-limits.md` — это ДРУГОЙ инцидент (нехватка RAM), не путать. +> +> ✅ **ИТОГ 2026-09-27:** причина №1 (Watchtower) **исправлена**. Причина №2 (порядок запуска) +> оказалась **снята с повестки** — `depends_on` внутри проекта работал корректно, слияние +> проектов не понадобилось. Стек поднят и healthy. Решение — § «ВЫПОЛНЕНО» ниже. +> +> 🔴 **Корень причины №1 лежал в документации:** [[family/how-to/kraken-watchtower]] утверждал +> «фикс ВЫПОЛНЕН 2026-09-20», но контейнер `watchtower` не был пересоздан (создан 2026-05-12, +> крутил `containrrr/watchtower` с `Cmd: []`). Правка в файле ≠ применённая правка. +> Проверять `docker inspect`, а не файл. ## Расклад контейнеров: три ОТДЕЛЬНЫХ compose-проекта diff --git a/personal/tech/watchtower-docker-updates.md b/personal/tech/watchtower-docker-updates.md index fd655004..d6795394 100755 --- a/personal/tech/watchtower-docker-updates.md +++ b/personal/tech/watchtower-docker-updates.md @@ -1,14 +1,15 @@ --- title: Watchtower — почему не обновляет контейнеры created: '2026-09-20' -updated: '2026-09-20' +updated: '2026-09-27' type: tech namespace: personal -tags: [watchtower, docker, updates, infra, pitfall] +tags: [watchtower, docker, updates, infra, pitfall, compose] related: - "[[family/how-to/kraken-watchtower]]" - "[[personal/tech/docker-data-root-mount-race]]" - "[[personal/tech/jellyfin-db-schema-mismatch]]" + - "[[personal/tech/kraken-docker-startup-order]]" --- # Watchtower — почему не обновляет контейнеры @@ -85,12 +86,93 @@ services: - `--cleanup` — удалять старые образы; без него диск забивается (на Kraken 87%) - `--label-enable` — оптин: обновляется только то, что явно помечено -## Проверка (обязательна) +## 🔴 Причина 5 (главная по коварству): правка compose НЕ ПРИМЕНЕНА -`Up` + отсутствие ошибок ≠ работа. Нужен живой прогон: +`--interval`, дедуп по API-версии, форк образа — всё это может быть **правильно прописано в файле** +и при этом **не действовать**, потому что контейнер не пересоздан. Compose-файл — **декларация**; +работающая конфигурация меняется только после `docker compose up -d`. + +**Маркер проблемы:** конфиг в файле ≠ `docker inspect` контейнера. + +```bash +# что РЕАЛЬНО запущено (а не что написано в файле) +docker inspect watchtower --format 'Image:{{.Config.Image}}' +docker inspect watchtower --format 'Cmd:{{.Config.Cmd}}' +docker inspect watchtower --format 'Created:{{.Created}}' +``` + +**Признак, что правка не применена:** `Created` (дата создания контейнера) **старше** даты правки +compose-файла. На Kraken: файл исправлен 2026-09-20, контейнер создан 2026-05-12 и не менялся — +4 месяца крутился старый `containrrr/watchtower` с `Cmd: []`. + +### 🔴 Почему фейковая проверка проходит: `docker run --rm` проверяет ОБРАЗ, а не контейнер + +Соблазнительный способ «проверить Watchtower»: ```bash docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ - nickfedor/watchtower:1.22.2 --run-once --monitor-only + nickfedor/watchtower:1.22.2 --run-once --monitor-only --label-enable +# → Update session completed failed=0 scanned=7 updated=0 ← выглядит как доказательство! +``` +Этот прогон создаёт **временный контейнер из указанного образа** и корректно отвечает на вопрос +«рабочий ли образ `nickfedor/watchtower:1.22.2`». Он **ничего не говорит** о том, какой образ +и с какими флагами крутится в боевом контейнере `watchtower`. На Kraken именно этот прогон был +записан в документацию как «проверено, фикс работает» — при том, что боевой Watchtower оставался +старым и **убивал контейнеры**. + +🔴 **Правило:** для проверки **работающего** сервиса смотреть `docker inspect `, +а не запускать отдельный экземпляр образа. `docker run --rm` = проверка образа. + +## 🔴 Watchtower может УБИВАТЬ контейнеры (SIGTERM → SIGKILL) + +Watchtower не только обновляет — он **останавливает** контейнеры, и это видно как: + +``` +container kill signal=2 ← SIGTERM +container kill signal=9 ← SIGKILL, пауза ~10 с = таймаут docker stop +container die exitCode=137 +``` + +`exitCode=137` = 128 + 9 (SIGKILL). **Не путать с OOM-killer:** проверить +`dmesg | grep -i -E 'oom|killed process'` и `journalctl -k -b` — если пусто, убийца не ядро. + +⚠️ **`restart: unless-stopped` НЕ защищает от Watchtower.** Политики рестарта действуют на сбои; +Watchtower останавливает контейнер сам, штатно, чтобы пересоздать его с новым образом. + +Признак в логе Watchtower, что он полез вне расписания: +``` +level=info msg="Waiting for running update to be finished..." +``` +Это значит предыдущая сессия обновления зависла и не закрылась — Watchtower дождался её +и пошёл по контейнерам немедленно. + +**Как исключить контейнер из-под Watchtower:** метка `com.centurylinklabs.watchtower.enable=false` +(требуется `--label-enable`, иначе метка игнорируется). + +## 🔴 `labels` нельзя изменить у работающего контейнера + +**`docker update --label-add` НЕ СУЩЕСТВУЕТ** (`unknown flag: --label-add`). Метки задаются +только при создании контейнера. Единственный путь: правка `labels` в compose + `docker compose up -d` +(пересоздание). Проверено на Docker 29.4.3 / Compose v5.1.3. + +Практическое следствие: «проставить метку без пересоздания» — нереализуемая идея. Пересоздание +безопасно, если данные в томах, но требует явного решения. + +## Проверка (обязательна) + +`Up` + отсутствие ошибок ≠ работа. **И `docker run --rm` ≠ проверка работающего сервиса** (см. выше). + +Правильный двухшаговый порядок: + +```bash +# ШАГ 1 — что РЕАЛЬНО запущено (главная проверка) +docker inspect watchtower --format 'Image:{{.Config.Image}} Cmd:{{.Config.Cmd}} Created:{{.Created}}' +# Image: nickfedor/watchtower:1.22.2 +# Cmd: [--interval 86400 --cleanup --label-enable] +# Created: <должна быть ПОЗЖЕ правки compose-файла> + +# ШАГ 2 — рабочий ли образ (вспомогательно) +docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ + nickfedor/watchtower:1.22.2 --run-once --monitor-only --label-enable ``` `--monitor-only` обязателен, иначе `--run-once` **реально обновит** контейнеры. @@ -102,6 +184,9 @@ level=info msg="Update session completed" failed=0 scanned=N skipped=0 updated=0 ``` `Found new image` = баг с метаданными вылечен. `updated=0` в monitor-only — нормально. +⚠️ Если `scanned` **больше** ожидаемого числа размеченных контейнеров — значит `--label-enable` +не действует, и Watchtower трогает всё подряд. Проверять `Cmd` из шага 1. + ## ⚠️ Автообновление может сломать сервис: пинить мажор версии Тип `latest` + автообновление = переход на новую **мажорную** версию без предупреждения. Для сервисов с миграциями БД (EF Core, Prisma, любые forward-only миграции) это необратимо: @@ -127,10 +212,26 @@ level=info msg="Update session completed" failed=0 scanned=N skipped=0 updated=0 ## Урок -Watchtower — компонент, который **молча не работает**. Его нельзя «поставить и забыть»: обязательны (а) явный `--interval`, (б) отсутствие `DOCKER_API_VERSION`, (в) периодический `--run-once --monitor-only` как health-check, (г) осознанное решение, каким контейнерам вообще разрешено автообновление. +Watchtower — компонент, который **молча не работает**. Его нельзя «поставить и забыть»: обязательны (а) явный `--interval`, (б) отсутствие `DOCKER_API_VERSION`, (в) периодический `--run-once --monitor-only` как health-check, (г) осознанное решение, каким контейнерам вообще разрешено автообновление, (д) **проверка `docker inspect`, что правка реально применена**. + +### 🔴 Два универсальных урока (2026-09-27) + +**1. Файл ≠ работающая конфигурация.** Compose-файл — декларация. Пока не выполнен +`docker compose up -d`, ничего не изменилось. Проверять **`docker inspect` контейнера**, а не файл. +Дата создания контейнера (`Created`) старше даты правки файла = **правка не применена**. + +**2. `docker run --rm <образ>` проверяет ОБРАЗ, а не работающий контейнер.** Прогон временного +контейнера из «правильного» образа выглядит как полноценное доказательство (`scanned=7 failed=0`), +но не говорит ничего о боевом сервисе. Именно так на Kraken в документацию попала запись +«фикс выполнен», при том что 4 месяца крутился старый Watchtower и **убивал контейнеры**. +Для проверки боевого сервиса — только `docker inspect` самого контейнера. + +Оба урока общие, не только про Watchtower: они применимы к любому сервису, правка которого +делается через compose-файл. ## Связанные - [[family/how-to/kraken-watchtower]] — живой разбор и точные команды на Kraken +- [[personal/tech/kraken-docker-startup-order]] — что Watchtower ломает на Kraken (убийство контейнеров, exit 137) - [[personal/tech/jellyfin-db-schema-mismatch]] — что ломает автообновление мажора - [[personal/tech/docker-data-root-mount-race]] — другой дефект «контейнеры пропали»