[2026-09-20] eagle: family/how-to/kraken-access.md personal/tech/docker-data-root-mount-race.md personal/tech/xray-reverse-tunnel-kraken-truenas.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-20 11:04:23 +06:00
1 parent af122c271e
commit 7f4faf0a19
3 files changed
+181 -14

No files matched your search

@@ -0,0 +1,135 @@
---
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/`.
@@ -45,7 +45,13 @@ verified_fix_2026_09_02: >-
>
> **Что это НЕ:** это **не** регрессия #6242, **не** REALITY mismatch, **не** `network_mode` — все прежние гипотезы по 2026-09-15 снимаются. Проверенный фикс 2026-09-02 (`26.4.25` + `network_mode: host`) остаётся в силе и не трогается.
>
> **План:** `systemctl restart docker` (диск уже смонтирован) + `docker start` по списку (RestartPolicy=null) + drop-in `RequiresMountsFor` + чистка fstab. Полный план и команды — [[family/how-to/kraken-access]], секция «2026-09-20». **Статус: восстановление НЕ начато, ждёт апрува Alex.**
> **План и статус (обновлено 2026-09-20): ✅ ВЫПОЛНЕНО И ПРОВЕРЕНО.**
> 1. ✅ Backup → `/home/kraken/backups/docker-recovery-20260920-105228/`.
> 2. ✅ `systemctl restart docker` → 25 контейнеров загружены, 12 Up (в т.ч. `xray-reverse-bridge`).
> 3. ✅ Drop-in `/etc/systemd/system/docker.service.d/mounts.conf` с `RequiresMountsFor=` + `After=` маунт-юнита → проверено `systemctl show docker -p Requires -p After` (маунт в обоих). **Гонка закрыта, reboot-безопасно.**
> 4. ✅ fstab: убран дубль строки `6194539b` (+ дубль bind-mount journal).
> ⚠️ `docker start` вручную **не потребовался**: RestartPolicy в daemon state есть (`unless-stopped`), контейнеры поднялись сами. Ранее записанное «RestartPolicy=null» — ошибка чтения `hostconfig.json` (он не отражает живую политику).
> Полный план и команды — [[family/how-to/kraken-access]], секция «2026-09-20».
>
> **Урок диагностики:** если portal пишет `non existing outTag: reverse-out` и «ноль соединений на 12346» — **сначала проверять, жив ли контейнер `xray-reverse-bridge` на Kraken** (`docker ps`), а не конфиг. 2026-09-15 этот шаг был пропущен: записали «Kraken недоступен → ждать», хотя машина была жива по LAN (`192.168.1.15`), а не запущен был только docker-контейнер бриджа.