Ответ
Файловая система Docker основана на концепции Union File System (UFS), которая позволяет объединять несколько файловых систем (слоёв) в одну.
Каждый образ Docker состоит из набора слоёв, доступных только для чтения. При запуске контейнера поверх этих слоёв добавляется один записываемый слой.
Ключевые принципы:
- Слои только для чтения (Read-only layers): Основа любого образа. Каждый слой представляет собой набор изменений (например,
RUN apt-get install) и является неизменяемым. Это обеспечивает переиспользование слоёв между разными образами и экономит место. - Записываемый слой (Writable layer): Создаётся для каждого контейнера. Все изменения, такие как создание, изменение или удаление файлов, происходят именно в этом слое.
- Механизм Copy-on-Write (CoW): Если контейнеру нужно изменить файл из нижележащего read-only слоя, Docker копирует этот файл в записываемый слой и вносит изменения уже там. Оригинальный файл в слое образа остаётся нетронутым.
Пример слоёв в Dockerfile:
# 1. Базовый слой из образа python
FROM python:3.9-slim
# 2. Слой с установкой зависимостей
RUN pip install flask
# 3. Слой с копированием кода приложения
COPY ./app /app
# При запуске контейнера будет добавлен 4-й, записываемый слой
CMD ["python", "/app/main.py"]
Эта архитектура делает образы легковесными и ускоряет развёртывание контейнеров. Реализация UFS зависит от storage driver (например, overlay2, aufs).
Ответ 18+ 🔞
А, ну это же та самая магия, из-за которой контейнеры такие быстрые и лёгкие! Смотри, как это работает, без всякой академической хуйни.
Представь себе бутерброд, но не простой, а многослойный, и каждый слой — прозрачный. Это и есть Union File System, или, как её часто обзывают, UFS. Суть в том, что она склеивает кучу файловых систем в одну виртуальную, и контейнер видит её как монолитную, целую папку.
Вот как это устроено, на пальцах:
-
Основа — read-only слои (только для чтения). Каждая команда в твоём
Dockerfile— это новый слой. Поставилapt-get update— слой. Скопировал файлы — ещё слой. Эти слои, блядь, запекаются намертво, как клинковый нож, их не изменить. И это гениально, потому что если два образа используют один и тот же базовый слой (например,ubuntu:latest), то на диске он хранится в одном экземпляре. Экономия места — овердохуища! -
Верхушка — writable layer (пишущий слой). Когда ты из образа запускаешь контейнер, поверх всей этой стопки read-only блинов Docker накидывает один-единственный, свежий, пустой слой. И вот в нём-то и происходит вся движуха контейнера. Создал файл? Он тут. Удалил что-то? Запись об удалении — тут. Это личное пространство контейнера, и оно живёт ровно столько, сколько живёт сам контейнер.
-
Волшебный механизм — Copy-on-Write (CoW). А вот тут самый сок, ёпта! Допустим, твоему контейнеру захотелось изменить какой-нибудь системный файл, который лежит глубоко в read-only слое. Docker не будет трогать оригинал — он же для всех общий! Вместо этого он тихонечко, как мартышлюшка, копирует этот файл на самый верх, в свой личный пишущий слой, и меняет уже копию. Оригинал остаётся нетронутым. Отсюда и название — «копирование при записи». Гениально и просто, как хуй с горы.
Вот смотри на примере простого Dockerfile, чтобы стало совсем ясно:
# 1. Базовый слой из образа python. Толстый, read-only блин.
FROM python:3.9-slim
# 2. Поверх него — ещё один read-only блин: все установленные пакеты.
RUN pip install flask
# 3. И ещё один блин — твой код.
COPY ./app /app
# А когда контейнер запустится, сверху ляжет тонкий, но главный блин — writable layer.
CMD ["python", "/app/main.py"]
Именно из-за этой архитектуры docker run такой моментальный. Не надо разворачивать целую ОС — просто накладывается один лёгкий слой поверх уже готовой стопки. А реализуется вся эта кухня через storage driver (типа overlay2), но это уже детали, в которые можно не влезать, если не хочешь, чтобы твоя бошка думала. Главное — принцип понял.