Rootless Docker на Linux – режим, у якому dockerd і контейнери працюють без системного root. Головний сенс тут у межі пошкодження: злам демона або контейнера не дає повного контролю над хостом. Для Ubuntu і Debian це зручно на окремому вузлі, CI-агенті або dev-машині, поки стек не впирається в привілейовані порти, host network і криві права на томах.
Підхід добре лягає на single-user сервер, домашній вузол, CI-агент або окрему dev-машину, де контейнери не повинні торкатися низькорівневої мережі й привілейованих пристроїв. Якщо ж потрібні --privileged, host network, проброс низьких портів без обхідних схем або важкі томи з чужими UID/GID, частина зручності зникає дуже швидко.
Коли Rootless Docker на Linux справді має сенс
Rootless Docker на Linux корисний там, де головний ризик – надто широкий доступ демона Docker до системи. Найкращий сценарій – сервіси рівня застосунків: web, API, невеликі бази, cron-контейнери, dev-стенди. Для таких задач виграш від меншого привілею реальний, а втрати часто терпимі.
Найпростіший приклад – вузол, де кілька Compose-проєктів запускає один користувач, а доступ до docker.sock не варто прирівнювати до root. У звичайному Docker членство в групі docker по суті дає контроль над системою. Тут цей ризик нижчий, бо daemon живе в user space.
Добре це працює і на тимчасових або напівізольованих хостах: CI runner, лабораторний сервер, VM для збірок. Якщо контейнер зламають через web-панель або вразливий image, атака впирається в права звичайного користувача. Це слабше за VM-ізоляцію, але різниця зі звичайним Docker помітна.
Поганий сценарій – стек, що активно чіпає хост: reverse proxy на 80/443 без додаткового проксі, мережеві сервіси з macvlan, hardware passthrough, доступ до системних каталогів або файлові шари з кривими правами. Там rootless режим часто створює більше обходів, ніж користі.
Що змінюється в моделі доступу і чому це не те саме, що userns-remap
Rootless Docker на Linux запускає сам daemon без root, використовуючи user namespaces і допоміжні механізми на кшталт newuidmap та newgidmap. Це інша модель, ніж userns-remap, де daemon лишається root, а в namespace зсуваються тільки контейнери.
У режимі userns-remap головний процес Docker все ще має системні привілеї. Якщо проблема виникає в самому daemon або через API-сокет, межа захисту слабша. Rootless переносить нижче весь стек: і API, і runtime, і контейнери працюють від імені звичайного користувача.
Є й зворотний бік. Частину дій, які звичайний Docker робить напряму через ядро або мережевий стек хоста, цей режим виконує через користувацькі прокладки. Звідси інші обмеження на порти, cgroups і мережу. Документація Docker описує це на офіційній сторінці rootless mode, але межа між “працює” і “зручно підтримувати” стає помітною вже після переїзду Compose-стека.
userns-remap часто простіше для production-хоста з жорсткими вимогами до сумісності. Rootless краще лягає там, де важливіше прибрати root із самого Docker, навіть ціною частини функцій.
Як встановити Rootless Docker на Ubuntu або Debian
Для запуску потрібен встановлений Docker Engine, user namespaces і пакети uidmap та dbus-user-session. На Ubuntu або Debian цього зазвичай досить, якщо в користувача є діапазони subordinate UID/GID у /etc/subuid та /etc/subgid.
Які пакети і системні умови потрібні
Спершу варто перевірити наявність subordinate ID. Без них rootlesskit не підніме namespace, і установка зупиниться на старті. У типових системах користувач отримує 65536 UID і GID.
sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io uidmap dbus-user-session grep "^$USER:" /etc/subuid grep "^$USER:" /etc/subgid
Якщо рядків немає, їх треба додати вручну. Для користувача homelab це виглядає так:
echo 'homelab:100000:65536' | sudo tee -a /etc/subuid echo 'homelab:100000:65536' | sudo tee -a /etc/subgid
На хості з кількома такими користувачами діапазони не повинні перетинатися. Якщо вони збігаються, проблеми вилізають не відразу: спершу все стартує, а потім з’являються дивні помилки з правами у шарах image.
Запуск інсталятора rootless mode
Після цього режим ставиться штатним скриптом Docker для поточного користувача. Команду треба виконувати без sudo, інакше буде встановлено не той user service.
dockerd-rootless-setuptool.sh install
Інсталятор зазвичай підкаже змінні середовища. Якщо вони ще не прописані, треба додати шлях до user-бінарників і сокет Docker у профіль оболонки:
echo 'export PATH=/usr/bin:$PATH' >> ~/.bashrc echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock' >> ~/.bashrc source ~/.bashrc
На частині систем шлях до клієнта вже правильний, а потрібен лише DOCKER_HOST. Після цього команди docker ps і docker compose підуть у rootless daemon, а не в системний сокет /var/run/docker.sock.
Як увімкнути systemd user service і запускати контейнери після ребута
Щоб daemon стартував після ребута без ручного логіна, треба ввімкнути user service і linger для цього користувача. Без linger сервіс живе тільки поки є активна сесія, і після перезавантаження все стоїть до першого входу.
Увімкнення user service
Після інсталяції сервіс зазвичай називається docker.service, але це саме user unit, не системний. Керувати ним треба через systemctl --user.
systemctl --user enable docker.service systemctl --user start docker.service systemctl --user status docker.service
Якщо команда падає з помилкою про шину сесії, майже завжди бракує dbus-user-session або середовище зламане нетиповим shell-login.
Що робить loginctl enable-linger
Команда нижче дозволяє systemd тримати user manager після виходу користувача з сесії. Без цього автозапуск контейнерів після ребута не вийде.
sudo loginctl enable-linger $USER loginctl show-user $USER | grep Linger
Далі Compose-проєкти можна піднімати окремими user unit або звичайним docker compose up -d, якщо вони переживають рестарти через політику restart. Для складніших сценаріїв краще виносити стек у systemd unit, інакше діагностика після ребута стає незручною.
Що перевірити після запуску Rootless Docker
Перші дві перевірки прості: клієнт має ходити в сокет у /run/user/<uid>/docker.sock, а команда docker info повинна показувати rootless режим. Якщо хоча б одна з них не збігається, запити йдуть у звичайний daemon.
Швидка перевірка сокета і режиму
echo $DOCKER_HOST docker context ls docker info | grep -i rootless docker info | grep "Docker Root Dir"
У rootless режимі каталог даних зазвичай лежить у домашньому каталозі користувача, а не в /var/lib/docker. Якщо ж docker info показує системний root dir, клієнт підключився не туди.
Тестовий контейнер і доступ до порту
Після перевірки сокета варто запустити простий контейнер і переконатися, що публікація порту працює. Для швидкого тесту достатньо nginx:
docker run -d --name nginx-test -p 8080:80 nginx docker ps curl http://127.0.0.1:8080 docker rm -f nginx-test
Якщо сервіс відповідає на 8080, базова схема жива. На портах нижче 1024 все складніше, про це – далі. Для HTTP-стеків із проксі є сенс окремо подивитися матеріал про reverse proxy у Docker, бо саме там rootless обмеження вилазять найчастіше.
Які обмеження Rootless Docker ламають звичні Compose-стеки
Найчастіше ламаються мережа, права на bind mounts і привілейовані можливості контейнера. Прості web-сервіси проходять майже безболісно, а складний Compose-стек із host network, device passthrough або сирими томами часто доводиться переробляти.
Порти, host network і мережеві драйвери
Daemon не має прямого доступу до привілейованих портів. Публікація 80:80 або 443:443 зазвичай не запуститься без додаткових налаштувань хоста. Типовий обхід – зовнішній системний reverse proxy, який слухає 80/443 і прокидує трафік на високі порти контейнерів.
Host network у такому режимі не дає того самого ефекту, що у звичайному Docker, і часто не підходить як заміна bridge-схемі. З macvlan ситуація ще гірша: якщо потрібна окрема L2-присутність контейнера в мережі, краще лишатися на звичайному daemon. Деталі самих режимів корисно звірити з розбором мережевих режимів Docker.
Bind mounts і права на файли
Файли на хості бачаться контейнеру через map UID/GID, а не напряму як у rootful Docker. Через це bind mount каталогу з незручними правами може читатися, але не писатися, хоча зовні все виглядає логічно. Особливо часто це ламає каталоги на NFS, CIFS або старі дані, які створювалися під іншим UID.
Якщо в стеку є томи на мережевому сховищі, схему прав краще перевірити одразу. Для NFS окремо допоможе матеріал про проблеми прав у Docker на NFS, бо цей режим там загострює старі помилки з UID/GID.
Devices, cgroups і привілейовані контейнери
--privileged, доступ до частини device nodes і низка capability або не працюють, або втрачають сенс. Стек із GPU, VAAPI, USB-пристроями чи тюнерами часто впирається саме сюди. Для медіасерверів із прискоренням відео краще окремо оцінити вимоги до hardware transcoding у Docker ще до переходу.
Compose-файл, який роками жив у rootful Docker, може зламатися навіть без екзотики. Хороший маркер ризику – велика кількість devices, network_mode: host, кастомних sysctl і host path томів. Якщо ще й сам стек громіздкий, як у сценарії зі складним Docker Compose, міграцію краще робити сервіс за сервісом, а не одним перемикачем.
Де Rootless Docker зазвичай спотикається: порти, bind mounts, cgroups і мережа
Типові збої виглядають так: контейнер стартує, але порт не слухається; том монтується, але запису немає; після ребута daemon мертвий; docker stats або ліміти ресурсів поводяться дивно. Майже завжди проблема не в Compose, а в межах user-space виконання.
Чому не відкривається порт 80 або 443
Порти нижче 1024 вимагають привілею на хості. У цій схемі контейнер сам цього не має. Варіантів два: або піднімати сервіс на високому порту, або дати хосту окремий фронт через системний nginx, Caddy чи інший проксі.
Іноді радять змінити net.ipv4.ip_unprivileged_port_start. Це працює, але вже змінює системну політику для всіх процесів, а не тільки для Docker. Для одного-двох web-сервісів проксі перед контейнерами зазвичай чистіший варіант.
Звідки береться permission denied на bind mount
Якщо контейнер пише в bind mount під UID, який не збігається з map користувача, виникає permission denied. Тут мало дати chmod 777: на мережевих ФС або з ACL це часто не рятує. Треба дивитися фактичні owner UID/GID на хості і як вони мапляться всередині namespace.
id stat -c '%u:%g %n' /path/to/data docker run --rm -v /path/to/data:/data alpine sh -c 'id; ls -ld /data; touch /data/test'
Якщо тест падає, проблема в правах або в самій ФС. Overlay і локальні ext4/xfs переживають це краще, ніж NFS з root squash.
Що з cgroups і лімітами ресурсів
На сучасних системах із systemd та cgroup v2 rootless режим почувається значно краще, ніж на старих схемах. Але якщо хост старий або змішаний, CPU і memory limits можуть працювати не так, як очікується. Особливо це помітно після міграції старих Compose-файлів, де ліміти писалися про запас і ніколи не перевірялися.
Для діагностики достатньо подивитися docker info, лог user service і стан сервісу.
docker info systemctl --user status docker.service journalctl --user -u docker.service -n 100 --no-pager
Чому після ребута все не піднялося
Якщо контейнери не стартують після перезавантаження, спершу треба перевірити linger і user service. Найчастіша причина банальна: сервіс був увімкнений, але user manager не запускається без логіна.
loginctl show-user $USER | grep Linger systemctl --user is-enabled docker.service systemctl --user is-active docker.service
Друга типова причина – змінна DOCKER_HOST прописана в інтерактивному shell, але не в середовищі, з якого виконуються скрипти або unit. Тоді частина команд іде в rootless daemon, а частина намагається говорити з системним сокетом.
Коли краще залишитися на звичайному Docker, а коли перейти на Podman
Звичайний Docker варто залишити, якщо хост обслуговує мережево-чутливі або апаратно-залежні сервіси. Якщо ж потрібен безrootовий підхід, але без прив’язки до daemon-моделі Docker, тоді є сенс дивитися в бік Podman. Вибір тут впирається в сумісність стека, а не в ідеологію.
| Сценарій | Що вибрати | Чому |
|---|---|---|
| Звичайний web/API стек на високих портах | Rootless Docker | Менше привілеїв, сумісність із звичним CLI і Compose |
| Reverse proxy на 80/443, host network, macvlan | Звичайний Docker | Менше обходів у мережі |
| GPU, USB, media devices, privileged workload | Звичайний Docker | Краще працює з devices і capability |
| Daemonless workflow, rootless як базова модель | Podman | Інша архітектура, часто природніше для таких сценаріїв |
Podman не вирішує всі обмеження ядра і user namespaces. Але там rootless модель закладена глибше, тому для частини workstation і CI-задач він відчувається природніше. Якщо весь стек зав’язаний на Docker Compose, Docker socket, сторонні інструменти і звичні шаблони деплою, перехід на Podman може створити новий пласт сумісності.
На окремому вузлі з web-сервісами та високими портами Rootless Docker на Linux закриває задачу добре. На універсальний контейнерний хост його краще не натягувати силоміць – мережа, томи і devices швидко покажуть межі цього підходу.
Коментарі
Коментарів ще немає. Обговорення - у Telegram.