Files
obsidian-vault/personal/tech/docker-data-root-mount-race.md
T
Taiga b53dd70a15 [2026-09-26 09:10:28] syncthing: personal/tech/archive-org-collection-harvest.md personal/tech/balda-go-fx-pitfalls.md personal/tech/bilety-oxota-interaktiv.md personal/tech/cloudflare-tunnel-ssh-published-route.md personal/tech/deepseek-language-drift.md personal/tech/docker-data-root-mount-race.md personal/tech/docker-mac-disk-issues.md personal/tech/docker_colima_setup.md personal/tech/eagle-dashboard-launchd.md personal/tech/hermes-agent-improvements.md personal/tech/hermes-browser-toolsets.md personal/tech/hermes-fork-vs-upstream.md personal/tech/hermes-git-repo.md personal/tech/hermes-memory-architecture.md personal/tech/hermes-secret-redaction-bypass.md personal/tech/hermes-self-improvement-review.md personal/tech/home-network-config.md personal/tech/inter-site-throughput-taiga-to-mac.md personal/tech/inter-site-throughput-taiga-to-mac.md.bak-nedstab2 personal/tech/jellyfin-db-schema-mismatch.md personal/tech/karabiner-keyboard-config.md personal/tech/kraken-media-inventory-facts.md personal/tech/kraken-truenas-media-copies.md personal/tech/lampa-jellyfin-plugin.md personal/tech/lampac-deployment.md personal/tech/lampac-torrserver.md personal/tech/llm-model-selection-2026.md personal/tech/mac-print-shared-services.md personal/tech/modbus-rtu-sniffer-debugging.md personal/tech/multistream-single-flow-limit.md personal/tech/multistream-single-flow-limit.md.bak-nedstab2 personal/tech/music-tags-repair-truenas.md personal/tech/obsidian-mcp-ecosystem.md personal/tech/original-xbox-2025-guide.md personal/tech/prowlarr-flaresolverr-cloudflare.md personal/tech/radarr-sonarr-phantom-records.md personal/tech/roundtrip-key-verification.md personal/tech/ryujinx-mac-setup.md personal/tech/skill-arr-media-stack-setup-additions.md personal/tech/skill-docker-data-root-mount-race.md personal/tech/skill-lampac-kraken-deploy.md personal/tech/tirith-trust.md personal/tech/truenas-local-tree-comparison.md personal/tech/truenas-nfs4-acl-and-arrmultiuser.md personal/tech/truenas-zfs-panic-recovery.md personal/tech/vault-doc-pruning.md personal/tech/vless-space-subscription-egress.md personal/tech/watchlist-sync-feed.md personal/tech/watchtower-docker-updates.md personal/tech/xray-outbound-subscription-3xui.md personal/tech/xray-reverse-limits-private-network.md personal/tech/xray-reverse-tunnel-kraken-truenas.md personal/tech/xray-reverse-tunnel-kraken-truenas.md.bak-nedstab2 personal/tech/yandex-music-api.md
2026-09-26 09:10:28 +00:00

129 lines
10 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker data-root ↔ монтирование диска — гонка (mount race)
> **Обнаружено:** 2026-09-20 на Kraken (RPi5/OMV, USB-HDD).
> **Симптом-класс:** «пропали все контейнеры», `docker ps -a` пусто, `docker info` = `Containers=0` — **при целых данных на диске**.
> **Связанные:** [[family/how-to/kraken-access]], [[personal/tech/xray-reverse-tunnel-kraken-truenas]], [[family/plans/kraken-backup-to-truenas]]
## Механизм (главное)
`docker.service` **не имеет зависимости от монтирования диска** (`RequiresMountsFor=` пусто). Если `dockerd` стартует **раньше**, чем монтируется диск с `data-root`:
1. dockerd открывает путь `data-root` → диска там ещё нет → видит **пустой** каталог на root-ФС (или создаёт новый).
2. Через N секунд диск монтируется **поверх** точки монтирования — реальные данные «появляются».
3. Но dockerd уже разобрал своё (пустое) состояние и держит его **в памяти**. Перечитывания нет.
Итог: **контейнеры целы на диске, но daemon их не видит.** Ничего не потеряно.
## Как распознать (диагностика)
```bash
# 1) daemon видит 0, а на диске metadata есть?
docker info --format "{{.DockerRootDir}} | containers={{.Containers}}"
sudo ls /srv/dev-disk-by-uuid-<...>/docker-data/containers/ | wc -l # 35 = целы
# 2) реальное время старта dockerd (ExecMainStartTimestamp ВРЁТ — stale!)
ps -o pid,lstart,cmd -p $(pgrep -x dockerd)
# 3) когда смонтирован диск (dmesg: "[130s] EXT4-fs (sda1): mounted")
uptime -s
findmnt /srv/dev-disk-by-uuid-<...>
sudo journalctl -u docker -b | head -5
# 4) есть ли зависимость от mount?
systemctl show docker -p RequiresMountsFor # пусто = ГОНКА ВОЗМОЖНА
```
**Ключевой признак:** `docker ps -a` пусто + `docker-data/containers/` полон → **это гонка**, а не потеря данных.
> ⚠️ **Питфолл:** `systemctl show docker -p ExecMainStartTimestamp` дал stale-время (`15:18:36`), истинное — из `ps -o lstart` (`17:31:01`). Сверять с `dmesg` по времени монтирования.
## Факт инцидента 2026-09-20 (Kraken)
| Событие | Время |
|---|---|
| Boot | 17:29:17 |
| Ядро видит `sda1` | +130 с → 17:31:27 |
| **dockerd стартовал** | **17:31:01** ← на 26 с раньше диска |
| Диск смонтирован | 17:31:27 |
USB-HDD на Kraken поднимается **~130 с** — dockerd его обгоняет.
## Фикс
### 1. Немедленно (диск уже смонтирован)
```bash
sudo systemctl restart docker # daemon перечитает диск → все контейнеры вернутся
```
### 2. Постоянно, reboot-безопасно — drop-in
`/etc/systemd/system/docker.service.d/mounts.conf`:
```ini
[Unit]
RequiresMountsFor=/srv/dev-disk-by-uuid-<UUID>/docker-data
After=srv-dev\x2ddisk\x2dby\x2duuid\x2d<UUID>\x2d....mount
```
Затем `sudo systemctl daemon-reload`. Проверка: `systemctl show docker -p Requires -p After` → маунт-юнит в **обоих**.
> Имя маунт-юнита: systemd экранирует `-` как `\x2d`. Взять из `systemctl list-units --type=mount`.
### 3. Проверить fstab на дубли
Симптом в journal: `Failed to create unit file ... already exists. Duplicate entry in '/etc/fstab'?`
На Kraken было **два** маунта одного диска (OMV-блок + ручная строка `UUID=...`) → неопределённость порядка. Убрать дубль.
## Сопутствующий питфолл: права на `~` контейнера
После перерегистрации диска каталог может получить **чужой UID**. На Kraken `docker/hermes/config` стал `10000:10000 700`, а контейнер работает под `USER 1000` → `mkdir: /opt/data: Permission denied` → **crash-loop** (`Restarting (1)`).
**Признак:** `Restarting (1)` + в логах циклический `Permission denied` на `HOME`-каталог.
**Фикс:** `sudo chown -R 1000:1000 <dir>` (UID взять из `docker inspect --format '{{.Config.User}}'`).
**Как запускать долгий chown:** `sudo -n systemd-run --unit=<name> --collect --property=Type=oneshot chown -R ...` — переживает разрыв SSH (большое дерево на USB-диске chown'ится минутами).
## Урок
Любой сервис, зависящий от данных на **медленно монтирующемся диске** (USB-HDD), ОБЯЗАН иметь `RequiresMountsFor`. Без него — гонка при каждом reboot, лотерея.
## ✅ Фикс подтверждён живым reboot'ом (2026-09-20)
Alex выключил Kraken (`sudo shutdown -h now`), затем включил. **Все контейнеры поднялись сами** — `docker ps` не пуст (12 Up: `cloudflared`, `xray-reverse-bridge`, `rclone`, `jellyfin`, sonarr/radarr/prowlarr, transmission, portainer, hermes-kraken, flaresolverr, watchtower). Ручных `docker start` не потребовалось.
**Команда проверки после каждого reboot:**
```bash
ssh kraken 'docker ps -q | wc -l' # ожидаем ~12, НЕ 0
```
Если 0 — гонка вернулась: `systemctl show docker -p RequiresMountsFor` должен быть непуст.
> ⚠️ **Не путать с другим дефектом:** после этого reboot `jellyfin` поднялся, но отдавал **503** — это **отдельная** проблема (сервер старше БД, `no such column: p.Permission_Permissions_Guid`), НЕ связанная с гонкой монтирования. См. [[personal/tech/jellyfin-db-schema-mismatch]]. При диагностике «сервис не работает после reboot» различать: контейнер не поднялся (гонка) vs поднялся, но падает внутри (своя ошибка приложения).
> ℹ️ **Обновление 2026-09-20 (позже в тот же день):** `radarr` был **пересоздан** (переведён из общего проекта `docker` в свой `docker/radarr`, обновлён до 6.4.4) — см. [[family/how-to/arr-stack-kraken]]. `watchtower` сменил образ на `nickfedor/watchtower:1.22.2` — см. [[family/how-to/kraken-watchtower]]. Число контейнеров и их состав могли измениться; проверка `docker ps -q | wc -l` по-прежнему корректна как индикатор гонки, но ожидаемое число сверять заново.
## Крон не догоняет пропущенное окно
Общий питфолл для сервисов на этом узле: если Kraken лежал, cron-задачи (бэкап `0 4 * * *`, t610-подобные) **пропускаются без компенсации**. Для критичных — добавлять `@reboot`-строку с проверкой «артефакт за сегодня уже есть?».
## 🔴 Диск НЕ нестабилен — «нестабильность» была ошибочным диагнозом (2026-09-25)
Формулировку **«USB-HDD на Kraken нестабилен»** нельзя использовать: это обобщение разового инцидента
2026-09-01 (циклы USB enumeration: `device descriptor read/64 error -110`, `device not accepting address
error -62`, `[sda] Read Capacity(10) failed` → `0 B`), который позже был **объяснён и закрыт**.
**Истинная причина всех симптомов «диск отвалился» — гонка монтирования** (этот документ, раздел выше):
диск монтируется ~130 с, `dockerd` стартует раньше и видит пустой `data-root`. Фикс
(`RequiresMountsFor` drop-in) применён 2026-09-20 и **проверен живым reboot'ом** — все контейнеры
поднялись сами, ручных `docker start` не потребовалось.
**Реальные характеристики диска (не дефекты):**
- 5400rpm 2.5" механика → ~95 МБ/с на чтение. Это лимит механики, не USB (линк USB3 = 5000 Mbps подтверждён).
- Медленный подъём при boot (~130 с) — свойство, а не поломка; обрабатывается зависимостью в systemd.
**Правило для будущих сессий:** при «контейнеры пропали / сервис не поднялся после reboot» — проверять
гонку (`docker info` vs содержимое `docker-data/containers/`), а не списывать на «нестабильный диск».
Не давать оценку «диск нестабилен» без новых доказательств (raw USB-ошибок в `dmesg` за пределами boot).
## Связанные заметки
- [[personal/tech/jellyfin-db-schema-mismatch]] — jellyfin после reboot: сервер старше БД (503)
- [[family/how-to/kraken-access]] — доступ к Kraken, список контейнеров
- [[family/plans/kraken-backup-to-truenas]] — бэкап стека Kraken на TrueNAS
- [[personal/tech/xray-reverse-tunnel-kraken-truenas]] — падение reverse-туннеля от той же гонки