Мігрування сховища, яке травмує, — це не те, що голосно збоїть. Це те, що «зробилося» й потроху обрізало кілька файлів, кілька прав або кілька мілісекунд довговічності, поки ваша база даних не відчує, як смакує ентропія.
Docker‑томи мають бути нудними. Потім ви їх переміщуєте: новий диск, новий хост, новий драйвер, інша назва стека Compose або «швидкий ребілд». Саме тут відбувається втрата даних — зазвичай через те, що ми ставилися до тому як до простої папки або до папки як до тому.
Трюк: міграція через контрольоване підключення контейнера (не через файлову систему хоста)
Ось крок, що запобігає більшості катастроф при міграції томів: не копіюйте «все, що під /var/lib/docker» і не довіряйте, що шлях bind‑маунту еквівалентний іменованому тому. Натомість підключіть старий том і нову ціль (новий том, bind‑маунт або SSH‑пайп) до одноразового утилітарного контейнера й копіюйте всередині цього контрольованого середовища.
Чому це працює: маунт контейнера дає стабільний вигляд вмісту тому саме так, як його бачить ваш застосунок. Ви не потрапляєте випадково в внутрішні метадані Docker. Ви не пропускаєте файли, бо скопіювали не ту директорію. І ви можете стандартизувати процес повторюваними командами.
Мій дефолт — підхід у два етапи:
- Фаза 1 (тепле копіювання): тримайте застосунок запущеним, зробіть початкову синхронізацію, щоб скоротити час простою.
- Фаза 2 (фінальне копіювання): зупиніть застосунок (або принаймні загальмуйте запис), потім виконайте фінальну синхронізацію й перевірку.
Якщо ви візьмете з цієї статті тільки одне: ставтеся до міграції як до розгортання. Потрібен план, відкат, перевірка та чисте переключення.
Цікаві факти й коротка історія, що пояснюють сучасні пастки
- Docker‑томи існували до Docker Compose. Томи були першим «офіційним» механізмом збереження стану; Compose пізніше зробив їх легкими для декларації і ще простішими для непорозумінь.
- Іменовані томи — це керовані об’єкти, а не просто шляхи. Вони відображаються на шляхи на хості, але Docker залишає за собою право обирати де і як. Копіювання «папки» часто копіює не ту папку.
-
Оверлейні файлові системи — не місце для зберігання стану.
overlay2від Docker чудовий для шарів образів і тимчасових записів контейнерів. Це не місце для вашої бази даних. - Томи приречені прожити довше за контейнери за задумом. Видалення контейнера не видаляє іменовані томи, якщо ви цього явно не зробите. Це функція надійності — і операційна пастка, коли старі томи накопичуються.
- Несумісності прав погіршилися з rootless Docker. Rootless‑рушії змінюють відображення UID/GID; міграція може здатися успішною й при цьому зламатися в runtime із «permission denied».
- На macOS/Windows Desktop є шар віртуалізації. На Desktop томи живуть всередині VM. «Копіювання з хоста» — це не те, що ви думаєте, а характеристики продуктивності сильно відрізняються.
-
Драйвери зберігання змінили поведінку за замовчуванням між версіями. Те, що було
aufs, сталоoverlay2у більшості дистрибутивів Linux; семантика томів залишалася стабільною, але «де дані» постійно плутає людей. -
Деякі бази даних трактують часові позначки як частину коректності. Якщо ваша міграція дивно змінює
mtime(або ви відновлюєте з неправильними опціями), певні навантаження стають повільнішими або поводяться дивно. - Мережеві файлові системи досі — поле мін. Опції NFS, блокування і поведінка fsync можуть зробити «успішну» міграцію джерелом тонкої корупції пізніше.
Ментальна модель: що таке Docker‑том (а що ні)
Іменований том проти bind‑маунту проти «даних у контейнері»
Три категорії сховища з’являються у реальних інцидентах:
-
Іменований том: стійке сховище під управлінням Docker. Двигун Docker створює його й монтує в контейнер.
Зазвичай він розташований під/var/lib/docker/volumes/<name>/_dataна Linux, але не автоматизуйте, припускаючи цей шлях. - Bind‑маунт: шлях хоста, змонтований в контейнер. Добре підходить для девелопменту і для деяких продакшн‑випадків, коли ви контролюєте структуру шляхів, бекапи й права.
- Записуваний шар контейнера: «diff» поверх образу. Він гине з контейнером. Якщо там бізнес‑дані — у вас не збереження, а враження.
Чому копіювання «/var/lib/docker» — погане захоплення
Docker зберігає образи, шари, кеш збірки, мережі, метадані контейнерів та томи під своїм data‑root. Копіювання цього під час роботи Docker може створити на вигляд консистентне дерево директорій, яке всередині буде неконсистентним. Ви виявите проблему тільки тоді, коли контейнери відмовляться стартувати або, що гірше, стартуватимуть з частковим станом.
Якщо ви повинні перемістити весь data‑root Docker, робіть це як хірургічну процедуру: зупиніть Docker, скопіюйте, верифікуйте, оновіть data-root, після чого запустіть Docker. Але це не «міграція тому». Це «переміщення нутрощів рушія».
Принцип надійності, що має значення
Коли ви мігруєте стан, ви переносите контракт: вміст, права, власність, розширені атрибути, жорсткі посилання, символічні лінки і іноді поведінку sparse‑файлів. Ваш інструмент має зберігати цей контракт.
Одна цитата, яку люди з експлуатації постійно перевчують:
Перефразована ідея — Richard Cook (системна безпека): «Успіх приховує складність системи; невдачі її виявляють.»
Жарти зазвичай — погана стратегія надійності, але ось один: міграція Docker‑тому — як переміщення акваріума — можна зробити швидко, або зробити двічі.
Швидкий план діагностики: знайти вузьке місце за хвилини
Коли міграція повільна або ризикована, вам не потрібен тиждень бенчмарків. Потрібен швидкий, дисциплінований тріаж.
Перевірте це в порядку:
1) У вас I/O‑звуження на джерелі, призначенні чи мережі?
- Запустіть
iostat/vmstatна обох кінцях. - Слідкуйте за великим await, високим util, низькою пропускною здатністю і підсвальнюванням.
- Якщо копіюєте через SSH, перевірте CPU теж; шифрування може бути вузьким місцем.
2) Ви випадково копіюєте не те (або набагато більше)?
- Підтвердіть том(и) і точки монтування через
docker volume inspectіdocker inspect. - Перелічіть, що реально всередині тому, використовуючи утилітарний контейнер.
- Виміряйте розмір через
du -xзсередини маунту.
3) Ви маєте справу з додатком, який треба загальмувати?
- Бази даних, черги й усе з write‑ahead logs потребують зупинки‑світу (або знімку).
- Якщо не можете зупинити, ваша «міграція» — це проєкт реплікації. Не прикидайтеся інакше.
4) Чи права/власність будуть проблемою після переключення?
- Перевірте UID/GID всередині запущеного контейнера.
- Перевірте, чи у вас rootless Docker або user namespaces.
- Очікуйте відмінностей SELinux/AppArmor між хостами.
5) Ви верифікуєте результат чимось більше, ніж надією?
- Порахуйте файли, порівняйте контрограми для вибірки та валідуйте стан на рівні застосунку (не лише статус «Up» контейнера).
- Зберігайте старий том непорушеним доти, поки не пройдете повний бізнес‑цикл.
Практичні завдання: команди, вивід і рішення
Це реальні виробничі завдання. Кожне включає: команду, як виглядає «нормальний» вивід і яке рішення ви ухвалюєте.
Використовуйте їх як будівельні блоки для власних ранбуків.
Задача 1: Визначити, чи ви використовуєте іменований том або bind‑маунт
cr0x@server:~$ docker inspect -f '{{range .Mounts}}{{println .Type .Source "->" .Destination}}{{end}}' app
volume /var/lib/docker/volumes/pgdata/_data -> /var/lib/postgresql/data
bind /srv/app/config -> /etc/app
Що це значить: у вас є іменований том (pgdata) та bind‑маунт (/srv/app/config).
Рішення: мігруйте pgdata через копіювання том→том; bind‑маунти мігруйте як звичайну файлову систему з явними правилами власності.
Задача 2: Перевірити метадані тому (драйвер, mountpoint)
cr0x@server:~$ docker volume inspect pgdata
[
{
"CreatedAt": "2026-01-20T10:22:14Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/pgdata/_data",
"Name": "pgdata",
"Options": {},
"Scope": "local"
}
]
Що це значить: драйвер local, scope host‑локальний. Це не кластерний том.
Рішення: плануйте копіювання на рівні хоста (tar/rsync) або використовуйте проміжний контейнер; не чекайте, що інший вузол автоматично його побачить.
Задача 3: Підтвердити, які контейнери використовують том
cr0x@server:~$ docker ps --format '{{.Names}} {{.Mounts}}' | grep pgdata
db pgdata
Що це значить: лише db використовує цей том.
Рішення: можна запланувати простої тільки для цієї служби; прихованих споживачів немає.
Задача 4: Виміряти розмір тому з утилітарного контейнера (довіряй, але перевіряй)
cr0x@server:~$ docker run --rm -v pgdata:/v alpine:3.20 sh -lc 'du -sh /v && df -h /v | tail -n +2'
12.4G /v
/dev/sda2 220G 96G 113G 46% /v
Що це значить: близько 12.4G даних, на файловій системі призначення достатньо місця.
Рішення: можна використовувати один tar‑потік; немає потреби дробити, але заплануйте тимчасовий простір, якщо створюєте архіви.
Задача 5: Перевірити тип файлової системи й опції монтування (продуктивність і коректність)
cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/docker
/dev/sda2 ext4 rw,relatime
Що це значить: ext4, стандартні опції.
Рішення: rsync/tar повинні поводитися передбачувано. Якби тут був NFS/CIFS, довелося б сповільнитися й перевіряти блокування та поведінку fsync.
Задача 6: Зупинити писачевця коректно (інакше ви копіюєте рухому ціль)
cr0x@server:~$ docker stop -t 60 db
db
Що це значить: контейнер зупинився в межах таймауту.
Рішення: переходьте до фінального синку. Якщо він не зупиняється, треба діагностувати хуки завершення перед міграцією.
Задача 7: Тепле копіювання між двома томами на тому самому хості (rsync)
cr0x@server:~$ docker volume create pgdata_new
pgdata_new
cr0x@server:~$ docker run --rm -i \
-v pgdata:/from:ro \
-v pgdata_new:/to \
alpine:3.20 sh -lc 'apk add --no-cache rsync >/dev/null && rsync -aHAX --numeric-ids --info=stats2 /from/ /to/'
Number of files: 14832 (reg: 12110, dir: 2711, sym: 11)
Number of created files: 14832 (reg: 12110, dir: 2711, sym: 11)
Total file size: 13,274,991,224 bytes
Total transferred file size: 13,274,991,224 bytes
Literal data: 13,274,991,224 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.210 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 13,278,112,901
Total bytes received: 412,220
sent 13,278,112,901 bytes received 412,220 bytes 34,201,351.55 bytes/sec
total size is 13,274,991,224 speedup is 1.00
Що це значить: rsync зберіг атрибути (-aHAX), використав числові ідентифікатори, створив очікувану кількість файлів.
Рішення: безпечно переходити до перевірки. Якщо «created files» значно менше, ви ймовірно вказали не ту вихідну доріжку або том.
Задача 8: Перевірити, що кількість файлів співпадає (дешевий sanity‑чек)
cr0x@server:~$ docker run --rm -v pgdata:/a -v pgdata_new:/b alpine:3.20 sh -lc 'cd /a && find . | wc -l; cd /b && find . | wc -l'
14865
14865
Що це значить: кількості співпадають.
Рішення: продовжуйте. Якщо ні — зупиніться. З’ясуйте, що було пропущено (права, спеціальні файли, проблеми маунта).
Задача 9: Вибіркова перевірка контрольних сум для репрезентативної вибірки (виявити тиху корупцію)
cr0x@server:~$ docker run --rm -v pgdata:/a -v pgdata_new:/b alpine:3.20 sh -lc '
apk add --no-cache coreutils >/dev/null
cd /a
find . -type f | head -n 200 | while read f; do
sha256sum "$f"
done | sort -k2 > /tmp/a.sha
cd /b
find . -type f | head -n 200 | while read f; do
sha256sum "$f"
done | sort -k2 > /tmp/b.sha
diff -u /tmp/a.sha /tmp/b.sha | head
'
Що це значить: відсутній вивід від diff означає, що вибірки співпали.
Рішення: прийміть міграцію з більшою довірою. Якщо є невідповідності — підозрівайте помилки диска, брак RAM або інструмент копіювання, що не зберігає вміст.
Задача 10: Переключити сервіс Compose на новий том
cr0x@server:~$ docker compose up -d
[+] Running 1/1
✔ Container db Started
Що це значить: сервіс запущений.
Рішення: не святкуйте ще; провалідуйте стан застосунку й логи. «Started» — не означає «правильно».
Задача 11: Перевірити логи на підказки про права й корупцію одразу після переключення
cr0x@server:~$ docker logs --tail=80 db
PostgreSQL Database directory appears to contain a database; Skipping initialization
LOG: database system was shut down at 2026-02-04 09:12:30 UTC
LOG: database system is ready to accept connections
Що це значить: чистий старт, розпізнано існуючу директорію даних.
Рішення: переходьте до smoke‑тесту запиту. Якщо бачите «permission denied» або «invalid checkpoint record», зупиніться і відкотіться на старий том.
Задача 12: Підтвердити, що контейнер бачить очікуване використання диска (щоб уникнути «порожніх томів»)
cr0x@server:~$ docker exec db sh -lc 'du -sh /var/lib/postgresql/data | cat'
12.4G /var/lib/postgresql/data
Що це значить: новий том змонтований і заповнений.
Рішення: продовжуйте спостереження, а старий том виводьте з експлуатації лише після безпечного вікна.
Задача 13: Міграція тому між хостами потоком tar через SSH (без проміжних файлів)
cr0x@server:~$ docker run --rm -v pgdata:/from alpine:3.20 sh -lc 'cd /from && tar -cpf - . ' | ssh ops@newhost 'docker volume create pgdata && docker run --rm -v pgdata:/to alpine:3.20 sh -lc "cd /to && tar -xpf -"'
pgdata
Що це значить: дані передалися по потоку; жодного проміжного архіву.
Рішення: використовуйте це, коли потрібен простий, з мінімумом залежностей переніс. Якщо потрібне відновлення/часткові повтори — надайте перевагу rsync.
Задача 14: Перевірити Docker data‑root, якщо ви переміщаєте весь сховищний рушій
cr0x@server:~$ docker info --format '{{.DockerRootDir}}'
/var/lib/docker
Що це значить: розташування data‑root рушія.
Рішення: якщо ваша реальна проблема — «диск заповнений», можливо треба перемістити data‑root або зробити prune — не плутайте це з міграцією одного тому.
Задача 15: Діагностувати драйвер зберігання й перевірити, що ви не змішуєте обов’язки
cr0x@server:~$ docker info --format 'Driver={{.Driver}}'
Driver=overlay2
Що це значить: storage driver — overlay2.
Рішення: тримайте стан у томах; не намагайтеся «мігрувати базу даних», копіюючи директорії шарів overlay2.
Задача 16: Виявити SELinux‑enforcement, що може зламати доступ після міграції
cr0x@server:~$ getenforce
Enforcing
Що це значить: SELinux застосовується в режимі Enforcing.
Рішення: якщо мігруєте на bind‑маунт, швидше за все потрібні правильні лейбли (наприклад, :Z/:z) або перяривка. Іменовані томи зазвичай уникали б цієї проблеми, але відмінності між хостами все одно можуть мати значення.
Безпечні шаблони міграції (той самий хост, новий диск, новий хост)
Шаблон A: той самий хост, міграція том→том (нудний переможець)
Якщо ви можете тримати рушій Docker на тому самому хості і лише треба перемістити дані на інший бекенд, створіть новий том і скопіюйте в нього. Це тримає метадані рушія стабільними.
Метод копіювання може бути rsync (краще для інкрементальних синків) або tar (простота).
Використовуйте rsync якщо:
- Хочете тепле копіювання + фінальне cutover‑копіювання.
- Очікуєте повтори або частковий прогрес.
- Хочете статистику і просту поведінку diff.
Використовуйте tar якщо:
- Потрібні мінімальні залежності (tar є майже всюди).
- Плануєте стримінг через SSH і не хочете тимчасових файлів.
- Можете дозволити собі одноразовий «усе або нічого» прохід.
Деталь, яку люди пропускають: запускайте інструмент копіювання всередині контейнера, щоб він бачив маунти так, як їх бачать контейнери. Це уникне плутанини з шляхами хоста і зменшить розбіжності «працює на моєму хості».
Шаблон B: міграція на bind‑маунт (коли хочете явний контроль)
Це підхід, коли ви хочете, щоб ваші дані були в /srv/data/postgres, бо ваша система бекапів, моніторингу й аудит вже знає цей шлях.
Це може бути правильно. Але також це може стати фестивалем прав доступу.
Найбезпечніший шлях:
- Створіть директорію призначення.
- Встановіть власність і права відповідно до очікуваного UID/GID контейнера.
- Скопіюйте дані rsync з збереженням числових ID.
- Змонтуйте з опціями, сумісними з SELinux, якщо потрібно.
Тут rootless Docker ускладнює життя: UID всередині контейнера може не відображатися так, як ви думаєте на хості. Не робіть «chmod 777» у продакшні, якщо ваша модель загроз — не «ні».
Шаблон C: міграція між хостами (tar‑потік або rsync через SSH)
Міжхостова міграція — це вибір між простотою і можливістю відновлення.
- tar через SSH простий і швидкий у налаштуванні, з малою кількістю рухомих частин.
- rsync через SSH ваш друг, коли передача велика, мережа нестабільна або ви хочете теплий‑синк потім фінал.
Для rsync між хостами без відкриття шляху тому на хості, ви все одно можете робити це контейнер→контейнер, змонтувавши том у утилітарний контейнер і запустивши rsync назовні через SSH. Трохи більше наборів команд, але значно менше плутанини.
Шаблон D: переміщення всього Docker data‑root (останній засіб, робіть правильно)
Іноді справжня потреба — «перенести Docker‑сховище з кореневого диска». Це не міграція тому; це релокація storage root рушія. Правильна послідовність:
зупинити Docker, скопіювати data‑root з збереженням, оновити конфіг демона, запустити Docker, а потім верифікувати образи/контейнери/томи.
Не робіть цього, щоб «виправити» один прикладний том, якщо не хочете створити більші зони ураження.
Перевірка, яка дійсно ловить погані міграції
Людей, що працюють зі сховищем, оплачують за недовіру сигналам успіху. Те, що контейнер «Up», означає лише, що init‑процес ще не впав. Це не гарантія правильності даних.
Перевірка повинна бути багаторівнева:
Рівень 1: санітарна перевірка на рівні файлової системи
- Перевірка розміру: очікуваний порядок величини, не точні байти.
- Перевірка кількості файлів: швидко ловить «скопійовано порожню директорію».
- Перевірка прав/власності: виявляє невідповідності UID/GID і відсутні xattrs.
Рівень 2: вибіркова перевірка вмісту
Повні контрольні суми для багаторозмірних терабайтних томів можуть бути незручними під час вікна обслуговування. Вибирайте розумно:
- Хешуйте найостанніше змінені файли.
- Хешуйте критичні директорії (WAL, індекси, метадані).
- Хешуйте випадкові вибірки по всьому дереву.
Рівень 3: істина на рівні застосунку
Для баз даних виконайте read і write запити. Для об’єктних сховищ — отримаєте об’єкт і повторно завантажте його. Для черг — поставте й отримайте повідомлення. Перевіряйте те, що справді важливо для бізнесу.
Дисципліна відкату
Зберігайте старий том цілим і відключеним. Не «прибирайте» його, поки не пройшли повний цикл, коли можуть виявитися тихі помилки. Якщо бізнес‑цикл — щотижневі звіти, «на завтра» — це недостатньо.
Другий жарт (і останній): резервні копії як парашути — якщо вони потрібні і у вас їх немає, вам більше не доведеться ними користуватися.
Три корпоративні історії з поля бою зі сховищем
Міні-історія 1: інцидент через неправильне припущення
Середня компанія запускала клієнтський API на одному Linux‑хості з Docker Compose. База даних була в іменованому томі.
Платформінженер вирішив «спростити бекапи», копіюючи /var/lib/docker/volumes щовечора на NAS за допомогою загального інструменту синхронізації файлів.
Припущення: том — це директорія, а копіювання директорій — те саме, що бекап. Це працювало тижнями. Завдання бекапу відзначало успіх. NAS повільно наповнювався. Ніхто не хвилювався.
Потім проблема диска змусила робити відновлення. Вони скопіювали директорію назад, перезапустили контейнер бази і БД піднялася з набором таблиць, у яких бракувало недавніх записів.
Корінна причина була боляче банальна. Бекап запускався під час запису бази. Інструмент копіювання створив точку‑в‑часі, якої ніколи не існувало: деякі файли «до» і деякі «після», плюс кілька частково скопійованих сегментів.
Не було знімка на рівні БД, немає загальмування і немає тесту відновлення. «Бекап» був купою файлів із правдоподібними іменами.
Виправлення вимагало відновлення з логічних експортів, що на щастя частково збереглися. Головний урок не «не користуватися Docker».
А такий: не змішуйте «файл‑копію пройшла» з «дані консистентні».
Міні-історія 2: оптимізація, що відбилася бумерангом
Інша організація запускала CI‑завантаження, що виробляли багато артефактів. Вони перемістили кеші збірок на спільну мережеву файлову систему і монтували її в контейнери, щоб зменшити локальний SSD‑знос.
Пропозиція була чудова: менше локальних апгрейдів, централізоване управління, простіше очищення.
На перший погляд продуктивність була нормальною. Потім з’явилися дивні баги: збірки іноді падали, тести таймаутувалися, зростала кількість помилок «file not found», які зникали при повторному запуску.
Інженери звинувачували flaky тесты. SRE‑и звинувачували мережу. У кожного була рація.
Причина крилася в семантиці файлової системи. Мережевий шар мав кешування й блокування, що не відповідали припущенням локального диска.
Деякі інструменти очікували атомарних перейменувань і надійного fsync; шар іноді затримував або переміщував видимість під навантаженням. Це не було «вимкненням». Це було «іншим».
Рішенням стало тримати дійсно спільні артефакти в об’єктному сховищі (з явною семантикою публікації), а тимчасові робочі дані — на локальному диску.
Оптимізація не провалилася через те, що NFS «злий»; вона провалилася, бо навантаження чутливе до затримки і розраховувало на гарантії локальної файлової системи.
Міні-історія 3: нудна практика, що врятувала день
Команда фінансових послуг мала правило: будь‑який stateful контейнер повинен мати письмовий план cutover, план відкату та крок перевірки, що включає реальну транзакцію.
Ніхто не любив папірці. Але це зробило міграції повторюваними.
Їм потрібно було перемістити том Postgres на новий хост через зміну контракту на обслуговування. План міграції використовував теплий rsync під час запуску БД, потім заплановану зупинку, фінальний rsync і перевірки на рівні застосунку.
План також включав «зберегти старий том у режимі read‑only і відключеним сім днів».
У ніч переключення все виглядало правильно. Логи чисті. Health‑чек зелені.
Наступного ранку звітна задача знайшла одну невідповідність у виведеній таблиці. Не катастрофу, але підозріло.
Оскільки вони зберегли старий том, вони змогли змонтувати його read‑only і порівняти невелику підмножину файлів і часових позначок.
Вони знайшли проблему: остання зміна схеми сталася під час вікна теплого синку, і процес cutover пропустив допоміжний файл, створений сайдкаром поза контейнером БД.
Вони повторно виконали цілеспрямований синк тієї директорії і перевірили. Без драм, без тривалого простоя, без «ми видалили старі дані, бо були впевнені».
Нудна дисципліна перемогла. Знову.
Поширені помилки: симптом → корінна причина → виправлення
1) Симптом: новий контейнер стартує «з чистого листа», ніби дані відсутні
Корінна причина: ви змонтували порожній том (нова назва) або замінили іменований том на bind‑маунт, який вказує в порожню директорію.
Виправлення: перевірте маунти через docker inspect. Підтвердіть назву тому в Compose. Заповніть правильну ціль і перезапустіть.
2) Симптом: «permission denied» після міграції
Корінна причина: невідповідність UID/GID, проблеми відображення rootless Docker або невірні SELinux‑лейбли на bind‑маунтах.
Виправлення: використовуйте --numeric-ids з rsync; підберіть власність під runtime‑користувача контейнера; застосуйте коректне SELinux‑маркування для bind‑маунтів.
3) Симптом: база стартує, потім падає з помилками на кшталт корупції
Корінна причина: ви копіювали під час запису БД, отримавши несумісний стан на диску.
Виправлення: зупиніть/загальмуйте БД для фінального синку або використовуйте нативні інструменти бекапу/знімків БД. Не покладайтеся на сире копіювання файлів живої бази даних.
4) Симптом: міграція надзвичайно повільна, CPU завантажений
Корінна причина: наклад шифрування SSH або неправильно виставлена компресія, особливо на інстансах з малою кількістю ядер.
Виправлення: виміряйте CPU на обох кінцях; розгляньте відключення компресії; спробуйте швидший шифр або перенесіть копіювання через приватну мережу. Або виконайте стадійне копіювання локально й потім передавайте кращим каналом.
5) Симптом: rsync завершився, але кількість файлів відрізняється
Корінна причина: виключені патерни, помилки прав, пропущені спеціальні файли, або ви копіювали з неправильного шляху маунта.
Виправлення: перезапустіть rsync з verbose і itemized змінами, перевірте stderr, запускайте від root всередині утилітарного контейнера при потребі і валідуйте маунти джерела/цілі.
6) Симптом: після переключення застосунок «здоровий», але продуктивність падає
Корінна причина: призначене сховище має іншу латентність (HDD vs SSD, мережеве сховище vs локальне, різні опції монтування).
Виправлення: запустіть невеликий бенчмарк читання/запису перед переключенням; перевірте файлову систему й опції монтування; для баз даних підтвердіть поведінку fsync і barrier.
7) Симптом: дані виглядають присутніми, але timestamps/власність змінилися несподівано
Корінна причина: інструмент копіювання не зберігав метадані за замовчуванням (наприклад, відсутній -a), або ви tar‑или без збереження прав.
Виправлення: використовуйте rsync з -aHAX де потрібно; tar з -p і перевірте поведінку при розпаковці; валідуйте вибірково через stat.
8) Симптом: контейнер не може змонтувати том після міграції хоста
Корінна причина: невідповідність драйвера тому або відсутній плагін на хості призначення (часто у випадку драйверів третьої сторони).
Виправлення: підтвердіть драйвер тому й опції; встановіть той самий плагін/драйвер; якщо ви мігруєте з local на plugin‑підтримуваний том, сприймайте це як архітектурне рішення, а не просте копіювання файлів.
Чек‑лісти / покроковий план
Чек‑ліст A: перед польотом (перед тим, як торкатися даних)
- Перелік маунтів для сервісу: іменовані томи, bind‑маунти, tmpfs.
- Класифікуйте навантаження: база даних/черга/об’єктне сховище проти «статичних файлів». Якщо транзакційне — плануйте загальмування або нативний бекап.
- Виміряйте розмір тому і підтвердіть вільне місце на цілі.
- Визначте метод копіювання: rsync для інкрементального, tar для простоти/стримінгу.
- Визначте вікно простою і таймаут на відкат.
- Напишіть кроки перевірки, що включають перевірку на рівні застосунку.
Чек‑ліст B: теплий синк (необов’язково, але рекомендовано)
- Створіть цільовий том (або директорію для bind‑маунта).
- Запустіть rsync зі старого на новий, поки застосунок працює.
- Збережіть кількість файлів і приблизні розміри.
- Не видаляйте нічого поки що.
Чек‑ліст C: фінальний синк (частина, що запобігає втраті даних)
- Зупиніть застосунок коректно (або призупиніть запис через механізм на рівні застосунку).
- Виконайте фінальний rsync (або tar‑копіювання) щоб захопити останні зміни.
- Перевірте кількість файлів і вибіркові контрольні суми.
- Оновіть визначення Compose/сервісу щоб вказувати на новий том/bind‑маунт.
- Запустіть сервіс і стежте за логами хвилини після старту.
- Виконайте smoke‑тест на рівні застосунку (читання і запис).
Чек‑ліст D: план відкату (написати його перед початком)
- Якщо перевірка провалена — зупиніть сервіс.
- Перенаправте на старий том.
- Запустіть сервіс, підтвердіть працездатність.
- Збережіть невдалий том для розслідування; не «виправляйте» шляхом перезапису доказів.
Чек‑ліст E: гігієна після переключення (не робіть це зарано)
- Моніторте повний бізнес‑цикл (пакетні задачі, звіти, бекапи).
- Лише після цього архівуйте або видаляйте старий том.
- Оновіть ранбуки, щоб наступна міграція не була разовим героїчним заходом.
Питання й відповіді
1) Який найбезпечніший спосіб мігрувати іменований Docker‑том?
Зупиніть писак для фінального синку, потім копіюйте зі старого в новий, використовуючи утилітарний контейнер, який монтує обидва томи.
Надавайте перевагу rsync для двопрохідних міграцій; верифікуйте кількістю файлів і перевіркою на рівні застосунку.
2) Чи можна просто копіювати /var/lib/docker/volumes?
Можна, але зазвичай не варто. Легко скопіювати не те, і копіювання живих даних не дає консистентності для баз даних.
Якщо треба, зупиніть Docker повністю і сприймайте це як релокацію data‑root, а не як копію одного тому.
3) Чи кращий tar, ніж rsync?
Tar простіший і чудовий для стримінгу. Rsync краще для інкрементальних копій, повторів і теплий+фінальний підходу.
Для уникнення втрат даних ключове питання не tar проти rsync; а загальмування записів і верифікація результату.
4) Як мігрувати томи між хостами, не знаючи внутрішніх шляхів Docker?
Використайте утилітарний контейнер з змонтованим томом і стримуйте tar через SSH в інший утилітарний контейнер, що розпаковує в цільовий том.
Це тримає вас подалі від внутрішніх директорій Docker.
5) Що щодо контейнерів баз даних — чи копіювати файлову директорію?
Тільки якщо можете гарантувати консистентний стан на диску (сервіс зупинено або знімок файлової системи з правильними гарантіями).
Нативні бекапи/реплікація БД часто безпечніші для нульового/низького простою, але це ширша задача, ніж «просто скопіювати».
6) Чому контейнер втратив права після переходу на bind‑маунт?
Бо bind‑маунти відкривають власність/лейбли файлової системи хоста напряму. Іменовані томи зазвичай простіші.
Виправте, підібравши UID/GID, використовуючи збереження числових ID при копіюванні і обробляючи SELinux‑лейбли, якщо вони застосовні.
7) Чи можна перейменувати Docker‑том?
Прямо — ні. Практичний «ренейм» — це: створити новий том з бажаною назвою, скопіювати туди дані, оновити посилання і потім видалити старий том пізніше.
8) Як уникнути простою зовсім?
Для дійсно stateful систем «без простою» зазвичай означає реплікацію: налаштуйте новий інстанс, реплікуйте/стримуйте зміни, потім перемкніть трафік.
Сира міграція тому зазвичай вимагає короткого простою, якщо навантаження не тільки для читання.
9) Який найсерйозніший червоний прапорець під час міграції?
Коли новий сервіс піднімається «чистим» і ініціалізує нову директорію даних. Це зазвичай означає, що він не побачив мігровані дані.
Зупиніться негайно і підтвердіть маунти.
10) Коли можна видаляти старий том?
Після верифікації і після того, як пройшов повний бізнес‑цикл, що виявляє тонкі проблеми.
Тримайте його довше, якщо є вимоги комплаєнсу або судово‑технічного аналізу; сховище дешевше, ніж реакція на інцидент.
Наступні кроки, які ви можете зробити цього тижня
Якщо ви запускаєте stateful контейнери в продакшні, зробіть ці практичні кроки:
- Напишіть один ранбук міграції з підходом utility‑container (монтувати старе + нове, rsync/tar, верифікувати, cutover, відкат). Зробіть його стандартом.
- Додайте верифікацію до definition of done: кількість файлів + вибіркові контрольні суми + перевірка на рівні застосунку (читання/запис).
- Маркуйте томи (в Compose або через угоди щодо імен), щоб знати, які stateful, а які кеші.
- Заплануйте тест відновлення з того, що ви називаєте «бекап». Найшвидший спосіб дізнатися, що він фальшивий — у вівторок вдень, а не під час інциденту.
- Визначте позицію щодо bind‑маунтів vs іменованих томів і задокументуйте її. Коливання призводить до трьох патернів збереження і відсутності єдиної історії бекапів.
«Трюк міграції томів» — не магія. Це дисципліна: монтуйте дані так, як це робить застосунок, копіюйте з збереженням метаданих, зупиніть писачевця для фінального проходу і перевірте, бо ви собі не довіряєте. І ви не повинні.