Files
obsidian-vault/personal/tech/docker-data-root-mount-race.md
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

10 KiB
Executable File
Raw Permalink Blame History

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 их не видит. Ничего не потеряно.

Как распознать (диагностика)

# 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. Немедленно (диск уже смонтирован)

sudo systemctl restart docker    # daemon перечитает диск → все контейнеры вернутся

2. Постоянно, reboot-безопасно — drop-in

/etc/systemd/system/docker.service.d/mounts.conf:

[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:

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).

Связанные заметки