← Docker
12 мин · intro · Урок 1 из 8

Зачем контейнеры

Чем контейнер отличается от 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 - свои частные пространства имён вместо общих с хостом. Основные:

Есть ещё 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обёртка над API dockerdдемон, хранит state containerdжизненный цикл runcOCI runtime, зовёт clone() Linux kernel: namespaces, cgroups, OverlayFS
Каждое звено делает свою часть работы, а реальную изоляцию создаёт нижний слой: ядро.

Это важно потому, что Docker — не единственный способ запускать контейнеры. Podman, containerd напрямую, kubelet в k8s всё равно в итоге упираются в runc и в те же ядерные механизмы.

Когда контейнер не подходит

Контейнер — не серебряная пуля. Случаи, когда лучше VM:

Для веба, бэкендов, баз, очередей, CI контейнер подходит в 99% случаев.

Что важно запомнить

Контейнер — это процесс на ядре хоста, которому ядро врёт через namespaces и которого ограничивает через cgroups.

VM — это полноценная ОС с собственным ядром, контейнер — нет. Эмуляция — это ещё и смена архитектуры CPU, отдельная медленная история.

Docker — не «технология контейнеризации». Это обёртка вокруг ядерных механизмов: namespaces + cgroups + OverlayFS. Замени Docker на Podman - ядро не заметит.

В следующем уроке запустим первый контейнер и разберём флаги docker run.

Как именно ядро строит эту изоляцию (namespaces, cgroups, capabilities), разобрано в треке «Linux: изоляция».

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

В чём разница между виртуализацией и контейнеризацией?

DOC-004
02

Что такое Docker и какие инструменты Linux лежат в основе? Для чего он используется?

DOC-001

Прошли материал до конца? Отметьте урок пройденным.