Docker, виртуализация и использование ядра ОС?

Меня ввело в заблуждение следующее утверждение: "При виртуализации (в вашем случае в Docker) используется ядро хостовой ОС".

Возникает резонный вопрос(ы): как в таком случае обстоит запуск контейнеров на хостовой ОС на базе ядра Windows NT? Запуск контейнеров в таком случае зависит от WSL (и эмулированного ядра Linux в таком случае)? В случае, например, запуска на хостовой ОС Linux будет использоваться её ядро?

Для контекста: я пытаюсь определить "границы" контейнеризации и эмуляции. Их отличия (более подробно в вопросе выше).


Ответы (1 шт):

Автор решения: Pak Uula

Коротко

как в таком случае обстоит запуск контейнеров на хостовой ОС на базе ядра Windows NT?

На ядре Windows запускаются контейнеры Windows.

Запуск контейнеров в таком случае зависит от WSL (и эмулированного ядра Linux в таком случае)?

В WSL запускаются контейнеры Linux.

В случае, например, запуска на хостовой ОС Linux будет использоваться её ядро?

В случае запуска линуксового контейнера на линуксе будет использоваться ядро хостовой машины.

Длинно

Докер не является ни технологией виртуализации, ни контейнеризации, ни эмуляции. Это посредник, который настраивает ядро линукса или виндовс для создания изолированного окружения и запускает в этом окружении заданные в описании контейнера процессы. Более того, он не единственный в своём классе. Как минимум есть podman для контейнеров в линуксе.

Всю виртуализацию, включая виртуальную сеть, реализует ядро ОС. В случае Линукс задействованы вот какие подсистемы:

  • cgroups - изоляция аппаратных ресурсов (CPU, память и т.д.)
  • namespaces - создание изолированных наборов идентификаторов (точки монтирования, идентификаторы процессов, идентификаторы пользователей, и т.д.)
  • union FS - объединение нескольких файловых систем "внахлёст": если файл отсутствует в "верхнем слое", ядро рекурсивно ищет в следующем слое.
  • iptables - создание транспорта между виртуальными и реальными сетевыми интерфейсами.

При запуске контейнера докер

  • создает ОС-контейнер (cgroups + namespace),
  • монтирует в созданный контейнер слои из образа,
  • создаёт в ОС-контейнере сетевое устройство и настраивает iptables,
  • запускает процессы в контейнере.

Как видно, всю работу выполняет ядро линукс, а докер только дирижирует.

Теперь про Windows.

Одно время (лет восемь назад) были надежды, что MS добавит в ядро Windows совместимые с Линуксом наборы интерфейсов контейнеризации. Но в Майкрософт пошли своим путём, и создали

  • интерфейсы контейнеризации для Windows: эти интерфейсы позволяют запускать контейнеры Windows
  • виртуальную машину для запуска в ней ядра линукса. Заодно весьма сильно перелопатив само ядро - лет десять назад люди из Майкрософт были самыми активными контрибюторами в ядро линукс, подпиливая и подтачивая ядро под свою виртуальную машину.

Насколько я понимаю, Docker Desktop пока единственный менеджер контейнеров, которому MS раскрыла подробности своих контейнерных интерфейсов. Я не знаю, как именно ядро Windows создаёт изолированные окружения. Но совершенно точно, что для запуска образов Windows нужен докер для Windows. При попытке скачать образ на Линуксе докер отвалится с ошибкой

$ docker pull mcr.microsoft.com/windows/nanoserver:ltsc2025
ltsc2025: Pulling from windows/nanoserver
no matching manifest for linux/amd64 in the manifest list entries

Докер в Windows выполняет образы Линукс в виртуальной машине:

PS> wsl --list
Windows Subsystem for Linux Distributions:
Ubuntu (Default)
docker-desktop

Видите виртуалку docker-desktop? Именно в ней Docker Engine выполняет контейнеры. Ядро там

PS> docker run --rm alpine uname -a
Linux caefb4cc2fda 5.15.167.4-microsoft-standard-WSL2 #1 SMP Tue Nov 5 00:21:55 UTC 2024 x86_64 Linux

Ядро Линукса в Windows - не эмулированное: https://github.com/microsoft/WSL2-Linux-Kernel От обычного ядра линкус оно отличается прежде всего тем, что из него выкинуты все аппаратные архитектуры, кроме x86-64, и все драйверы кроме тех, которые нужны для устройств MS-ной виртуальной машины.

Чтобы усложнить жизнь ещё больше, напомню, что есть докер для OSX. Он тоже использует ядро Linux в виртуалке LinuxKit.

Про эмуляцию

Как я понимаю "эмулированное ядро": это когда ядро ОС, не совместимое с Linux, выставляет наружу набор системных вызовов Linux. Например, приложение вызывает функцию write, библиотека glibc делает системный вызов syscall_write, ядро его перехватывает и разбирает его параметры в соответствии с Linux ABI, обрабатывает по-своему, а затем возвращает результаты опять-таки в соответствии с Linux ABI.

Именно так была реализована первая версия WSL. Там не было ядра Linux, а была прослойка в ядре Windows, которая обрабатывала линуксовые системные вызовы в ядре Windows. Но в конечном итоге в MS сдались поддерживать безумный зоопарк системных вызовов линукса и сделали отдельную виртуальную машину, в которой крутится полноценное ядро линукса со всеми его bells and whistles.

Теперь про виртуальные машины и эмуляцию. Под виртуальными машинами понимают создание окружения, которое для низкоуровневого кода, такого как загрузчик, ядро, драйверы, выглядит как "настоящая аппаратура".

В этом контексте эмуляторами обычно называют программы, которые выполняются в непривилегированном режиме и интерпретируют бинарный код. Пожалуй, самый известный пример - qemu. Эта программа просматривает бинарный код и интепретирует его средствами хостовой платформы. Есть версии qemu для запуска на платформе amd64 кодов для ARM, PowerPC, MIPS и не только. Есть даже qemu на amd64 который предназначен для запуска amd64 - этот эмулятор предпросматривает бинарный код и, если находит системные вызовы или прерывания, то заменяет их на вызовы служебных подпрограмм. Используется для разработки и отладки всяких встроенных системок.

Но для масштабных ОС, такой как Линукс, для виртуализации используются аппаратные средства. Такие системы виртуализации называются гипервизорами. Примеры: KVM, Hyper-V.

Гипервизоры выполняются на ещё более глубоком уровне привилегий, чем ядро ОС. В частности, они перехватывают прерывания и системные вызовы и принимают решение, в какой из доменов передать для обработки. WSL2 построена на гипервизоре Hyper-V. В отличие от эмулятора WSL2 не модифицирует исполняемый код. Когда в приложении происходит системный вызов, он действительно происходит в процессоре. Только обработчик вызывается не в ядре ОС, а в гипервизоре. Тот, исходя из каких-то внутренних соображений, догадывается, что системный вызов был в приложении Linux, и передаёт системный вызов в ядро Linux. Или понимает, что вызов был в приложении Windows, и передаёт на обработку в Windows.

WSL2 знает о существовании гипервизора Hyper-V, и при запуске обращается к гипервизору, чтобы сконфигурировать виртуальную машину и настроить обработку прерываний и системных вызовов.

Разумеется, в гипервизорных виртуальных машинах есть место эмуляции. Но в них эмулируется не центральный процессор, а переферия. Например, шина PCI, сетевые устройства и контроллеры накопителей / диски. CPU используется хостовый[1]


[1] с оговорками - в гипервизоре можно отключить выполнение в госте наборов машинных инструкций, таких как векторная арифметика, но для вашего вопроса это несущественно

→ Ссылка