[2026-09-27] eagle: family/how-to/kraken-watchtower.md personal/tech/kraken-docker-startup-order.md personal/tech/watchtower-docker-updates.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 16:46:04 +06:00
1 parent b4645e072f
commit 4d62d8608f
3 files changed
+217 -12

No files matched your search

+12 -1
View File
@@ -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-проекта
+107 -6
View File
@@ -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 <container>`,
а не запускать отдельный экземпляр образа. `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]] — другой дефект «контейнеры пропали»