Контейнеризация - это метод виртуализации на уровне операционной системы, позволяющий запускать приложения в изолированных окружениях, которые называют контейнерами
В отличие от виртуальных машин, которые эмулируют аппаратное обеспечение, контейнеры используют ядро хостовой системы, что делает их значительно более легковесными и быстрыми в запуске
В операционной системе Linux контейнеризация возможна с помощью этих механизмов:
Пространства имён - это механизм ядра, который изолирует и виртуализирует глобальные системные ресурсы. Процессы внутри одного пространства имён видят свой собственный изолированный набор этих ресурсов и не могут влиять на процессы в других пространствах имён или видеть их
Linux поддерживает несколько типов пространств имён, каждый из которых изолирует определённый аспект системы:
root внутри контейнераСамый простой способ создать пространств имён - это использовать утилиту unshare. Она запускает программу в новых, изолированных пространствах имён. Например, для создания пространства UTS используется команда:
sudo unshare --uts /bin/bash
Далее командой hostname <новое имя хоста> можно задать другое имя. Для создания других пространств используются флаги --ipc, --mount, --net, --pid, --uts, --user, --cgroup, --time
Важно заметить, что для создания пространства PID нужно использовать флаг --fork, иначе просто изменится псевдофайловая система /proc
Контрольные группы позволяют ограничивать, приоритизировать и учитывать использование таких ресурсов, как центральный процессор, память, ввод/вывод диска и сеть
Существует две версии контрольных групп:
Начиная с Linux 5.10, вторая версия является предпочтительной, и многие современные дистрибутивы используют её по умолчанию
Все операции с контрольными группами второй версии производятся через псевдофайловую систему cgroup2, которая обычно смонтирована в /sys/fs/cgroup
Далее, чтобы создать контрольную группу, нужно:
Создать директорию для новой группы:
sudo mkdir /sys/fs/cgroup/my-group
Созданная директория наполнится нужными файлами. Группы могут выстраивать иерархию с наследованием ограничений
Настроить лимиты в нужных файлах
Запустить процесс и поместить его в группу - для этого нужно записать идентификатор процесса в файл cgroup.procs:
echo $APP_PID | sudo tee /sys/fs/cgroup/my-group/cgroup.procs
Всего контроллеров во второй версии контрольных групп девять:
cpu - контроллер для ограничения выделяемого процессорного времениcpuset - контроллер для привязки процессов к определенным ядра ЦПУfreezer - контроллер для временной остановки всех процессов в группеhugetlb - контроллер для ограничения лимита на использование больших страниц памяти ОЗУ (они бывают по 64 Кб (только для ARM), 2 Мб и 1 Гб)io - контроллер для ограничения доступа к блочным устройствам ввода/выводаmemory - контроллер для ограничения выделяемой оперативной памяти (в том числе раздела подкачки)perf_event - контроллер для сбора статистики потребляемых ресурсов группы процессовpids - контроллер для ограничения количества созданных в группе процессовrdma - контроллер для ограничения доступа ресурсов, связанных с RDMA (Remote Direct Memory Access), технологией доступа к памяти удаленного узлаСписок доступных контроллеров можно узнать в файле /sys/fs/cgroup/cgroup.controllers. Чтобы добавить контроллер, можно воспользоваться командой:
echo "+cpu" | sudo tee /sys/fs/cgroup/cgroup.subtree_control
Управлять списком контроллеров можно в каждой группе, выстраивая иерархию. Контроллер создает нужные файлы в каталоге группы, например:
cpu.max - верхнее ограничение процессорного временmemory.max - верхнее ограничение выделяемой памяти, memory.swap.max - верхнее ограничение для раздела подкачкиpids.max - ограничение на количество процессов в группеПо умолчанию контейнер, созданный с изолированным сетевым пространством имён, будет полностью отрезан от сети. Чтобы обеспечить связь, необходимо создать виртуальные сетевые интерфейсы
Для этого используют veth (Virtual Ethernet) - пару виртуальных сетевых интерфейсов, которые действуют как туннель
Обычно один конец пары veth1 помещается в сетевое пространство имён контейнера, а другой veth0 остаётся на хосте или подключается к сетевому мосту
Чтобы контейнер имел доступ к внешней сети, на хосте необходимо настроить трансляцию адресов - это позволит контейнеру выходить в интернет через IP-адрес хоста
Для изоляции файловой системы внутри используются два основных системных вызова:
chroot - он изменяет корневую директорию для текущего процесса. Процесс и его потомки будут видеть указанную директорию как /
Однако chroot не изменяет окружение полностью и не предотвращает выход из изолированной среды
pivot_root - более совершенный механизм, который перемещает корневую файловую систему текущего процесса в другую директорию, а на её место монтирует новую
Этот системный вызов предпочтительнее для контейнеров, так как он позволяет корректно размонтировать старую корневую файловую систему и обеспечивает более строгую изоляцию
Для оптимизации дискового пространства используют объединенную файловую систему - она позволяет создать иллюзию того, что содержимое нескольких каталогов объединено в один, не изменяя при этом исходные файлы в этих каталогах
Одна из первых реализаций была UnionFS. Сейчас используют OverlayFS, которая устроена через три слоя:
lowerdir - один или несколько каталогов, доступных только для чтения. Они составляют файлы, из которых состоит контейнерный образ.upperdir - один каталог, доступный для чтения и записи. Сюда записываются все изменения, которые контейнер вносит в процессе работы.merged - виртуальный каталог, который видит процесс в контейнере. Он представляет собой результат объединения нижнего и верхнего слояДля обеспечения атомарности операций используется 4-ый каталог workdir
Когда процесс читает файл из merged, OverlayFS сначала ищет его в upperdir. Если его там нет, то файл ищется в lowerdir. Все операции записи (создание, изменение, удаление) всегда происходят только в upperdir
Создать объединенную файловую систему можно с помощью команды mount:
sudo mount -t overlay overlay \
-o lowerdir=$HOME/overlayEX/lower,upperdir=$HOME/overlayEX/upper,workdir=$HOME/overlayEX/work \
$HOME/overlayEX/merged
Сейчас на практике используют Docker - платформу контейнеризации, которая упрощает работу с пространствами имён, контрольными группами и объединёнными файловыми системами