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

167 lines
9.3 KiB
Markdown
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.
---
title: Docker data-root ↔ mount race — диагностика
created: '2026-09-20'
type: tech
namespace: personal
tags:
- docker
- systemd
- mount
- kraken
- diagnostics
confidence: high
related:
- '[[family/how-to/kraken-access]]'
- '[[personal/tech/xray-reverse-tunnel-kraken-truenas]]'
---
# Docker data-root ↔ mount race
> Обнаружено 2026-09-20 на Kraken (RPi5/OMV, USB-HDD). Зеркало скилла
> `docker-data-root-mount-race` (категория `docker-service-administration`).
## Триггер
Хост, у которого Docker `data-root` лежит на **диске, монтирующемся поздно**
(USB-HDD, OMV `/srv/dev-disk-by-uuid-*`, RPi). Признаки:
- `docker ps -a` **пусто**, но контейнеры раньше были.
- `docker info` → `Containers=0`, при этом диск с `docker-data` смонтирован.
- После reboot сервисы в контейнерах (cloudflared, reverse-proxy bridge, jellyfin…)
молча «down» — но контейнеры **не удалялись**.
- journal: `Duplicate entry in '/etc/fstab'?` от `systemd-fstab-generator`.
## Механизм
`dockerd` стартует **раньше**, чем монтируется диск с его `data-root`. Открывает
путь, видит пусто → инициализирует **пустое** состояние (0 контейнеров). Через
секунды диск монтируется поверх точки, реальный `containers/` появляется — но
dockerd уже разобрал состояние и **не перечитывает**.
**Ничего не потеряно.** Metadata цела на диске, daemon её просто не видит.
Причина: у `docker.service` **нет `RequiresMountsFor=`** на маунт-юнит диска.
## Диагностика
```bash
# 1) daemon думает, что пусто
docker ps -a --format "{{.Names}}\t{{.Status}}" # пусто
docker info --format "{{.DockerRootDir}} | containers={{.Containers}}"
# 2) но metadata НА ДИСКЕ (нужен root; каталог 711 root:root)
sudo ls -la /srv/dev-disk-by-uuid-<DISK>/docker-data/
sudo ls /srv/dev-disk-by-uuid-<DISK>/docker-data/containers/ | wc -l # >0 = целы
# 3) подтвердить гонку: dockerd стартовал РАНЬШЕ диска
ps -o pid,lstart,cmd -p "$(pgrep -x dockerd)" # истинное время старта
uptime -s # время boot
sudo dmesg | grep -iE "EXT4-fs \(sd[a-z]" | tail -5 # когда ФС смонтировалась
findmnt /srv/dev-disk-by-uuid-<DISK>
# 4) подтвердить отсутствие зависимости (корень)
systemctl show docker -p RequiresMountsFor # пусто = нет порядка
grep -c "<DISK-UUID>" /etc/fstab # 2 = дубль
```
**ПИТФОЛЛ — не то поле времени.** `systemctl show docker -p ExecMainStartTimestamp`
может вернуть **stale** значение (прошлая активация). Реальное время —
`ps -o lstart -p $(pgrep -x dockerd)`.
**ПИТФОЛЛ — не тот источник RestartPolicy.** `RestartPolicy.Name` из
`docker-data/containers/<id>/hostconfig.json` показывает то, что передал
*исходный* `docker run` — часто `null`, даже когда политика есть в живом daemon
state. НЕ делать вывод «контейнеры не поднимутся сами». Проверять живой daemon:
```bash
docker inspect <name> --format '{{.HostConfig.RestartPolicy.Name}}'
```
Если `unless-stopped` — контейнеры **сами** вернутся при рестарте daemon, ручной
`docker start` не нужен.
## Фикс
### Немедленный (вернуть сервисы — диск уже смонтирован)
```bash
sudo systemctl restart docker
docker info --format "containers={{.Containers}} running={{.ContainersRunning}}"
```
Контейнеры с `restart: unless-stopped` поднимаются сами.
### Постоянный (сделать reboot безопасным)
1. Бэкап: `/etc/fstab`, `/etc/docker/daemon.json`, compose-каталоги сервисов →
датированная папка.
2. Drop-in, чтобы systemd не стартовал dockerd раньше диска:
`/etc/systemd/system/docker.service.d/mounts.conf`
```ini
[Unit]
RequiresMountsFor=/srv/dev-disk-by-uuid-<DISK>/docker-data
After=srv-dev\x2ddisk\x2dby\x2duuid\x2d<DISK>.mount
```
Имя `.mount` юнита = путь, где `/` → `-`, а каждый литеральный `-` escaped как
`\x2d` (systemd escaping).
3. `sudo systemctl daemon-reload`
4. Убрать **дубль** строки монтирования того же диска из `/etc/fstab` (systemd
отказывается от дублей; OMV часто добавляет свою рядом с ручной `UUID=...`).
5. Проверка:
```bash
findmnt --verify --fstab # rc=0
systemctl show docker -p Requires -p After # маунт-юнит в ОБОИХ
sudo systemctl restart docker # sanity: контейнеры перезагрузятся
```
## Нельзя
- Не предполагать потерю данных, если `docker ps` пусто, но диск смонтирован —
**сначала посчитать `docker-data/containers/`**. `Containers=0` в `docker info`
не доказательство удаления.
- Не `rm -rf` «устаревший» docker-data — это и есть данные.
- Не править `/etc/fstab` через sed; писать полный файл локально, валидировать
`findmnt --verify --fstab`, затем копировать на место.
## Чеклист после фикса
- [ ] `docker info` count == число каталогов в `docker-data/containers/`.
- [ ] `systemctl show docker -p RequiresMountsFor` **не пусто** и указывает на `docker-data`.
- [ ] `grep -c "<DISK-UUID>" /etc/fstab` = `1`.
- [ ] После тестового `systemctl restart docker` критичные контейнеры `Up`.
- [ ] При следующем плановом reboot: `docker ps -q | wc -l` возвращает ожидаемое число, не `0`.
## Применение на Kraken (2026-09-20)
Полный кейс: [[family/how-to/kraken-access]] и
[[personal/tech/xray-reverse-tunnel-kraken-truenas]].
Кратко: boot 17:29:17, dockerd 17:31:01, диск 17:31:27 (на 26 с позже) → 0
контейнеров. Фикс: drop-in `RequiresMountsFor=/srv/.../6194539b.../docker-data`
+ чистка fstab. Бэкап: `/home/kraken/backups/docker-recovery-20260920-105228/`.
## Смежная ловушка: смешанные владельцы под data-root
Бэкап (или tar) дерева сервисов на том же диске часто упирается в **разных владельцев** —
часть каталогов `root:root` (созданы root-контейнерами), часть `1000:1000`.
Бэкап от обычного юзера получит `Permission denied` на root-каталогах и **молча
сделает неполный архив**.
```bash
sudo ls -ldn /srv/dev-disk-by-uuid-<DISK>/docker/*/ | awk '{print $3, $4, $NF}'
```
Если `sudo -n true` проходит (NOPASSWD есть) — архивировать под `sudo -n`, а на приёмной
стороне выставить владельца бэкап-юзера: `rsync --chown=<user>:<group>` (не голое копирование,
иначе файл ляжет `root:root`).
**ПИТФОЛЛ — длинный `chown -R`/архив умирает вместе с SSH-сессией.** Рекурсивный `chown`
(или большой `tar`) по медленному USB-HDD идёт минутами и убивается при обрыве SSH.
Запускать отвязанно:
```bash
sudo systemd-run --unit=<name> --collect --property=Type=oneshot chown -R 1000:1000 <path>
systemctl is-active <name> # 'activating' = ещё идёт, доживёт до конца независимо от SSH
```
Тем же приёмом — любую долгую удалённую задачу через флаки-линк.
**ПИТФОЛЛ — контейнер в `Restarting (1)` с `mkdir: Permission denied` на своём каталоге данных** —
тот же класс багов, но наоборот: UID владельца на хосте не совпадает с UID, под которым
работает контейнер (образ `USER` или compose `user:`). Лечится `chown <container-uid>:<gid> <host-dir>`.
Ожидаемый UID: `docker inspect <name> --format '{{.Config.User}}'`.
Первые несколько `mkdir`-ошибок могут быть **остатком буфера** от прежнего падения — прежде
чем объявлять провал, убедиться, что контейнер реально держится `Up`.