
Привет, Хабр! Меня зовут Александр, я работаю DevOps-инженером в команде Мир Plat.Form. Одна из достаточно сложных задач, которая стояла перед отделом DevOps, это миграция Jenkins в K8S и развертывание динамических (эфемерных) агентов.
Сама миграция Jenkins оказалась относительно простой частью. Но поскольку для сборки и деплоя наших приложений мы активно используем подход DinD (Docker-in-Docker) при переносе Jenkins-агентов в Kubernetes нам пришлось учитывать ряд технических особенностей.
Для запуска Docker внутри Kubernetes мы решили использовать Docker в режиме Rootless. Именно этот подход и добавил миграции немало технических нюансов, о которых я расскажу дальше.
Коротко о Rootless-режиме
Так как Rootless обеспечивает более высокий уровень безопасности в отличие от классического Docker, который работает от имени суперпользователя, контейнеры в классическом режиме получают практически неограниченный доступ к ресурсам хостовой ОС. Любая уязвимость в коде Docker, ошибка конфигурации или компрометация одного контейнера может привести к полному захвату контроля над хост-машиной. Злоумышленник, получивший доступ к сокету Docker (/var/run/docker.sock), автоматически получает возможность выполнять произвольные команды на уровне всей системы, создавать новые контейнеры с привилегированным доступом и манипулировать сетевыми настройками.
Rootless-режим переносит весь рабочий процесс из привилегированного пространства в пользовательское. Речь идет не о запуске отдельных контейнеров с опцией --user для ограничения прав процессов внутри них, а о полном отказе от привилегий на уровне всего Docker-демона. В Rootless-режиме весь стек Docker — включая демон, рантаймы и управляющие утилиты — работает от имени пользователя без прав root.
Важный момент, Docker Rootless может работать в двух форматах:
запускаться как systemd юнит (через запуск скрипта «/usr/bin/dockerd-rootless-setuptool.sh install», который и настроит docker в нужном режиме);
запускаться через скрипт «/usr/bin/dockerd-rootless.sh».
В чем принципиальные отличия? Запуск с systemd корректно формирует окружение внутри POD, контейнера или ОС, например создаются необходимые cgroups и т.д. В втором случае этого не происходит и, например, Jenkins плагин «withDockerContainer» не отрабатывает, так как плагин выполняет команду «docker top» и не может получить данные контейнера, что приводит к падению пайплайна.
С тем, что такое Rootless-режим, разобрались. Теперь перейдем к тому, как все это работает.
Механизмы пространств имён (User Namespaces или UserNS)
В основе Rootless-режима лежат два ключевых механизма пространств имён Linux. Пользовательские пространства имён (user namespaces) обеспечивают маппинг идентификаторов пользователей и групп между хост-системой и контейнером. Это позволяет непривилегированному пользователю на хосте (например, с UID 1000) внутри контейнера иметь права root (UID 0) без реальных привилегий на основной системе. Маппинг задаётся через файлы /etc/subuid и /etc/subgid, где для каждого пользователя выделяется диапазон доступных ID, например:
user1:100000:65536 user2:165536:65536
Маппинг UID 1000 в псевдо-корневой UID 0 в UserNS.
$ whoami user1 id -u 1000 $ unshare --user --map-root-user cat /proc/self/uid_map 0 1000 1 cat /proc/self/gid_map 0 1000 1 id -u 0
Network namespace предоставляет изоляцию сетевого стека, но в Rootless-режиме реализация отличается от традиционной. Поскольку непривилегированный пользователь не может создавать стандартные сетевые интерфейсы (veth, bridge), используется пользовательский сетевой стек. Для этого обычно применяют slirp4netns — утилиту, создающую пользовательский сетевой стек и эмулирующую TCP/IP-функциональность без требований к привилегиям.
Как запустить Rootless Docker
В Rootless-архитектуре демон работает как обычный пользовательский процесс без привилегий. Сетевые операции перенаправляются через slirp4netns, работа с файловыми системами обеспечивается с помощью fuse-overlayfs, а управление маппингом UID/GID — через утилиты newuidmap/newgidmap.
Установка Rootless Docker
Устанавливаем пакет docker-ce-rootless-extras, в который входит скрипт для настройки и зависимости:
sudo apt install docker-ce-rootless-extras
И далее, после установки пакета, запускаем скрипт:
dockerd-rootless-setuptool.sh install
Скрипт сделает следующие действия:
проверит наличие настроенных подчинённых UID/GID в /etc/subuid и /etc/subgid;
создаст необходимые директории конфигурации в ~/.local/share/docker;
настроит и запустит пользовательский сервис systemd для управления демоном.
Настройка переменных окружения
Для корректной работы в Rootless-режиме нужно настроить переменные окружения. Нужно добавить следующие строки в ~/.bashrc или ~/.profile:
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock export PATH=/home/$(whoami)/bin:$PATH
Это настроит DOCKER_HOST для подключения к пользовательскому демону и добавит пути к бинарным файлам Rootless Docker в PATH.
Примените изменения:
source ~/.bashrc
Проверка установки
Убедитесь, что установка прошла успешно, проверив статус демона и конфигурацию:
systemctl --user status docker.service docker info | grep Rootless docker version
Вывод команды docker info | grep Rootless должен содержать Rootless: true.
Дополнительная настройка
Для автоматического запуска демона при входе в систему выполните:
systemctl --user enable docker
Убедитесь, что задержка (linger) включена для вашего пользователя:
sudo loginctl enable-linger $(whoami)
После этого Docker будет работать в полностью изолированном Rootless-режиме, обеспечивая повышенную безопасность без потери функциональности.
Заключение
Rootless-режим в Docker — это шаг вперёд в сторону безопасного контейнерного окружения. Он устраняет главный риск классической модели — работу демона с правами суперпользователя, что позволяет запускать контейнеры без угрозы для всей системы.
Да, Rootless имеет ограничения по производительности, сетевым настройкам и совместимости, однако для большинства сценариев разработки и тестирования он остаётся практичным и надёжным решением. В продакшене такой режим может стать дополнительным уровнем защиты, а при необходимости — сочетаться с традиционным Docker и механизмами усиленной безопасности (AppArmor, SELinux).
В итоге Rootless-режим — это баланс между удобством, функциональностью и безопасностью, который стоит рассмотреть каждой команде, работающей с контейнерами.

