Ви відправляєте контейнер, який «точно працює на моєму ноутбуці», розгортаєте його на хості з GPU, і ваша модель падає назад на CPU, ніби сповідує покаяння.
У логах написано «no CUDA-capable device detected», nvidia-smi відсутній, або PyTorch чемно повідомляє, що CUDA недоступна.
Тим часом GPU сидить, нудьгує, дорогий і рахунок за нього йде погодинно.
Відмови GPU в контейнері рідко містять містику. Майже завжди це невеликий набір невідповідностей: драйвер vs. toolkit, рантайм vs. конфіг Docker,
вузли пристроїв vs. дозволи, або планувальник і рівень безпеки роблять саме те, що ви попросили — просто не те, що ви мали на увазі.
Практична модель: що насправді означає «GPU у контейнері»
Контейнери не віртуалізують апаратне забезпечення. Вони ізолюють процеси через неймспейси і контролюють доступ до ресурсів через cgroups.
Ваш GPU усе ще належить хосту. Драйвер ядра все ще розташований на хості. Вузли пристроїв у /dev все ще походять із хоста.
Контейнер — по суті, процес із іншим оглядом файлової системи, мережі та PID-простору.
Для NVIDIA GPU критичний ланцюг виглядає так:
- Драйвер на хості: модулі ядра та бібліотеки користувацького простору на хості. Якщо це зламано — жодна контейнерна магія не врятує.
- Вузли пристроїв:
/dev/nvidia0,/dev/nvidiactl,/dev/nvidia-uvmтощо. Без них користувацький простір не може говорити з драйвером. - Інтеграція рантайму з контейнером: щось має змонтувати потрібні пристрої та вставити потрібні бібліотеки в контейнер. Саме це робить NVIDIA Container Toolkit.
- Користувацький стек CUDA: у вашому образі контейнера можуть бути бібліотеки CUDA, cuDNN і фреймворки (PyTorch, TensorFlow). Вони мають бути сумісні з драйвером хоста.
- Права та політика: rootless Docker, SELinux/AppArmor, політика пристроїв cgroup і контексти безпеки Kubernetes можуть блокувати доступ, навіть якщо все інше правильне.
Якщо хоча б одне посилання відсутнє, ви отримаєте звичні симптоми: CUDA недоступна, libcuda.so не знайдено, nvidia-smi не виконується,
або застосунки мовчки переходять на CPU.
Одна операційна істина: шлях до GPU — не «поставив і забув». Це «налаштував, зафіксував версії й моніторив відхилення».
Оновлення драйверів, оновлення ядра, апгрейди Docker і зміни режиму cgroup — це чотири вершники.
Цікаві факти та коротка історія, які пояснюють сьогоднішній безлад
- Факт 1: Ранні підходи до «GPU у контейнерах» використовували крихкі прапорці
--deviceі ручне монтування бібліотек, бо рантайм не мав стандартизованого GPU-хука. - Факт 2: Історія контейнерів NVIDIA зміцнилася, коли екосистема перейшла до рантайм-хуків (OCI), які можуть інжектувати маунти/пристрої під час старту контейнера.
- Факт 3: Бібліотеки користувацького простору CUDA можуть жити всередині контейнера, але драйвер ядра — ні; драйвер має відповідати ядру хоста і керується на хості.
- Факт 4: «Версія CUDA», яку ви бачите в
nvidia-smi, — не те саме, що CUDA toolkit у вашому контейнері; вона відображає можливості драйвера, а не вміст образу. - Факт 5: Перехід від cgroup v1 до cgroup v2 змінив поведінку доступу та делегування пристроїв і тонко зламав робочі налаштування GPU під час оновлень.
- Факт 6: Планування GPU у Kubernetes стало масовим лише після стандартизації device plugin; до цього це був дикунський захід привілейованих подів.
- Факт 7: Multi-instance GPU (MIG) ввів нову одиницю виділення — «шматки» GPU — що зробило питання «який GPU отримав мій контейнер?» нетривіальним.
- Факт 8: «Rootless контейнеризація» чудова для безпеки, але GPU не дуже дружні до rootless, бо права на вузли пристроїв — це жорстка перешкода, а не рекомендація.
Швидкий план діагностики (перевірте це насамперед)
Коли продакшен горить, вам не потрібен PhD з упаковки NVIDIA. Потрібна коротка послідовність, яка швидко звузить домен відмови.
Почніть із хоста, далі рантайм, потім образ.
Перше: доведіть, що GPU на хості працює (ще без контейнерів)
- Запустіть
nvidia-smiна хості. Якщо воно не працює — зупиніться. Спочатку виправте драйвер/ядро хоста. - Підтвердіть наявність вузлів пристроїв:
ls -l /dev/nvidia*. Якщо їх немає, драйвер не завантажився або udev не створив вузли. - Перевірте стан модулів ядра:
lsmod | grep nvidiaіdmesgна наявність помилок GPU/драйвера.
Друге: доведіть, що Docker може підключити GPU до контейнера
- Запустіть мінімальний образ CUDA з
--gpus allі виконайтеnvidia-smiвсередині. Якщо це не працює, проблема в рантаймі/toolkit, а не в додатку. - Перегляньте конфіг Docker щодо рантаймів: переконайтеся, що NVIDIA runtime hook встановлений і вибирається за потреби.
Третє: доведіть, що ваш образ застосунку сумісний
- Всередині контейнера вашого застосунку перевірте видимість
libcuda.soта збірку фреймворку з підтримкою CUDA. - Валідуйте сумісність драйвера/тулкіта: драйвер занадто старий для CUDA у контейнері, або контейнер очікує бібліотеки, яких немає.
- Перевірте дозволи/політику: rootless режим, SELinux/AppArmor і обмеження cgroup щодо пристроїв.
Вузьке місце зазвичай виявляється на кроці 4 або 5. Якщо ви все ще гадали після цього, ви, ймовірно, дебажите одразу три проблеми.
Не робіть так. Зведіть обсяг, поки не впаде одне й те саме.
Практичні завдання: команди, очікуваний вивід і рішення
Ось завдання, які я реально виконую, коли GPU-контейнер поводиться некоректно. Кожне включає: команду, що означає вивід, і яке рішення прийняти.
Виконуйте їх у порядку, якщо хочете чисто провести бінарний пошук по стеку.
Завдання 1: Перевірка здоровʼя хоста за допомогою nvidia-smi
cr0x@server:~$ nvidia-smi
Thu Jan 3 10:14:22 2026
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14 Driver Version: 550.54.14 CUDA Version: 12.4 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA A10 On | 00000000:17:00.0 Off | Off |
| 0% 39C P0 62W / 150W | 512MiB / 23028MiB | 3% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
Що це означає: Драйвер завантажено і він спілкується з GPU. «CUDA Version» — це те, що підтримує драйвер, а не те, що постачається у вашому контейнері.
Рішення: Якщо це не вдається, виправляйте хост: встановлення драйвера, заголовки ядра, Secure Boot, перебудова DKMS або апаратні проблеми.
Завдання 2: Підтвердіть наявність вузлів пристроїв
cr0x@server:~$ ls -l /dev/nvidia*
crw-rw-rw- 1 root root 195, 0 Jan 3 10:10 /dev/nvidia0
crw-rw-rw- 1 root root 195, 255 Jan 3 10:10 /dev/nvidiactl
crw-rw-rw- 1 root root 235, 0 Jan 3 10:10 /dev/nvidia-uvm
crw-rw-rw- 1 root root 235, 1 Jan 3 10:10 /dev/nvidia-uvm-tools
Що це означає: Символьні пристрої існують; користувацький простір може говорити з драйвером ядра.
Рішення: Якщо відсутні, завантажте модулі (modprobe nvidia) і зʼясуйте, чому udev не створив вузли (або чому драйвер не завантажився).
Завдання 3: Перевірте, чи модулі ядра завантажені
cr0x@server:~$ lsmod | grep -E 'nvidia|nouveau'
nvidia_uvm 1556480 0
nvidia_drm 94208 2
nvidia_modeset 1564672 1 nvidia_drm
nvidia 62480384 80 nvidia_uvm,nvidia_modeset
Що це означає: Пропрієтарні модулі NVIDIA завантажені; nouveau не показано, що зазвичай добре для обчислювальних вузлів.
Рішення: Якщо nouveau завантажено на compute-хості — чекайте проблем. Додайте його до чорного списку і перебудуйте initramfs згідно з політикою ОС.
Завдання 4: Шукайте помилки драйвера в dmesg
cr0x@server:~$ dmesg -T | tail -n 12
[Thu Jan 3 10:10:05 2026] nvidia: loading out-of-tree module taints kernel.
[Thu Jan 3 10:10:05 2026] nvidia: module license 'NVIDIA' taints kernel.
[Thu Jan 3 10:10:06 2026] nvidia-nvlink: Nvlink Core is being initialized, major device number 510
[Thu Jan 3 10:10:06 2026] nvidia 0000:17:00.0: enabling device (0000 -> 0003)
[Thu Jan 3 10:10:07 2026] nvidia_uvm: Loaded the UVM driver, major device number 235
Що це означає: Нормальні повідомлення про завантаження модулів. Потрібно шукати «failed»; «tainted» не обовʼязково проблема; «RmInitAdapter failed» — проблема.
Рішення: Якщо бачите помилки ініціалізації — перестаньте ганятися за Docker. Виправляйте драйвер/ядро/апаратне забезпечення.
Завдання 5: Підтвердьте встановлення NVIDIA Container Toolkit (на хості)
cr0x@server:~$ dpkg -l | grep -E 'nvidia-container-toolkit|nvidia-container-runtime'
ii nvidia-container-toolkit 1.15.0-1 amd64 NVIDIA Container toolkit
ii nvidia-container-runtime 3.14.0-1 amd64 NVIDIA container runtime
Що це означає: Пакети toolkit/runtime присутні (стиль Debian/Ubuntu). На RPM-системах використовуйте rpm -qa.
Рішення: Якщо відсутні — встановіть їх. Якщо присутні, але застарілі — оновіть; GPU-ентаймент не те, що можна «налаштувати і ніколи не оновлювати».
Завдання 6: Перевірте, чи Docker бачить рантайм
cr0x@server:~$ docker info | sed -n '/Runtimes/,+3p'
Runtimes: io.containerd.runc.v2 nvidia runc
Default Runtime: runc
Що це означає: Docker знає про рантайм nvidia. За замовчуванням усе ще runc, що нормально, якщо ви використовуєте --gpus.
Рішення: Якщо рантайм nvidia відсутній — toolkit не підключений до Docker (або Docker потребує перезапуску).
Завдання 7: Мінімальний тест контейнера з GPU
cr0x@server:~$ docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
Thu Jan 3 10:16:01 2026
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14 Driver Version: 550.54.14 CUDA Version: 12.4 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
|===============================+======================+======================|
| 0 NVIDIA A10 On | 00000000:17:00.0 Off | Off |
+-------------------------------+----------------------+----------------------+
Що це означає: Рантайм успішно інжектував пристрої GPU і бібліотеки драйвера; контейнер може опитувати GPU.
Рішення: Якщо це не вдається, ваш образ додатку не має значення. Виправляйте toolkit/рантайм, політику або дозволи на пристрої.
Завдання 8: Подивіться, які пристрої GPU були інжектовані
cr0x@server:~$ docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 bash -lc 'ls -l /dev/nvidia*'
crw-rw-rw- 1 root root 195, 0 Jan 3 10:16 /dev/nvidia0
crw-rw-rw- 1 root root 195, 255 Jan 3 10:16 /dev/nvidiactl
crw-rw-rw- 1 root root 235, 0 Jan 3 10:16 /dev/nvidia-uvm
crw-rw-rw- 1 root root 235, 1 Jan 3 10:16 /dev/nvidia-uvm-tools
Що це означає: Контейнер бачить ті самі типи символьних пристроїв, що й хост.
Рішення: Якщо пристроїв немає в контейнері, ви фактично не використовуєте GPU-рантайм (--gpus відсутній, рантайм неконфігурований або політика блокує).
Завдання 9: Перевірте інжектовані бібліотеки драйвера (libcuda)
cr0x@server:~$ docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 bash -lc 'ldconfig -p | grep -E "libcuda\.so|libnvidia-ml\.so" | head'
libcuda.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libcuda.so.1
libnvidia-ml.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
Що це означає: Контейнер може розвʼязувати ключові бібліотеки, які взаємодіють із драйвером.
Рішення: Якщо libcuda.so відсутній у контейнері, інжекція toolkit зламана або у вас нестандартний базовий образ, який плутає рантайм.
Завдання 10: Перевірте, що Docker думає, що ваш контейнер запитував
cr0x@server:~$ docker inspect --format '{{json .HostConfig.DeviceRequests}}' gpu-test
[{"Driver":"","Count":-1,"DeviceIDs":null,"Capabilities":[["gpu"]],"Options":{}}]
Що це означає: Контейнер попросив GPU через сучасний інтерфейс device request (--gpus all показує Count:-1).
Рішення: Якщо це порожньо для робочого навантаження, яке ви думали GPU-підтримуване — ви знайшли баг: ваш run/compose-спек не запитав GPU.
Завдання 11: Діагностика cgroup v2 та делегації
cr0x@server:~$ stat -fc %T /sys/fs/cgroup/
cgroup2fs
Що це означає: Ви на cgroup v2. Більшість сучасних дистрибутивів саме на ньому. Деякі старіші налаштування GPU припускали поведінку v1.
Рішення: Якщо доступ до GPU став нестабільним після оновлення ОС, повторно перевірте версію NVIDIA Container Toolkit і стек рантайму на сумісність з cgroup v2.
Завдання 12: Перевірте стан AppArmor (поширений тихий блокувальник)
cr0x@server:~$ aa-status | sed -n '1,8p'
apparmor module is loaded.
54 profiles are loaded.
51 profiles are in enforce mode.
docker-default
/usr/bin/man
/usr/sbin/sshd
Що це означає: AppArmor активний. Дефолтний профіль Docker зазвичай підходить, але кастомні профілі можуть блокувати доступ до пристроїв.
Рішення: Якщо застосовано жорсткий профіль, переконайтеся, що він дозволяє доступ до /dev/nvidia*, або протестуйте з відомо робочим профілем.
Завдання 13: Перевірте режим SELinux (якщо застосовано)
cr0x@server:~$ getenforce
Enforcing
Що це означає: SELinux у режимі Enforcing. Це не проблема само по собі; проблема — у неправильно налаштованій політиці.
Рішення: Якщо GPU працює в permissive, але не в enforcing — потрібні правильні SELinux-лейбли/політика для вузлів пристроїв і рантайму контейнера.
Завдання 14: Реальність rootless Docker
cr0x@server:~$ docker info | grep -E 'Rootless|Security Options'
Rootless: true
Security Options:
seccomp
rootless
Що це означає: Ви запускаєте rootless Docker. Це чудово для безпеки, але доступ до GPU часто блокується, бо користувач не може відкрити вузли пристроїв.
Рішення: Якщо rootless необхідний, плануйте підтримуваний підхід (часто: не робіть GPU через rootless на загальнодоступних хостах, якщо не контролюєте дозволи на пристрої).
Завдання 15: Підтвердіть всередині контейнера застосунку, що фреймворк бачить CUDA
cr0x@server:~$ docker run --rm --gpus all myapp:latest bash -lc 'python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.version.cuda)"'
2.2.1
True
12.1
Що це означає: Збірка вашого фреймворку підтримує CUDA і може її ініціалізувати.
Рішення: Якщо is_available() — false, але nvidia-smi працює, підозрівайте відсутні бібліотеки CUDA у образі, wheel тільки для CPU або несумісний libc/glibc.
Завдання 16: Відловіть класичну невідповідність «драйвер занадто старий»
cr0x@server:~$ docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 bash -lc 'cuda-samples-deviceQuery 2>/dev/null || true'
bash: cuda-samples-deviceQuery: command not found
Що це означає: Базові образи не містять прикладів; це нормально. Сенс у тому, щоб памʼятати, що «образ CUDA» не означає «інструменти тулкіта встановлені».
Рішення: Якщо потрібні інструменти компіляції — використовуйте devel образ. Не ставте збіркові тулчейни в рантайм-образи, якщо не хочете повільного CI і несподіваних вразливостей.
Чому це ламається: основні режими відмов у продакшені
1) Драйвер на хості зламаний (а ви все одно дебажите Docker)
Контейнери зручні як цап відбувайло. Але якщо драйвер хоста не може ініціалізувати GPU, ви не «в одному прапорці» від успіху.
Поширені причини: оновлення ядра без перебудови модулів DKMS, Secure Boot блокує підписані модулі, або часткове оновлення драйвера, що залишило користувацький і ядровий простори несинхронними.
Підказка: nvidia-smi не працює на хості. Все інше — шум.
2) NVIDIA Container Toolkit не встановлено або не підключено до Docker
Docker потребує OCI-хук, який додає вузли пристроїв, монтує бібліотеки драйвера і встановлює змінні оточення для можливостей.
Без toolkit --gpus all або нічого не робить, або лаконічно падає, залежно від версій.
Якщо ви використовуєте containerd напряму, точка інтеграції змінюється. Якщо Kubernetes — ще один шар, де device plugin може зламатися.
Та сама ідея, більше рухомих частин.
3) Ви в голові попросили «GPU», а не у специфікації
Ви б подумали, що цього не станеться. Відбувається постійно. Файли Compose без потрібної секції, Helm чарт без лімітів,
або CI job, який запускає docker run але без --gpus.
Найгірша версія — тихий перехід на CPU. Сервіс залишається «здоровим», латентність повзе вгору, витрати зростають, і ніхто не помічає, поки не настане квартальний огляд продуктивності.
Жарт 1: GPU, який не виділено вашому контейнеру — просто дуже дорогий обігрівач простору — але навіть руки ним не зігріти, бо він простаює.
4) Несумісність можливостей драйвера і тулкіта CUDA у контейнері
Драйвер визначає, які можливості CUDA runtime можуть підтримуватися. Контейнер визначає, з якими бібліотеками користувацького простору ваш додаток лінкується.
Якщо ви постачаєте контейнер, збудований під CUDA 12.x, а драйвер хоста підтримує тільки старішу версію, ініціалізація може провалитися.
На практиці сучасні стеки більш поблажливі, ніж раніше, але «поблажливість» — це не план.
Правильний план: фіксуйте версії драйверів на хості і фіксуйте версії базових CUDA-образів у збірці.
Розглядайте сумісність як контракт інтерфейсу, а не як відчуття.
5) Rootless контейнери і права на вузли пристроїв
Вузли пристроїв захищені Unix-права і іноді додатковими шарами безпеки.
Rootless Docker запускає контейнери від імені непривілейованого користувача — чудово, поки не потрібно відкрити /dev/nvidia0 і друзів.
Іноді це можна вирішити, підкоригувавши групову власність (наприклад, video), правила udev або використавши привілейований хелпер.
Але будьте чесні: на мультиорендних системах GPU + rootless часто — це політична боротьба під технічним прикриттям.
6) Політика безпеки блокує доступ до GPU (SELinux/AppArmor/seccomp)
Системи безпеки не «ламали» речі — вони застосовують правила. Якщо ви не дозволили пристрої GPU, доступ відмовлено.
Це проявляється як помилки доступу або фреймворки, що не можуть ініціалізуватися й дають криптичні повідомлення.
В підприємствах виправлення зазвичай не «вимкнути SELinux», а «написати політику, яка дозволяє мінімально потрібний доступ до пристроїв».
Вимкнення контролів безпеки через запізніле ML-завдання — шлях до іншого типу інциденту.
7) Kubernetes: невідповідність виділення GPU і device plugin
У Kubernetes рантайм контейнера — один шар, але виділення ресурсів — інший. Якщо NVIDIA device plugin не встановлено,
планувальник не може призначити GPU і ваш pod або не буде запущений, або запуститься без доступу до GPU.
Також: ресурси GPU у Kubernetes зазвичай запитуються через ліміти. Якщо ви забудете ліміт — GPU не отримаєте.
Це послідовно, передбачувано і все одно дивує когось щотижня.
8) MIG і «бачить GPU, але не той, який ви очікували»
З MIG «GPU», який ви отримали, може бути слайсом. Бібліотеки й інструменти покажуть інші ідентифікатори.
Оператори плутають це з «GPU зник». Ні: одиниця виділення змінилася.
9) Дивності файлової системи і шляхів до бібліотек (scratch images, distroless, мінімальні ОС)
Деякі мінімалістичні образи не мають ldconfig, стандартних директорій бібліотек або використовують нестандартні розташування динамічного лінкера.
Інжекція NVIDIA відбувається шляхом монтування бібліотек драйвера в очікувані шляхи. Якщо ваш образ надто «кмітливий», він стає несумісним з цим підходом.
Будьте прагматичні: якщо хочете максимально мінімальний образ — заплатіть інтеграційну ціну. Інакше оберіть базовий образ, який поводиться як звичайний дистрибутив Linux.
10) Проблеми з продуктивністю: «працює», але повільно
Працюючий GPU-контейнер все одно може бути провалом у продакшені, якщо він повільний. Поширені винуватці:
CPU занадто зашитий, топологія PCIe і NUMA неузгодженість, обмеження памʼяті контейнера, що призводять до свапінгу, або вузькі місця у зберіганні, які не дають даним подачі GPU.
Використання GPU на 5% з високим CPU і великим iowait — це не проблема GPU. Це проблема конвеєра даних.
перефразована ідея — John Ousterhout: надійність приходить від простоти; складність — там, де люблять ховатися баги та відмови.
Поширені помилки: симптом → корінна причина → виправлення
«CUDA недоступна» у PyTorch/TensorFlow, але GPU на хості в порядку
Симптом: torch.cuda.is_available() повертає false; очевидних помилок немає.
Корінна причина: Збірка фреймворку тільки для CPU в контейнері, або відсутні бібліотеки CUDA у користувацькому просторі.
Виправлення: Встановіть CUDA-версію wheel/conda з підтримкою GPU і переконайтеся, що базовий образ містить сумісні бібліотеки CUDA. Перевірте завдання 15.
nvidia-smi працює на хості, але не в контейнері: «command not found»
Симптом: Контейнер не може виконати nvidia-smi.
Корінна причина: Ваш образ не містить nvidia-smi (це частина утиліт NVIDIA у користувацькому просторі), навіть якщо passthrough GPU правильний.
Виправлення: Використовуйте базові образи nvidia/cuda для налагодження або встановіть nvidia-utils-* у образ, якщо дійсно потрібно (часто це не потрібно).
nvidia-smi падає в контейнері: «Failed to initialize NVML»
Симптом: Помилки NVML всередині контейнера.
Корінна причина: Відсутня/некоректно змонтована libnvidia-ml.so, несумісні бібліотеки драйверів або проблема з правами/політикою.
Виправлення: Перевірте інжекцію toolkit (Завдання 5–9). Підтвердіть наявність вузлів пристроїв у контейнері (Завдання 8). Перевірте SELinux/AppArmor (Завдання 12–13).
Контейнер запускається, але GPU не видимі
Симптом: Фреймворк не бачить жодних пристроїв; /dev/nvidia* відсутні.
Корінна причина: Ви не запитали GPU (--gpus відсутній) або у Compose/K8s спеку немає запиту GPU.
Виправлення: Додайте --gpus all (або конкретні пристрої) і перевірте через Завдання 10. У Kubernetes правильно виставте ліміти GPU.
Працює під root, але падає як non-root
Симптом: Root може запускати CUDA; непривілейований користувач отримує access denied.
Корінна причина: Права/групи вузлів пристроїв не дозволяють доступ, особливо у rootless Docker.
Виправлення: Перегляньте власність вузлів пристроїв (udev rules), членство в групах або уникайте rootless для GPU-навантажень на цьому хості (Завдання 14).
Після оновлення ОС GPU-контейнери перестали працювати
Симптом: Все було добре, а потім «раптом» зламалося.
Корінна причина: Оновлення ядра зламало збірку NVIDIA DKMS, або змінився режим cgroup, або Docker/containerd оновилися без відповідного toolkit.
Виправлення: Повторно перевірте драйвер хоста (Завдання 1–4), пакети toolkit (Завдання 5), рантайми Docker (Завдання 6) і режим cgroup (Завдання 11).
Машина з декількома GPU: контейнер бачить не той GPU
Симптом: Завдання працює на GPU 0, але ви хотіли GPU 2; або кілька завдань конфліктують.
Корінна причина: Відсутній явний вибір пристрою; покладаєтесь на імпліцитний порядок; відсутній планувальник.
Виправлення: Використовуйте --gpus '"device=2"' або планувальник із правильним виділенням. Для MIG перевірте ID слайсів і логіку виділення.
Працює, але продуктивність жахлива
Симптом: Низьке використання GPU, велике час виконання.
Корінна причина: Ботлнеки в data loader, пропускна здатність сховища, привʼязка CPU, NUMA-невідповідність або малі batch size, які не завантажують GPU.
Виправлення: Профілюйте енд-ту-енд: перевірте використання CPU, iowait і пропускну здатність сховища. Не «оптимізуйте» GPU, поки не доведете, що GPU — вузьке місце.
Три корпоративні історії з поля бою
Інцидент через неправильне припущення: «Версія CUDA в nvidia-smi — це те, що в контейнері»
Середнє підприємство випустило новий інференсний контейнер, збудований під новішу версію CUDA. Команда перевірила GPU-вузли і побачила,
що nvidia-smi повідомляє недавню версію CUDA. Вони припустили, що драйвер і рантайм узгоджені, і розгорнули деплой.
Сервіс не впав. Він деградував. Фреймворк тихо вирішив, що CUDA не годиться, і перейшов на CPU. Латентність зросла, автоскейл увімкнувся,
і кластер розрісся, ніби знайшов шведський стіл.
Інженер на чергуванні зробив правильне: перестав дивитися логи застосунку і запустив мінімальний CUDA-контейнер. Він упав через невідповідність можливостей драйвера.
«CUDA Version» у nvidia-smi була можливістю драйвера, а не обіцянкою, що стек користувача у їхньому контейнері ініціалізується.
Виправлення було нудним: зафіксувати драйвер хоста на версії, сумісній з CUDA у контейнері, і додати preflight job, який запускає
nvidia-smi всередині точного production-образу. Також вони додали алерт на падіння використання GPU при одночасному зростанні CPU — явний сигнал тихого відкату.
Оптимізація, яка обернулася проти: обрізали образ, поки інжекція GPU не зламалася
Інша компанія мала вимогу безпеки зменшити образи контейнерів. Хтось занадто захопився: перейшов від стандартного дистрибутива
до мінімального/distroless-подібного образу, прибрав кеш лінкера і викинув «непотрібні» директорії.
Образ став меншим, сканер вразливостей був щасливий, демо пройшло в CI. Потім у продакшені почалися відмови лише на певних вузлах.
Той самий образ працював в одному пулі GPU і падав в іншому. Така нестабільність змушує дорослих говорити слова, яких не повинно бути в тикетах.
Корінь проблеми не в «NVIDIA хитра». Це була тонка припущення про те, як рантайм інжектує бібліотеки драйвера і чого очікує від файлової системи контейнера.
Мінімальний образ не мав звичних шляхів пошуку бібліотек, тож інжектовані бібліотеки існували, але динамічний лінкер їх не знаходив.
Вони повернулися до звичного базового образу для GPU-навантажень, а потім застосували зважений підхід до жорсткості:
багатоступеневі збірки, залишати рантайм-образи компактними, але не ворожими, і перевіряти інтеграцію тестом, що реально ініціалізує CUDA.
Жарт 2: Нічого так не кричить «безпека», як контейнер настільки мінімальний, що не може знайти свої власні бібліотеки.
Нудна, але правильна практика, яка врятувала день: золотий вузол + фіксовані версії + канарні тести
Регульоване підприємство запускало GPU-навантаження на виділеному кластері. У них були два правила, які звучали бюрократично:
(1) GPU-вузли збираються з золотого образу з фіксованим ядром, фіксованим драйвером NVIDIA і фіксованим toolkit.
(2) Кожна зміна проходить через канарний пул, який запускає синтетичні GPU-тести щогодини.
Якось оновлення репозиторію ОС внесло патч у ядро, який був нешкідливий для загальних обчислень, але викликав проблему перебудови DKMS у їхньому середовищі.
Канарний пул засвітився за годину: nvidia-smi падав на нових вузлах; синтетичний контейнерний тест теж упав.
Оскільки кластер використовував зафіксовані версії, зона ураження була обмежена. Жодні продакшн-вузли не «дріфтнули» автоматично.
Команда зупинила оновлення, виправила пайплайн збірки, перебудувала золотий образ і підвищила його після проходження канарок.
Мораль не в «ніколи не оновлювати». Мораль у «оновлювати навмисно». GPU-стек — це три-тельне завдання: ядро, драйвер, рантайм контейнера.
Потрібен процес, який не дає фізиці перетворитися на астрологію.
Укріплення та надійність: зробити GPU-контейнери нудними
Фіксуйте версії серйозно
У GPU-стеку є кілька вісьових версій: ядро, драйвер, toolkit, CUDA runtime, фреймворк і іноді NCCL.
Якщо дозволяти їм плавати незалежно, рано чи пізно ви отримаєте несумісну комбінацію.
Робіть так:
- Фіксуйте версії драйверів NVIDIA на кожному пулі вузлів.
- Фіксуйте теги базових CUDA-образів (не використовуйте
latest, якщо любите спати). - Фіксуйте версії фреймворків і записуйте, під якою CUDA-варіацією вони збудовані.
- Оновлюйте як бандл у контрольованому розгортанні.
Зробіть доступність GPU явним сигналом SLO
«Сервіс працює» — не те саме, що «сервіс використовує GPU». Для інференсу тихий fallback на CPU — це бомба витрат і затримки.
Для тренувань — бомба по графіку.
Операційно: випускайте метрики використання GPU, памʼяті GPU і бінарний «CUDA ініціалізовано успішно» при старті процесу.
Налаштуйте алерти на невідповідність (CPU високо, GPU низько) для сервісів, які залежать від GPU.
Відокремте «дебагові образи» від «продакшн образів»
Ви хочете компактні образи в продакшені. Але вам також потрібні інструменти для налагодження о 03:00. Не плутайте цілі.
Тримайте дебаг-варіант, який містить bash, procps, можливо pciutils, і можливість запустити nvidia-smi або невеликий CUDA-тест.
Продакшн-образи можуть залишатися лаконічними.
Будьте обережні з ескалацією привілеїв
GPU passthrough зазвичай не вимагає --privileged. Якщо ви використовуєте його «бо так виправляється» — ви, ймовірно, закриваєте справжню проблему конфігурації рантайму або політики.
Привілейовані контейнери збільшують поверхню атаки і ускладнюють перевірки на відповідність.
Не ігноруйте NUMA і топологію PCIe
Коли GPU видимий, проблеми продуктивності часто зводяться до топології: потоки CPU запущені на NUMA-ноди, далекі від GPU,
або NIC і GPU на різних сокетах, що спричиняє трафік між сокетами.
Контейнери не виправляють погане розміщення самим собою. Ваш планувальник і конфігурація вузлів — ось хто це робить.
Контрольні списки / покрокові плани
Покроково: підняти підтримку GPU на новому Docker-хості (NVIDIA)
-
Встановіть і перевірте драйвер на хості.
Запустіть Завдання 1. Якщоnvidia-smiне працює — не продовжуйте. -
Підтвердіть вузли пристроїв.
Виконайте Завдання 2. Відсутні вузли означають, що драйвер не завантажено правильно. -
Встановіть NVIDIA Container Toolkit.
Перевірте через Завдання 5. -
Перезапустіть Docker і підтвердіть рантайми.
Перевірте через Завдання 6. -
Запустіть мінімальний GPU-контейнерний тест.
Завдання 7 має бути успішним. -
Зафіксуйте версії.
Запишіть ядро, драйвер, toolkit, версію Docker. Зафіксуйте їх для пулу вузлів.
Покроково: дебаг провального продакшн-навантаження
- Підтвердіть стан GPU хоста (Завдання 1). Якщо він зламаний — зупиніться.
- Підтвердіть шлях рантайму відомим-робочим CUDA-образом (Завдання 7).
- Підтвердіть пристрої і бібліотеки в контейнері (Завдання 8–9).
- Підтвердіть, що навантаження дійсно запитало GPU (Завдання 10).
- Перевірте шари політики (Завдання 12–14), особливо після вжорсточень.
- Перевірте стек застосунку (Завдання 15) на предмет збірок тільки для CPU або відсутніх залежностей.
- Лише потім ганяйтеся за продуктивністю: CPU, I/O, batch size, NUMA placement.
Операційний чекліст: запобігання регресіям
- Тримайте канарний пул GPU-вузлів. Запускайте синтетичні GPU-тести щогодини.
- Налаштуйте алерт «GPU очікується, але не використовується»: низьке використання GPU + високий CPU для GPU-тегованих сервісів.
- Фіксуйте і прокочуйте оновлення драйвера/toolkit/ядра як бандл.
- Відслідковуйте зміни режиму cgroup як критичні.
- Майте дебаг-контейнер, який може швидко виконати базову перевірку GPU.
- Документуйте точні шаблони Compose/Helm, які запитують GPU; забороніть ad-hoc copy/paste конфігурації.
FAQ
1) Чому nvidia-smi працює на хості, але не всередині контейнера?
Зазвичай тому, що рантайм контейнера не інжектував пристрої/бібліотеки (відсутній toolkit або відсутній --gpus),
або політика безпеки блокує доступ до пристроїв. Перевірте Завдання 6–9 та 12–13.
2) Чи потрібно встановлювати драйвер NVIDIA всередині контейнера?
Ні. Драйвер ядра має бути на хості. Контейнер зазвичай несе бібліотеки користувацького простору CUDA (runtime/toolkit/фреймворк),
а NVIDIA Container Toolkit інжектує бібліотеки драйвера хоста, потрібні для розмови з драйвером ядра.
3) Що насправді означає «CUDA Version» у nvidia-smi?
Це вказує на максимальну CUDA-діалектну можливість, яку підтримує встановлений драйвер. Воно не показує, який CUDA toolkit у вашому образі.
Розглядайте це як «драйвер розуміє до цього CUDA-діалекту», а не як «toolkit встановлено».
4) Чи варто встановлювати nvidia як дефолтний рантайм Docker?
У більшості сучасних налаштувань — ні. Використовуйте --gpus і залишайте дефолтний рантайм як runc.
Встановлення nvidia як дефолтного може здивувати не-GPU навантаження і ускладнити дебаг.
5) Чому мій фреймворк каже, що CUDA недоступна, навіть коли nvidia-smi працює?
Бо nvidia-smi лише доводить, що NVML може говорити з драйвером. Фреймворки потребують також правильних бібліотек CUDA runtime,
правильної збірки фреймворку (не тільки для CPU) і іноді сумісного glibc. Завдання 15 — ваш найшвидший детектор істини.
6) Чи можна використовувати GPU з rootless Docker?
Іноді так, але очікуйте тертя. Основна проблема — дозвіл на відкриття вузлів /dev/nvidia*.
Якщо не можете гарантувати дозволи на пристрої і політику — rootless + GPU стає ризиком надійності.
7) Мій контейнер бачить GPU, але продуктивність жахлива. Чи Docker це гальмує?
Наклад Docker рідко основний винуватець для обчислень на GPU. Проблеми продуктивності зазвичай походять від CPU/I/O вузьких місць,
NUMA розміщення, маленьких батчів або голодування конвеєра даних. Доведіть, що GPU — справжнє обмеження, перш ніж «оптимізувати» CUDA.
8) Який найнадійніший спосіб дати контейнеру доступ до GPU?
Запитуйте тільки ті GPU, які потрібні (не all за замовчуванням), уникайте --privileged і покладайтеся на шлях інжекції NVIDIA runtime.
Поєднайте це з точним фіксуванням образів і ізоляцією пулів вузлів для GPU-навантажень.
9) Як запобігти тихому fallback на CPU?
Додайте стартап-перевірки, які швидко падають, якщо ініціалізація CUDA не вдається, експортуйте метрику використання GPU і налаштуйте алерти на невідповідність CPU/GPU.
«Здорово, але неправильно» — класична пастка надійності.
Висновок: практичні наступні кроки
Проблеми GPU в контейнерах відчуваються хаотичними, бо є багато шарів, і кожен шар ламається по-своєму.
Виправлення — це дисципліна: перевірте хост, перевірте шлях інжекції рантайму, а потім перевірте образ застосунку.
Не пропускайте кроки. Не дебажте на підставі відчуттів.
Наступні кроки, які можна зробити сьогодні:
- Запустіть Швидкий план діагностики на одному зламаному хості і на одному відомо робочому; порівняйте виводи.
- Додайте smoke-тест у CI, який запускає
nvidia-smi(або ініціалізацію CUDA фреймворка) всередині вашого production-образу. - Зафіксуйте версії драйвера/toolkit/CUDA-образу і прокочуйте їх разом через канарний пул.
- Налаштуйте алерт «GPU очікується, але не використовується», щоб впіймати тихе падіння на CPU раніше, ніж фінанси це помітять.