Files
obsidian-vault/personal/tech/skill-docker-data-root-mount-race.md
T

7.3 KiB
Raw Blame History

Skill mirror: docker-data-root-mount-race

Зеркало скилла docker-data-root-mount-race (категория docker-service-administration) для поиска по vault. Создано: 2026-09-20. Источник: Kraken mount-race инцидент. Связанные: personal/tech/docker-data-root-mount-race (полный разбор), personal/tech/jellyfin-db-schema-mismatch, family/plans/kraken-backup-to-truenas

Когда применять

Хост, у которого Docker data-root лежит на поздно монтирующемся диске (USB HDD, OMV /srv/dev-disk-by-uuid-*, RPi + внешний диск):

  • docker ps -a пусто, хотя контейнеры были.
  • docker info → Containers=0 при смонтированном диске.
  • После reboot сервисы в контейнерах «молча down» (туннели, reverse-proxy, jellyfin) — но не удалены.
  • Journal: Duplicate entry in '/etc/fstab'?

Механизм

dockerd стартует раньше, чем монтируется диск с data-root → видит пустой каталог → загружает пустое состояние (0 контейнеров). Диск монтируется позже, но daemon не перечитывает. Данные целы.

Причина: docker.service без RequiresMountsFor=.

Диагностика

docker ps -a --format "{{.Names}}\t{{.Status}}"                 # пусто
docker info --format "{{.DockerRootDir}} | containers={{.Containers}}"
sudo ls <data-root>/containers/ | wc -l                         # >0 = целы
ps -o pid,lstart,cmd -p "$(pgrep -x dockerd)"                   # РЕАЛЬНОЕ время старта
uptime -s; sudo dmesg | grep -iE "EXT4-fs \(sd[a-z]" | tail -5
systemctl show docker -p RequiresMountsFor                      # пусто = гонка возможна
grep -c "<DISK-UUID>" /etc/fstab                                # 2 = дубль

Питфоллы диагностики:

  • systemctl show docker -p ExecMainStartTimestamp даёт stale время → только ps -o lstart.
  • RestartPolicy.Name в hostconfig.json = null, но в live-состоянии политика есть. Проверять docker inspect <name> --format '{{.HostConfig.RestartPolicy.Name}}'. При unless-stopped контейнеры вернутся сами, ручной docker start не нужен.

Фикс

Немедленно: sudo systemctl restart docker (диск уже смонтирован).

Постоянно: drop-in /etc/systemd/system/docker.service.d/mounts.conf:

[Unit]
RequiresMountsFor=/srv/dev-disk-by-uuid-<DISK>/docker-data
After=srv-dev\x2ddisk\x2dby\x2duuid\x2d<DISK>.mount
  • systemctl daemon-reload, + убрать дубль строки из /etc/fstab.

Проверка: systemctl show docker -p Requires -p After → маунт-юнит в обоих.

Подтверждение реальным reboot

systemctl restart docker — НЕ тот же тест, что reboot (диск уже смонтирован). Проверка после реального выключения/включения 2026-09-20: все 12 контейнеров поднялись сами.

ssh kraken 'docker ps -q | wc -l'      # ожидаем ~12, НЕ 0

🔴 «Сервис down после reboot» — ДВЕ разные причины, не путать

Наблюдение Причина Где смотреть
docker ps пусто (0) mount race этот скилл
Контейнер Up, но 5xx / unhealthy своя ошибка приложения лог внутри контейнера

Реальный пример путаницы: после reboot jellyfin был Up, но 503 — из-за несовпадения версий (образ старше схемы БД), НЕ из-за монтирования. См. personal/tech/jellyfin-db-schema-mismatch.

Правило: контейнер Up → mount race ни при чём, читать свой лог приложения.

Питфолл — логи не в stdout. docker logs может быть пуст, если приложение пишет в файл внутри volume (jellyfin: /config/log/log_YYYYMMDD.log). Читать файл; фильтровать старые ошибки по StartedAt.

Питфолл — дрейф версий у latest-тегов. docker inspect image:latest показывает digest, откуда скачали, а не текущий latest. Локальный latest может быть на месяцы старше. Проверять версию внутри образа: docker run --rm --entrypoint sh <image>:latest -c '<bin> --version'. Если версия БД выше версии сервера → пересоздать контейнер на верном образе, НЕ даунгрейдить данные.

Смежная ловушка: смешанные владельцы под data-root

Бэкап/tar дерева сервисов на том же диске: часть каталогов root:root, часть 1000:1000 → обычный юзер получает Permission denied и молча частичный архив.

sudo ls -ldn <data-root>/../docker/*/ | awk '{print $3, $4, $NF}'   # владельцы

Если sudo -n true проходит — архивировать под sudo -n, а на приёмнике ставить владельца: rsync --chown=<user>:<group>.

Питфолл — долгий chown -R/tar умирает с SSH-сессией. Запускать отсоединённо:

sudo systemd-run --unit=<name> --collect --property=Type=oneshot chown -R 1000:1000 <path>

Питфолл — Restarting (1) + mkdir: Permission denied на data-каталоге: владелец на хосте ≠ UID контейнера. docker inspect <name> --format '{{.Config.User}}' → chown <uid>:<gid>. Первые mkdir-ошибки могут быть буферизованным остатком от прошлого запуска — убедиться, что контейнер реально держится Up.

Никогда

  • Не считать docker ps пустым признаком потери данных — сначала проверить containers/ count.
  • Не rm -rf «устаревший» docker-data — это реальные данные.
  • Не править /etc/fstab через sed — писать файл локально, findmnt --verify --fstab, затем копировать.

Чеклист после фикса

  • docker info count == число каталогов в containers/
  • RequiresMountsFor непусто и указывает на docker-data
  • grep -c "<UUID>" /etc/fstab == 1
  • После systemctl restart docker критичные контейнеры Up
  • После реального reboot: docker ps -q | wc -l == ожидаемое, не 0