Skip to main content

Чим контейнер відрізняється від віртуальної машини (VM)?

Container і Virtual Machine обидва ізолюють робочі навантаження, але роблять це на абсолютно різних рівнях стеку. Container це процес на kernel хоста; VM це повна операційна система, що крутиться на hypervisor.

Теорія

TL;DR

  • Container = ізольований процес на kernel хоста через Linux namespaces і cgroups. ~10 MB оверхеду, стартує за мілісекунди.
  • VM = повна гостьова OS на hypervisor (KVM, VMware, Hyper-V). Сотні MB лише на guest kernel і init, стартує за секунди.
  • Щільність на 128 GB сервері: 100-200 container vs 10-15 VM з того самого заліза.
  • Ізоляція: VM сильніша за замовчуванням (окремий kernel на tenant). Container поділяє kernel з хостом, тому kernel CVE потенційно зачіпає всі container на машині.
  • Беремо container, коли треба app-level пакування, швидкі деплої, щільність microservices. Беремо VM, коли треба жорстка ізоляція, інша OS (Windows guest на Linux host), або legacy-софт, що очікує повну машину.

Швидкий приклад

bash
# Container: від "нічого на диску" до робочого веб-сервера за ~2 секунди $ time docker run -d -p 80:80 nginx real 0m1.842s # VM: той самий nginx, але гостьова OS має спочатку завантажитися $ time vagrant up real 0m38.504s # (і це вже після того, як box image скачаний)

Той самий софт, той самий результат (порт 80 з nginx). Різниця у два порядки, бо container перевикористовує твій kernel, а VM привезла свій.

Головна різниця

Container фундаментально це процес хоста з обмеженою видимістю. Linux kernel робить роботу ізоляції через namespaces (окремі в'юхи процесів, мережі, файлової системи, користувачів) і cgroups (ліміти CPU, пам'яті, I/O). VM, навпаки, використовує hypervisor для віртуалізації самого заліза: кожна VM думає, що має свої CPU, RAM і диск, і запускає повноцінну гостьову OS поверх. Тому одне вимірюється у мегабайтах і мілісекундах, а інше у сотнях мегабайт і секундах.

Таблиця порівняння

АспектContainerVirtual Machine
Примітив ізоляціїLinux namespaces + cgroupsHypervisor (KVM, VMware, Hyper-V)
Гостьова OSНемає, поділяє kernel хостаПовна гостьова OS зі своїм kernel
Час стартуМілісекундиСекунди (після того, як image локальний)
Оверхед пам'яті~10 MB baseline на containerСотні MB на guest OS + застосунки
Щільність (128 GB host)100-200 інстансів10-15 інстансів
Сила ізоляціїProcess-level, слабшаHardware-level, сильніша
Розмір imageДесятки MB до кількох GBКілька GB мінімум (повна OS)
Інша OS, ніж у хостаНі (поділяє Linux kernel)Так (Windows guest на Linux host тощо)
Радіус ураження при збоїKernel CVE впливає на всі containerОдна VM може впасти, інші продовжують
Типовий час життяСекунди-дні (замінні)Місяці (довгоживучі сервери)
Найкраще дляMicroservices, CI/CD, швидкі деплоїMulti-tenant clouds, інша OS, legacy

Коли container правильний вибір

  • Ти шипиш багато малих сервісів, що поділяють однакову Linux-основу. Process-level ізоляція достатня, hardware-level зайва.
  • Частота деплоїв висока: PR збирає image, CI запускає проти нього, прод замінює. Швидкий старт container тримає цикл швидким.
  • Потрібен паритет середовища від лептопа до проду, і ти контролюєш kernel хоста.
  • Щільність важлива: впихнути 100 сервісів на одну машину, масштабуватися горизонтально.

Коли VM все ще правильний вибір

  • Multi-tenant інфраструктура, де ти не довіряєш робочим навантаженням. Kernel-експлойт у container це tenant-crossing проникнення; у VM воно стримане.
  • Інша OS, ніж у хоста. Windows на Linux-хості потребує VM. Container так не вміє.
  • Жорсткі compliance-режими (PCI DSS, частина healthcare), що вимагають hardware-level ізоляції.
  • Legacy-софт, написаний під повну машину: він очікує systemd, справжній init, kernel-модулі, raw block-пристрої.

На практиці більшість продакшен-сетапів використовують обидва. AWS EC2 і подібні хмари знизу крутяться на VM; застосунки всередині цих VM запускаються як container на Kubernetes. VM дає cloud-провайдеру tenant ізоляцію, container дає команді щільність і швидкість.

Як ізоляція реально працює

Для container Linux kernel тримає окремі namespaces на процес. PID namespace змушує container думати, що process 1 це його власний init; mount namespace дає йому свою в'юху файлової системи; network namespace дає приватні інтерфейси. Cgroups обліковують і лімітують CPU, пам'ять, I/O. Все це enforce'ить один kernel: твій.

Для VM hypervisor сидить між залізом і гостем. KVM (Linux), VMware ESXi або Hyper-V віртуалізують CPU, пам'ять, пристрої. Гостьова OS бутиться нормально, ніби на справжньому залізі, з власним kernel, init, драйверами. Дві VM на одному фізичному хості не бачать пам'яті одна одної, бо hypervisor enforce'ить hardware-level межі.

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

Типові помилки

«Container це легка VM»

Ні. Container це процес хоста з додатковими обмеженнями. Немає guest kernel, немає init у традиційному сенсі, немає віртуалізованого заліза. Ставлення до container як до маленької VM (запуск кількох сервісів через systemd всередині одного container, наприклад) суперечить дизайну і створює проблеми з логуванням, сигналами і заміною.

Брати VM для ефемерних навантажень

bash
# НЕПРАВИЛЬНО: піднімати VM під кожен CI-білд # (5-10 хвилин на boot втрачено в кожному пайплайні) # ПРАВИЛЬНО: container для короткоживучих навантажень docker run --rm node:22 npm test # Готово за секунди, нічого не залишилось.

VM створювалися для довгоживучих серверів. Використання одної на 30-секундний тестовий ран витрачає більшу частину її життя на boot.

Довіряти container-ізоляції для multi-tenant навантажень

Якщо ти запускаєш недовірений код від різних клієнтів на одному хості, тільки container не вистачить. Постав VM-межу між tenant'ами (gVisor і Firecracker це проміжні варіанти, що додають тонкий VM-шар саме для цього). Звичайний Docker на shared host, де один tenant поряд з іншим, це один kernel CVE від проникнення.

Плутати розмір image з оверхедом

Docker image на 1 GB не з'їдає 1 GB RAM. Шари поділяються на диску і в пам'яті між container від того самого image. Три container з того самого image на 1 GB все ще використовують одну копію шарів. З VM так не виходить: кожна отримує свій disk image, свою пам'ять.

Реальне застосування

  • AWS EC2: інстанс, що ти орендуєш, це VM (Xen або Nitro hypervisor). Container-платформи поверх (ECS, EKS) кладуть твої container всередину цих VM. Два шари ізоляції: VM між tenant'ами, container між застосунками.
  • Firecracker (AWS Lambda, Fargate): microVM, заточений під container-подібний старт. ~125 мс на boot, ~5 MB оверхеду. Дає Lambda-функціям VM-level ізоляцію без VM-level затримки.
  • Google Cloud Run: container під капотом, але кожен запит отримує sandbox-середовище. Той самий патерн: швидкість container, посилена ізоляція.
  • Локальна розробка: container всюди, VM майже ніколи. Docker Desktop на Mac насправді крутить маленьку Linux VM за лаштунками, бо container'ам потрібен Linux kernel, але ти цього не бачиш.

Питання для поглиблення

Q: Якщо container слабші на ізоляції, чому хтось взагалі використовує їх у multi-tenant сетапах?


A: Більшість і не використовують, принаймні не наївно. Публічні хмари кладуть container всередину per-tenant VM (або microVM типу Firecracker), щоб поєднати швидкість container з VM-level межами. Всередині одного tenant, де ти довіряєш своїм навантаженням, звичайні container нормально.

Q: Що таке microVM і як він заходить між container і повною VM?


A: microVM це урізана VM, оптимізована під швидкий boot і мінімальний оверхед, типово 100-200 мс старту і кілька MB RAM. Firecracker (AWS) і Cloud Hypervisor добре відомі приклади. Кейс саме у проміжку між container і VM: коли треба сильніша ізоляція ніж у container, але час boot повної VM неприйнятний.

Q: Чи можна запускати container на Windows або Mac, якщо обом потрібен Linux kernel?


A: Так, але прихована Linux VM робить роботу. Docker Desktop на macOS використовує крихітну Linux VM через macOS virtualization framework. Windows може крутити Linux container через WSL 2 (теж Linux VM) або Windows-native container (що використовують Windows kernel features замість Linux).

Q: Чому VM іноді здаються швидшими за container у бенчмарках?


A: Зазвичай для прикладних навантажень не швидші, але кілька кейсів існує. Якщо навантаження I/O-важке, і container використовує network-mounted volume, а VM локальний віртуальний диск, VM може виграти. Змінна тут шлях до сховища, а не примітив ізоляції.

Q: (Senior) Коли б ти обрав Kata Containers, gVisor або Firecracker замість стандартного runc?


A: Коли навантаження вимагає сильнішої ізоляції за namespaces+cgroups, але ти все ще хочеш container UX. gVisor перехоплює системні виклики у userspace (добре для зменшення радіусу ураження, повільніше для syscall-важких застосунків). Kata запускає кожен container у легкій VM (близько до нативної швидкості, повна kernel-ізоляція). Firecracker спеціально під serverless, використовується Lambda і Fargate. Рішення зазвичай зводиться до: недовірений код → microVM-class ізоляція; довірений код → стандартний runc.

Приклади

Те саме навантаження у двох світах

bash
# Container шлях: легко і швидко $ docker run -d --name redis-c -p 6379:6379 redis:7 # Стартонув за ~1 секунду. Пам'ять: ~15 MB. # VM шлях: повний гість, повний boot $ vagrant init ubuntu/jammy64 $ vagrant up $ vagrant ssh -c "sudo apt-get install redis && sudo systemctl start redis" # Сумарно: 60+ секунд, сотні MB на гостьову OS

Той самий Redis, той самий порт, той самий результат. Container поділяє твій kernel і перевикористовує базові шари. VM привозить свій kernel, init, package manager і idle-демони ще до того, як redis запуститься.

Шарова архітектура: VM + container

+-------------------------------+ | Фізичний сервер (128 GB RAM) | | | | +-----------+ +-----------+ | | | VM A | | VM B | | <- KVM hypervisor ізолює tenant | | (Tenant 1)| | (Tenant 2)| | | | | | | | | | +-------+ | | +-------+ | | | | |Cont. 1| | | |Cont. 1| | | <- Docker ізолює застосунки в tenant | | |Cont. 2| | | |Cont. 2| | | | | +-------+ | | +-------+ | | | +-----------+ +-----------+ | +-------------------------------+

Так виглядає більшість публічних cloud-Kubernetes сетапів. Hypervisor enforce'ить tenant-межу; Docker (або containerd) enforce'ить app-межу. Ти отримуєш VM-level ізоляцію між клієнтами і container-level щільність всередині слайсу кожного клієнта.

Як обрати на співбесіді

Відповідь, яку хочуть почути, рідко це «завжди container» або «завжди VM». Це decision rule:

  • Довірені навантаження на спільній інфраструктурі → container
  • Недовірені навантаження (публічний PaaS, multi-tenant clouds) → VM або microVM-межа між tenant, container всередині
  • Інша OS, ніж у хоста → VM
  • Legacy-софт, що очікує реальну машину → VM
  • Все інше зі спільною Linux-основою → container

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів