Образ, контейнер и чем это не виртуальная машина
Вопрос, с которого начинается блок про контейнеры почти на каждом собеседовании.
Вопрос: «Чем образ отличается от контейнера? И чем контейнер отличается от виртуальной машины?»
Что на самом деле проверяет интервьюер
Первая часть — терминологическая гигиена: человек, который говорит «залил контейнер в registry», почти наверняка использовал Docker поверхностно. Вторая часть — понимание, что контейнер это не маленькая виртуалка, а обычный процесс хоста с ограничениями. Из этого понимания вытекает всё остальное: почему контейнеры стартуют за миллисекунды, почему нельзя запустить контейнер с другим ядром и почему изоляция контейнера слабее, чем у ВМ.
Развёрнутый ответ: образ и контейнер
Образ — это неизменяемый (immutable) шаблон: набор слоёв файловой системы плюс метаданные (какую команду запускать, какие переменные окружения, какой рабочий каталог). Образ лежит в registry и одинаков у всех.
Контейнер — это запущенный экземпляр образа: те же read-only слои плюс тонкий writable-слой сверху, куда попадают все изменения, сделанные во время работы. Из одного образа можно поднять сто контейнеров, у каждого будет свой writable-слой, и все они будут переиспользовать общие read-only слои.
Аналогия, которая обычно нравится интервьюеру: образ — это класс, контейнер — экземпляр класса. Или: образ — установочный ISO, контейнер — работающая с него система.
docker images # список ОБРАЗОВ (шаблонов)
docker ps -a # список КОНТЕЙНЕРОВ (экземпляров)
docker run --name a -d nginx
docker run --name b -d nginx # два контейнера из одного образа
docker diff a # что изменилось в writable-слое контейнера a
Важное следствие: удалили контейнер — потеряли writable-слой вместе со всем, что в него записали. Это подводит к теме volume, разбираем её в третьем уроке раздела.
Развёрнутый ответ: контейнер против виртуальной машины
Виртуальная машина эмулирует железо: гипервизор даёт гостю виртуальные диск, сетевую карту и процессор, гость грузит собственное ядро и полноценную ОС. Контейнер ничего не эмулирует — это процесс, запущенный на ядре хоста, которому ядро подсунуло изолированное представление о мире.
Изоляция стоит на трёх механизмах ядра Linux, и назвать их — признак сильного кандидата:
- namespaces — что процесс видит. Отдельные пространства имён для PID (внутри контейнера ваш процесс — PID 1), для сети (свой сетевой стек и интерфейсы), для точек монтирования, для пользователей, для hostname.
- cgroups — сколько процесс может потребить: лимиты CPU, памяти, дискового ввода-вывода. На них же построены requests/limits в Kubernetes.
- union filesystem (overlay2) — как несколько read-only слоёв и один writable собираются в единое дерево файлов.
| Критерий | Контейнер | Виртуальная машина |
| Ядро | общее с хостом | собственное |
| Старт | миллисекунды | десятки секунд |
| Накладные расходы | почти нулевые | гостевая ОС целиком |
| Изоляция | на уровне ядра, слабее | аппаратная, сильнее |
| Разные ОС на одном хосте | нет (только то же ядро) | да |
Проверить общее ядро легко: команда ниже покажет одну и ту же версию на хосте и внутри контейнера, даже если контейнер собран из Alpine, а хост — Ubuntu.
uname -r
# 6.1.0-18-amd64
docker run --rm alpine uname -r
# 6.1.0-18-amd64 ← ядро то же самое
# а вот дистрибутив внутри действительно другой
docker run --rm alpine cat /etc/os-release | head -1
# NAME="Alpine Linux"
Практические следствия, которые стоит проговорить
- Нельзя запустить Windows-контейнер на Linux-хосте и наоборот: ядро одно. Docker Desktop на macOS и Windows поэтому втихую поднимает Linux-ВМ.
- Уязвимость ядра — общая для всех контейнеров. Container escape реален, поэтому для недоверенного кода берут gVisor, Kata Containers или обычные ВМ.
- Контейнер видит ядро хоста и его параметры, но не видит его процессы — благодаря PID namespace.
- Плотность. На одном сервере спокойно живут сотни контейнеров, потому что не нужно платить за сотни копий ОС.
Типичные ошибки кандидатов
- «Контейнер — это лёгкая виртуалка». Формулировка допустима как метафора для менеджера, но на техническом собеседовании её попросят уточнить, и без namespaces/cgroups ответ рассыпется.
- Путают образ и контейнер в речи: «пересобрал контейнер», «запушил контейнер».
- Считают, что контейнер изолирован так же надёжно, как ВМ — и не могут объяснить, почему в мультитенантных облаках всё равно используют виртуализацию.
- Не знают, что writable-слой умирает вместе с контейнером, и хранят в нём данные.
- Думают, что Alpine-контейнер «приносит своё ядро Linux». Он приносит только userspace: библиотеки и утилиты.
Как ответить кратко (20–30 секунд)
«Образ — неизменяемый шаблон из слоёв файловой системы и метаданных, он лежит в registry. Контейнер — запущенный экземпляр образа: те же слои плюс тонкий writable-слой, который умирает вместе с контейнером. От виртуалки контейнер отличается тем, что не эмулирует железо и не грузит своё ядро: это процесс на ядре хоста, которому namespaces ограничивают видимость, а cgroups — потребление ресурсов. Отсюда старт за миллисекунды и почти нулевой оверхед, но и более слабая изоляция, чем у ВМ.»