Томи CIFS у Docker повільні: правда та кращі альтернативи

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

Все починається зі скарги, яка звучить розмито й нешкідливо: «Контейнер повільний». Потім придивляєшся й бачиш завжди ту саму картину: Docker-том на базі CIFS/SMB, і додаток, який торкається файлів так, ніби за кожен виклик системи платять окремо.

На папері CIFS — «просто мережева файлова система». У продакшені це підсилювач затримок, загорнутий у метадані, відображення прав, обмеження кешування та оптимістичну віру, що ваша робоче навантаження переважно послідовні читання. Спойлер: ваше навантаження — ні.

Що насправді означає «повільний CIFS-том»

Коли кажуть «CIFS повільний», зазвичай вони стискають кілька різних режимів відмов у одну незадоволену фразу:

  • Висока затримка на операцію: кожен stat(), open, close, chmod, rename стає мережею кругового обходу (або кількох).
  • Навантаження, яке багато працює з метаданими: менеджери пакетів, git-checkout, вебзастосунки з великою кількістю дрібних файлів, Python import-шторм, PHP автозавантажувачі, Node node_modules, сканування Java classpath. Це — природні вороги CIFS.
  • Семантика стійкості записів: SMB прагне зберегти Windows-дружні семантики блокувань і консистентності. Це не безкоштовно.
  • Обмеження клієнтського кешування: Linux CIFS часто мусить жертвувати агресивністю кешування заради коректності, особливо при доступі кількох клієнтів.
  • Відображення прав та трансляція ідентичностей: неефектно, але додає накладні витрати й викликає повторні спроби та сюрпризи.

І ще: Docker не робить CIFS магічно швидшим. Контейнери просто полегшують випадково помістити базу даних на мережевий шар і потім дивуватися, що вона поводиться як база даних на мережевому диску.

Жорстке правило: якщо ваш додаток виконує багато дрібного випадкового вводу/виводу або викликів метаданих файлової системи, CIFS у контейнері — це податок на продуктивність, який ви платите за кожен запит. Ваша «пропускна здатність» може виглядати нормально в тесті копіювання великих файлів і все одно бути непридатною в реальності.

Факти та історія: чому SMB поводиться так

Трохи контексту допомагає, бо SMB/CIFS не повільний через те, що він «поганий». Він повільний, бо виконує роботу, якої ви не просили, для клієнтів, яких ви не кликали, через мережі, яких ви не мали на увазі.

8 корисних фактів (коротко, конкретно, корисно)

  1. «CIFS» фактично — старіший діалект SMB (ера SMB1). Більшість сучасних налаштувань — SMB2/SMB3, але люди досі кажуть «CIFS», наче це бренд серветок.
  2. SMB1 був чатливим за замовчуванням. SMB2 значно зменшив кількість кругових обходів, але навантаження з великою кількістю метаданих все одно болючі, бо клієнту потрібні відповіді.
  3. SMB3 додав шифрування та multichannel. Добре для безпеки і стійкості, інколи не дуже добре для CPU і затримок, якщо неправильно налаштовано.
  4. Opportunistic locks (oplocks) і leases існують для покращення кешування, але вони генерують трафік інвалідації й складні семантики при конкуренції.
  5. Windows-семантика важлива: SMB створено, щоб зберегти Windows-блокування й режими спільного доступу. Linux-додатки, що припускають POSIX-поведінку, можуть викликати додаткові перевірки.
  6. Linux CIFS використовує клієнт у ядрі (cifs.ko) і його поступово покращували, але обмеження коректності все ще стримують агресивне кешування в сценаріях з кількома записувачами.
  7. Вартість метаданих домінує при високих RTT: навіть 2–5 мс RTT можуть зробити «тисячі дрібних операцій» повільними, незалежно від гігабітної пропускної здатності.
  8. Docker-томи — не чудо-абстракція зберігання: ядро все одно виконує ті самі mount/I/O. Docker просто полегшує постійно повторювану помилку.

Справжні корені проблеми (не міфи)

Міф: «Проблема в пропускній здатності. Потрібна швидша мережа.»

Пропускна здатність важлива для великих послідовних операцій читання/запису. Болі CIFS-томів у контейнерах зазвичай — це IOPS і затримка плюс ампліфікація метаданих. У вас може бути 10 GbE лінк і все одно ви постраждаєте від 3 мс RTT і 20k stat() викликів на запит. Ваш лінк нудьгує, поки додаток на колінах.

Міф: «Це накладні витрати Docker.»

Docker додає деякі накладні витрати в окремих випадках (overlay filesystem, user namespaces, особливості propagation mounts). Але для bind mount або іменованого тому на CIFS домінуючий кошт — семантика мережевої файлової системи. Docker здебільшого невинний спостерігач, що тримає мішок.

Реальність #1: Затримка робить навантаження на метадані нелінійним

Якщо запит викликає 500 операцій з метаданими файлової системи, і кожна коштує мережевий круговий обхід, математика жорстока. CIFS може пропускати й пакетувати деякі операції, але не настільки, щоб врятувати чатливе навантаження.

Реальність #2: Кешування обмежене вимогами коректності

Linux CIFS може кешувати атрибути й записи директорій, але якщо кілька клієнтів можуть змінювати одне дерево, кешування стає ризиком. Ви можете налаштовувати кешування (далі про це), але завжди торгуєте коректністю й крос-клієнтською когерентністю за швидкість.

Реальність #3: Контейнери приховують невідповідності ідентичностей і прав

У контейнері UID/GID може не збігатися з тим, чого очікує SMB-сервер. Це призводить до перевірок прав, помилок записів і іноді до поведінки з відкатами. Навіть коли «все працює», ви можете отримати додаткові chown/chmod спроби, що завантажують шляхи з метаданими.

Реальність #4: Підписування/шифрування SMB можуть непомітно з’їдати CPU

SMB-підписування та шифрування — корисні, часто необхідні, але іноді витратні в плані CPU. Якщо на будь-якій стороні немає апаратного прискорення AES або ви жорстко прив’язали CPU контейнера, ваша «проблема зі сховищем» насправді — «крипто з’їдає ресурси».

Реальність #5: Семантика блокувань кусає бази даних і інструменти збірки

SQLite, багато систем збірки та деякі сервери додатків покладаються на файл-блокування та fsync-патерни, які нормальні на локальному ext4/xfs. Через SMB ці семантики можуть бути повільнішими або, гірше, тонко відрізнятися. Ось як з’являються «повільно» й «дивно» в одній скарзі.

Парафраз ідеї від Werner Vogels: «Все ламається, весь час — проектуйте під це». CIFS через мережу винайде креативніші способи відмов, ніж ваш локальний диск коли-небудь знала.

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

Ось порядок, який я використовую, коли хтось пише «CIFS-том повільний», і мені треба відповідь до обіду.

1) Доведіть, що це саме монтування (а не додаток)

  • Порівняйте ту ж операцію на CIFS і на локальному диску: створення файлу, шторм stat, дрібні записи.
  • Перевірте, чи уповільнення корелює з операціями метаданих, а не з пропускною здатністю.

2) Вимірюйте затримку, а не лише пропускну здатність

  • Ping RTT до сервера.
  • Шукайте повторні передачі, проблеми з конгестивністю, дуплексні проблеми, сюрпризи Wi‑Fi «enterprise».

3) Визначте діалект SMB і параметри безпеки

  • Підтвердіть SMB3 vs SMB2 vs випадкове повернення до SMB1.
  • Перевірте статус підписування/шифрування; перевірте насичення CPU на обох сторонах.

4) Перегляньте кешування та опції монтування

  • Кешування атрибутів (actimeo), клієнтське кешування (cache=), розміри читання/запису та чи ігноруються параметри.

5) Визначте, чи є кілька писаків

  • Якщо кілька вузлів/контейнерів пишуть у те саме дерево, у вас вузьке поле для налаштування кешування.
  • Якщо є один записувач, можна бути більш ризиковим у налаштуваннях.

6) Перевірте сервер і шлях

  • SMB-сервер — Windows, Samba чи NAS-апарат?
  • Чи повільне базове сховище (шпінделі, перевантажені RAID, тонкопровізіонований cloud-диск)?

Жарт #1: Мережеві файлові системи як офісні принтери — коли працюють, ніхто не помічає; коли ні, раптом усі стають експертами з інфраструктури.

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

Нижче реальні завдання, які можна виконати на Linux Docker-хості. Кожне включає: команду, приклад виводу, що це означає і яке рішення прийняти.

Завдання 1: Підтвердити тип монтування та опції, які дійсно використовуються

cr0x@server:~$ findmnt -T /var/lib/docker/volumes/appdata/_data -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET                              SOURCE                FSTYPE OPTIONS
/var/lib/docker/volumes/appdata/_data //nas01/share        cifs   rw,relatime,vers=3.1.1,cache=strict,username=svc_app,uid=1000,gid=1000,actimeo=1

Що це означає: Це CIFS-монтування (SMB) з SMB 3.1.1 і строгим кешуванням. Таймаут кешу атрибутів — 1 секунда.

Рішення: Якщо навантаження тяжіє до метаданих і є один записувач, розгляньте збільшення actimeo або зміну cache= для пришвидшення. Якщо кілька записувачів — будьте обережні.

Завдання 2: Перевірити діалект SMB і можливості через логи ядра

cr0x@server:~$ dmesg | grep -i cifs | tail -n 5
[ 9342.112233] CIFS: VFS: \\nas01 negotiated SMB3.1.1 dialect
[ 9342.112240] CIFS: VFS: cifs_mount failed w/return code = -13
[ 9410.445566] CIFS: VFS: \\nas01 Server supports multichannel
[ 9410.445577] CIFS: VFS: \\nas01 requires packet signing

Що це означає: Погоджено SMB3.1.1, підтримується multichannel, і потрібне підписування. Раніше була помилка дозволу (-13).

Рішення: Переконайтеся, що ви не падаєте непомітно до SMB1. Також дослідіть накладні витрати підписування і ту ранню помилку автентифікації (може означати повторні спроби/бекаф).

Завдання 3: Виміряти базовий RTT мережі та джиттер

cr0x@server:~$ ping -c 20 nas01
PING nas01 (10.20.1.50) 56(84) bytes of data.
64 bytes from 10.20.1.50: icmp_seq=1 ttl=63 time=1.92 ms
64 bytes from 10.20.1.50: icmp_seq=2 ttl=63 time=2.08 ms
...
--- nas01 ping statistics ---
20 packets transmitted, 20 received, 0% packet loss, time 19024ms
rtt min/avg/max/mdev = 1.71/2.05/2.61/0.21 ms

Що це означає: RTT ~2 мс. Це «добре» для людей і «дорого» для тисячі викликів метаданих.

Рішення: Якщо навантаження метадані́чне, потрібні кешування, зміни в навантаженні або інший бекенд зберігання — не лише більше пропускної здатності.

Завдання 4: Перевірити повторні передачі та TCP-проблеми

cr0x@server:~$ ss -ti dst 10.20.1.50:445 | head -n 20
ESTAB 0 0 10.20.1.10:52144 10.20.1.50:445
	 cubic wscale:7,7 rto:204 rtt:2.3/0.4 ato:40 mss:1448 pmtu:1500 rcvmss:1448 advmss:1448 cwnd:10 bytes_acked:1289341 segs_out:10873 segs_in:10122 send 50.3Mbps lastsnd:12 lastrcv:12 lastack:12 pacing_rate 100.6Mbps retrans:0/12

Що це означає: Низький RTT, історія деяких повторів, але зараз активних повторів немає. Якщо бачите зростаючі retransmits — мережа саботує ваше сховище.

Рішення: Якщо retransmits значущі, виправте мережу перш ніж налаштовувати SMB. Жодна опція монтування не виправить втрачені пакети.

Завдання 5: Швидкий мікро-бенчмарк метаданих (шторм створення файлів)

cr0x@server:~$ time bash -c 'd=/var/lib/docker/volumes/appdata/_data/testmeta; rm -rf "$d"; mkdir -p "$d"; for i in $(seq 1 5000); do : > "$d/f_$i"; done'
real	0m38.412s
user	0m0.311s
sys	0m5.992s

Що це означає: 5 000 дрібних створень зайняли 38 секунд. Це не «трохи повільно», це «ваш додаток почне таймаути».

Рішення: Це проблема затримки метаданих. Розгляньте переміщення «гарячих» шляхів на локальний диск, використання NFS з налаштуваннями або локальний шар кешу.

Завдання 6: Порівняти той самий тест на локальному диску

cr0x@server:~$ time bash -c 'd=/tmp/testmeta; rm -rf "$d"; mkdir -p "$d"; for i in $(seq 1 5000); do : > "$d/f_$i"; done'
real	0m0.486s
user	0m0.169s
sys	0m0.301s

Що це означає: Те ж навантаження приблизно у 80× швидше локально. Саме тому люди думають «Docker повільний», коли насправді «віддалені метадані повільні».

Рішення: Не ставте навантаження, що тяжіє до метаданих, на CIFS, якщо ви не готові платити за це або переробити навантаження.

Завдання 7: Переглянути статистику CIFS (підказки на стороні клієнта)

cr0x@server:~$ cat /proc/fs/cifs/Stats
Resources in use
CIFS Session: 2
Share (unique mount targets): 1
SMB Request/Response Buffer: 1 Pool size: 5
SMB Small Req/Resp Buffer: 1 Pool size: 30
Operations (MIDs): 0
Total vfs operations: 482109
Total ops: 612990
Total reconnects: 3

Що це означає: Є перез’єднання. Навіть кілька перез’єднань можуть створювати «випадкові паузи», що виглядають як збої додатку.

Рішення: Якщо перез’єднання ростуть, перевірте стабільність SMB-сервера, тайм-аути простору імен, стан firewall, перебої в мережі.

Завдання 8: Підтвердити, чи ввімкнено шифрування SMB (і чи коштує це CPU)

cr0x@server:~$ grep -iE 'Encryption|Signing|Dialect' /proc/fs/cifs/DebugData | head -n 20
Dialect: 3.1.1
Security: NTLMSSP
Signing: Enabled
SMB3 encryption: Enabled

Що це означає: Шифрування ввімкнене. Добре для безпеки; можливо дороговартісно для CPU.

Рішення: Якщо CPU насичується під час I/O, розгляньте, чи потрібне шифрування наскрізь. Якщо воно потрібно — забезпечте сучасні CPU і уникайте дрібних I/O.

Завдання 9: Дивитися steal/насичення CPU під час I/O

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.5.0 (server) 	01/03/2026 	_x86_64_	(16 CPU)

12:03:10 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:03:11 AM  all   22.10  0.00  18.44   9.33 0.00  0.88   0.00 49.25
12:03:12 AM  all   25.20  0.00  21.77  10.12 0.00  1.01   0.00 41.90

Що це означає: Високий %sys і %iowait підказує, що ядро виконує багато роботи по файловій системі/мережі. Якщо %usr не високий, але %sys високий — накладні витрати SMB ймовірні.

Рішення: Якщо CPU — вузьке місце, налаштування mount опцій не врятують. Потрібні зміни навантаження, менше дрібних операцій або інше сховище.

Завдання 10: Визначити найгірших порушників (метадані чи дані)

cr0x@server:~$ strace -f -tt -T -o /tmp/trace.txt docker exec app sh -c 'php -r "for($i=0;$i<2000;$i++){stat(\"/data/smallfile_$i\");}"'
cr0x@server:~$ tail -n 5 /tmp/trace.txt
12:04:21.553221 stat("/data/smallfile_1998", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 <0.006842>
12:04:21.560219 stat("/data/smallfile_1999", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 <0.006991>

Що це означає: Кожен stat() займає ~7 мс. Помножте це на тисячі — отримаєте квиток у службу підтримки.

Рішення: Або збільшіть кешування (якщо безпечно), або зменшіть кількість stat у конфігурації додатку, або перемістіть ці файли локально.

Завдання 11: Бенчмарк пропускної здатності SMB великим послідовним записом (щоб не вводити себе в оману)

cr0x@server:~$ dd if=/dev/zero of=/var/lib/docker/volumes/appdata/_data/dd.test bs=8M count=256 oflag=direct status=progress
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 13 s, 165 MB/s
256+0 records in
256+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 13.0256 s, 165 MB/s

Що це означає: Пропускна здатність у нормі. Класична пастка: «165 MB/s, отже сховище швидке». Тим часом ваш додаток вмирає на метаданих.

Рішення: Перестаньте тестувати лише пропускну здатність. Завжди робіть тест на метадані також.

Завдання 12: Перевірити propagation монтувань Docker і чи ви стекуєте файлові системи

cr0x@server:~$ docker inspect app | grep -n '"Type"\|"Source"\|"Destination"' | head -n 20
145:            "Type": "volume",
146:            "Source": "/var/lib/docker/volumes/appdata/_data",
147:            "Destination": "/data",

Що це означає: Контейнер бачить том, змонтований в /data. Якщо контейнер також використовує overlayfs для інших шляхів, це нормально. Головне, що ваш гарячий шлях напряму потрапляє на CIFS.

Рішення: Якщо додаток записує тимчасові файли, перенаправте тимчасові директорії на локальне сховище (tmpfs або локальний том) і залишайте CIFS лише для холодних/спільних даних.

Завдання 13: Підтвердити версію модуля CIFS ядра і завантажені параметри

cr0x@server:~$ modinfo cifs | egrep 'version:|parm:|filename:' | head -n 15
filename:       /lib/modules/6.5.0/kernel/fs/smb/client/cifs.ko
version:        2.45
parm:           CIFSMaxBufSize:Network buffer size (int)
parm:           enable_oplocks:Enable or disable oplocks (int)
parm:           linux_ext:Enable Linux CIFS Extensions (int)

Що це означає: Ви використовуєте клієнт SMB у ядрі. Версія і можливості змінюються між релізами ядра; апгрейди можуть змінити поведінку.

Рішення: Якщо у вас дуже старе ядро, подумайте про оновлення. Продуктивність і коректність SMB-клієнта суттєво покращувалися з часом.

Завдання 14: Перевірити, що DNS та резолюція імен не додають затримок

cr0x@server:~$ time getent hosts nas01
10.20.1.50      nas01
real	0m0.006s
user	0m0.002s
sys	0m0.003s

Що це означає: Резолюція імен швидка. Якщо це займає сотні мс через проблемний DNS, SMB-перез’єднання та спроби монтування стануть повільними і ненадійними.

Рішення: Виправте DNS або використайте стабільні IP у визначеннях монтування (з урахуванням відмовостійкості).

Параметри монтування, які допомагають (і які вводять в оману)

Параметри монтування — не магічні заклинання. Це компроміси, зазвичай між продуктивністю та когерентністю. Якщо у вас кілька клієнтів, що пишуть у одну теку, поле для налаштувань невелике.

Почніть із розумних дефолтів

Для сучасних SMB-серверів явно вказуйте SMB3. Не дозволяйте переговорам «розібратися самі». Коли вони «розбираються» неправильно, ви проведете тиждень на монстрах-бенчмарках.

cr0x@server:~$ sudo mount -t cifs //nas01/share /mnt/share \
  -o vers=3.1.1,username=svc_app,uid=1000,gid=1000,serverino,rw

Опції, що часто мають значення

  • vers=3.1.1: зафіксуйте сучасний діалект. Якщо сервер не підтримує — краще знати це відразу.
  • actimeo=: таймаут кешу атрибутів. Більші значення можуть суттєво пришвидшити читання, багате на метадані; можуть також приховати зміни від інших клієнтів.
  • cache= (strict, loose, none): контролює політику кешування клієнта. «Loose» може бути швидшим, але менш коректним.
  • rsize=, wsize=: налаштуйте розмір блоків читання/запису. Допомагає пропускній здатності, менш корисно для штормів метаданих.
  • noserverino vs serverino: поведінка inode. Невідповідності можуть ламати додатки, що покладаються на стабільні номера inode; вплив на продуктивність варіюється, але коректність важлива.

Опції, які часто неправильно розуміють

  • nounix/unix: впливає на Unix-розширення, права та поведінку. Використовуйте свідомо, не копіюйте фрагмент з блогу 2014 року.
  • soft/hard: це концепт NFS; семантика відмов SMB інша. Вам все одно потрібно думати про таймаути й повторні спроби, але цей перемикач не те саме.
  • «Просто збільшити wsize і все виправиться»: якщо ваша проблема — метадані, більші буфери I/O не допоможуть.

Реалістичний приклад налаштування для «один записувач, переважно читання»

Якщо у вас один контейнер пише, а решта читають (або один вузол), можна ризикнути більше:

cr0x@server:~$ sudo mount -t cifs //nas01/share /mnt/share \
  -o vers=3.1.1,username=svc_app,uid=1000,gid=1000,rw,cache=loose,actimeo=30

Що отримуєте: Менше кругових обходів для stat і обходів директорій.

Що платите: Інший клієнт може не побачити оновлень одразу. Якщо кілька записувачів — ви щойно створили лотерею когерентності.

Особливості Docker із CIFS-тoмами

Іменовані томи з локальним драйвером та CIFS

Поширений патерн — використання локального volume driver Docker з CIFS-опціями. Зручно, відтворювано, але легко помилитися в конфігурації.

cr0x@server:~$ docker volume create \
  --driver local \
  --opt type=cifs \
  --opt device=//nas01/share \
  --opt o=vers=3.1.1,username=svc_app,password=REDACTED,uid=1000,gid=1000,rw \
  appdata

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

Overlayfs не є лиходієм, але може бути колатеральною шкодою

Коли файлову систему контейнера побудовано на overlayfs, а додаток змішує шляхи overlay з CIFS-монтуваннями, можна отримати дивні патерни: швидкі читання з image layer, повільні читання з mounted data, і заплутані strace, які змушують ганятися за неправильними причинами.

Простори імен користувачів та невідповідності UID/GID

Якщо ви запускаєте Docker з userns-remap або в rootless-режимі, відображення UID/GID для CIFS може швидко ускладнитися. «uid 1000» у контейнері може відображатися інакше на хості. Потім — permission-denied спроби, fallback-поведінка і круті падіння продуктивності, що зникають, коли запускаєш як root (що само по собі теж ризик).

Healthchecks можуть перетворитися на випадкове навантаження

Healthchecks, що торкаються файлів на CIFS щосекунди у багатьох контейнерах, створюють постійний дрібний потік операцій з метаданими. Помножте на десятки контейнерів — і ви створили акуратний DDoS проти власного NAS, повільно, ввічливо й постійно.

Жарт #2: Ставити базу даних на CIFS — як тягнути гоночний автомобіль на візку для покупок: технічно рухається, але всім незручно.

Три корпоративні міні-історії з практики

Міні-історія 1: Інцидент через неправильне припущення

У середньому за розміром компанії команда перенесла легасі-додаток у контейнери. Потрібен був «спільний диск» між двома Docker-хостами для аплоудів і згенерованих мініатюр. Швидкий шлях до демо — CIFS-шар з існуючого Windows файлохосту. Воно працювало. Усі аплодували. Тикет закрили з усмішкою.

Через два тижні маркетингова кампанія вдарила. Додаток не впав одразу. Він просто почав таймаутити хвилями. CPU нормальний. Пам’ять нормальна. Графіки мережі спокійні. Логи додатку повні попереджень про повільні запити і випадкових помилок file-not-found, які не мали сенсу.

Неправильне припущення було тонким: «Якщо файл існує на шарі, кожен контейнер побачить його одразу». Насправді кешування атрибутів, перелік директорій та конкурентні записи дали застарілі уявлення. Один контейнер створив мініатюру, інший ще не побачив її і створив заново. Іноді вони змагалися, іноді перезаписували. Іноді читач отримував частково записаний файл, бо додаток припускав локальну атомарність, якої тут не було.

Виправлення не було героїчним. Вони перестали ділити гарячий шлях. Завантаження зберігалося локально спочатку, потім асинхронна задача штовхала фіналізовані артефакти у спільне сховище. Спільне сховище стало точкою розповсюдження, а не робочою областю. Помилки зникли. Продуктивність стабілізувалася. Постмортемний висновок був прямим: «Ми ставились до мережевої файлової системи як до локальної під конкуренцією».

Міні-історія 2: Оптимізація, що обернулась проти них

В іншому місці була «винахідлива правка». Команда мала Node.js сервіс з величезними деревами залежностей. Старт займав багато часу, бо читав тисячі дрібних файлів з CIFS-тома. Хтось знайшов пораду: агресивно збільшити кешування. Вони поставили cache=loose і підняли actimeo високо. Початковий запуск покращився кардинально. Знову аплодисменти.

Потім з’явився відкат: деплоя почали падати новим способом. Сервіс стартував з старою версією конфігурації, хоч файл оновлювався на шарі деплой-джобом. Роллаути стали недетермінованими. Половина флоту поводилася як нова версія, інша половина — як стара. Дебаг перетворився на цирк, бо «просто перезапустити» іноді допомагало, іноді ні.

Причина була передбачувана: вони поміняли когерентність на швидкість, не змінивши робочий процес. Мережевий шар використовували для спільної конфігурації й живого коду, які потребують гарантій свіжості. Кеш, що «loose», — це угода з нестабільністю.

Виправили це, переставши використовувати CIFS для цієї задачі. Побудували іміджі з упакованими залежностями й налаштуваннями за замовчуванням, і використали сервіс конфігурації для runtime-параметрів. CIFS залишився, але лише для великих артефактів, де застарілість кешу не створювала багів коректності.

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

У великій організації були CIFS-томи в продакшені через корпоративні обмеження: команда зберігання на Windows, існуючі права доступу й аудитори, яким подобалося слово «SMB», бо звучало знайомо. SRE-команда не була в захваті, але була реалістична: деякі бої — це бої за бюджет, а не технічні.

Тому вони зробили нудну річ. Створили стандарт: CIFS-монти дозволялися тільки для холодних даних і даних, що в основному дописуються (append-mostly), ніколи для баз даних, ніколи для дерев залежностей, ніколи для scratch-збірки, ніколи для тимчасових директорій з високою змінністю. Кожне навантаження на CIFS мало мікро-бенчмарк у CI з тестом пропускної здатності й тестом метаданих з порогами.

Також додали рутинні перевірки: алерти на перез’єднання CIFS, відстеження CPU SMB-сервера та вимірювання p99 затримки файлових операцій зсередини репрезентативних контейнерів. Ніхто не святкував ці дашборди. Вони не були модними. Вони були правильними.

Через шість місяців команда зберігання змінила налаштування підписування SMB. Продуктивність трохи впала. Дашборди вчасно сповістили, і відкат зробили до впливу на клієнтів. «Нудний стандарт» запобіг інциденту, бо звузив, де CIFS могло зашкодити, і моніторинг зробив режим відмови очевидним.

Кращі альтернативи (і коли що вибирати)

Якщо ви нічого іншого не прочитаєте: перестаньте використовувати CIFS як дефолт для продуктивних робочих навантажень у контейнерах. Використовуйте його, коли потрібна Windows-нативна семантика шарингу або коли вас до цього змушують. Інакше обирайте інструмент під завдання.

1) Локальний диск (ext4/xfs) + реплікація (переважно для БД)

Якщо дані належать сервісу (Postgres, MySQL, Elasticsearch, Redis persistence), використовуйте локальне сховище і робіть реплікацію на рівні додатку. Отримаєте передбачувану затримку, коректну поведінку fsync і менше дивних блокувань.

2) NFSv4.1+ для спільного POSIX-подібного сховища

NFS не обов’язково «швидше», але зазвичай краще узгоджується з Linux-семантикою і інструментами. Може показувати кращу продуктивність для метаданих, залежно від налаштувань сервера/клієнта. Також відмовляє по‑іншому. Іноді це саме те, що треба.

3) Об’єктне сховище для артефактів (S3-совісний API)

Якщо контейнери читають/пишуть блоби, не прикидайте, що це файлова система. Використайте об’єктне сховище. Ви жертвуєте POSIX-семантикою за масштабованість і набагато кращу поведінку під конкуренцією. Додаток може потребувати змін, але ваша майбутня команда подякує.

4) Блокове сховище (iSCSI, Fibre Channel, cloud volumes) + файлова система

Якщо вам потрібні семантика файлової системи й продуктивність, створіть файлову систему на блоковому сховищі і монтуйте локально. Потім шарте через механізми рівня додатку, а не дозволяйте кільком клієнтам «бити» одну файлову систему, хіба ви навмисно не використовуєте кластерну FS.

5) Локальний шар кешу перед CIFS

Якщо корпоративна реальність каже, що CIFS лишиться, поставте кеш перед ним. Варіанти:

  • Синхронізація на локальне при старті (щось на зразок rsync) і періодичне оновлення.
  • Патерни write-back для логів/артефактів (локальний спул, асинхронне завантаження).
  • Явне кешування в додатку (in-memory, Redis, CDN для статичного контенту).

Це працює, бо змінює форму I/O: менше мережевих кругових обходів, менше операцій з метаданими, менше синхронних записів.

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

Цей розділ запобігає повторним інцидентам.

1) Симптом: «Пропускна здатність добра, але додаток все одно таймаутить»

Корінь: Затримка метаданих домінує (stat/open/close-шторми). Тести на великі файли брешуть.

Виправлення: Проведіть мікро-бенчмарки метаданих; збільшіть actimeo тільки якщо безпечно; перемістіть дерева залежностей і тимчасові файли локально; переробіть навантаження, щоб робити менше викликів файлової системи.

2) Симптом: «Випадкові зависання 5–30 секунд»

Корінь: SMB-перез’єднання, проблеми на сервері, таймаути firewall/NAT, або затримки DNS під час перепідключення.

Виправлення: Перевірте /proc/fs/cifs/Stats на перез’єднання, TCP retransmits, dmesg; стабілізуйте мережевий шлях; забезпечте keepalives; зменшіть idle timeouts; виправте DNS.

3) Симптом: «Працює на одному хості, повільно на іншому»

Корінь: Різні опції монтування, різний діалект SMB, різні версії ядра або відмінності CPU щодо crypto-акселерації.

Виправлення: Порівняйте вивід findmnt; зафіксуйте vers=; уніфікуйте версії ядра і налаштування CIFS; перевірте шифрування/підписування і використання CPU.

4) Симптом: «Access denied» у міксі зі сповільненням

Корінь: Невідповідність UID/GID, простори імен користувачів або серверні ACL, що викликають повторні відмови.

Виправлення: Вирівняйте ідентичності; використовуйте консистентні uid=/gid=; розгляньте Samba з Unix-розширеннями для POSIX-прав, якщо потрібно; уникайте chown-штормів під час старту контейнера.

5) Симптом: «Файл існує, але додаток його ще не бачить»

Корінь: Кешування клієнта або кеш директорії; затримки когерентності між клієнтами.

Виправлення: Зменшіть агресивність кешування для спільної конфігурації/коду; не використовуйте CIFS для координації; застосуйте сервіс реєстрації/конфігурації або явну синхронізацію.

6) Симптом: «Пошкодження БД / помилки блокувань / дивна поведінка fsync»

Корінь: Семантика мережевої файлової системи не відповідає очікуванням БД щодо відмов, блокувань або стійкості.

Виправлення: Не запускайте бази даних на CIFS. Використовуйте локальний диск або спеціалізоване кластерне сховище з відомими семантиками для цієї БД.

7) Симптом: «Стрибки CPU під час копіювання файлів»

Корінь: Накладні витрати шифрування/підписування SMB, можливо разом з дрібним I/O.

Виправлення: Підтвердіть шифрування/підписування; забезпечте AES‑NI/прискорення CPU; збільшіть розміри I/O де можливо; розгляньте альтернативний шлях зберігання, якщо CPU — вузьке місце.

Контрольні списки / покроковий план

Покроково: стабілізувати повільне CIFS-backed Docker-навантaження (практичний план)

  1. Визначте гарячі шляхи: які директорії на CIFS і які чутливі до затримок (конфіг, deps, temp, кеш, файли БД).
  2. Запустіть два бенчмарки: один на пропускну здатність (dd) і один на метадані (шторм створення/stat). Зафіксуйте часи.
  3. Виміряйте RTT і retransmits: якщо мережа «брудна», спочатку виправляйте її.
  4. Підтвердіть діалект/безпеку: SMB3.1.1 і чи ввімкнене підписування/шифрування.
  5. Перевірте перез’єднання: якщо вони є — трактуйте це як надійність, а не лише як продуктивність.
  6. Перемістіть тимчасові директорії локально: встановіть TMPDIR, кеші додатку, виводи збірки на локальний диск або tmpfs.
  7. Видаліть БД з CIFS: мігруйте на локальні томи з реплікацією/бекапом.
  8. Зменшіть тиск на метадані: вбудовуйте залежності в образи; уникайте runtime-установок з CIFS; мінімізуйте сканування директорій.
  9. Налаштовуйте кешування лише якщо безпечно: якщо один записувач — збільшуйте actimeo і розглядайте cache=loose для read-heavy дерев.
  10. Уніфікуйте визначення монтувань: однакові опції всюди, зафіксуйте vers=, обробляйте облікові дані безпечно.
  11. Інструментуйте p95/p99 затримки файлової системи: вибірковий strace, таймінги додатків і CIFS-статистика.
  12. План виходу: оберіть альтернативний дизайн зберігання на наступний квартал; не дозволяйте «тимчасовому CIFS» стати вічним.

Операційний чекліст: коли CIFS неминучий

  • Зафіксуйте діалект SMB (vers=3.1.1 або відомо‑добрий).
  • Документуйте, чи шар є single-writer чи multi-writer.
  • Тримайте CIFS для великих файлів і холодних даних, не для метаданих-штурмів.
  • Алертуйте на перез’єднання та підвищені retransmits.
  • Уніфікуйте опції монтування між хостами.
  • Тестуйте після оновлень ядра; поведінка клієнта SMB змінюється з часом.

Часті питання

1) Чи «CIFS» те саме, що SMB?

У повсякденних операціях — так. Технічно CIFS зазвичай відноситься до старіших діалектів SMB1. На Linux часто монтують з -t cifs, навіть коли погоджується SMB3.

2) Чому CIFS особливо болісний у контейнерах?

Контейнери сприяють практикам «примонтувати шар і запускати все на ньому», включно з деревами залежностей, кешами і іноді базами даних. Контейнери також множать кількість одночасних клієнтів і операцій з метаданими.

3) Який найбільший предиктор «CIFS повільний»?

Навантаження, що тяжіє до метаданих, плюс нетривіальний RTT. Якщо додаток робить багато дрібних викликів файлової системи — ви відчуєте кожен мілісекунд.

4) Чи можуть опції монтування справді виправити це?

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

5) Чи варто використовувати cache=loose?

Тільки коли ви розумієте компроміс когерентності і шар не використовується для живої координації між записувачами. Це важіль для продуктивності з цінником за коректність.

6) Чи завжди NFS швидше за SMB?

Ні. Але NFS частіше краще узгоджується з Linux-навантаженнями і може показувати кращу продуктивність для метаданих, залежно від конфігурації сервера/клієнта і того, що за сховище під ним.

7) Чи прискорить SMB multichannel?

Воно може допомогти пропускній здатності та стійкості при правильній конфігурації на обох сторонах з кількома NIC. Воно не виправить затримку метаданих і додає складність.

8) Чому великі копії файлів виглядають нормально, поки додаток повільний?

Послідовний I/O добре стримується через SMB. Додатки зазвичай виконують змішане I/O з багатьма open/stat/дрібними записами. Це інша всесвіт продуктивності.

9) Чи підтримується запуск Postgres/MySQL на CIFS?

Навіть якщо «працює», це азартна гра щодо надійності й продуктивності. Використовуйте локальний диск або сховище, призначене для БД. CIFS — протокол шарингу файлів, не платформа для БД.

10) Який безпечний патерн, якщо треба ділитися даними між контейнерами?

Діліться фіналізованими артефактами, а не робочими наборами. Пишіть локально, потім публікуйте. Трактуйте CIFS як шар розповсюдження, а не як транзакційну робочу область.

Наступні кроки, які можна зробити цього тижня

Якщо у вас вже є CIFS-томи в Docker і це повільно, не починайте з переписування світу. Зробіть це:

  1. Запустіть мікро-бенчмарк метаданих на CIFS-монті та локально. Якщо різниця велика — діагноз готовий.
  2. Перенесіть гарячий churn (тимчасові файли, кеші, інсталяції залежностей, виводи збірки) на локальне сховище. Це часто зменшує біль в рази.
  3. Підтвердіть діалект SMB і налаштування безпеки, щоб ви не погоджували щось древнє або дороге випадково.
  4. Уніфікуйте опції монтування і виміряйте до/після. Не налаштовуйте в сліпу.
  5. Виберіть альтернативу на наступну ітерацію: локальний диск + реплікація для БД, NFS для POSIX-спільних дерев, об’єктне сховище для блобів або кеш перед CIFS, якщо CIFS обов’язковий.

Потім запишіть це як правило: CIFS прийнятний для холодних спільних даних. Це не типовий домашній каталог для продукційних контейнерів. Ваша майбутня відповідальна команда невимушено оцінить вашу відсутність «креативності».

← Попередня
Таблиці, дружні до коду: фіксована vs авто-верстка, обгортання й вирівнювання чисел
Наступна →
PCIe і GPU: коли x8 підходить, а коли шкодить

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