Docker на сервере: установка, настройка, обслуживание
Инструкция доводит чистый сервер до состояния «Docker и Compose работают, приложения публикуются безопасно, диск не забивается логами». Отдельным разделом — полная переустановка, когда прежняя установка сломана.
7 минутИнструкция доводит чистый сервер до состояния «Docker и Compose работают, приложения публикуются безопасно, диск не забивается логами». Отдельным разделом — полная переустановка, когда прежняя установка сломана.
Что нужно до начала: сервер Ubuntu 22.04/24.04 или Debian 12 с доступом sudo.
Место в цепочке
| Откуда пришли | Этот документ | Куда ведёт |
|---|---|---|
| чистый сервер | Docker, Compose, правила публикации портов, ротация логов | запуск приложения и его шлюза, затем домен и TLS |
Что этот документ оставляет следующему звену: рабочий Docker и правило публикации портов —
приложение и его шлюз публикуются на loopback, поэтому наружу их выставляет только nginx хоста,
завершающий TLS. Нарушение этого правила делает настройку домена бессмысленной: сервис остаётся
доступен по http://example.com:8000 в обход шлюза и шифрования.
Шаг 1. Установка
Ставится официальный репозиторий Docker: в стандартных репозиториях дистрибутива версия отстаёт, а
пакет docker-compose из них — устаревшая отдельная утилита вместо плагина docker compose.
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now dockerДля Debian замените ubuntu на debian в обеих ссылках.
docker --version # ожидается версия Docker Engine
docker compose version # ожидается версия Compose v2 (команда из двух слов, без дефиса)
sudo docker run --rm hello-worldОжидается: контейнер hello-world печатает приветствие и завершается. Ошибка
permission denied при обращении к сокету — см. шаг 2.
Шаг 2. Запуск без sudo
Добавьте пользователя в группу docker, чтобы не писать sudo перед каждой командой:
sudo usermod -aG docker "$USER"
newgrp docker # применить в текущей сессии; иначе перелогиньтесьdocker ps # ожидается таблица контейнеров без ошибки правЧленство в группе docker равносильно правам root на этой машине: через сокет можно запустить
контейнер, смонтировав корень файловой системы. Добавляйте в группу только тех, кому такой уровень
доступа положен.
Шаг 3. Ротация логов
По умолчанию Docker пишет логи контейнеров без ограничения размера, и на долго работающем сервере они занимают весь диск. Ограничение задаётся один раз для всех контейнеров:
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
sudo systemctl restart dockerdocker info --format '{{.LoggingDriver}}' # ожидается: json-fileНастройка действует на контейнеры, созданные после перезапуска демона; уже запущенные нужно пересоздать.
Шаг 4. Публикация портов
Порт публикуется с явным адресом. Это главное правило эксплуатации: от него зависит, доступен ли сервис в обход шлюза и TLS.
services:
gateway:
image: nginx:alpine
ports:
- "127.0.0.1:8000:80" # доступно только с этой машины
# - "8000:80" # доступно всему интернетуЗапись без адреса ("8000:80") публикует порт на всех интерфейсах. Правила ufw при этом не
действуют: Docker добавляет свои правила в iptables в цепочку, которая обрабатывается раньше,
поэтому порт остаётся открытым снаружи, даже если ufw его запрещает.
curl -m 5 -I http://example.com:8000 # ожидается таймаут или отказ соединенияЕсли ответ пришёл — сервис доступен напрямую, в обход шлюза. Исправьте публикацию и повторите.
sudo ss -ltnp | grep :8000 # ожидается адрес 127.0.0.1:8000
docker compose ps --format 'table {{.Service}}\t{{.Ports}}'ports и expose — разные вещи
| Директива | Что делает | Кому доступен порт |
|---|---|---|
ports: ["127.0.0.1:8000:80"] | публикует порт на хосте | процессам этой машины |
ports: ["8000:80"] | публикует порт на всех интерфейсах | всему интернету |
expose: ["8000"] | ничего не публикует, только объявляет порт | контейнерам в общих сетях |
Внутри одной сети Compose сервисы обращаются друг к другу по имени сервиса и внутреннему порту — публикация для этого не нужна. Публикуют только то, что должно быть видно с хоста.
Отсюда правило для микросервиса: он не публикует порты вовсе, у него expose. Наружу его
выводит шлюз, стоящий перед ним. Публикация порта микросервиса — распространённая ошибка: она
открывает обход шлюза со всеми проверками, которые на шлюзе настроены.
Шаг 5. Автозапуск после перезагрузки
Сам Docker включён (systemctl enable), но контейнеры поднимутся, только если это задано политикой
перезапуска:
services:
gateway:
restart: unless-stoppedsudo reboot
# после подключения:
docker compose ps # ожидается: сервисы в состоянии runningОбслуживание
Место на диске. Неиспользуемые образы и слои копятся при каждой сборке:
docker system df # что сколько занимает
docker image prune -f # удалить образы без тегов
docker builder prune -f # очистить кэш сборкиОсторожно с полной очисткой. Команда ниже удаляет и тома, то есть данные баз, которые в них лежат:
docker system prune -af --volumes # удаляет ВСЁ неиспользуемое, включая тома с даннымиВыполняйте её только на машине, где нет данных, которые нужно сохранить, и после проверки
docker volume ls.
docker compose logs -f --tail=100 gatewayПереустановка со сносом
Применяется, когда установка сломана и чинить её дороже, чем поставить заново. Все контейнеры, образы и тома с данными будут удалены — сделайте резервные копии томов до начала.
docker ps -aq | xargs -r docker stop
docker system prune -af --volumes
sudo systemctl stop docker docker.socket
sudo apt-get purge -y docker docker-engine docker.io containerd runc \
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo rm -rf /var/lib/docker /var/lib/containerd /etc/docker
sudo rm -f /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.gpg
sudo apt-get autoremove -yДалее — с шага 1.
Изменение параметров демона
Параметры демона задаются в /etc/docker/daemon.json (шаг 3). Если нужно поменять параметры
запуска службы, не редактируйте /lib/systemd/system/docker.service: этот файл принадлежит
пакету и будет заменён при первом же обновлении Docker. Используйте drop-in:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/override.conf >/dev/null <<'EOF'
[Service]
LimitNOFILE=1048576
EOF
sudo systemctl daemon-reload
sudo systemctl restart dockersystemctl show docker -p LimitNOFILE # ожидается заданное значениеТипичные отказы
| Признак | Причина | Что делать |
|---|---|---|
permission denied на /var/run/docker.sock | пользователь не в группе docker | шаг 2, затем перелогиниться |
docker-compose: command not found | ожидается старая отдельная утилита | использовать docker compose (плагин v2) |
порт доступен снаружи, хотя ufw его запрещает | порт опубликован без адреса | указать 127.0.0.1: в публикации (шаг 4) |
| контейнеры не поднялись после перезагрузки | нет политики перезапуска | добавить restart: unless-stopped |
| «нет места на устройстве» при сборке | накопился кэш и старые образы | docker system df, затем image prune и builder prune |
| логи занимают десятки гигабайт | не ограничен размер логов | шаг 3 и пересоздать контейнеры |
| после обновления Docker пропали правки службы | правился файл пакета | перенести правки в drop-in override.conf |
| контейнер не видит соседа по имени | сервисы в разных сетях Compose | привести к одной сети либо обращаться по имени сервиса внутри общей сети |
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.