Права доступу Docker при bind‑mount на Windows: найменш болюча конфігурація

Було корисно?

Ви робите bind‑mount свого проєкту в контейнер. Спочатку працює. Потім — ні. Раптом ваша збірка не може записати кеш, додаток не може створити лог‑файл, і все або належить root, або показує «Доступ заборонено». Ласкаво просимо в дивну долину файлових прав на Windows: виглядає як Unix, пахне як Unix, а поводиться як комітет.

Якщо хочете найменш болючу конфігурацію, треба зробити вибір: або трактувати Windows як тимчасовий хост і виконувати роботу всередині WSL2, або погодитися на шар трансляції та збудувати захисні правила. Добра новина: це можна зробити нудно стабільно. Погана новина: потрібна точність.

Правило, яке значно спрощує все

Тримайте файли проєкту всередині файлової системи Linux у WSL2 (наприклад, /home/you/project), а не на C:\, і робіть bind‑mount звідти.

Ось і все. Якщо так робити, права поводитимуться як у Linux, бо… це Linux. Щойно ви змонтуєте Windows‑шлях у Linux‑контейнер, ви просите дві моделі прав, дві семантики файлової системи й одну межу віртуалізації «просто погодитися». Вони не погодяться. Вони почнуть домовлятися. І згоди — шлях до інцидентів о 3 годині ночі.

Жарт на завершення: відображення прав між Windows і Linux — як двомовна дитина, що перекладає юридичні контракти: зусилля вражають, точність — під питанням.

Якщо ваша команда наполягає на роботі з C:\Users\..., це все ще можна зробити функціональним. Просто це не буде найменш болісним шляхом. Це буде «біль з цільовим рівнем обслуговування».

Чому права доступу на Windows поводяться дивно (що відбувається насправді)

Bind‑mount — це не просто «файли, надані контейнеру»

Bind‑mount — це коли контейнер бачить каталог, який фізично зберігається в іншому місці. На Linux‑хості це просто: семантика VFS ядра збігається; UID/GID і бітові права означають те, що очікує контейнер.

На Windows Docker Desktop фактично запускає Linux‑VM (двигун WSL2) і потім показує Windows‑файли цій VM через шар спільної файлової системи. Ваш контейнер не монтує NTFS напряму; він монтує трансляцію NTFS.

Де саме проявляється невідповідність

  • UID/GID vs SIDs/ACLs: Linux використовує числові ідентифікатори користувача/групи та бітові права. Windows використовує SID і ACL. Відображення втратне.
  • семантика chmod/chown: На «справжній» Linux‑файловій системі chmod і chown зазвичай роблять те, що написано. На Windows‑задіних монтуваннях вони можуть виглядати як успішні, але не зберігатися, або видавати «Operation not permitted».
  • виконувальний біт: Linux потребує виконувального біта для скриптів/бінарників. Windows — ні. Тому шар трансляції здогадується.
  • чутливість до регістру знаків: Linux чутливий до регістру, Windows зазвичай — ні (хоча сучасний Windows може ввімкнути чутливість для окремої директорії). Інструменти, які роблять припущення про одну модель, можуть поводитися дивно в іншій.
  • inotify / спостереження за файлами: Дев‑сервери та системи збірки залежать від подій файлової системи. Переклад подій через кордон може затримуватися або втрачатися. Це виглядає як «hot reload не працює».

Два різні «налаштування Windows», які люди плутають

Налаштування A: Робота у файловій системі WSL2 (краще). Ваш код живе під /home у WSL. Двигун Docker базується на WSL2. Контейнери монтують шляхи Linux. Ви майже перестаєте думати про Windows‑права.

Налаштування B: Робота у файловій системі Windows (іноді потрібно). Ваш код живе під C:\. Docker монтує /mnt/c/... у контейнери. Тепер ви в країні трансляції. Потрібні політики: який користувач запускає контейнер, які каталоги дозволені для запису і як уникнути артефактів, що належать root.

Є афоризм про надійність, який варто мати на столі. Ось перефразована думка, бо слово в слово різниться в переказах: перефразована думка: «Складні системи ламаються складними способами; простота — це риса надійності.» — John Ousterhout (перефразована думка)

Найменш болюча еталонна архітектура (що робити)

Рішення 1: Тримайте робоче місце у WSL2

Зробіть WSL2 файловою системою вашої «розробницької робочої станції», навіть якщо робоче місце — Windows. Ви все ще можете використовувати VS Code або JetBrains у Windows і редагувати файли через інтеграцію WSL. Git‑інструменти, тулчейни мов і права файлів узгодяться.

Рішення 2: Запускайте контейнери не від root, а під користувачем з UID/GID вашого WSL

Якщо контейнер записує файли в bind‑mount, ви хочете, щоб ці файли належали вашому WSL‑користувачу, а не root. Практичний підхід:

  • Дізнайтеся свій WSL UID/GID (id).
  • Збирайте образ, який створює користувача з цим UID/GID (або використайте логику в entrypoint).
  • У Compose вкажіть user: "UID:GID" для dev‑контейнерів.

Рішення 3: Використовуйте іменовані томи для директорій з інтенсивним записом

Bind‑mount чудово підходять для коду. Вони можуть бути жахливими для кешів залежностей і баз даних. Покладіть це в Docker‑томи:

  • Node: node_modules часто швидше в томі.
  • Python: .venv, кеш pip.
  • Rust/Go/Java: кеші збірки та каталоги артефактів.
  • Бази даних: завжди використовуйте томи, якщо вам не до вподоби історії про корупцію даних.

Рішення 4: Якщо використовуєте Windows‑монти — тримайте їх переважно тільки для читання

Якщо корпоративні політики або інструменти змушують тримати код на C:\, поводьтеся з цим як зі спільним мережею диском: читайте звідти, компілюйте при потребі, але не записуйте багато дрібних файлів через нього. Перенаправте кеші в томи або локальне сховище контейнера.

Другий короткий жарт: найшвидший спосіб виправити продуктивність bind‑mount на Windows — перестати робити bind‑mount з Windows. Це не елегантно, але ефективно.

План налаштування (крок за кроком)

1) Переконайтеся, що ви використовуєте двигун WSL2

Docker Desktop може працювати з різними бекендами залежно від версії та налаштувань. Якщо ви хочете найменш болючий шлях — оберіть WSL2.

2) Створіть директорію проєкту в WSL2

Клоньте репозиторій всередині WSL, не під /mnt/c. Якщо у вас вже є клон у Windows, переклоніть. Так, це здається зайвим. Ні, це не зайве.

3) Додайте dev‑користувача в образ (або запускайте під своїм UID)

Для development‑образів я віддаю перевагу створенню користувача в образі, бо так налагодження передбачуване. Для швидких рішень достатньо user: "${UID}:${GID}" в Compose.

4) Розділіть «source bind mounts» і «write‑heavy volumes»

Монтуйте вихідники проєкту. Помістіть кеші та стан у томи. Це зменшує сюрпризи з правами і робить продуктивність менш залежною від моста Windows‑WSL.

5) Визначте політику щодо власності файлів

Оберіть одну та дотримуйтеся:

  • Політика A (рекомендована): контейнер записує від імені такого самого UID/GID, як ваш WSL‑користувач.
  • Політика B: контейнер записує як root, і ви погоджуєтеся, що очищення/редагування на хості вимагатиме sudo. Частий джерело роздратування.
  • Політика C: контейнер ніколи не пише в bind‑mount; пише лише в томи. Це добре для герметичних збірок, менш зручно для деяких dev‑циклів.

Практичні задачі: команди, вивід і рішення (12+)

Це задачі, які я фактично виконую, коли хтось каже «bind‑mount не працює». Кожна містить: команду, що означає її вивід, і що робити далі.

Задача 1: Перевірити доступність Docker і поточний контекст

cr0x@server:~$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop-linux       Docker Desktop                            unix:///var/run/docker.sock

Значення: Ви бачите, до якого Docker‑ендоінта ви підключені. На Windows з WSL2 зазвичай використовують контекст Desktop Linux.

Рішення: Якщо ви не в очікуваному контексті, перемкніть: docker context use desktop-linux. Проблеми з «Permission denied» іноді виявляються через те, що ви спілкуєтесь з іншим двигуном, ніж думаєте.

Задача 2: Підтвердіть дистрибутив WSL і ядро (WSL2 vs WSL1)

cr0x@server:~$ uname -a
Linux cr0x 5.15.133.1-microsoft-standard-WSL2 #1 SMP Wed Oct 25 00:00:00 UTC 2023 x86_64 GNU/Linux

Значення: В роботі — WSL2‑ядро (зверніть увагу на «WSL2»).

Рішення: Якщо ви не на WSL2, спочатку виправте це. WSL1 має іншу поведінку файлової системи і зазвичай погіршує сценарії з Docker Desktop.

Задача 3: Знайдіть, де фактично розміщений ваш проєкт

cr0x@server:~$ pwd
/mnt/c/Users/cr0x/work/myapp

Значення: Ви в Windows‑монті. Очікуйте дивні права і потенційно низьку продуктивність.

Рішення: Перенесіть або переклонуйте в /home/cr0x/work/myapp. Якщо політика забороняє, продовжуйте з оборонною конфігурацією (далі в задачах).

Задача 4: Визначте ваш UID/GID у WSL

cr0x@server:~$ id
uid=1000(cr0x) gid=1000(cr0x) groups=1000(cr0x),27(sudo),44(video)

Значення: Ваш користувач має UID 1000, GID 1000.

Рішення: Використовуйте 1000:1000 як користувача контейнера для dev‑bind‑mount або вбудуйте це в образ.

Задача 5: Відтворіть проблему з правами в мінімальному контейнері

cr0x@server:~$ docker run --rm -it -v "$PWD:/work" alpine:3.20 sh -lc 'id; ls -ld /work; touch /work/.permtest && ls -l /work/.permtest'
uid=0(root) gid=0(root) groups=0(root)
drwxrwxrwx    1 root     root          4096 Jan  3 10:31 /work
-rwxrwxrwx    1 root     root             0 Jan  3 10:31 /work/.permtest

Значення: Контейнер працює як root; створений файл належить root. Бітові права широкі, що типово для Windows‑підтримуваних монтувань.

Рішення: Якщо артефакти, що належать root, — ваша біль, запускайте контейнер під вашим UID/GID.

Задача 6: Перевірте запуск контейнера під вашим UID/GID

cr0x@server:~$ docker run --rm -it --user 1000:1000 -v "$PWD:/work" alpine:3.20 sh -lc 'id; touch /work/.uidtest && ls -l /work/.uidtest'
uid=1000 gid=1000
-rwxrwxrwx    1 1000     1000            0 Jan  3 10:32 /work/.uidtest

Значення: Власність файлу тепер відповідає UID/GID 1000 всередині контейнера. На Linux‑файлових системах це зазвичай відображається коректно. На Windows‑монтах ви все одно можете бачити широкі бітові права та дивну поведінку, але принаймні перестанете псувати файли, що належать root.

Рішення: Впишіть user: "1000:1000" у Compose для dev або реалізуйте створення користувача в Dockerfile.

Задача 7: Перевірте, чи працює chmod/chown на вашому монті

cr0x@server:~$ docker run --rm -it -v "$PWD:/work" alpine:3.20 sh -lc 'touch /work/chmodtest; chmod 600 /work/chmodtest; ls -l /work/chmodtest'
-rwxrwxrwx    1 root     root             0 Jan  3 10:33 /work/chmodtest

Значення: chmod не зберігся (все ще 777‑подібно). Це типовий випадок для Windows‑монтувань без підтримки метаданих.

Рішення: Не покладайтесь на бітові права для семантики безпеки на такому монті. Керуйте контролем доступу на рівні додатка або перенесіть робоче місце в файлову систему WSL, де chmod працює.

Задача 8: Перевірте, чи ви на Windows‑монті (DrvFs) чи на нативній Linux‑файловій системі

cr0x@server:~$ df -T .
Filesystem     Type 1K-blocks      Used Available Use% Mounted on
C:\            9p  976762876 123456789 853306087  13% /mnt/c

Значення: Тип файлової системи — 9p (поширено для Windows‑монтів у WSL2). Це межа трансляції.

Рішення: Очікуйте особливостей. Якщо вам потрібні коректні Unix‑права — переїжджайте на ext4 у WSL2 (/home), а не /mnt/c.

Задача 9: Перевірте опції монтування всередині контейнера

cr0x@server:~$ docker run --rm -v "$PWD:/work" alpine:3.20 sh -lc 'mount | grep " /work " || true'
/dev/sdd on /work type 9p (rw,relatime,dirsync,aname=drvfs;path=C:\Users\cr0x\work\myapp;uid=0;gid=0;metadata;symlinkroot=/mnt/,mmap,access=client,msize=65536,trans=fd,rfd=8,wfd=8)

Значення: Ви бачите, що це DrvFs/9p‑монтаж, і прапори як metadata, які впливають на поведінку прав.

Рішення: Якщо metadata відсутня — chmod/chown ще менш значущі. Якщо metadata є, поведінка прав може бути кращою, але все одно не ідентична ext4.

Задача 10: Перевірка Compose: під яким користувачем працює сервіс?

cr0x@server:~$ docker compose exec app sh -lc 'id; umask'
uid=0(root) gid=0(root) groups=0(root)
0022

Значення: Сервіс у Compose працює як root. Umask нормальний, але не має значення, якщо бітові права не зберігаються.

Рішення: Встановіть user: у Compose або виправте образ. Для розробки root рідко того вартий, якщо не хочете спричиняти побічні проблеми.

Задача 11: Швидке вимірювання продуктивності монтування (маленькі файли)

cr0x@server:~$ docker run --rm -v "$PWD:/work" alpine:3.20 sh -lc 'time sh -c "i=0; while [ $i -lt 2000 ]; do echo $i > /work/t_$i; i=$((i+1)); done"'
real    0m6.842s
user    0m0.187s
sys     0m1.214s

Значення: Створення 2000 файлів зайняло кілька секунд. На нативній Linux‑файловій системі це часто значно швидше.

Рішення: Якщо ваш інструмент збірки створює тисячі файлів (привіт, Node і Java), перемістіть кеші в томи або весь проєкт у файлову систему WSL.

Задача 12: Порівняння з іменованим Docker‑томом (контрольний експеримент)

cr0x@server:~$ docker run --rm -v perfvol:/work alpine:3.20 sh -lc 'time sh -c "i=0; while [ $i -lt 2000 ]; do echo $i > /work/t_$i; i=$((i+1)); done"'
real    0m0.403s
user    0m0.179s
sys     0m0.205s

Значення: Іменований том значно швидший для операцій з інтенсивним записом. Також: права всередині тому — повноцінні Linux‑семантики.

Рішення: Помістіть кеші збірки та каталоги залежностей у томи. Тримайте bind‑mount для вихідного коду й конфігурацій.

Задача 13: Підтвердіть власність файлів на боці хоста (WSL) після запису з контейнера

cr0x@server:~$ ls -ln .uidtest .permtest 2>/dev/null
-rwxrwxrwx 1 1000 1000 0 Jan  3 10:32 .uidtest
-rwxrwxrwx 1    0    0 0 Jan  3 10:31 .permtest

Значення: Є файли, що належать root, бо раніше ви записували як root; файл з UID‑мапінгом належить 1000.

Рішення: Якщо є артефакти, що належать root, виправте користувача в контейнері й очистіть директорію (може знадобитися sudo у WSL).

Задача 14: Перевірте том і переконайтесь, що він не випадково не є bind‑mount

cr0x@server:~$ docker volume inspect perfvol
[
  {
    "CreatedAt": "2026-01-03T10:34:12Z",
    "Driver": "local",
    "Labels": null,
    "Mountpoint": "/var/lib/docker/volumes/perfvol/_data",
    "Name": "perfvol",
    "Options": null,
    "Scope": "local"
  }
]

Значення: Це локальний Docker‑том у Linux‑VM. Ось чому він швидкий і має коректні права.

Рішення: Використовуйте томи для «гарячих» шляхів. Якщо хтось наполягає «bind‑mount нормальний», покажіть їм цифри з задач 11 і 12.

Швидкий план діагностики

Коли щось повільне або «Permission denied», не потрібен тиждень теорії. Потрібні три перевірки, які ізолюють домен проблеми.

Перше: Ви монтуєте Windows‑файли чи Linux‑файли?

  • Запустіть pwd і df -T . у WSL.
  • Якщо ви під /mnt/c і тип файлової системи — 9p, ви в країні трансляції.
  • Рішення: Якщо проблема — коректність прав або продуктивність, перемістіть робоче місце в /home (ext4) як стандартне виправлення.

Друге: Який користувач записує в bind‑mount?

  • Запустіть docker compose exec app id (або docker run ... id).
  • Якщо це root, ви отримаєте артефакти, що належать root, і іноді збої запису залежно від шару монтування.
  • Рішення: Примусьте UID/GID через Compose user: або створіть відповідного користувача в образі.

Третє: Це проблема коректності чи продуктивності?

  • Запустіть бенчмарк для малих файлів (Задача 11) на bind‑mount і порівняйте з томом (Задача 12).
  • Рішення: Якщо том значно швидший, не «налаштовуйте права». Змініть розподіл сховища: bind‑mount для коду, том для інтенсивного запису.

Бонус‑перевірка: Чи варто тут очікувати, що chmod/chown працюватиме?

  • Спробуйте chmod‑тест (Задача 7). Якщо не спрацьовує — припиніть проектувати робочі процеси навколо POSIX‑бітів на цьому монті.
  • Рішення: Перенесіться у файлову систему WSL або тримайте чутливі файли поза Windows‑монтом.

Поширені помилки: симптом → причина → виправлення

1) «Permission denied» при записі в bind‑mount

Симптом: Додаток не може створити файли під /work або /app, виникає EACCES.

Причина: Контейнер працює як некореневий користувач, але bind‑монтувана директорія відображається в Windows ACL, яка не дозволяє записів у звичний спосіб; або директорія належить root через попередні запуски.

Виправлення: Перенесіть проєкт у файлову систему WSL. Інакше запускайте контейнер під вашим WSL UID/GID і очистіть файли, що належать root (вимагає sudo у WSL).

2) «chmod: Operation not permitted» або chmod ніби нічого не змінює

Симптом: chmod повертає помилку або ls -l ніколи не змінюється.

Причина: Windows‑монти не повністю підтримують POSIX‑метадані (або підтримка метаданих не увімкнена).

Виправлення: Не покладайтеся на chmod на Windows‑монтах. Зберігайте скрипти/бінарники у файловій системі WSL. Якщо потрібна точність Unix‑прав, використовуйте ext4 у WSL2 або іменований том.

3) Hot reload / file watcher не бачить змін

Симптом: Node/webpack, Python‑перезавантажувач або Go live reload не спрацьовує при збереженні.

Причина: Переклад подій файлів між Windows ↔ WSL2 ↔ контейнером нестабільний, особливо на /mnt/c.

Виправлення: Тримайте робоче місце у файловій системі WSL. Якщо прив’язано до Windows‑монту, налаштуйте polling‑watchers (повільніше, але надійніше) або перенесіть лише директорії, що спостерігаються, у WSL.

4) Збірка надто повільна, процесор виглядає не завантаженим

Симптом: Встановлення залежностей або компіляція займає вічність; використання CPU низьке.

Причина: Операції з великою кількістю малих файлів гальмуються шаром спільної файлової системи; кожен stat/open/write переходить через межу.

Виправлення: Помістіть інтенсивні для запису шляхи в томи; перемістіть проєкт у WSL; уникайте монтування залежностей з Windows.

5) «Все належить root» і Git починає шкодувати

Симптом: Ви бачите власність root, і Git скаржиться на підозрілу власність або відмовляється працювати в деяких конфігураціях.

Причина: Контейнер запускався як root і писав у bind‑mount; власність збереглася. У змішаних середовищах спрацьовують перевірки безпеки.

Виправлення: Перестаньте запускати як root для розробки. Очистіть робоче місце і зробіть власність детермінованою через відображення UID/GID.

6) Контейнер бази даних працює до певного моменту, потім падає

Симптом: Postgres/MySQL стартує, потім падає з помилками fsync або прав; або продуктивність падає.

Причина: База даних змонтована на Windows‑файлову систему; гарантії POSIX і семантика fsync відрізняються.

Виправлення: Завжди використовуйте іменований том для даних бази, особливо на Windows‑хостах.

7) «Але у мене працює» у команді з Windows‑вагою

Симптом: У одного розробника все добре; у іншого — помилки прав і повільна робота.

Причина: Робочі місця зберігаються в різних місцях (WSL vs /mnt/c), різні налаштування Docker Desktop і різні користувачі за замовчуванням в образах.

Виправлення: Уніфікуйте: місце зберігання робочого каталогу, користувача в Compose і стратегію томів. Розглядайте «dev‑середовище» як інфраструктуру з визначеним специфікаціями.

Три корпоративні історії (як це ламається в реальному житті)

Міні‑історія 1: Інцидент через хибне припущення

Команда мала крос‑платформену dev‑налаштування: Linux‑ноутбуки, macOS і багато Windows. Вони запустили нове контейнеризоване dev‑середовище з bind‑mount репозиторію і контейнером, що працює як root «щоб уникнути проблем з правами». Нікому це не подобалося, але працювало… поки не перестало.

Хибне припущення було тонким: вони вважали, що «root у контейнері завжди може писати в bind‑mount». На Linux‑хостах це часто вірно (за винятком SELinux/AppArmor). На Windows‑монтах root не завжди обходить Windows ACL. Шар трансляції може блокувати операції, і робить це непослідовно залежно від конфігурації шару.

Це проявилося як переривчасті помилки в генераторі коду, що писав у репозиторій. Половина Windows‑розробників бачили «permission denied», інші — ні, і обробка помилок у генераторі була слабкою. Він агресивно повторювався і лишав часткові результати. Репозиторій опинився з мішаниною згенерованих файлів з різною власністю й часовими мітками. Збірки падали й знову успішно проходили.

Поставилися як до невпевненого інструмента. Але проблема була в семантиці зберігання. Виправлення було нудним: перенести робоче місце в WSL2, запускати контейнери як WSL‑користувач і зберігати згенеровані артефакти в томі з контрольованою експортною процедурою. Генератор перестав «рандомно падати», бо більше не вів переговори з NTFS через вузьку трубу.

Міні‑історія 2: Оптимізація, що відкатала назад

Група інженерів хотіла пришвидшити встановлення для JavaScript‑монорепо. Хтось запропонував bind‑mountити node_modules з хоста, щоб залежності зберігалися між перезбираннями контейнера. Це підносили як «безкоштовний кеш». Керівництву сподобалася безкоштовність.

На Linux‑машинах це було непогано. На Windows‑машинах — повільна катастрофа. Встановлення залежностей створює і видаляє велику кількість дрібних файлів і симлінків. Шар Windows‑монтування справлявся, але з великим оверхедом. Встановлення стали повільнішими, ніж чисті встановлення всередині файлової системи контейнера, а file watchers стали ненадійними.

Гірше: права перетворилися на хаос. Деякі розробники запускали контейнери як root, деякі — як юзер. Теж саме каталоги залежностей набули змішаної власності. Інструменти почали падати з EPERM, і розробники «вирішували» це, видаляючи каталог і перевстановлюючи—знову і знову.

Вони врешті відмінили «оптимізацію» і замінили її іменованим томом для кешу залежностей, і окремим томом для node_modules, коли це було потрібно. Репозиторій залишився bind‑mounted, а churn пішов у томи. Команда перестала платити I/O‑податок шару Windows. Урок закарбувався: кешування — не безкоштовне, якщо кеш зберігається в неправильному місці.

Міні‑історія 3: Нудна практика, що врятувала день

Регульована команда мала дивно розумну практику: будь‑яка зміна dev‑середовища вимагала невеликого «ops‑стилю» оновлення чек‑ліста. Не бюрократичний роман — просто сторінка з командами для перевірки правильності, продуктивності й власності.

Коли вони прийняли Docker Desktop з WSL2, додали три перевірки: (1) шлях проєкту під /home, (2) docker compose exec app id відповідає UID розробника, і (3) бенчмарк для малих файлів під bind‑mount не перевищує грубого порогу. Якщо перевищував — чек‑лист наказував перекласти кеші в томи.

Місяць потому оновлення Docker Desktop змінило поведінку на частині машин. Люди почали скаржитися на повільні збірки і дивний chmod. Чек‑лист зробив все очевидним: робоче місце мігрувало назад у /mnt/c, бо новий співробітник слідував старому гайду з онбордингу, що припускав Windows‑шляхи.

Рутинна практика, що врятувала день, була нудною: примусове розташування робочих каталогів і короткий діагностичний ритуал. Вони відредагували гайд з онбордингу, переклонили репозиторії у WSL, і інцидент помер тихо. Нічого героїчного — найкращий результат в операційній роботі.

Цікаві факти та історичний контекст (8 пунктів)

  1. Початкова модель Docker передбачала Linux‑ядро. Підтримка Windows з’явилася пізніше і вимагала або Windows‑контейнери, або Linux‑VM для Linux‑контейнерів.
  2. Docker Desktop на Windows зазвичай запускає Linux‑контейнери всередині WSL2. Ось чому Linux‑семантика «реальна» всередині VM, але стає «трансляцією», коли торкаєшся Windows‑файлів.
  3. WSL1 і WSL2 принципово різні. WSL1 — шар трансляції системних викликів; WSL2 — справжнє Linux‑ядро у легкій VM, що суттєво змінює поведінку файлової системи.
  4. NTFS використовує ACL, а не бітову модель. Відображення на бітові rwx є наближене, і інструменти, що виходять з POSIX‑припущень, можуть вестися оманливо.
  5. Чутливість до регістру на Windows еволюціонувала. Windows може вмикати чутливість для окремих директорій, але багато тулчейнів і досі ґрунтуються на типовій нечутливості NTFS.
  6. Спостереження за файлами — пастка портативності. Inotify — рідний для Linux; Windows має інші API; мостити події між ними завжди неточно, ситуація покращувалась з роками, але лишається джерелом болю для розробки.
  7. Bind‑mounts — не «томи, але простіші». Вони успадковують семантику файлової системи хоста, що на Linux — чудово, а на Windows/macOS — складно.
  8. Іменовані Docker‑томи часто швидші, бо лишаються в Linux‑файловій системі. Менше переходів через межі — менше сюрпризів і менше падінь продуктивності.

Чек‑листи / покроковий план

Чек‑лист A: Новий Windows‑лаптоп, найменш болючий Docker‑dev

  1. Увімкніть WSL2 і встановіть Linux‑дистрибутив.
  2. Встановіть Docker Desktop і переконайтеся, що він використовує двигун WSL2.
  3. У WSL створіть робочу директорію: mkdir -p ~/work.
  4. Клоньте репозиторії в ~/work, а не в /mnt/c.
  5. Визначте політику UID/GID: контейнер для розробки запускається під вашим UID/GID.
  6. Оновіть Compose: bind‑mount для вихідників, томи для кешів і даних.
  7. Додайте однорядковий smoke‑test: контейнер створює файл у bind‑mount і перевіряє власність.

Чек‑лист B: Шаблони Compose, що зменшують біль

  • Bind‑mount: лише вихідний код і мінімальна конфігурація.
  • Томи: каталоги залежностей, результати збірки, дані БД, кеші пакетів.
  • Відображення користувача: вкажіть user: "${UID}:${GID}" для dev‑сервісів, що пишуть у монти.
  • Entrypoints: уникайте скриптів, що припускають, що chmod працює на bind‑mount.
  • Health checks: якщо ваш додаток пише стан, перевіряйте це при старті і фіксуйте помилку одразу.

Чек‑лист C: Коли ви мусите тримати репо на C:\

  1. Прийміть, що chmod/chown можуть бути ненадійними; проектуйте навколо цього.
  2. За можливості запускайте контейнери як не‑root, щоб уникнути артефактів root.
  3. Перенесіть всі інтенсивні для запису шляхи в томи.
  4. Якщо file watching нестабільний — використовуйте polling‑watchers і документуйте компроміс.
  5. Тримайте тести на правильність bind‑mount у CI або скриптах онбордингу, щоб знос виявлявся рано.

Питання та відповіді

1) Чому chmod не працює на моєму bind‑mount з Windows?

Тому що підкладова файлова система — NTFS, виставлена через шар трансляції. Бітові права — не рідна концепція там, тому вони можуть не зберігатися або емулюватися.

2) Чи найпростіше вирішення — запускати контейнер як root?

Це найпростіший спосіб створити інший набір проблем. Ви отримаєте артефакти, що належать root, складне очищення і іноді ті самі помилки запису на Windows‑монтах.

3) Який найчистіший спосіб уникнути файлів, що належать root?

Запускайте контейнер під вашим WSL UID/GID і тримайте робоче місце у файловій системі WSL. Ця комбінація — найближче до «поведінка як Linux».

4) Використовувати bind‑mount чи томи для баз даних на Windows?

Томи. Завжди. Бази даних розраховують на певні POSIX‑гарантії і характеристики продуктивності, яких Windows‑bind‑mountи не гарантують.

5) Мені потрібно редагувати файли Windows‑додатками. Чи можна тримати репо у WSL?

Так, якщо ваш редактор підтримує інтеграцію з WSL або віддалений доступ до файлової системи. Це сучасний і підтримуваний шлях для серйозної розробки на Windows з Linux‑контейнерами.

6) Моя збірка повільна, але права виглядають нормально. Що робити?

Пропустіть бенчмарк I/O малих файлів на bind‑mount і в томі (Задачі 11 і 12). Якщо том значно швидший — перемістіть кеші і каталоги залежностей у томи.

7) Чому file watching/hot reload не працює тільки на Windows?

Бо API подій файлів різняться, і події мають проходити через шари (Windows ↔ WSL2 ↔ контейнер). Чим далі файли від нативної файлової системи контейнера, тим більша ймовірність пропусків.

8) Чи можна «виправити» це одним налаштуванням Docker Desktop?

Не надійно. Деякі налаштування покращать ситуацію, але найефективніше архітектурне рішення: тримати код у файловій системі WSL і використовувати томи для інтенсивного запису.

9) Що робити, якщо команда використовує змішані ОС‑хости?

Уніфікуйте поведінку користувача в контейнері і макет сховища. Ваш Compose не повинен робити припущення лише про Linux‑семантику, якщо Windows‑хости — першокласні. Документуйте вимогу WSL‑workspace.

Висновок: наступні кроки, які можна зробити сьогодні

Якщо хочете «найменш болісно», припиніть вести переговори з NTFS про Linux‑семантику. Покладіть репо у файлову систему Linux WSL2, робіть bind‑mount звідти і запускайте dev‑контейнери під своїм UID/GID. Потім використовуйте іменовані томи для всього, що багато записує.

Конкретні кроки:

  1. Перевірте шлях до репозиторію. Якщо він під /mnt/c, переклонуйте у ~/work.
  2. Встановіть user: "1000:1000" (або ваш фактичний UID/GID) у Compose для dev‑сервісів, що пишуть у монти.
  3. Перенесіть каталоги залежностей і кеші в томи і повторіть збірку. Порівняйте числа з Задач 11 і 12.
  4. Додайте невеликий smoke‑тест у процес онбордингу: створення файлу в bind‑mount і перевірка власності та можливості запису.

Ви не намагаєтесь виграти суперечку з Windows. Ви намагаєтесь запустити production‑якісні dev‑процеси на ноутбуці. Зробіть макет зберігання нудним — і решта піде за ним.

← Попередня
Чому два GPU часто гірші за один (справжні причини)
Наступна →
ZFS zfs list -o space: Подання, яке пояснює «Куди воно поділося?»

Залишити коментар