Свежая виртуальная машина с Ubuntu Server 24.04 встречает нового владельца тихим приглашением по ssh и почти пустым списком процессов. Первая задача на таком сервере почти всегда одна: поставить Docker и дальше работать с контейнерами, а не расписывать приложения по системным пакетам. И здесь кроется ловушка, в которую попадали и новички, и опытные администраторы. Поиск по архиву услужливо предлагает пакет docker.io, тот ставится одной командой и внешне работает. Вот только картина хитрее, чем кажется на первый взгляд: сам движок в архиве подтянулся через карман обновлений до ветки 29, а вот его спутники отстали всерьёз. Пакет docker-compose-v2 стоит на релизной версии 2.24.6, когда официальный репозиторий Docker раздаёт уже 5.6.0. Разница не в месяцах, а в поколениях: другой синтаксис, другие скорости и заметно другие возможности сборки.
Официальный репозиторий проекта живёт по другому расписанию. Релизы приходят туда сразу после анонса, подписываются ключом Docker и ставятся тем же самым apt без всяких скриптов-комбайнов. На момент подготовки материала свежий движок нумеруется как 29.9.0, а плагин Docker Compose добрался до версии 5.6.0. Ниже полный маршрут: подключение ключа и репозитория, установка пяти пакетов, проверка работы, безопасная работа без sudo и настройка daemon.json, которая уберегает диск от разросшихся журналов.
Чем официальный репозиторий Docker выигрывает у архива Ubuntu
Философия у архивов дистрибутива своя и по-своему честная: большинство пакетов замораживается на релизе, дальше едут только исправления безопасности, и лишь немногим быстрым проектам разрешают перепрыгивать ветку в обновлениях. Для СУБД или веб-сервера такой подход разумен. Для платформы, которая обновляется каждые несколько недель, он превращается в якорь. Движок в архиве обновляется, но с опозданием на несколько релизов: карман обновлений отдаёт 29.1.3 при актуальных 29.9.0. Хуже дело со спутниками: плагины едут в архив отдельными пакетами и стоят на месте, docker-compose-v2 на релизной 2.24.6, docker-buildx на 0.20.1. Официальный репозиторий ставит всё семейство сразу и без опозданий: движок 29.9.0, плагин Compose 5.6.0 и сборщик buildx свежей линии одной командой.
Официальный репозиторий устроен иначе. Пакеты собирает и сопровождает сам проект, ключ подписи принадлежит Docker, обновления приезжают через привычный apt upgrade без дополнительных ритуалов. Поддерживаются и классические amd64, и arm64, поэтому инструкция одинаково работает на обычных VPS и на мини-компьютерах. Наконец, в репозитории лежит полный комплект из пяти пакетов, и у каждого своя роль.
Ставятся они одной командой, и полезно заранее понимать, что именно приедет на сервер:
- docker-ce устанавливает службу dockerd, то есть сам движок, который принимает запросы клиента и управляет контейнерами;
- docker-ce-cli приносит консольную утилиту docker, через которую администратор делает всю повседневную работу;
- containerd.io доставляет среду исполнения, она непосредственно запускает и сопровождает процессы внутри контейнеров;
- docker-buildx-plugin подключает современный сборщик образов с кэшированием и многоступенчатыми сборками;
- docker-compose-plugin даёт подкоманду compose для описания целых стеков из множества контейнеров одним файлом.
Для самых нетерпеливых существует и установочный скрипт проекта, вызываемый одной строкой:
curl -fsSL https://get.docker.com | sh
Он делает ровно то же самое, но вслепую. Ручное подключение репозитория приводит сервер в то же состояние, зато каждая строка видна и понятна, а в администрировании это дорогого стоит.
Подготовка сервера и удаление конфликтных пакетов
Готовить под Docker почти нечего: нужен чистый Ubuntu Server 24.04 с доступом в сеть и правами sudo, остальное приедет пакетами. Начать стоит с уборки. Если на сервере когда-то стоял Docker из архива или из чужих репозиториев, его остатки будут конфликтовать с новыми пакетами по файлам и службам. Команда ниже не навредит чистой системе, apt просто ответит, что пакетов нет. На захламлённой она уберёт старые версии и освободит дорогу.
sudo apt-get remove -y docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install -y ca-certificates curl
Пакеты ca-certificates и curl нужны на следующие пару минут: первым скачивается ключ подписи, затем идут обращения к репозиторию. Больше на этом этапе ничего не требуется.
Ключ подписи и строка репозитория в настройках apt
Начиная с Ubuntu 22.04 механизм apt-key считается устаревшим, и правильный путь выглядит так: ключ лежит отдельным файлом в каталоге keyrings, а строка источника ссылается на него параметром signed-by. Звучит мудрёно, на деле четыре команды.
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Что здесь происходит. Первая строка создаёт каталог keyrings с правами 0755, без него ключ класть некуда. Вторая скачивает открытый ключ подписи Docker в современном формате. Третья разрешает чтение ключа всем пользователям, этого требует apt при проверке подписи. Четвёртая кладёт строку источника в отдельный файл: noble это кодовое имя Ubuntu 24.04, stable означает ветку релизов, а $(dpkg --architecture) подставит amd64 или arm64 в зависимости от железа. Отдельный файл в sources.list.d удобен и на прощание: отключить репозиторий можно одной командой rm, не трогая общие списки источников.
Осталось обновить индексы и убедиться, что apt видит свежую версию.
sudo apt-get update
apt-cache policy docker-ce
В выводе последней команды в строке Кандидат отобразится текущая версия движка, и это будет 29.9.0, а не 29.1.3 из архива обновлений.
Установка движка и проверка автозагрузки службы
Теперь сама установка. Пять пакетов из списка выше ставятся одним вызовом apt, вместе с ними подтянутся зависимости.
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Установочный сценарий включает автозагрузку службы самостоятельно, но верить на слово в администрировании не принято, проверка занимает секунды:
systemctl is-enabled docker
sudo systemctl enable --now docker
Первая команда отвечает enabled либо disabled. Вторая страхует: даже если автозагрузка по какой-то причине не включилась, она включит её и поднимет службу сразу. Рядом с docker работает и containerd как отдельная единица systemd, обе должны быть активны. Убедиться, что клиент видит сервер, проще всего сокращённым выводом:
sudo docker version
В ответе будут секции Client и Server с совпадающей версией 29.9.0. Если секция Server пустая, служба не поднята, и смотреть надо журнал командой journalctl -u docker. Ещё полезен вызов docker info: одной пачкой он показывает драйвер хранения, число контейнеров и текущие настройки службы.
Дальше обновления приезжают обычным путём: apt update && apt upgrade подтянет новые версии движка и плагинов, как только проект их выпустит. Возвращаться к пакетам архива после этого не нужно.
Место на диске тоже стоит понимать заранее. Образы, слои и журналы живут в каталоге /var/lib/docker, объём покажет команда sudo du -sh /var/lib/docker, а раскладку по типам даёт docker system df. На свежем сервере цифры скромные, но растут быстрее, чем кажется, особенно если тянуть многослойные образы.
Первый контейнер и проверка версии плагина Compose
Классическая проверка установки выглядит почти как шутка, но проверяет всю цепочку целиком: и клиент, и службу, и доступ к хранилищу образов.
sudo docker run hello-world
Сообщение Hello from Docker появляется, когда клиент доложился службе, служба скачала крошечный образ, запустила его и вернула вывод в терминал. Если текст получен, инфраструктура жива. Дальше проверяют плагины:
docker compose version
docker buildx version
Первая команда ответит строкой вида Docker Compose version v5.6.0. Пара слов о номере. Название Docker Compose v2 закрепилось за переписанной на Go утилитой, которая сменила старый docker-compose на Python, и с тех пор Compose живёт как подкоманда внутри клиента docker. Мажорный номер давно ушёл от двойки, но суть не меняется: стеки описываются файлом YAML и запускаются командой docker compose up, без отдельной утилиты и без лишних сущностей.
Для чистоты эксперимента стоит прогнать и полноценный контейнер с фоновым режимом. Запуск, взгляд в список и остановка занимают четыре команды:
sudo docker run -d --name testweb -p 8080:80 nginx:stable-alpine
sudo docker ps
curl -I http://127.0.0.1:8080/
sudo docker rm -f testweb
Ответ с кодом HTTP подтверждает, что образ скачался, порт проброшен и сеть работает. После теста контейнер удаляется, и сервер остаётся чистым.
Группа docker и цена удобной работы без sudo
Каждый вызов docker требует прав администратора, потому что клиент общается со службой через сокет, принадлежащий root. Постоянно тянуть sudo утомляет, и стандартное решение выглядит так:
sudo usermod -aG docker $USER
newgrp docker
Первая команда добавляет текущего пользователя в группу docker, вторая применяет членство без выхода из сеанса, после чего docker ps работает без sudo. Удобство оплачено, и полезно понимать, какой ценой. Доступ к сокету Docker равнозначен правам root на самом сервере: участник группы может запустить контейнер с монтированием корневой файловой системы хоста и сделать внутри что угодно. Состав группы должен быть коротким и проверяемым, взгляд бросается командой getent group docker. Для машин, где давать людям власть над сервером нельзя, у Docker есть rootless режим: пакет docker-ce-rootless-extras и утилита dockerd-rootless-setuptool.sh поднимают службу от имени обычного пользователя, контейнеры работают без прав администратора и видят только его файлы. Системе для этого нужны настроенные диапазоны в файлах /etc/subuid и /etc/subgid, утилита настройки сама сообщит, если их не хватает.
Сокет службы живёт по пути /var/run/docker.sock, и любые инструменты управления, претендующие на доступ к Docker, попросят именно его. Это же и главная дверь: доступ к сокету равен доступу к серверу целиком, что бы ни говорила таблица пользователей.
Ограничение роста журналов контейнеров через daemon.json
Последний штрих установки незаметен до первого переполнения диска. По умолчанию драйвер json-file пишет стандартные потоки контейнера в файлы без всяких пределов. Приложение с шаловливыми руками способно за сутки наговорить гигабайты, и диск закончится в самый неудобный момент, прихватив с собой базы и системные журналы.
Лечится это файлом daemon.json, он создаётся в каталоге /etc/docker и применяется службой при рестарте.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"live-restore": true
}
Смысл настроек простой. Пара max-size и max-file превращает журнал каждого контейнера в набор файлов по 10 мегабайт, максимум три штуки, то есть потолок около 30 мегабайт на контейнер. Параметр live-restore разрешает контейнерам переживать рестарт самой службы: при обновлении Docker или перезапуске по любой причине рабочие приложения не падают, а продолжают жить, пока служба поднимается. Владельцам брандмауэра ufw достаётся ещё одна тонкость. Движок управляет таблицами iptables напрямую, и опубликованные порты контейнеров открываются в обход правил ufw. Служба выглядит защищённой, а порт из примера выше виден снаружи даже при запрете в ufw, поэтому порты публикуют осознанно, а доступ ограничивают на уровне фронтального веб-сервера.
Осталось применить настройки:
sudo systemctl restart docker
docker info | grep -i live
Строка Live Restore Enabled: true в ответе подтверждает, что опция принята. Проверить, что лимиты журналов достались конкретному контейнеру, можно так:
docker inspect --format '{{.HostConfig.LogConfig}}' $(docker ps -q)
В ответе отобразятся принятые значения max-size и max-file. Есть нюанс, о котором молчат многие инструкции: лимиты касаются только новых контейнеров. У давно работающих ограничения появятся после пересоздания, и это ещё один аргумент держать стеки в Compose и поднимать их одной командой.
С этого момента сервер готов к настоящей работе: движок свежий, автозагрузка включена, журналы под присмотром. Следующий логичный шаг это описание первого стека из нескольких контейнеров в одном файле, и именно там Docker Compose раскрывается полностью.