Зачем контейнеры
Чем контейнер отличается от VM и эмуляции. namespaces и cgroups коротко, и что под капотом запускает Docker.
Какую проблему вообще решают контейнеры
Старый сценарий: «работает у меня, не работает у тебя». Один разработчик ставит зависимости в одну версию, другой — в другую, прод вообще на старой Ubuntu. Тащить всё это вручную — больно, неконсистентно, ломается.
Контейнер — это упакованное приложение вместе с его файловой системой, зависимостями и точкой входа. На любой Linux-машине этот пакет разворачивается одинаково. Прод и dev перестают расходиться.
Под капотом контейнер — обычный процесс ядра Linux с отдельными namespaces и лимитами cgroups. Никакой отдельной технологии: ядро просто изолировало процесс от остальной системы.
VM, контейнер и эмуляция
Три похожих, но разных слова. Путать их — дорого.
| Что | Что запускается | Своё ядро | Накладные расходы | Архитектура CPU |
|---|---|---|---|---|
| VM (KVM, VMware) | полноценная гостевая ОС | да | гигабайты, секунды старта | та же, что у хоста |
| Контейнер | процесс на ядре хоста | нет | мегабайты, миллисекунды | та же, что у хоста |
| Эмуляция (QEMU) | гостевая ОС другой архитектуры | да | очень дорого по CPU | можно другую (arm на x86) |
Главное отличие контейнера от VM: у контейнера нет своего ядра. Когда ты запускаешь Postgres в контейнере на Linux-хосте, postgres — это обычный процесс, который видит хост-ядро. Просто ядро врёт ему, что кроме него никого больше нет.
Эмуляция — это вообще отдельная история. QEMU умеет эмулировать чужую архитектуру (например, ARM на x86), но платит за это огромными накладными расходами на каждом такте. Поэтому, когда docker на Mac тащит amd64-образ, под капотом включается эмуляция (QEMU, а на Apple Silicon Docker Desktop умеет Rosetta), и всё работает медленнее нативного. Насколько медленнее, зависит от хоста и версии.
Namespaces: что именно изолируется
Когда ядро запускает «контейнер», оно создаёт процессу новые namespace - свои частные пространства имён вместо общих с хостом. Основные:
- PID — свой счётчик процессов. Внутри контейнера твой процесс — это pid 1, как будто он init. Снаружи (на хосте) у него обычный pid где-нибудь 14000.
- NET — свой сетевой стек: интерфейсы, IP, таблица маршрутизации,
правила iptables. Контейнер видит только свой
eth0, а не хостовой. - MNT — своя файловая система. Корень контейнера — это не корень хоста, а слепленный из слоёв образа rootfs (см. урок про образы).
- UTS — свой hostname и domain name. Поэтому
hostnameвнутри контейнера показывает короткий ID, а не имя хост-машины. - IPC — своя межпроцессная коммуникация (shared memory, очереди).
Есть ещё USER (свой mapping uid/gid) и cgroup namespace, но на старте
их можно опустить.
Все эти namespace — это флаги системного вызова clone() ядра. То
есть «создать контейнер» в самом узком смысле = «вызвать clone с нужными
флагами». На Linux это можно сделать одной командой unshare, без всякого
Docker.
cgroups: лимиты по ресурсам
Namespace изолируют то, что процесс видит. cgroups (control groups) ограничивают то, что он может взять: CPU, RAM, IO, сеть.
Когда ты запускаешь docker run --memory=512m --cpus=1, Docker под капотом
создаёт cgroup и кладёт в неё процесс контейнера. Если процесс попытается
взять больше памяти, OOM-killer убьёт его, а не других соседей.
Без cgroups один шумный контейнер мог бы съесть всё на хосте. С cgroups это локальная проблема, а не системная.
Слои образа и copy-on-write
Образ контейнера состоит из read-only слоёв. Каждая инструкция в
Dockerfile (COPY, RUN, …) добавляет новый слой — архив с дельтой
файловой системы.
Когда ты запускаешь контейнер, поверх стопки read-only слоёв ложится один writable-слой. Все изменения, которые делает процесс внутри контейнера, пишутся туда. Базовые слои не меняются: их шарят все контейнеры, запущенные из этого образа.
Это и называется copy-on-write: пока никто файл не трогал, он лежит в базовом слое. Как только кто-то его меняет, копия уезжает в верхний слой, и дальше изменения идут уже там.
Подробно разберём в уроке про образы. Сейчас важно понять: образ - неизменяемый, контейнер — изменяемый поверх него.
Что вообще запускает контейнер
Когда ты пишешь docker run, под капотом происходит цепочка вызовов.
Каждый слой делает свою маленькую работу:
docker(CLI) — тонкая обёртка, шлёт http-запрос вdockerd.dockerd— демон, хранит список образов и контейнеров, разговаривает с registry, дёргаетcontainerd.containerd— управляет жизненным циклом контейнеров: pull образа, start/stop/kill, ловит выход.runc— OCI-runtime. Берёт rootfs иconfig.jsonи зовётclone()ядра с нужными namespace-флагами.- Ядро — всё, что выше, для реальной изоляции не нужно. Без них
тоже можно запустить контейнер вручную, через
unshare. Просто неудобно.
Это важно потому, что Docker — не единственный способ запускать
контейнеры. Podman, containerd напрямую, kubelet в k8s всё равно в
итоге упираются в runc и в те же ядерные механизмы.
Когда контейнер не подходит
Контейнер — не серебряная пуля. Случаи, когда лучше VM:
- Нужно другое ядро. Windows-приложение не запустишь в Linux-контейнере на Linux-хосте. Только в Windows-контейнере на Windows-хосте, или вообще в VM.
- Жёсткая изоляция. Контейнеры делят ядро. Если приложение враждебное (выполняет чужой код, multi-tenant SaaS), дырка в ядре == побег из контейнера. VM-граница толще.
- Древний софт, который ставится только в полную ОС. Иногда проще завести VM, чем переупаковывать в Dockerfile.
Для веба, бэкендов, баз, очередей, CI контейнер подходит в 99% случаев.
Что важно запомнить
Контейнер — это процесс на ядре хоста, которому ядро врёт через namespaces и которого ограничивает через cgroups.
VM — это полноценная ОС с собственным ядром, контейнер — нет. Эмуляция — это ещё и смена архитектуры CPU, отдельная медленная история.
Docker — не «технология контейнеризации». Это обёртка вокруг ядерных механизмов: namespaces + cgroups + OverlayFS. Замени Docker на Podman - ядро не заметит.
В следующем уроке запустим первый контейнер и разберём флаги
docker run.
Как именно ядро строит эту изоляцию (namespaces, cgroups, capabilities), разобрано в треке «Linux: изоляция».
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
В чём разница между виртуализацией и контейнеризацией?
DOC-004Что такое Docker и какие инструменты Linux лежат в основе? Для чего он используется?
DOC-001Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс