Установка Docker на Rocky Linux и AlmaLinux
Установите Docker Engine через dnf и настройте среду для работы в RHEL-системах. Решите конфликты с Podman и исправьте ошибки доступа SELinux при монтировании томов.
Установка Docker на Rocky Linux и AlmaLinux
Для установки Docker на Rocky Linux или AlmaLinux необходимо добавить собственный репозиторий Docker, установить движок вместе с плагином compose, а затем активировать службу. Эта процедура состоит из четырех команд и идентична для обоих дистрибутивов, так как оба являются пересборками Red Hat Enterprise Linux (RHEL) и используют общую структуру пакетов. CentOS Stream работает аналогичным образом. Все, что описано ниже, применимо к обоим дистрибутивам, поэтому если вы все еще выбираете между ними, решающими факторами станут обязательства по совместимости, которые берет на себя каждый проект, и наличие поддержки вашего старого процессора.
Установка занимает мало времени, поэтому большая часть этого руководства посвящена отличиям Enterprise Linux (EL) от Ubuntu. Возможно, команда docker в вашем образе уже занята Podman. SELinux блокирует файлы, смонтированные через bind-mount, пока им не будет присвоена правильная метка. Firewalld не фильтрует порты, опубликованные Docker, поэтому порт контейнера может быть открыт для доступа из Интернета, в то время как firewall-cmd будет сообщать, что порты закрыты.
Не используйте вспомогательный скрипт Docker с get.docker.com. В официальной документации Docker указано, что он не рекомендуется для использования в production. Скрипт без запроса перезаписывает конфигурацию ваших репозиториев, и его нельзя безопасно запустить повторно для обновления. Ручное добавление репозитория означает, что dnf upgrade будет обрабатывать Docker так же, как и любой другой пакет в системе. Это также позволяет использовать dnf-automatic, если вы настроили автоматическую установку обновлений безопасности по расписанию, поэтому заранее решите, хотите ли вы, чтобы Docker обновлялся автоматически или дожидался окна технического обслуживания. В любом случае, при обновлении заменяется бинарный файл пакета, в то время как старый dockerd продолжает работать, и needs-restarting — это команда, которая показывает, какие службы все еще используют код, который вы только что заменили.
Отвечает ли Podman на команду docker?
В Rocky Linux и AlmaLinux пакет Podman входит в стандартные репозитории, и многие образы VPS устанавливают его автоматически. Некоторые образы идут дальше и устанавливают podman-docker, который размещает скрипт оболочки по пути /usr/bin/docker, вызывающий Podman. В результате каждая введенная вами команда docker запускает Podman, поэтому руководство, написанное для Docker, начинает выдавать неожиданные результаты.
Первый признак — это баннер. Скрипт /usr/bin/docker проверяет наличие файла /etc/containers/nodocker, и если этот файл отсутствует, он выводит одну строку перед выполнением любых действий:
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.Кто-то мог создать этот файл, чтобы скрыть баннер, поэтому не полагайтесь только на него. Запросите у базы данных пакетов, какой пакет владеет этим бинарным файлом:
command -v docker
rpm -qf "$(command -v docker)"Ответ, начинающийся с podman-docker, означает, что на команды отвечает Podman. Ответ, начинающийся с docker-ce-cli, означает, что установлен настоящий Docker. Если rpm -qf сообщает, что ни один пакет не владеет файлом, значит, он был установлен вручную, и вам следует изучить скрипт, прежде чем доверять ему.
Podman запускает те же образы OCI и является разумным выбором. Если он вас устраивает, остановитесь на этом. Оба являются движками контейнеров Linux, поэтому, если выбор платформы еще открыт, полезно знать, что FreeBSD jails изолируют полноценное пользовательское окружение вместо запуска слоистых образов из реестра. Если вам нужен Docker Engine, сначала удалите конфликтующие пакеты. Вот список, который Docker рекомендует для RHEL:
sudo dnf remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runcОзнакомьтесь с тем, что именно dnf собирается удалить, прежде чем подтверждать операцию. На чистом образе VPS этот список будет коротким. На сервере, который уже использовался, удаление podman может привести к удалению cockpit-podman или другого инструмента, зависящего от него.
Совместное использование Podman и Docker в принципе возможно: удалите только podman-docker, чтобы освободить имя docker, и runc, который заменяет пакет containerd.io. Документация Docker рассматривает Podman как конфликтующий пакет, поэтому такая конфигурация не поддерживается Docker официально. Если при установке по-прежнему возникает конфликт, используйте полный список для удаления, приведенный выше.
Добавление репозитория Docker с помощью dnf config-manager
Docker публикует RPM-пакеты для Enterprise Linux по адресу download.docker.com. Файл репозитория указывает на дерево CentOS, которое используют Rocky Linux и AlmaLinux для разрешения зависимостей. Указание репозитория CentOS для системы Rocky Linux может показаться ошибкой, пока вы не узнаете как оба дистрибутива произошли от CentOS после того, как Red Hat превратила CentOS в Stream в 2020 году. По состоянию на август 2026 года Docker рекомендует этот репозиторий для CentOS Stream 9 и CentOS Stream 10.
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoВ 5-й версии dnf аргумент --add-repo был удалён, поэтому вторая команда завершится ошибкой в новых релизах. Проверьте версию dnf и выберите подходящий вариант:
dnf --versionЕсли выводится версия 5.x, используйте форму с подкомандой:
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoОбе команды создают одинаковый файл по пути /etc/yum.repos.d/docker-ce.repo. Неверный вариант вызовет ошибку из-за неизвестного аргумента, а не выполнит действие некорректно, поэтому вы не пропустите проблему.
Файл репозитория устанавливает baseurl в путь, содержащий $releasever, и dnf подставляет в эту переменную значение из вашего пакета релиза. Rocky Linux и AlmaLinux устанавливают её как номер мажорной версии, поэтому получается 9 на EL 9 и 10 на EL 10. Именно поэтому репозиторий CentOS корректно работает на системе Rocky Linux. Проверьте подстановку перед установкой:
sudo dnf repoinfo docker-ce-stableИзучите строку Repo-baseurl. Она должна заканчиваться на /9/x86_64/stable или /10/x86_64/stable. Если ваш релиз устанавливает $releasever в значение минорной версии, например 9.6, dnf сообщит об ошибке Status code: 404 для этого URL при получении метаданных. Исправьте это, отредактировав /etc/yum.repos.d/docker-ce.repo и заменив $releasever на номер мажорной версии.
Установка движка и плагина compose
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginПакет состоит из пяти компонентов, каждый из которых выполняет свою задачу. docker-ce — это демон, dockerd. docker-ce-cli — это команда docker, которую вы вводите. containerd.io — это среда выполнения контейнеров, которой управляет демон. docker-buildx-plugin отвечает за сборку образов. docker-compose-plugin предоставляет docker compose в качестве подкоманды.
Эти пакеты не устанавливают бинарный файл docker-compose с дефисом. Это был Compose v1, жизненный цикл которого завершился в июле 2023 года. Любые вызовы docker-compose через дефис необходимо заменить на docker compose с пробелом.
При первой установке процесс прерывается для импорта ключа подписи Docker и отображения его отпечатка. Ключ поступает из gpgkey=https://download.docker.com/linux/centos/gpg в файле репозитория, который вы только что добавили, поэтому сверьте отпечаток, выведенный dnf, с этим URL перед тем, как принять его.
Одна ошибка встречается достаточно часто, чтобы её упомянуть. Если dnf сообщает, что containerd.io требует container-selinux, но этот пакет не найден, значит, ваш репозиторий AppStream отключен. Выполните dnf repolist и убедитесь, что appstream присутствует в списке, так как именно там находится container-selinux в дистрибутивах EL 9 и EL 10.
Запуск Docker и проверка работоспособности
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-worldRPM-пакеты Docker оставляют демон остановленным и отключенным после установки. Именно поэтому данный шаг присутствует на странице Docker для CentOS, но отсутствует на странице для Ubuntu, где deb-пакет запускает службу автоматически. Если пропустить enable, Docker будет работать только до следующей перезагрузки, после чего выключится вместе со всеми контейнерами.
Команда systemctl status должна вывести Active: active (running). Контейнер hello-world должен вывести This message shows that your installation appears to be working correctly. и завершить работу. Если вместо этого выводится ошибка прав доступа к /var/run/docker.sock, значит, вы пропустили sudo, что исправляется в разделе про группу docker ниже.
Проверьте плагин compose отдельно, так как это другой пакет, который может отсутствовать, даже если движок работает исправно:
docker compose versionКорректный ответ выглядит как Docker Compose version v2.x.x. Автоматический запуск сервисов после перезагрузки — это отдельный вопрос, не связанный с включением демона, и политики перезапуска определяют, будут ли сервисы Compose запускаться при загрузке системы.
Почему bind mount выдает ошибку permission denied?
В Rocky Linux и AlmaLinux SELinux (Security-Enhanced Linux) по умолчанию работает в режиме enforcing. Проверьте это командой getenforce, которая выводит Enforcing.
Контейнеры Docker работают с типом SELinux container_t, и этот тип может читать и записывать только файлы с меткой container_file_t. Директория, созданная вами на хосте, наследует метку родительского пути, которая не является container_file_t. Контейнеру отказывается в доступе, даже если владелец, группа и права доступа выглядят корректно с точки зрения хоста. Воспроизвести это можно тремя командами:
sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.htmlКонтейнер выведет:
cat: can't open '/usr/share/nginx/html/index.html': Permission deniedДве команды покажут причину. ls -ldZ /srv/site выводит метку, которая для пути внутри /srv является system_u:object_r:var_t:s0, а не container_file_t. Затем sudo ausearch -m avc -ts recent выводит запись аудита ядра, содержащую avc: denied { read }, поле scontext= с именем container_t и поле tcontext= с меткой, которую вы только что увидели на директории. Несоответствие между этими двумя полями и является причиной ошибки.
Решение заключается в добавлении суффикса к аргументу volume. Docker перемаркирует путь за вас:
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z в нижнем регистре перемаркирует содержимое как общее (shared), чтобы несколько контейнеров могли использовать одну и ту же директорию. :Z в верхнем регистре перемаркирует её как частную (private) и неразделяемую, привязанную к одному контейнеру; второму контейнеру, читающему тот же путь, будет отказано в доступе. Используйте :z для всего, к чему также обращается sidecar или контейнер резервного копирования. Используйте :Z для директории базы данных, которой владеет один контейнер.
Документация Docker содержит предупреждение, которое стоит повторить, так как перемаркировка выполняется рекурсивно. Bind-mount системной директории, такой как /home или /usr, с использованием :Z «делает хост-машину неработоспособной, и вам, возможно, придется перемаркировать файлы хоста вручную». Применяйте эти суффиксы к директориям, которые вы создали для контейнера, но никогда не используйте их для системных путей.
В Compose суффикс добавляется к той же строке:
services:
web:
image: nginx:alpine
volumes:
- /srv/site:/usr/share/nginx/html:ro,zСуществует два ограничения, с которыми легко столкнуться. Флаг --mount не может устанавливать метку SELinux, поэтому используйте -v, если она вам нужна. Именованные тома (named volumes) не требуют суффикса, так как Docker сам маркирует директории, которые создает в /var/lib/docker/volumes.
Не отключайте SELinux. Используйте sudo setenforce 0 только для минутной проверки: если после этого контейнер заработал, значит проблема в метке, и решением является :z. Сразу же верните режим обратно командой sudo setenforce 1. В Enterprise Linux ошибка permission denied при bind mount имеет две разные причины, которые выглядят одинаково изнутри контейнера. Первая — это метка SELinux. Вторая — обычные числовые идентификаторы владельца и группы, которые решаются с помощью переменных PUID и PGID. ls -lnZ показывает права доступа, числового владельца и метку в одной строке, чтобы вы могли понять, с чем именно имеете дело.
Почему опубликованный порт доступен, если firewalld показывает, что он закрыт?
Firewalld — это стандартный межсетевой экран в Rocky Linux и AlmaLinux. Проверьте его статус с помощью sudo systemctl is-active firewalld. Если вы еще не настраивали его на этом сервере, сначала выполните открытие SSH и веб-порта с помощью firewalld, так как приведенная ниже особенность понятна только при наличии настроенного набора правил зоны. Теперь опубликуйте порт и посмотрите, что firewalld считает открытым:
sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-portsfirewall-cmd выводит пустую строку. С другой машины curl -I http://YOUR_SERVER_IP:8080/ возвращает HTTP/1.1 200 OK. Порт открыт для интернета, а ваш межсетевой экран не сообщает об этом.
Причина кроется в пути прохождения пакета. Правила зон firewalld фильтруют трафик, адресованный самому хосту. Опубликованный порт не адресован хосту: Docker устанавливает правило назначения NAT (network address translation), которое перезаписывает адрес назначения на адрес контейнера до того, как пакет достигнет входного пути хоста, поэтому ядро пересылает пакет вместо локальной доставки. Затем Docker помещает свои мостовые интерфейсы в зону firewalld под названием docker, целевым значением которой является ACCEPT, и добавляет политику пересылки docker-forwarding, разрешающую пересылку из любой зоны в зону docker. Ваши правила зон никогда не видят этот пакет.
Самое чистое решение не требует правил межсетевого экрана. Привяжите хост-часть публикации к loopback и установите перед ней reverse proxy:
sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/Локальный curl возвращает HTTP/1.1 200 OK, а аналогичный запрос с другой машины больше не устанавливает соединение. Любой порт, указанный в аргументе -p без адреса хоста, публикуется на всех интерфейсах, поэтому считайте использование простого -p 8080:80 решением открыть сервис для публичного доступа.
Если вам нужно, чтобы сервис был доступен с одних адресов, но не с других, Docker резервирует для вас цепочку. DOCKER-USER обрабатывается до собственных правил accept в Docker, поэтому правило, добавленное туда, сохраняется после перезапуска Docker и перезаписи его цепочек:
sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USERИспользуйте имя интерфейса из ip route show default, а не предполагайте eth0, так как современные образы EL используют имена вроде enp1s0 или ens3. В Rocky и AlmaLinux команда iptables является уровнем совместимости поверх nftables, и цепочки Docker видны через неё. Правила, добавленные таким образом, исчезают после перезагрузки, если их не сохранить, поэтому запишите их в systemd unit, когда убедитесь в их корректности.
Docker Engine 28.0, выпущенный в 2025 году, закрыл смежную уязвимость: прямой маршрутизируемый доступ к портам контейнеров, которые не были опубликованы, теперь блокируется в цепочке DOCKER. Это изменение не затрагивает опубликованные порты, поэтому всё вышесказанное остается актуальным для текущих версий. Стоит выработать полезную привычку: после любого sudo firewall-cmd --reload повторно тестируйте опубликованный порт. Если он перестал отвечать, sudo systemctl restart docker переустановит правила Docker.
Администраторы Ubuntu сталкиваются с той же проблемой через другой инструмент, что объясняет, почему опубликованные порты Docker игнорируют правила ufw. В обоих случаях причина кроется в пути NAT. Меняется только используемый межсетевой экран.
Добавление пользователя без прав root в группу docker
Ввод sudo перед каждой командой docker быстро утомляет, а группа docker позволяет этого избежать:
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldusermod -aG редактирует /etc/group, но текущая оболочка уже загрузила список групп, поэтому изменения не вступят в силу до начала новой сессии. newgrp docker запускает оболочку с примененными правами группы, что позволяет сразу проверить результат. Новые SSH-сессии подхватывают изменения автоматически.
Четко осознавайте, какие права дает эта группа. Членство в ней предоставляет права на запись в /var/run/docker.sock, а любой процесс, имеющий доступ к этому сокету, может дать демону команду запустить контейнер с примонтированной файловой системой хоста. Одна команда наглядно показывает, что это значит:
docker run --rm -v /:/host alpine wc -l /host/etc/shadowОна считывает файл, доступный только пользователю root, из учетной записи без прав sudo. Официальная документация Docker по пост-установке подтверждает это: группа docker предоставляет привилегии, эквивалентные root. Добавляйте учетную запись в эту группу только в том случае, если вы готовы предоставить ей права sudo. Если вы настраиваете учетные записи на новом сервере, решайте этот вопрос одновременно с остальными этапами настройки пользователей с минимальными привилегиями на VPS, а не постфактум.
В Docker также предусмотрен rootless-режим, в котором демон запускается от имени непривилегированного пользователя. Это отдельный путь установки, который меняет работу драйверов хранилища и портов ниже 1024, поэтому планируйте его как самостоятельный проект, а не как флаг, который можно добавить позже.
Дальнейшие шаги
Теперь у вас есть движок, плагин compose, сервис, который автоматически запускается после перезагрузки, и три специфичных для EL поведения, описанных выше. Следующим шагом будет создание compose.yaml для каждого сервиса, а в структуре файла Compose подробно разобраны формат файла и команды для управления им. Если это ваш первый контейнерный хост, в руководстве запуск Docker на VPS рассматриваются вопросы выбора ресурсов, хранения данных и гигиены образов, которые остались за рамками этого руководства.
FAQ
Работает ли репозиторий Docker для CentOS на Rocky Linux и AlmaLinux?
Да. Добавьте https://download.docker.com/linux/centos/docker-ce.repo с помощью dnf config-manager. Файл baseurl содержит переменную $releasever. Rocky Linux и AlmaLinux подставляют в неё номер мажорной версии дистрибутива. В результате система на базе EL 9 обращается к дереву CentOS 9, а на базе EL 10 — к дереву CentOS 10. Проверьте подстановку командой sudo dnf repoinfo docker-ce-stable и изучите строку Repo-baseurl. Ошибка Status code: 404 при получении метаданных через dnf означает, что переменная раскрылась в конкретный минорный релиз. Исправьте это, указав в /etc/yum.repos.d/docker-ce.repo только номер мажорной версии.
Можно ли установить Docker и podman на один сервер?
Документация Docker указывает podman и runc как конфликтующие пакеты и рекомендует удалить их перед установкой Docker Engine. Основной конфликт вызывает пакет podman-docker, который владеет путем /usr/bin/docker и превращает любую команду docker в вызов podman. Выполните rpm -qf "$(command -v docker)", чтобы узнать, какой пакет владеет этим путем. Если вывод начинается с podman-docker, значит, запросы обрабатывает podman. Docker не поддерживает одновременную работу обоих движков, поэтому на важном сервере выберите что-то одно.
Почему при использовании bind mount в контейнере возникает ошибка permission denied?
В Rocky Linux и AlmaLinux по умолчанию включен SELinux. Контейнеры запускаются с типом container_t и могут работать только с файлами, имеющими метку container_file_t. Созданная вами директория имеет другую метку, поэтому доступ запрещен независимо от владельца и прав доступа. Проверьте это с помощью ls -ldZ для пути на хосте и sudo ausearch -m avc -ts recent, которая выведет avc: denied с двумя несовпадающими контекстами. Добавьте :z к аргументу volume для контента, общего для нескольких контейнеров, или :Z для контента, используемого только одним контейнером. Никогда не указывайте :Z на /home или /usr, так как рекурсивная перемаркировка нарушит работу хостовой системы.
Нужно ли открывать порт в firewalld для публикации порта контейнера?
Нет, и в этом заключается проблема. NAT-правило Docker перезаписывает адрес назначения до того, как пакет попадет в цепочку input хоста, поэтому правила зон firewalld не проверяют этот трафик. Docker также помещает свои мосты в зону firewalld с именем docker и целью ACCEPT. Контейнер, запущенный с -p 8080:80, доступен из интернета, даже если sudo firewall-cmd --list-ports ничего не выводит. Публикуйте порты на конкретный адрес с помощью -p 127.0.0.1:8080:80, если доступ к сервису должен иметь только хост, либо добавляйте правила фильтрации в цепочку DOCKER-USER, которую Docker обрабатывает перед своими правилами accept.
Безопасно ли добавлять пользователя в группу docker?
Это равносильно получению прав root. Участник группы docker может записывать данные в /var/run/docker.sock, а docker run --rm -v /:/host alpine wc -l /host/etc/shadow затем считывает файл, доступный только root, от имени пользователя без прав sudo. Документация Docker после установки подтверждает это соответствие. Добавляйте в группу только тех пользователей, которым вы уже доверяете права sudo, а для общих или сервисных учетных записей используйте sudo docker. Режим rootless — это альтернатива для запуска контейнеров от имени непривилегированного пользователя; это отдельный способ установки, а не просто настройка.