Якщо ви використовуєте Docker Desktop на Windows із WSL2 і здається, що контейнери роблять усе крізь мокрий цемент, — вам не здається. «npm install» займає геологічний час. Слідкувачі файлів пропускають зміни або їдять CPU. Простий git status може змусити засумніватися у виборі професії.
Це можна виправити. Але не через вбрання, «регістрні» хаки з форуму або клацання випадкових чекбоксів, поки вентилятор не заспокоїться. Ви виправляєте це, визначивши, яка підсистема справді повільна: межа файлової системи, диск VHDX, планування CPU, тиск пам’яті, DNS або поведінка кешу збірки.
Швидкий план діагностики
Більшість команд витрачають дні на «налаштування Docker», коли вузьке місце — одна межа: файли Windows на /mnt/c, або bind mount у контейнер, або VHDX, що роздувся і тепер повільно працює з I/O. Ось короткий шлях до істини.
По-перше: визначте, де живуть повільні файли
- Якщо ваш репозиторій під
/mnt/c(або будь-яким/mnt/<drive>): вважайте, що вузьке місце — файлові операції, поки не доведете протилежне. - Якщо ваш репозиторій у Linux-файловій системі (
~у WSL2): файлові операції зазвичай нормальні; перевірте натомість тиск CPU/пам’яті або шаблони маунтів контейнерів.
По-друге: ідентифікуйте «клас повільних операцій»
- Багато дрібних файлів (node_modules, директорії vendor, монорепозиторії): bind mounts і Windows-interop будуть шкодити.
- Навантаження баз даних (Postgres, MySQL, Elasticsearch): випадковий I/O + fsync + проблеми тонкого виділення пам’яті.
- Збірки (Docker builds): домінують розташування кешу та передача контексту.
- Мережеві навантаження (завантаження образів, приватні реєстри): DNS і проксі, а не «продуктивність Docker».
По-третє: запустіть три тести для локалізації вузького місця
- Тест файлової системи WSL: створіть і stat велику кількість файлів під
~у WSL2. - Тест /mnt: повторіть під
/mnt/c. - Тест маунта в контейнері: запустіть той самий тест всередині контейнера на bind mount проти іменованого тому.
Якщо /mnt/c значно повільніший за ~, зупиніться. Перенесіть репозиторій. Якщо bind mounts у контейнері повільніші за named volumes, змініть стратегію маунтів. Якщо обидва в порядку, ймовірно, проблема з CPU/пам’яттю або DNS.
По-четверте: перевірте нестачу ресурсів
Якщо WSL2 недопропонований (або надто багато ресурсів віддано так, що Windows страждає), усе стає нестабільним: слідкувачі, компілятори, бази даних. Виправте ліміти пам’яті та swap, потім повторіть тести.
Чому повільно: реальна архітектура та її пастки
Docker Desktop на Windows із WSL2 — це не «Docker, запущений на Windows». Це Docker, що працює в Linux VM із купою проводки, щоб почуватися рідним. Саме в цій проводці продуктивність любить заводити хобі.
У загальних рисах:
- WSL2 — це легкий VM, який під капотом використовує Hyper-V.
- Ваша Linux-дистрибуція (Ubuntu, Debian тощо) живе на ext4-файловій системі всередині VHDX.
- Docker Desktop зазвичай запускає демон у спеціальній WSL2-дистрибуції (часто
docker-desktop). - Коли ви звертаєтеся до Windows-файлів з WSL2 (наприклад,
/mnt/c/Users/...), ви переходите межу, реалізовану спеціальним шаром файлової системи. - Коли ви робите bind-mount Windows-шляху в Linux-контейнер, ви часто знову переходите ту саму межу, плюс накладаються overlay і семантика маунтів контейнера.
Проблеми з продуктивністю зазвичай виникають через одну з таких причин:
- Накладні витрати на межі інтеракції файлових систем: Linux-інструменти очікують дешевих операцій метаданих. Windows-файли, доступні через шар монтування WSL, можуть робити
stat()і обходи директорій дорогими. - Занадто багато файлів: мільйони дрібних файлів — це випробування для будь-якого шару обміну файлами між ОС.
- Поведінка образу диска: VHDX легко зростає; зменшити розмір не тривіально; фрагментація і розташування вільного простору важливі більше, ніж хотілося б.
- Тиск пам’яті: WSL2 агресивно кешує; під тиском ви отримаєте хвилі видалення кешу і моменти «чому все почало свопитись?».
- Передача контексту збірки: коли ви збираєте з Windows-шляху, Docker може витрачати реальний час на надсилання контексту в VM.
- Inotify і сповіщувачі: деякі тулчейни спираються на події файлової системи; перетин меж може зламати семантику або змусити перейти на опитування.
Одна цитата, щоб тримати себе в реальності: «Надія не стратегія.»
— парафраз ідеї, приписуваної ген. Gordon R. Sullivan, яка часто повторюється у надійності. Замініть «надію» на «випадкові перемикання», і ви готові до продакшену.
Факти та історичний контекст (коротко, корисно)
- WSL1 vs WSL2 — це був обмін у файловій площині: WSL1 транслював Linux-системні виклики в Windows; WSL2 перейшов на справжнє ядро Linux у VM. Багато чого стало суміснішим, але доступ до крос-файлової системи став важливішим.
- Docker Desktop раніше використовував Hyper-V до інтеграції з WSL2: ранні Windows-конфігурації Docker запускали LinuxKit VM; WSL2 зменшив накладні витрати й покращив інтеграцію, але не усунув межі VM.
- ext4 у VHDX швидкий… поки ви його не ускладните: він поводиться як звичайна Linux-файлова система, але підлягає циклу життя VHDX (зростання, фрагментація, компактування).
- Навантаження з великою кількістю метаданих — звичний біль: пакетні менеджери й мовні тулчейни роблять безліч
stat(),readdir()і короткотривалих записів. - Семантика спільного доступу файлів відрізняється: чутливість до регістру, біти виконуваності, символьні посилання і відображення прав — все це не безкоштовно. «Працює» не означає «швидко працює».
- BuildKit змінив правила гри для збірок: сучасні Docker-збірки можуть кешувати розумніше, але тільки якщо ви не саботуєте кеш величезними контекстами й змінними часовими мітками.
- Named volumes — не лише для продакшену: вони обходять частину повільних шарів спільного доступу хоста і можуть бути значно швидшими для «гарячих» директорій.
- Мережа WSL2 віртуалізована: DNS і маршрутизація можуть відрізнятися від Windows, а корпоративні VPN дуже люблять втручатися.
Зробіть це першими: 12+ перевірок з командами та рішеннями
Це реальні завдання, які ви можете виконати. Кожне містить: команду, приклад виводу, що це означає і яке рішення прийняти.
Завдання 1: Підтвердіть, де насправді лежить ваш репозиторій
cr0x@server:~$ pwd
/home/cr0x/work/myapp
Значення: Це всередині Linux-файлової системи (добре). Якщо натомість ви бачите /mnt/c/..., це основний підозрюваний.
Рішення: Якщо ваш проєкт на /mnt/c, плануйте перемістити його в ~ і використати редактор з підтримкою WSL remote.
Завдання 2: Порівняйте продуктивність дрібних файлів: домашня WSL vs /mnt/c
cr0x@server:~$ mkdir -p ~/io-test && cd ~/io-test && time bash -lc 'rm -rf a && mkdir a && for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m3.412s
user 0m1.102s
sys 0m2.101s
cr0x@server:~$ mkdir -p /mnt/c/Users/Public/io-test && cd /mnt/c/Users/Public/io-test && time bash -lc 'rm -rf a && mkdir a && for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 1m12.883s
user 0m2.004s
sys 0m8.991s
Значення: Якщо ваш запуск на /mnt/c на порядок повільніший, ви знайшли основну проблему.
Рішення: Тримайте код і каталоги залежностей у файловій системі WSL. Ставтесь до /mnt/c як до зручного монтування, а не як до ферми збірок.
Завдання 3: Перевірте контекст Docker і деталі engine
cr0x@server:~$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based configuration npipe:////./pipe/docker_engine
Значення: На Windows CLI може говорити з Docker Desktop через Windows named pipe, навіть якщо engine працює в WSL2.
Рішення: Якщо ви змішуєте Windows і WSL CLI, оберіть один робочий процес. Краще запускати Docker CLI всередині WSL, щоб зменшити переходи меж.
Завдання 4: Переконайтеся, що ви будуєте з Linux-шляхів (контекст має значення)
cr0x@server:~$ docker build -t myapp:ctxcheck .
[+] Building 9.6s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load .dockerignore 0.0s
=> [internal] load metadata for docker.io/library/node:20-alpine 0.9s
=> [internal] load build context 6.8s
=> => transferring context: 412.34MB 6.6s
...
Значення: «transferring context», що займає секунди, нормальне для великих контекстів; якщо це десятки секунд, ви платите податок межі або надсилаєте занадто багато.
Рішення: Додайте реальний .dockerignore і збирайте в межах WSL. Якщо ваш контекст величезний, припиніть надсилати node_modules демону.
Завдання 5: Перевірте, чи ввімкнено BuildKit (повинно бути)
cr0x@server:~$ docker buildx version
github.com/docker/buildx v0.12.1
Значення: Buildx існує; BuildKit доступний.
Рішення: Використовуйте можливості BuildKit, як-от cache mounts для пакетних менеджерів. Якщо у вас ще старий builder, чекайте болю.
Завдання 6: Виміряйте продуктивність bind mount проти named volume у контейнері
cr0x@server:~$ docker volume create voltest
voltest
cr0x@server:~$ mkdir -p ~/bindtest
cr0x@server:~$ time docker run --rm -v ~/bindtest:/work alpine sh -lc 'cd /work; rm -rf a; mkdir a; for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m18.911s
user 0m0.211s
sys 0m0.404s
cr0x@server:~$ time docker run --rm -v voltest:/work alpine sh -lc 'cd /work; rm -rf a; mkdir a; for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m4.022s
user 0m0.203s
sys 0m0.380s
Значення: Якщо named volumes значно швидші, вузьке місце — шлях bind mount.
Рішення: Розміщуйте «гарячі» директорії (залежності, кеші, дані БД) на named volumes. Bind mount лишайте тільки для коду, який ви активно редагуєте.
Завдання 7: Перевірте пам’ять, swap і тиск у WSL
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 7.6G 6.8G 220M 120M 650M 390M
Swap: 2.0G 1.4G 600M
Значення: Ви близькі до обриву. Низьке «available» і активний swap означають, що у вас будуть затримки по всій системі.
Рішення: Збільшіть пам’ять для WSL2, зменшіть паралелізм або не запускайте одночасно п’ять баз даних плюс браузер і Teams. Оберіть двоє.
Завдання 8: Визначте «гарячі» I/O і процеси всередині WSL
cr0x@server:~$ sudo apt-get update -qq && sudo apt-get install -y -qq iotop
...
cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 12.34 M/s | Total DISK WRITE: 45.67 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2421 be/4 postgres 0.00 B/s 9.12 M/s 0.00 % 85.12 % postgres: checkpointer
9911 be/4 cr0x 0.00 B/s 5.44 M/s 0.00 % 62.10 % node dev-server
Значення: Ви бачите, хто б’є по диску. Тривалі записи від Postgres checkpointer часто означають налаштування fsync + checkpoint або невідповідність навантаження.
Рішення: Якщо бази даних домінують, перемістіть їхні каталоги даних на named volumes і перегляньте налаштування БД для dev (не продакшен) компромісів швидкості/надійності.
Завдання 9: Перевірте опції монтування файлової системи для /mnt/c (поведінка метаданих)
cr0x@server:~$ mount | grep -E ' /mnt/c '
C: on /mnt/c type 9p (rw,noatime,dirsync,aname=drvfs;path=C:\;uid=1000;gid=1000;metadata;symlinkroot=/mnt/)
Значення: Параметри drvfs/9p впливають на поведінку. metadata може покращити семантику прав, але мати накладні витрати; dirsync часто дорогий.
Рішення: Не налаштовуйте прапорці монтування бездумно. Якщо ваш репозиторій на /mnt/c, виправлення — перемістити його. Якщо ж залишаєтесь, розгляньте коригування опцій automount, але вважайте це крайнім заходом.
Завдання 10: Подивіться, чи VHDX величезний і чи ви за це платите
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sdd 251G 201G 38G 85% /
Значення: Велике використання і великий ext4-файловий образ може корелювати з повільнішими операціями, особливо якщо ви постійно перетасовуєте залежності.
Рішення: Почистіть кеші, зробіть prune Docker-артефактів і розгляньте періодичне компактування/зменшення VHDX (обережно, з бекапами).
Завдання 11: Виміряйте поведінку DNS зсередини контейнера
cr0x@server:~$ docker run --rm alpine sh -lc 'apk add -q bind-tools; time nslookup registry-1.docker.io'
real 0m1.882s
user 0m0.012s
sys 0m0.008s
Server: 192.168.65.1
Address: 192.168.65.1:53
Non-authoritative answer:
Name: registry-1.docker.io
Address: 54.161.123.11
Значення: DNS-запити, що тривають кілька секунд, зроблять завантаження/збірки «повільними», навіть якщо диск в порядку.
Рішення: Якщо DNS повільний, виправляйте DNS (генерацію resolv.conf у WSL, корпоративний DNS, split tunneling VPN). Не звинувачуйте overlay2 за ваш резолвер.
Завдання 12: Переконайтеся, що у вас немає патологічного розростання образів/томів
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 8 18.4GB 12.7GB (68%)
Containers 19 2 1.2GB 900MB (75%)
Local Volumes 31 6 54.0GB 40.5GB (75%)
Build Cache 0 0 0B 0B
Значення: Томи, що займають 54GB, нормальні лише якщо ви працюєте з реальними датасетами. В dev це часто покинуті БД і кеші.
Рішення: Прудіть те, що можете, але з наміром. Якщо томи містять важливий стан, мігруйте або зробіть бекап перед видаленням.
Завдання 13: Перевірте, чи контейнер виконує надто багато fsync (БД)
cr0x@server:~$ docker exec -it my-postgres sh -lc 'psql -U postgres -c "SHOW synchronous_commit; SHOW fsync;"'
synchronous_commit
-------------------
on
(1 row)
fsync
-------
on
(1 row)
Значення: У dev synchronous_commit=on і fsync=on безпечно, але може бути повільним на віртуалізованих шляхах зберігання.
Рішення: Для швидкості тільки в dev розгляньте послаблення налаштувань (приймаючи можливість втрати даних при краху). Для чогось, що імітує продакшен, тримайте за замовчуванням і оптимізуйте шлях зберігання.
Завдання 14: Помітити, чи слідкувачі файлів переходять на опитування (спалювання CPU)
cr0x@server:~$ ps aux | grep -E 'watch|chokidar|webpack|nodemon' | head
cr0x 18821 65.2 3.1 1245320 251212 ? Sl 10:11 5:44 node node_modules/.bin/webpack serve
cr0x 18844 22.7 1.2 981244 96120 ? Sl 10:11 2:03 node node_modules/chokidar/index.js
Значення: Велике споживання CPU від слідкувачів часто означає, що вони не отримують нативних подій і сканують повторно.
Рішення: Тримайте відстежувані директорії на ext4 у WSL, а не на /mnt/c. Зменшіть область стеження. Віддавайте перевагу конфігураціям інструментів, які ефективно використовують inotify.
Виправлення, які дійсно дають результат
1) Покладіть код у файлову систему WSL. Так, серйозно.
Це єдине виправлення з найвищим ROI для робочих навантажень розробників, що інтенсивно працюють з дрібними файлами. Тримайте репозиторії під ~/src всередині WSL. Доступайте їх з Windows через WSL-aware інструменти замість навпаки.
- Робіть:
git cloneвсередині WSL. - Не робіть: тримати репозиторій на NTFS і «просто змонтувати». Саме так ви отримуєте 70-секундні обходи директорій.
Жарт №1: Якщо ви наполягаєте запускати node_modules з /mnt/c, ви не налаштовуєте продуктивність — ви відтворюєте повільний катастрофічний фільм уповільнено.
2) Використовуйте named volumes для «гарячих» даних, bind mounts — для циклу редагування
Ментальна модель, що рятує: bind mounts — для коду; named volumes — для швидкого churn. Бази даних, кеші пакетних менеджерів, каталоги залежностей, все, що створює тисячі файлів швидко — дайте цьому том.
Приклад стратегії:
- Bind mount:
./app:/app - Named volume:
node_modules:/app/node_modules - Named volume:
pgdata:/var/lib/postgresql/data
Чому це допомагає: named volumes живуть безпосередньо у файловому шарі Linux VM, уникаючи повільних шарів спільного доступу хоста.
3) Перестаньте відправляти сміття в контекст збірки Docker
Якщо ваш контекст збірки — сотні МБ, ви платите за tarball + передавання + розпакування до того, як перший рядок Dockerfile матиме значення. Ваш .dockerignore має бути агресивним: ігноруйте каталоги залежностей, артефакти збірки, тести і локальні кеші.
Більшість команд роблять це напівшляхом: вони ігнорують node_modules, але забувають .git, dist, .next, coverage і кеші мов. Потім дивуються, чому «load build context» домінує.
4) Використовуйте кеш-маунти BuildKit для пакетних менеджерів
BuildKit може кешувати каталоги між збірками, не вбудовуючи їх у шари. Це означає швидші повторні збірки й менші образи. Якщо ви робите повторні збірки на WSL2, це важливо.
Прикладні патерни (концептуально): кешуйте кеш завантажень пакетного менеджера, а не вихід програми. Зберігайте збірку детермінованою.
5) Тримайте Docker CLI всередині WSL коли можливо
Використання Windows CLI для керування демоном у WSL2 не завжди жахливе, але додає межі, відмінності середовищ і плутанину шляхів. Запуск CLI у WSL зменшує тертя і робить шляхи адекватними.
6) Правильно розподіліть ресурси WSL2 (і не голодуйте Windows)
WSL2 із задоволенням з’їсть RAM для кешу, потім іноді віддасть її… колись. Якщо нічого не робити, у вас може бути відмінна продуктивність доти, доки її не стане.
Типова стратегія: обмежте пам’ять, встановіть розумний swap і уникайте виділення всіх ядер, якщо Windows має залишатися інтерактивною.
7) Будьте обережні з антивірусом і індексацією
Коли Windows Defender (або корпоративний endpoint protection) сканує ті самі файли, що ви інтенсивно чіпаєте через WSL2 interop, з’являється «таємна» повільність. Виключення можуть допомогти, але робіть їх разом із командою безпеки, а не в паніці о 2 ранку.
8) Обробляйте налаштування надійності БД як контракт dev/prod
Якщо ви послабите надійність (fsync вимкнений, async commit), ваша локальна БД стане швидкою, але зможе втратити дані при краху VM. Це підходить для локальної розробки; неприйнятно для інтеграційних тестів, що імітують продакшен.
Визначте, що саме ви робите, і налаштуйте відповідно.
9) Виправляйте DNS, якщо завантаження і підключення повільні
У корпоративних мережах DNS може бути прихованим лиходієм. WSL2 має власну конфігурацію резолвера, Docker — внутрішній DNS, а VPN-клієнти люблять змінювати налаштування під час роботи.
Правило: якщо nslookup повільний всередині контейнера, виправляйте DNS спочатку. Не чіпайте налаштування зберігання, поки резолвер не відповідає менше ніж за 100 мс для основних доменів.
10) Тримайте слідкувачі там, де працює inotify
Файлові слідкувачі є множниками продуктивності: inotify означає подієвий підхід; опитування означає «скануйте планету знову і знову». Розміщуйте спостережувані дерева на ext4 у WSL2, зменшуйте область стеження і налаштовуйте інструменти, щоб уникати спостереження директорій залежностей.
11) Прудьте і компактніть свідомо, а не як ритуал
Docker-артефакти накопичуються. Образи дисків WSL2 ростуть. Prune може допомогти, але безрозсудне видалення знищує стан і примушує витрачати час на відновлення. Встановіть ритм: чистка кешу збірки щотижня, видалення покинутих томів щомісяця, компактування дисків при великому рості через churn.
Жарт №2: «Docker prune» — це не стратегія продуктивності; це зізнання, що ви не знаєте, що використовує диск.
Три корпоративні історії з поля бою
1) Інцидент через хибне припущення: «У мене SSD, отже швидко»
Одна команда, з якою я працював, перейшла з Mac на Windows через закупівельні причини. Docker Desktop + WSL2 здавалося найпростішим способом зберегти робочий процес «як був». Вони клонували репозиторії в C:\Users\..., відкривали їх у Windows IDE і дозволяли WSL2 та контейнерам робити інше.
За тиждень симптоми були скрізь: тести таймаутяться, режим watch пропускає збірки, і почастішали звернення «Docker не працює». Логи були чисті. Контейнери — здорові. Але кожен розробник описував те саме: дрібні операції повільні, і сповільнення здавалось нелінійним, ніби воно гіршало протягом дня.
Хибне припущення було простим: «код на SSD, отже I/O не може бути вузьким місцем». Вони забули, що код не доступний як сирий NTFS для Linux-процесів. Він доступний через межу, з трансляцією і семантикою метаданих зверху.
Ми зробили дурний тест: створити 20 000 дрібних файлів на /mnt/c і в ~. Дельта була настільки велика, що ніхто не сперечався. Вони перемістили репозиторії в WSL, використали інтерфейс редактора, що підтримує WSL, і зробили bind mount з Linux-шляху в контейнери.
Драматичний момент був не в виправленні. Він полягав у тому, як швидко зник наратив «Docker повільний», коли робочий процес перестав робити кругосвітнє обходження через Windows-файлові семантики для кожного stat().
2) Оптимізація, що відкотилась: «Давайте покладемо все на томи»
Інша організація побачила bind mounts як «повільну частину», що часто правда. Тож вони зробили все: все перемістили на named volumes, включно з кодом. Їхній Compose створював том на сервіс, потім вони використовували docker cp для синхронізації коду в том і запускали додаток звідти.
Продуктивність покращилась миттєво. Збірки були швидшими. Встановлення Node перестало таймаутитись. Було святкування.
Потім відкот: досвід розробника погіршився. Інкрементальні правки не завжди прогресували передбачувано. Налагодження стало незручним, бо джерело правди стало «код у томі», а не «код у репозиторії». Декілька розробників випадково мали застарілий код у томах і витратили години на пошук фантомних багів, яких «нікому іншому не вдається відтворити».
Ще гірше: сканери безпеки й ліцензійні інструменти очікували перевіряти репозиторій на диску. Тепер потрібно було нове рішення для сканування вмісту томів, за яке ніхто не хотів відповідати. Продуктивність перемогла, але робочий процес став крихким.
Стабільний компроміс виявився нудним: bind mount джерела (з WSL ext4), тримати гарячі директорії (залежності, кеші, дані БД) на named volumes і зберігати єдине джерело правди — ваш git-чек-аут.
3) Нудна, але правильна практика, що врятувала день: «Виміряй межу, потім стандартизуй»
Платформна команда, що підтримувала кілька продакт-команд, бачила повторювані скарги: «Docker повільний на Windows». Вони могли написати вікю і молитися. Натомість вони створили мінімальний діагностичний скрипт і зробили його частиною онбордингу.
Він робив три речі: бенчмарк створення дрібних файлів під ~ і під /mnt/c, бенчмарк bind-mount у контейнері і таймінг DNS-запиту. Він друкував результати з порогами і рекомендованою дією («move repo», «use named volume», «check VPN DNS»).
Ця практика була не гламурною. Але вона запобігла місяцям низької продуктивності. Нові співробітники знали очікуваний робочий процес з першого дня: тримати репозиторії в WSL, використовувати віддалені інструменти, не робити bind mount Windows-шляхів і не звинувачувати Docker у проблемах DNS.
Коли після оновлень Windows з’являлись регресії продуктивності, у них були базові результати для порівняння. Ось як не ставити продуктивність у розряд фольклору.
Типові помилки: симптом → корінна причина → виправлення
1) «npm install займає вічність»
Симптом: встановлення тривають хвилини; CPU низький; дискова активність виглядає завантаженою, але не насиченою.
Корінна причина: дерево залежностей знаходиться на /mnt/c або bind-mounted з Windows-шляху; операції метаданих дорогі.
Виправлення: перемістіть репозиторій в WSL ext4; покладіть node_modules на named volume; переконайтеся, що build context ігнорує залежності.
2) «Слідкування за файлами ненадійне або їсть CPU»
Симптом: hot reload пропускає зміни; вентилятори шумлять; слідкувачі показують високий CPU.
Корінна причина: inotify-події не проходять через межу; інструмент переходить на опитування великих дерев.
Виправлення: тримайте відстежувані директорії у файловій системі WSL; зменшуйте область спостереження; не стежте за директоріями залежностей; налаштуйте інструменти під WSL.
3) «Docker build повільний навіть з кешем»
Симптом: «load build context» домінує; кеш часто промахується.
Корінна причина: величезний контекст, поганий .dockerignore, збірка з Windows-шляхів; часові мітки або згенеровані файли руйнують шари.
Виправлення: агресивний .dockerignore; збірка з WSL-шляхів; використовуйте кеш-маунти BuildKit; стабілізуйте вхідні дані.
4) «База даних у контейнері страшенно повільна»
Симптом: велика затримка простих запитів; багато дискових записів; іноді фризи.
Корінна причина: каталог даних БД на bind mount, що перетинає Windows-межу; налаштування надійності підсилюють I/O вартість; тиск пам’яті викликає часті чекпоінти.
Виправлення: зберігайте дані БД на named volume; виділіть більше пам’яті; для dev можна розглянути послаблення надійності (усвідомлено).
5) «Завантаження образів повільні; збірки зависають на apt/apk/npm»
Симптом: мережеві кроки зависають; повторні спроби; працює на домашньому Wi‑Fi, не працює на VPN.
Корінна причина: затримки DNS або зламаний резолвер у WSL2/Docker; взаємодія корпоративного проксі/VPN.
Виправлення: виміряйте DNS зсередини контейнера; налаштуйте DNS; координуйтесь з ІТ щодо політики split DNS/proxy.
6) «Усе було нормально, потім повільно за місяці»
Симптом: поступове деградирование; зріст використання диска; prune «допомагає» тимчасово.
Корінна причина: зростання VHDX, накопичення образів/томів, churn залежностей; можлива фрагментація і мало вільного місця.
Виправлення: чистити свідомо (томи/образи/кеш збірки); тримати вільне місце; компактувати VHDX як планову операцію обслуговування.
Чеклісти / покроковий план
Чекліст A: Виправте dev-робочий процес (швидкі перемоги)
- Перенесіть репозиторії в WSL:
~/src— ваша домашня база. - Запускайте Docker CLI в WSL: зменшіть плутанину шляхів і переходи меж.
- Bind mount тільки з WSL-шляхів: ніколи не робіть bind mount
C:\шляхів у Linux-контейнери, якщо вам важлива швидкість. - Покладіть churn на томи: дані БД, каталоги залежностей, кеші пакетних менеджерів.
- Укріпіть
.dockerignore: зменшіть передачу контексту і інвалідацію кешу. - Знову виміряйте: перезапустіть бенчмарки дрібних файлів і маунтів, щоб довести покращення.
Чекліст B: Стабілізуйте поведінку ресурсів WSL2
- Оберіть верхній ліміт пам’яті, який тримає Windows відзивною і WSL продуктивним.
- Встановіть swap свідомо (не нуль, не нескінченність). Swap ховає проблеми, поки не перестає.
- Стежте за «available» пам’яттю під час збірок і тестів; уникайте тривалого swap-in/out.
- Не запускайте дублюючі стеки: не ставте ту саму БД у Windows і в контейнерах «на всякий випадок».
Чекліст C: Зробіть збірки швидкими і передбачуваними
- Увімкніть BuildKit і використовуйте cache mounts де доречно.
- Мінімізуйте контекст; якщо контекст великий, ви платите податок кожної збірки.
- Відокремте залежності від коду додатку в Dockerfile, щоб максимально використовувати кеш.
- Не bind-mountьте вихід збірки назад у Windows, якщо вам це не потрібно; тримайте гарячі артефакти в Linux-шляхах.
Чекліст D: Налагодьте мережеву повільність без гадань
- Виміряйте DNS всередині контейнера (
nslookupтаймінг). - Підтвердіть налаштування проксі у кроках збірки (proxy під час збірки != proxy під час виконання).
- Перепровірте в режимі з VPN і без, щоб ізолювати ефекти корпоративної мережі.
- Виправте конфігурацію резолвера перед тим, як міняти налаштування зберігання Docker.
FAQ
1) Чи варто використовувати WSL2 або бекенд Hyper-V для Docker Desktop?
Використовуйте WSL2, якщо немає конкретної суміснісної причини не робити цього. Великі проблеми з продуктивністю зазвичай не «WSL2 vs Hyper-V», а «шляхи файлової системи Windows vs шляхи Linux».
2) Чи безпечно тримати репозиторій у WSL? Я його не загублю?
Це так само безпечно, як будь-яке локальне середовище розробки: робіть бекапи і пуште в віддалений репозиторій. ext4-in-VHDX у WSL стабільна, але не трактуйте «тільки локально» як план довгострокової надійності.
3) Чому /mnt/c набагато повільніший для dev-інструментів?
Тому що ви переходите шар трансляції з відмінною семантикою метаданих. Linux-інструменти виконують багато дрібних системних викликів; межа робить кожен з них дорожчим.
4) Чи потрібно вимикати Windows Defender?
Ні. Можливо, потрібні цілеспрямовані виключення для директорій розробки, якщо ваша політика безпеки це дозволяє. Погоджуйтеся з ІТ/безпекою; випадкове відключення породжує цікаві інциденти.
5) Чи завжди named volumes швидші за bind mounts?
Не завжди. Bind mounts з WSL ext4 можуть бути ок. Bind mounts з Windows-шляхів часто повільні. Named volumes зазвичай стабільно швидші для директорій з інтенсивним churn.
6) Чому передача контексту Docker така велика?
Тому що ви надсилаєте занадто багато: каталоги залежностей, артефакти збірки і іноді навіть весь історія .git. Виправіть .dockerignore і збирайте з WSL-шляхів.
7) Моя база даних повільна в контейнері; чи просто встановити її у Windows?
Іноді це практичний хід, але це збільшує відмінності від продакшену і ускладнює інструменти. Спробуйте спочатку: named volume для даних + адекватна пам’ять + уникати маунтів Windows-шляхів.
8) Чому продуктивність погіршується з часом?
Образи дисків ростуть, кеші накопичуються, томи складаються, вільне місце зменшується. Також тулчейни змінюються і генерують більше файлів. Розглядайте очищення і компактування як планове обслуговування, а не як кнопку паніки.
9) Де запускати IDE: у Windows чи всередині WSL?
Обидва варіанти можуть працювати, але ключове — де лежать файли. Windows IDE з підтримкою WSL remote — поширений «смачний» варіант: UI Windows, файлові системи Linux.
10) Чи є «відключити спільний доступ файлів» виправленням?
Це спосіб перестати випадково використовувати повільні шляхи. Справжнє виправлення — структурувати робочий процес так, щоб вам не потрібен був крос‑ОС доступ до «гарячих» шляхів.
Наступні кроки
Якщо ви хочете, щоб Docker на Windows з WSL2 був адекватним, зробіть це у порядку:
- Запустіть бенчмарки (Завдання 2 і Завдання 6). Не сперечайтесь; виміряйте.
- Перенесіть репозиторій в WSL, якщо
/mnt/cповільний. Це вирішує найбільший клас проблем. - Переключіть гарячі директорії на named volumes (дані БД, каталоги залежностей, кеші).
- Виправте роздутий build context і використайте кешування BuildKit свідомо.
- Перевірте DNS, якщо завантаження/кроки збірки застопорюються; виправте резолвер перед тим, як чіпати налаштування зберігання.
- Правильно налаштуйте ресурси WSL і тримайте вільне місце; продуктивність любить простір для дихання.
Зробіть це, і мем «Docker повільний на Windows» стане тим, чим має бути: іноді скарга, а не визначальна риса вашого дня.