Віртуальна машина має власне ядро ОС, що працює поверх гіпервізора. Контейнер - звичайний процес хоста, який ділить ядро з хостом і з іншими контейнерами, але бачить обмежене «оточення».
Два головні механізми ядра Linux:
1. Простори імен (namespaces) - що процес бачить:
- PID - власне дерево процесів; процес контейнера має PID 1 всередині й не бачить процесів хоста;
- network - власні інтерфейси, адреси, таблиці маршрутизації, порти;
- mount - власна файлова система (образ + томи);
- UTS - власне ім'я хоста;
- IPC - власні черги повідомлень і спільна пам'ять;
- user (опційно) - відображення користувачів: root усередині може бути звичайним користувачем ззовні.
2. Контрольні групи (cgroups) - скільки ресурсів процес може використати: пам'ять, процесор, кількість процесів, ввід-вивід. Саме cgroups реалізують --memory, --cpus, --pids-limit.
Додаткові шари захисту: обмежені можливості (capabilities) root, профіль seccomp (заборонені системні виклики), AppArmor/SELinux.
Що з цього випливає:
- контейнери легкі: старт - мілісекунди, накладні витрати мінімальні, бо немає окремого ядра й гостьової ОС;
- ізоляція слабша, ніж у ВМ: вразливість у ядрі хоста потенційно доступна з будь-якого контейнера. Тому недовірений код (код користувачів, багатоорендні платформи) часто запускають у пісочницях з додатковою ізоляцією - gVisor, Kata Containers, Firecracker;
- ядро одне: Linux-контейнер не запуститься на ядрі Windows напряму. Docker Desktop на macOS і Windows запускає контейнери всередині легкої Linux-віртуальної машини;
- «контейнер - це межа безпеки» з обережністю: для довірених застосунків вона достатня, але не варто покладатися на неї як на єдиний захист.
Перевірити простори імен процесу: ls -l /proc/<pid>/ns на хості.