Какие технологии лежат в основе работы Docker-контейнеров?

Ответ

Docker использует несколько ключевых технологий ядра Linux для обеспечения изоляции, управления ресурсами и эффективного хранения данных контейнеров:

  1. Namespaces (Пространства имен): Обеспечивают изоляцию системных ресурсов. Каждый контейнер получает свой набор пространств имен, что создает иллюзию отдельной операционной системы. К ним относятся:

    • PID (Process ID): Изоляция процессов.
    • NET (Network): Изоляция сетевых интерфейсов, IP-адресов, портов.
    • MNT (Mount): Изоляция файловой системы.
    • UTS (Unix Time-sharing System): Изоляция hostname и доменного имени.
    • IPC (Interprocess Communication): Изоляция межпроцессного взаимодействия.
    • USER (User ID): Изоляция пользовательских ID.
  2. Cgroups (Control Groups): Позволяют ограничивать, учитывать и изолировать использование ресурсов (CPU, память, дисковый I/O, сетевой I/O) для групп процессов. Это гарантирует, что один контейнер не сможет монополизировать все ресурсы хоста.

  3. Union File Systems (Объединяющие файловые системы): Такие как OverlayFS или AUFS, позволяют создавать слоистые файловые системы. Образы Docker состоят из множества слоев, каждый из которых представляет собой изменение относительно предыдущего. Это обеспечивает:

    • Эффективность хранения: Общие слои используются несколькими контейнерами.
    • Иммутабельность образов: Базовые слои образа доступны только для чтения.
    • CoW (Copy-on-Write): Изменения в контейнере записываются в новый, записываемый слой, не затрагивая базовый образ.
  4. Container Runtime (runc и containerd):

    • runc: Низкоуровневая утилита, реализующая спецификацию OCI (Open Container Initiative) для запуска контейнеров. Она непосредственно взаимодействует с Namespaces и Cgroups для создания и запуска изолированных процессов.
    • containerd: Высокоуровневый демон, который управляет жизненным циклом контейнеров (создание, запуск, остановка, удаление), управляет образами и хранилищем. Docker Engine использует containerd для выполнения этих задач.

Пример использования unshare для изоляции:

# Запуск bash в новом PID и Mount namespace
sudo unshare --pid --mount --fork bash

# Внутри нового bash: виден только текущий процесс
ps aux

# Попытка смонтировать что-либо не повлияет на хост-систему
mkdir /tmp/test_mnt
sudo mount -t tmpfs tmpfs /tmp/test_mnt
ls /tmp/test_mnt

Docker Engine объединяет эти компоненты, предоставляя удобный API и CLI для управления контейнерами, образами, сетями и томами, абстрагируя пользователя от низкоуровневых деталей.

Ответ 18+ 🔞

А, ну вот, смотри, опять про эту вашу магию контейнеров. Все думают, что Docker — это такая волшебная коробочка, а на самом деле там под капотом старый добрый Линукс просто хуярит по полной, как Герасим в огороде, блядь.

Так, слушай сюда, а то я сейчас всё разжую. Докер, сука, просто взял родные фичи ядра и так их завернул, что получился контейнер. Никакой магии, просто умно, как жопа с ушами.

Вот смотри, главные три кита, на которых всё держится:

1. Namespaces (Пространства имён) Это типа как отдельные квартиры в общем доме. Каждому контейнеру дают свою квартиру, и он думает, что он тут один такой царь и бог. Ему свой набор всего подсовывают:

  • PID — свой список процессов. Он запустит top и увидит только себя любимого и пару системных демонов, а не все 300 процессов с хоста, ебать.
  • NET — своя сеть. Свой IP, свои порты. Может слушать на 80-м порту, и хосту похуй, у него свой 80-й.
  • MNT — своя файловая система. Монтирует себе что хочет, а снаружи никто не видит.
  • UTS — свой hostname. Может назваться super-puper-container, и все будут так его звать.
  • IPC — своя система общения между процессами. Чтобы контейнеры не лезли в чужие семафоры и очереди сообщений.
  • USER — свои пользователи. UID 0 внутри контейнера — это не root на хосте, а какой-то полупидор с ограничениями. Хитро, блядь!

2. Cgroups (Control Groups) А это чтоб жадные контейнеры не сожрали все ресурсы, как та собака Муму, которая всё ест. Ты же не хочешь, чтобы один твой говнокод сожрал всю память и CPU? Вот и я нет. Cgroups — это такой строгий папа, который говорит: «Тебе, сынок, 512 МБ RAM и одно ядро процессора. Больше — нихуя. Сиди и не выёбывайся». Ограничивает CPU, память, дисковый и сетевой ввод-вывод. Без этого был бы пиздец и бардак.

3. Union File Systems (Объединяющие файловые системы, типа OverlayFS) Вот это, блядь, гениально. Представь слоёный пирог. Образ — это рецепт пирога (базовый слой Ubuntu, потом слой с Python, потом слой с твоим кодом). Каждый слой только для чтения, immutable, ёпта! А когда ты запускаешь контейнер, сверху кладётся тонкий слой для записи (Copy-on-Write). Все изменения — только туда. Тысяча контейнеров могут использовать один базовый слой Ubuntu, экономя овердохуища места. Удалил контейнер — выкинул верхний слой. Красота!

4. Container Runtime (runc и containerd) А это уже рабочие лошадки, которые всю эту хуйню собирают в кучу.

  • runc — это тупой и сильный работяга, как Герасим. Ему дают спецификацию (OCI), а он тупо создаёт namespaces, настраивает cgroups и запускает процесс. «Муму!» — и контейнер побежал.
  • containerd — это уже бригадир. Он управляет образами, следит за жизнью контейнеров (родился, живёт, подох), общается с runc. А над ним уже стоит Docker Engine, который даёт тебе красивые команды docker run и прячет всю эту кухню.

Вот тебе пример, как это выглядит вживую, без всей этой докерной обёртки:

# Запускаем bash в новых PID и Mount namespace (как в отдельной квартирке)
sudo unshare --pid --mount --fork bash

# Заходим внутрь, смотрим процессы — видим почти пустоту, только себя
ps aux

# Пробуем смонтировать tmpfs — это будет только в нашей "квартире", хосту похуй
mkdir /tmp/test_mnt
sudo mount -t tmpfs tmpfs /tmp/test_mnt
ls /tmp/test_mnt

Итог: Docker Engine — это такой хитрожопый менеджер, который берёт containerd, тот дёргает runc, а тот уже использует namespaces, cgroups и OverlayFS, чтобы создать тебе эту иллюзию лёгкости и изоляции. Всё гениальное — просто, если знать, как Герасим Муму топи