SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor

Docker-контейнер чи віртуальна машина: у чому різниця

Docker на Linux не є віртуальною машиною, а контейнери Linux на Windows і macOS працюють у Linux VM. Пояснюємо різницю, перевірки uname і systemd-detect-virt та що обрати для VPS.

Docker-контейнер чи віртуальна машина: коротка відповідь

Docker-контейнер не є віртуальною машиною, якщо він працює на Linux. Там контейнер це звичайний процес, ізольований засобами ядра, і він ділить ядро з хостом. Віртуальна машина (VM) завантажує власне ядро поверх гіпервізора, наприклад KVM (Kernel-based Virtual Machine). Але на Windows і macOS Docker Desktop справді запускає Linux VM, і вже в ній працюють ваші Linux-контейнери. Тому фраза «Docker це просто легка віртуалка» наполовину правдива для вашого ноутбука і хибна для вашого сервера.

Більшість розробників починає з Docker Desktop на ноутбуці з Windows або на MacBook. Там між вашими Linux-контейнерами і залізом справді стоїть VM. Потім той самий docker compose up ви запускаєте на VPS, і одного шару вже немає. Нижче розберемо обидва випадки, дамо команди для перевірки і пояснимо, коли контейнера мало і потрібна окрема VM.

Що таке контейнер: ізольований процес, а не окремий комп'ютер

Документація Docker визначає контейнер просто: це ізольований процес з усіма файлами, які йому потрібні для роботи. Ключове слово тут «процес». Він працює на тому самому ядрі, що й решта системи. Docker пише прямо: якщо ви запускаєте кілька контейнерів, усі вони ділять одне ядро.

Ізоляцію дають дві можливості ядра Linux.

  • Namespaces (простори імен). Простір імен обгортає глобальний ресурс системи так, що процеси всередині бачать власну ізольовану копію цього ресурсу. Сторінка namespaces(7) перелічує типи: Cgroup, IPC, Network, Mount, PID, Time, User і UTS. Тому процес у контейнері бачить свій список процесів, свою мережу і своє ім'я хоста. Docker створює для кожного контейнера окремий набір namespaces.
  • Cgroups (control groups, контрольні групи). Це можливість ядра, яка організовує процеси в ієрархічні групи і керує тим, скільки ресурсів вони споживають. Документація Docker з безпеки пояснює наслідок: завдяки cgroups один контейнер не може покласти всю систему, вичерпавши один із ресурсів.

Чого з цього не випливає: окремого ядра в контейнера немає. Окремого завантаження ОС теж немає. Образ ubuntu:24.04 дає файли користувацького простору Ubuntu: apt, bash, бібліотеки. Ядро під ними завжди хостове.

Що таке віртуальна машина

Віртуальна машина відтворює цілий комп'ютер. Docker описує VM як цілу операційну систему з власним ядром, драйверами, програмами і застосунками. Гіпервізор, наприклад KVM у Linux, ділить фізичне залізо між кількома такими машинами. Кожна гостьова ОС завантажує своє ядро і не бачить ядер сусідів.

Звідси різниця у вартості. Docker прямо пише, що запускати VM лише заради ізоляції одного застосунку означає великі накладні витрати. Контейнер стартує як процес. VM спершу завантажує ядро і систему ініціалізації. Ваш VPS теж є віртуальною машиною, і різницю між цими термінами пояснює стаття VPS, VM і VPC: чим вони відрізняються.

Чому на Windows і macOS Docker справді працює у VM

Контейнерам Linux потрібне ядро Linux. На Windows і macOS його немає, тому Docker Desktop приносить його з собою у віртуальній машині.

Windows. Сторінка встановлення Docker Desktop називає WSL 2 бекендом за замовчуванням, а Linux-контейнери режимом за замовчуванням. Microsoft пише прямо: WSL 2 запускає ядро Linux у легкій службовій віртуальній машині, і ця VM працює як віртуальна машина Hyper-V. Ядро для неї Microsoft збирає з останньої стабільної гілки. Документація Docker додає важливу деталь: у WSL 2 кілька дистрибутивів Linux працюють поруч на одному спільному ядрі. Для середовищ, яким потрібна суворіша ізоляція, Docker радить режим Hyper-V замість WSL 2. Hyper-V доступний лише при встановленні Docker Desktop для всіх користувачів.

Окремий випадок це Windows-контейнери. Docker Desktop перемикається на них пунктом меню «Switch to Windows containers», і за документацією Docker вони працюють безпосередньо на хості Windows, а не в Linux VM. Вони доступні лише при встановленні для всіх користувачів і лише на Windows 10 або 11 у редакціях Pro чи Enterprise. Microsoft описує для них два режими ізоляції. У режимі process контейнери ділять ядро з хостом і між собою, майже як контейнери Linux. У режимі Hyper-V кожен контейнер працює в оптимізованій віртуальній машині і фактично отримує власне ядро. На Windows 10 Pro і Enterprise за замовчуванням діє ізоляція Hyper-V, на Windows Server режим process.

macOS. Docker Desktop підтримує кілька менеджерів віртуальних машин (VMM, virtual machine manager), які запускають Linux VM для контейнерів. Набір доступних VMM змінюється від версії до версії, тож дивіться налаштування своєї копії Docker Desktop.

Docker Desktop для Linux теж запускає VM, і їй потрібна підтримка KVM. Це часто плутають. Docker Engine, тобто демон dockerd з пакетів, власної VM не створює: він запускає контейнери як процеси на ядрі тієї машини, де стоїть. На сервері ставлять саме Docker Engine, і встановлення Docker Engine на VPS розібране окремо.

Що це означає на практиці. На ноутбуці з Windows і бекендом WSL 2 ваші Linux-контейнери ділять ядро з VM WSL 2, а не з Windows. З іншим бекендом, наприклад Hyper-V, ядро для них дає Linux VM, якою керує Docker Desktop. Лише Windows-контейнери в режимі process ділять ядро з самою Windows. На VPS контейнери ділять ядро з самим VPS. Код той самий, але шар під ним інший.

Шари на KVM VPS: від заліза до контейнера

  1. Фізичний сервер у дата-центрі провайдера.
  2. Ядро хоста з гіпервізором KVM. Документація ядра Linux називає цей рівень L0.
  3. Ваш KVM VPS: віртуальна машина з власним ядром. Це рівень L1.
  4. Docker Engine всередині VPS як звичайна служба.
  5. Ваші контейнери: процеси на ядрі VPS, ізольовані namespaces і cgroups.

Між кроками 4 і 5 гіпервізора немає. Контейнер не додає ще одну VM. Тому десяток контейнерів на одному VPS обходиться дешево: ядро одне, і завантажується воно один раз. Сервіси такого стеку зручно описати одним файлом, і основи Docker Compose на VPS показують, як це зробити.

Як перевірити самому: uname -r і systemd-detect-virt

Дві команди відповідають на питання «що піді мною» краще за будь-яку схему. Запустіть їх на своїй машині. Ми не наводимо тут чужий вивід, бо у вас він буде свій.

Спершу на хості, тобто на VPS або в терміналі WSL:

uname -r
systemd-detect-virt
systemd-detect-virt --vm
systemd-detect-virt --container

uname -r друкує версію (release) ядра. systemd-detect-virt визначає, чи працює система у віртуалізованому середовищі. Прапорець --vm шукає лише апаратну віртуалізацію. Прапорець --container шукає лише контейнерну, яку man-сторінка прямо називає віртуалізацією зі спільним ядром. Код виходу 0 означає, що віртуалізацію знайдено. Ненульовий код означає, що не знайдено. Перевірте код командою echo $? одразу після виклику.

Потім те саме питання до контейнера:

docker run --rm ubuntu:24.04 uname -r

Прапорець --rm видаляє контейнер і його анонімні томи після завершення, тож після перевірки нічого не лишиться. Образ Ubuntu тут лише приклад. Підійде будь-який образ, у якому є uname.

Як читати результат:

  • Версія ядра в контейнері збігається з хостом. Так і має бути на Linux із Docker Engine. Контейнер не має свого ядра, тому uname -r питає те саме ядро, що й на хості. Образ ubuntu:24.04 при цьому може працювати на хості з Debian або Rocky Linux, і uname -r все одно покаже ядро хоста.
  • Версія ядра в контейнері не та, яку ви очікували. На Windows або Mac це нормально: ви бачите ядро Linux VM, яку запустив Docker Desktop, а не ядро вашої ОС. З Docker Desktop для Linux картина та сама, бо там теж є VM.
  • systemd-detect-virt --vm на VPS називає гіпервізор. Man-сторінка перелічує такі ідентифікатори апаратної віртуалізації: qemu, kvm, amazon, zvm, vmware, microsoft, oracle, powervm, xen, bochs, uml, parallels, bhyve, qnx, acrn, apple, sre, google. На KVM VPS очікуйте kvm, хоча деякі провайдери можуть показувати інший ідентифікатор, наприклад qemu. Головне: ви у VM зі своїм ядром.
  • systemd-detect-virt --container на хості щось знаходить. Контейнерні ідентифікатори в man-сторінці такі: openvz, lxc, lxc-libvirt, systemd-nspawn, docker, podman, rkt, wsl, proot, pouch. У терміналі WSL очікуйте wsl, і це нормально. Але якщо ваш «VPS» відповідає openvz або lxc, то це не віртуальна машина. Ви орендуєте контейнер на чужому ядрі, і про наслідки йдеться нижче.

Чому дешевий OpenVZ або LXC «VPS» може відмовитися запускати Docker

На контейнерному тарифі (LXC, OpenVZ, Virtuozzo) усі клієнти ділять ядро провайдера. uname -r там показує ядро хоста, спільне з усіма іншими контейнерами на цій машині. Завантажити модуль ядра через modprobe ви не можете, бо ядро не ваше.

Docker сам є менеджером контейнерів. Він створює namespaces, cgroups і мережеві інтерфейси для кожного контейнера. Усередині чужого контейнера це вже вкладені контейнери, і вони працюють лише тоді, коли провайдер це дозволив. Docker усередині LXC працює тільки якщо провайдер увімкнув вкладення (nesting) і драйвер сховища з цим справляється. Тому такий тариф може відмовитися запускати Docker, а може й запустити. Усе залежить від налаштувань хоста, яких ви не бачите. Як відрізнити типи тарифів ще до оплати, пояснює стаття KVM, Xen чи LXC: що насправді працює під вашим VPS.

Якщо вам потрібен Docker, найпростіший шлях це KVM VPS. Там ядро ваше, і Docker Engine ставиться як на звичайний сервер.

Коли справді потрібна вкладена віртуалізація

Звичайним контейнерам Docker вкладена віртуалізація не потрібна. Контейнер є процесом, а процесу гіпервізор не потрібен. Ця вимога з'являється тоді, коли ви хочете запустити VM всередині свого VPS.

Документація ядра описує рівні так: L0 це фізичний хост із KVM, L1 це гостьова VM на ньому, L2 це VM всередині L1, тобто вкладений гість. Ваш VPS це L1. Параметр nested модуля kvm_intel або kvm_amd читають на L0, а туди має доступ лише провайдер. З боку VPS перевірка простіша:

ls -l /dev/kvm

За документацією KVM, програма відкриває /dev/kvm, щоб отримати доступ до підсистеми KVM. Якщо файлу немає, інструменти, яким потрібен KVM, не мають що відкрити. Отже, Firecracker, Kata Containers чи Docker Desktop для Linux у такому VPS не запрацюють.

Типові випадки, коли вкладена віртуалізація справді потрібна: VM з Windows або FreeBSD усередині VPS, тестова лабораторія з кількох VM, microVM через Firecracker або Kata, Docker Desktop для Linux. Як перевірити і що просити в провайдера, читайте в посібнику з вкладеної віртуалізації на VPS.

Контейнер чи VM: що обрати для свого завдання

Беріть контейнери, коли:

  • ви пакуєте застосунок разом із залежностями, щоб він однаково запускався на ноутбуці й на сервері;
  • на одному невеликому VPS треба тримати багато сервісів: базу даних, кеш, вебзастосунок і зворотний проксі;
  • усім сервісам підходить одне ядро Linux;
  • код ваш або з образів, яким ви довіряєте.

Беріть окрему VM, коли:

  • потрібна інша ОС: Windows або FreeBSD. Контейнер ділить ядро Linux хоста, тому іншого ядра в ньому не буде. Чим відрізняється підхід FreeBSD до ізоляції, пояснює порівняння FreeBSD jails і Docker-контейнерів;
  • потрібна конкретна версія ядра Linux, не та, що на хості;
  • потрібні власні модулі ядра. Модуль завантажується у спільне ядро, тобто зачіпає хост і всі контейнери на ньому;
  • потрібна жорстка межа ізоляції для недовіреного коду або для різних клієнтів на одній машині.

MicroVM: Firecracker і Kata Containers як середній варіант

Між контейнером і повною VM є ще один варіант: microVM. Це справжня віртуальна машина з власним ядром, але мінімальна, щоб стартувати майже так само швидко, як контейнер.

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

Kata Containers це відкрите середовище виконання контейнерів (runtime), яке будує легкі VM і вбудовує їх у звичну екосистему контейнерів. Навантаження працює на виділеному ядрі, а апаратна віртуалізація дає другу лінію захисту.

Обидва варіанти побудовані на KVM. На VPS це означає вимогу з попереднього розділу: потрібен /dev/kvm, тобто вкладена віртуалізація від провайдера.

Модель загроз: що означає спільне ядро для безпеки

Чесна відповідь лежить посередині. За замовчуванням контейнер досить добре ізольований від інших контейнерів і від хоста, так пише сама документація Docker. Але ця ізоляція тримається на ядрі, яке спільне для всіх. Та сама документація попереджає: можливості ядра можуть давати неповну ізоляцію, самі по собі або разом із вразливостями ядра.

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

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

Не перебільшуйте ризик ні в той, ні в інший бік. Для власних сервісів на власному VPS контейнери цілком підходять, бо ви й так довіряєте цьому коду. Межа VM стає важливою, коли поруч працює код, якому ви не довіряєте: чужі збірки, код клієнтів, експерименти з AI-агентами.

FAQ

Docker це віртуальна машина чи ні?

На Linux ні. Контейнер Docker Engine це ізольований процес, який ділить ядро з хостом, а ізоляцію йому дають namespaces і cgroups. Віртуальна машина завантажує власне ядро на гіпервізорі, наприклад KVM. На Windows і macOS Docker Desktop запускає Linux VM (на Windows за замовчуванням через WSL 2), і Linux-контейнери працюють у ній. Windows-контейнери, на які Docker Desktop можна перемкнути на Windows, працюють на самому хості Windows, а не в Linux VM.

Чому uname -r у контейнері показує те саме ядро, що й на хості?

Бо контейнер не має власного ядра. uname -r друкує версію ядра, а в контейнері на Linux це ядро хоста. Образ на кшталт ubuntu:24.04 дає лише файли дистрибутива. Якщо версії не збігаються, між вами і контейнером є VM, як у Docker Desktop на Windows або Mac.

Чи потрібна вкладена віртуалізація, щоб запустити Docker на VPS?

Для звичайних контейнерів Docker Engine вона не потрібна. Потрібен VPS із власним ядром, наприклад KVM VPS. Вкладена віртуалізація потрібна тоді, коли ви запускаєте всередині VPS іншу VM, Firecracker, Kata Containers або Docker Desktop для Linux. Перевірити її з боку VPS можна командою ls -l /dev/kvm.

Чому Docker не працює на моєму OpenVZ або LXC VPS?

Такий тариф уже є контейнером на спільному ядрі провайдера, і systemd-detect-virt --container покаже там openvz або lxc. Docker усередині такого контейнера працює лише тоді, коли провайдер дозволив вкладені контейнери і драйвер сховища це підтримує. Найпростіше рішення: перейти на KVM VPS з власним ядром.

Чи можна запустити Windows або FreeBSD у Docker-контейнері на Linux?

Як контейнер ні, бо контейнер ділить ядро Linux хоста і не може завантажити інше ядро. Для Windows або FreeBSD потрібна окрема віртуальна машина. Якщо ви хочете запустити її всередині VPS, провайдер має дати вкладену віртуалізацію, тобто у VPS має з'явитися /dev/kvm.

Чи була ця стаття корисною?