Ядро Linux умеет разделять видимый мир для групп процессов так что каждый контейнер живет в собственной реальности. Пространства имен стали фундаментом этой магии. Docker собирает их вместе и создает ощущение что приложение работает на отдельном сервере хотя на самом деле все процессы делят одно ядро. Такая изоляция решает сразу несколько проблем. Конфликты портов исчезают. Файлы не пересекаются. Права не выходят за границы. Системы становятся безопаснее а развертывание проще. Многие разработчики заметили как после перехода на контейнеры жизнь стала заметно спокойнее.

Почему процессы требовали изоляции без тяжелых виртуальных машин

Раньше разделение приложений означало запуск полноценной виртуальной машины. Каждой требовалось собственное ядро память и диск. Это работало надежно но отнимало слишком много ресурсов. Серверы простаивали а управление превращалось в головную боль. Инженеры искали способ легче. Они хотели чтобы процессы на одном ядре видели разные версии одних и тех же ресурсов. Так родилась идея пространств имен.

Первое пространство появилось еще в две тысячи втором году. С тех пор их количество выросло до восьми. Ядро не копирует данные. Оно просто меняет указатели видимости. Процесс внутри пространства думает что он единственный на машине. На практике это похоже на отдельные комнаты в одном доме. Стены не дают увидеть что происходит за ними но сама конструкция остается общей.

Такой подход резко снизил накладные расходы. Контейнер стартует за секунды а не минуты. Ресурсы используются эффективнее. При этом уровень безопасности остается высоким. Один сервис больше не может случайно повлиять на другой.

Какие пространства имен скрывают разные части системы

Ядро предлагает четкий набор пространств. Каждое отвечает за свою область. Вместе они создают полный барьер.

Вот список основных типов которые чаще всего используются:

  • монтирование файловых систем
  • идентификаторы процессов
  • сетевой стек
  • имя хоста и домена
  • межпроцессное взаимодействие
  • идентификаторы пользователей и групп
  • иерархия контрольных групп
  • восприятие времени

Монтирование дает каждому контейнеру свой корень файловой системы. Процесс видит только свои каталоги и диски. Сеть создает отдельные интерфейсы IP адреса и таблицы маршрутизации. Два контейнера спокойно слушают порт восемьдесят без конфликта.

Идентификаторы процессов превращают контейнер в самостоятельный мир где первый процесс получает номер один. Он становится родителем для всех остальных. Если он завершается пространство очищается целиком. Имя хоста позволяет задать уникальное название. Приложения внутри уверены что работают на собственном сервере.

Пользовательские идентификаторы добавляют слой безопасности. Контейнер может работать от root внутри себя а на хосте этот пользователь отображается на непривилегированного. Права не просачиваются наружу. Контрольные группы скрывают реальную картину ресурсов. Процесс видит только свою ветку дерева.

Все пространства накладываются последовательно. Ядро следит чтобы видимость не пересекалась ни в одной точке.

Системные вызовы которые создают и управляют изоляцией

В основе лежат три ключевых вызова. Clone создает новый процесс и сразу указывает какие пространства сделать отдельными. Флаги CLONE_NEWPID CLONE_NEWNET или CLONE_NEWNS включают нужные виды.

Unshare отсоединяет текущий процесс от старых пространств и формирует свежие. Это удобно для экспериментов прямо в терминале. Setns позволяет присоединиться к уже существующему пространству по файловому дескриптору.

Ядро хранит связи в специальных структурах. Каждый процесс связан со своим набором пространств. Чтобы увидеть эту связь достаточно заглянуть в proc. Символьные ссылки показывают уникальные номера inode. Именно по ним ядро отличает одно пространство от другого.

Вот как быстро проверить пространства текущего процесса

Bash
 
ls -l /proc/$$/ns/
 
 

Каждая ссылка покажет тип и номер inode. Если два процесса ссылаются на одинаковый inode они видят один мир.

Нагрузка от таких операций минимальна. Ядро меняет только указатели в памяти. Производительность почти не падает.

Как Docker собирает пространства в готовый контейнер

При команде docker run runtime запускает runc. Тот выполняет точную последовательность. Сначала создается пространство монтирования. В него монтируется образ через overlayfs. Затем добавляется пространство процессов. Первый процесс получает идентификатор один.

Дальше настраивается сеть. Появляется виртуальный интерфейс и правила iptables. Пространство имени хоста получает свое значение. Если нужно включается пользовательское пространство. Все происходит за доли секунды.

Docker по умолчанию активирует пять основных пространств. Монтирование процессы сеть имя и взаимодействие. Остальные подключаются по флагам. Для максимальной изоляции добавляют пользовательское пространство. Тогда даже при прорыве внутри контейнера на хосте прав почти не будет.

Пример запуска с явным указанием пространств

Bash
 
docker run --rm -it --pid=host --userns=host nginx sh
 
 

Чтобы посмотреть пространства конкретного контейнера используйте

Bash
 
docker inspect -f '{{.State.Pid}}' mycontainer
 
 

Затем

Bash
 
sudo nsenter --target 12345 --net ip addr
 
 

Команда покажет только интерфейсы контейнера.

Практические команды для ручной проверки изоляции

Попробовать пространства вручную совсем не сложно. Команда unshare открывает дверь к экспериментам.

Bash
 
sudo unshare --pid --fork --mount-proc /bin/bash
 
 

Внутри новой оболочки команда ps покажет только процесс под номером один. Хост видит настоящий идентификатор. Чтобы выйти и очистить пространство достаточно завершить оболочку.

Для входа в чужое пространство подойдет nsenter. Сначала находим идентификатор процесса контейнера. Потом выполняем

Bash
 
sudo nsenter --target 12345 --mount --pid --net /bin/bash
 
 

Теперь внутри вы видите мир контейнера. Проверить монтирование можно через

Bash
 
findmnt
 
 

или

Bash
 
cat /proc/self/mountinfo
 
 

Еще один удобный инструмент lsns. Он выводит все пространства на сервере с типами и связанными процессами. Одна команда и сразу понятно кто с кем делит видимость.

Такие проверки помогают понять тонкости. Один пропущенный флаг и изоляция исчезает. Поэтому Docker тщательно проверяет настройки перед стартом каждого контейнера.

Что меняется в повседневной работе после внедрения пространств имен

С появлением изоляции администрирование становится заметно легче. Контейнеры можно размещать плотно друг к другу без страха что один заберет все порты или файлы. Приложения продолжают работать как на обычном сервере. Их код не требует изменений.

Разработчики получают предсказуемость. То что тестировалось локально ведет себя точно так же в продакшене. Конфликты версий библиотек уходят в прошлое благодаря отдельным монтированиям.

Администраторы могут делегировать отладку. Нужно посмотреть сеть контейнера достаточно войти в его пространство без риска затронуть остальную систему. Масштабирование происходит почти без усилий. Новый контейнер добавляется быстро и чисто.

Конечно пространства не дают абсолютной защиты от всех угроз. Их всегда сочетают с контрольными группами и другими механизмами. Но в комплексе они создают крепкий фундамент. Системы становятся гибче. Ресурсы используются рациональнее. Каждый новый сервис живет в своем четко очерченном мире.

В итоге пространства имен превратили Linux в платформу где сотни приложений спокойно уживаются на одном ядре. Docker просто умело пользуется этой возможностью. Он показывает как небольшие изменения в ядре могут полностью изменить подход к развертыванию. Технология не стоит на месте. Новые версии ядра добавляют нюансы и улучшения. Основная идея остается прежней. Четкие невидимые границы позволяют процессам работать вместе и при этом оставаться полностью отдельными.

Кто однажды настроил изоляцию вручную уже не возвращается к старым методам. Результат виден сразу. Системы работают чище быстрее и надежнее. А это именно то чего ждут от современной инфраструктуры.