Ответ
Docker использует механизм пространств имен Linux (namespaces), в частности пространство имен монтирования (mount namespace или CLONE_NEWNS), для изоляции файловой системы контейнера.
Механизм наследования:
- При запуске нового контейнера Docker (через
runc) вызывает системный вызовclone()илиunshare()с флагомCLONE_NEWNS. - Это создает новое, изолированное пространство имен монтирования для процесса контейнера.
- Изначально это новое пространство наследует копию таблицы монтирований (
mount tree) от родительского процесса (обычноdockerdилиcontainerd). - После создания пространства имен, Docker внутри него монтирует корневую файловую систему контейнера (образ) и другие необходимые файловые системы (
/proc,/sys,/dev).
Ключевое следствие: Изменения в дереве монтирований внутри контейнера (например, mount /dev/sdb1 /mnt) остаются видимыми только в его собственном пространстве имен и не влияют на хост или другие контейнеры.
Практический пример с bind mount:
Когда вы используете -v /host/data:/container/data, Docker создает bind mount, который отображает каталог хоста в пространство имен контейнера.
# На хосте
$ ls /host/data
file.txt
# Запуск контейнера с пробросом директории
$ docker run -it -v /host/data:/data alpine sh
# Внутри контейнера мы видим файл из хоста
/ # ls /data
file.txt
# Монтирование внутри контейнера НЕ видно на хосте
/ # mount -t tmpfs tmpfs /data
Проверка изоляции:
На хосте можно увидеть, что у каждого контейнера свой mount namespace:
# Находим PID основного процесса контейнера
$ docker inspect --format '{{.State.Pid}}' <container_id>
12345
# Смотрим его пространство имен монтирования
$ ls -la /proc/12345/ns/mnt
lrwxrwxrwx 1 root root 0 Apr 10 12:00 /proc/12345/ns/mnt -> 'mnt:[4026531841]'
# У каждого контейнера будет уникальный идентификатор в квадратных скобках.
Таким образом, наследование — это начальная копия, а последующая изоляция обеспечивает безопасность и предсказуемость работы контейнеров.
Ответ 18+ 🔞
Давай разберём эту тему, а то у некоторых создаётся впечатление, будто контейнеры — это магия какая-то, ебушки-воробушки. На самом деле, под капотом у Docker всё довольно прямолинейно, хоть и хитрая жопа.
Смотри, вся изоляция файловой системы в контейнере держится на механизме пространств имён Linux, а конкретно — на пространстве имён монтирования (mount namespace или CLONE_NEWNS). Это не какая-то своя разработка, это стандартная фича ядра.
Как это работает, по шагам:
- Когда ты запускаешь контейнер,
runc(или кто там у них сейчас) дёргает системный вызовclone()илиunshare()с волшебным флагомCLONE_NEWNS. - Бах — и для процесса контейнера создаётся новое, своё, отдельное пространство имён для монтирований.
- Вот тут важный момент: изначально это новое пространство наследует копию всей таблицы монтирований от родительского процесса (обычно это
dockerdилиcontainerd). То есть стартует не с чистого листа, а с той же картины, что и хост. - А дальше Docker внутри этого свежеиспечённого пространства начинает свою работу: монтирует корневую файловую систему из образа, подцепляет
/proc,/sys,/devи всё остальное, что нужно.
Ключевое следствие, которое многие не понимают: Любые изменения в дереве монтирований внутри контейнера — например, если ты там взбрело в голову смонтировать флешку (mount /dev/sdb1 /mnt) — остаются видны только внутри его собственного пространства имён. На хост-систему или в другие контейнеры это нихуя не просачивается. Изоляция, ёпта.
Практический пример, чтобы совсем понятно стало:
Допустим, ты пробрасываешь директорию с хоста внутрь контейнера через -v.
# На хосте у нас лежит файл
$ ls /host/data
file.txt
# Запускаем контейнер и пробрасываем эту папку
$ docker run -it -v /host/data:/data alpine sh
# Внутри контейнера мы этот файл видим — логично
/ # ls /data
file.txt
# А теперь сделаем что-то внутри контейнера, например, смонтируем tmpfs
/ # mount -t tmpfs tmpfs /data
И что ты думаешь? Это монтирование НЕ БУДЕТ ВИДНО на основном хосте. Оно живёт только в песочнице контейнера. Удивление пиздец, да?
Как проверить эту изоляцию на практике:
На хосте можно прямо посмотреть, что у каждого контейнера свой собственный mount namespace.
# Находим PID основного процесса в контейнере
$ docker inspect --format '{{.State.Pid}}' <container_id>
12345
# Смотрим ссылку на его пространство имён монтирования
$ ls -la /proc/12345/ns/mnt
lrwxrwxrwx 1 root root 0 Apr 10 12:00 /proc/12345/ns/mnt -> 'mnt:[4026531841]'
# Вот эти цифры в квадратных скобках — уникальный идентификатор пространства. У каждого контейнера — свой.
Так что наследование — это просто начальная точка, копия. А вся соль — в последующей изоляции. Благодаря этому контейнеры и не лезут друг другу в файловые системы, и доверия к ним больше, чем к скрипту, скачанному с подозрительного форума.