Ты поднял контейнер — что-то не работает. Или работает, но непонятно как. Или работало, а потом перестало. Эта шпаргалка закроет 90% вопросов по Docker которые возникают в реальной работе на сервере.
Здесь не будет рассказа о том, что такое контейнеризация и почему Docker это хорошо. Ты это уже знаешь. Здесь команды, примеры, объяснение почему именно так, и что делать когда что-то пошло не по плану.
Что понадобится: сервер или VPS с Ubuntu/Debian/CentOS, root или sudo, установленный Docker. Установку разберём отдельным блоком.
Время на освоение: зависит от задачи. Базовые команды — 20 минут. Compose и сети — ещё час. Полный справочник оставь открытым и возвращайся по мере необходимости.
Содержание
- Архитектура Docker: как это работает
- Установка Docker на Ubuntu и Debian
- Системные команды: daemon, info, версия
- Образы: pull, build, tag, push
- Контейнеры: run, ps, exec, stop, rm
- Логи и диагностика
- Volumes: хранение данных
- Networks: связь между контейнерами
- Docker Compose: запуск стека
- Dockerfile: собрать образ своими руками
- Продакшен: лимиты, рестарт, безопасность
- Очистка и обслуживание
- Troubleshooting
- FAQ
Архитектура Docker: как это работает
Прежде чем копировать команды — одна минута на структуру. Это сэкономит час отладки позже.
Docker daemon (dockerd) — это сердце системы. Он получает команды от CLI, управляет образами, контейнерами, сетями и томами. Если daemon не запущен — ничего не работает вообще. Первая точка проверки при любой проблеме.
Образ (image) — шаблон. Неизменяемый. Из одного образа можно запустить сколько угодно контейнеров.
Контейнер (container) — запущенный экземпляр образа. Изолированный процесс с собственной файловой системой. Останови его — данные внутри исчезнут, если не используешь volume.
Volume — постоянное хранилище, живёт отдельно от контейнера. База данных без volume — это не продакшен, это лотерея.
Установка Docker на Ubuntu и Debian
Есть два пути: docker.io из репозитория дистрибутива и официальный репозиторий Docker Inc. Первый — проще. Второй — свежее версия и это важно если используешь Compose v2 или BuildKit.
Вариант 1: из репозитория Ubuntu/Debian (проще)
apt update && apt upgrade -y
apt install -y docker.io
systemctl start docker
systemctl enable docker
Вариант 2: официальный репозиторий Docker (рекомендую для продакшена)
# Убираем старые версии если есть
apt remove docker docker-engine docker.io containerd runc 2>/dev/null
# Зависимости
apt update
apt install -y ca-certificates curl gnupg lsb-release
# GPG ключ
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
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" | tee /etc/apt/sources.list.d/docker.list > /dev/null
# Установка
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Проверяем установку
docker --version
docker info
systemctl status docker
После docker --version должна появиться версия. После docker info — подробная информация о системе, контейнерах, образах, хранилище. Если вылезло Cannot connect to the Docker daemon — daemon не запущен, смотри следующий раздел.
Добавляем пользователя в группу docker
Чтобы не запускать каждую команду через sudo:
usermod -aG docker $USER
newgrp docker
Таблица: совместимость версий
| ОС | Версия | Docker CE | Compose Plugin |
|---|---|---|---|
| Ubuntu | 22.04 LTS | 24.x, 25.x, 26.x | v2.x |
| Ubuntu | 24.04 LTS | 26.x, 27.x | v2.x |
| Debian | 11 (Bullseye) | 24.x, 25.x | v2.x |
| Debian | 12 (Bookworm) | 26.x, 27.x | v2.x |
| CentOS/RHEL | 8, 9 | 25.x, 26.x | v2.x |
На момент публикации актуальна версия Docker 27.x. Перед установкой проверь свежие релизы на docs.docker.com/engine/release-notes.
Системные команды: daemon, info, версия
Управление daemon через systemctl
# Статус
systemctl status docker
# Запуск
systemctl start docker
# Перезапуск
systemctl restart docker
# Остановка
systemctl stop docker
# Автозапуск при старте системы
systemctl enable docker
# Отключить автозапуск
systemctl disable docker
Информация о системе
# Версия клиента и сервера
docker version
# Подробная инфо: контейнеры, образы, хранилище, драйвер
docker info
# Использование диска Docker
docker system df
# Подробно с размерами
docker system df -v
Команда docker system df покажет сколько места занимают образы, контейнеры, volumes и build cache. На серверах которые давно не чистили цифра бывает неприятно большой.
Логи docker daemon
# Логи daemon в journald
journalctl -u docker
# Последние 100 строк
journalctl -u docker -n 100
# В реальном времени
journalctl -u docker -f
Образы: pull, build, tag, push
Работа с Docker Hub
# Скачать образ последней версии
docker pull nginx
# Конкретная версия (тег)
docker pull nginx:1.25
# Конкретная версия под архитектуру
docker pull --platform linux/amd64 nginx:1.25
# Список скачанных образов
docker images
# Подробный список с датами
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
# Удалить образ
docker rmi nginx:1.25
# Удалить образ принудительно (если используется контейнером)
docker rmi -f nginx:1.25
Поиск образов
# Поиск на Docker Hub
docker search nginx
# Только официальные образы
docker search --filter is-official=true nginx
Авторизация в реестре
# Docker Hub
docker login
# Приватный реестр
docker login registry.example.com
# Выйти
docker logout
Tag и push
# Пометить образ своим тегом
docker tag nginx:latest myregistry.com/myapp:1.0
# Отправить в реестр
docker push myregistry.com/myapp:1.0
Инспект образа
# Метаданные образа
docker inspect nginx
# Только конкретное поле (например, переменные окружения)
docker inspect --format='{{.Config.Env}}' nginx
# История слоёв образа
docker history nginx
Контейнеры: run, ps, exec, stop, rm
Это основа. Здесь половина всех команд которые ты будешь использовать каждый день.
docker run: запуск контейнера
# Простой запуск (в foreground, Ctrl+C останавливает)
docker run nginx
# Фоновый режим (detached)
docker run -d nginx
# С именем
docker run -d --name web nginx
# Проброс порта: порт_хоста:порт_контейнера
docker run -d -p 8080:80 --name web nginx
# Переменные окружения
docker run -d -e MY_VAR=value --name web nginx
# Несколько переменных
docker run -d -e DB_HOST=localhost -e DB_PORT=5432 --name app myapp
# Монтирование директории хоста
docker run -d -v /host/path:/container/path nginx
# Монтирование volume
docker run -d -v myvolume:/data nginx
# Автоматически удалять контейнер после остановки
docker run --rm nginx
# Интерактивный режим (зайти внутрь сразу)
docker run -it ubuntu bash
# Ограничение памяти
docker run -d --memory="512m" nginx
# Ограничение CPU
docker run -d --cpus="1.5" nginx
# Политика рестарта
docker run -d --restart=always nginx
# Подключить к сети
docker run -d --network mynetwork nginx
Флаги -d и -it не совместимы как основной режим: -d запускает в фоне, -it открывает терминал. Для продакшена почти всегда -d. Для отладки — -it --rm.
Таблица: политики рестарта
| Политика | Поведение | Когда использовать |
|---|---|---|
| no | Не перезапускать (по умолчанию) | Разработка, тестовые запуски |
| always | Всегда перезапускать | Продакшен сервисы |
| unless-stopped | Перезапускать, кроме ручной остановки | Продакшен, гибкий контроль |
| on-failure | Перезапускать только при ненулевом exit code | Задачи с обработкой ошибок |
| on-failure:3 | Не более 3 попыток перезапуска | Предотвращение restart loop |
Управление запущенными контейнерами
# Список запущенных контейнеров
docker ps
# Все контейнеры включая остановленные
docker ps -a
# Только ID контейнеров
docker ps -q
# Форматированный вывод
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Остановить контейнер (SIGTERM, ждёт 10 секунд)
docker stop web
# Остановить немедленно (SIGKILL)
docker kill web
# Запустить остановленный
docker start web
# Перезапустить
docker restart web
# Пауза / снятие паузы
docker pause web
docker unpause web
# Переименовать
docker rename web web-old
# Удалить остановленный контейнер
docker rm web
# Удалить принудительно (работающий)
docker rm -f web
# Удалить все остановленные контейнеры
docker container prune
# Инспект контейнера (полная информация)
docker inspect web
# Статистика ресурсов в реальном времени
docker stats
# Статистика одного контейнера без обновления
docker stats web --no-stream
# Процессы внутри контейнера
docker top web
docker exec: работа внутри контейнера
# Зайти в bash
docker exec -it web bash
# Если bash не установлен - sh
docker exec -it web sh
# Выполнить одну команду
docker exec web nginx -t
# Выполнить команду от имени конкретного пользователя
docker exec -u www-data web whoami
# Открыть ещё один терминал в уже подключённой сессии
docker exec -it web bash
Если контейнер отвечает на docker exec -it web bash ошибкой OCI runtime exec failed — контейнер либо не запущен, либо bash в нём нет. Проверь через docker ps что он вообще работает.
Копирование файлов
# Скопировать файл из контейнера на хост
docker cp web:/etc/nginx/nginx.conf ./nginx.conf
# Скопировать файл с хоста в контейнер
docker cp ./nginx.conf web:/etc/nginx/nginx.conf
# Скопировать директорию
docker cp web:/var/log/nginx/ ./logs/
Логи и диагностика
Команды для работы с логами
# Показать все логи контейнера
docker logs web
# Последние N строк
docker logs web --tail 100
# Следить в реальном времени (как tail -f)
docker logs -f web
# С временными метками
docker logs -t web
# Логи за последний час
docker logs --since 1h web
# Логи с определённого времени
docker logs --since "2024-01-15T10:00:00" web
# Логи между двумя временными точками
docker logs --since 2h --until 1h web
Диагностика без захода внутрь
# Полный инспект контейнера в JSON
docker inspect web
# IP адрес контейнера
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
# Подключённые порты
docker inspect -f '{{.NetworkSettings.Ports}}' web
# Переменные окружения
docker inspect -f '{{.Config.Env}}' web
# Смонтированные тома
docker inspect -f '{{.Mounts}}' web
# События в реальном времени (старт/стоп контейнеров и т.п.)
docker events
# События за последние 30 минут
docker events --since 30m
Volumes: хранение данных
Всё что записывается внутрь контейнера — исчезает при его удалении. Volumes решают эту проблему. Для баз данных, конфигов и любых данных которые должны пережить перезапуск контейнера — только volumes.
Управление volumes
# Создать volume
docker volume create pgdata
# Список volumes
docker volume ls
# Инспект volume (где хранится, кто использует)
docker volume inspect pgdata
# Удалить volume
docker volume rm pgdata
# Удалить все неиспользуемые volumes
docker volume prune
# Удалить volume принудительно
docker volume rm -f pgdata
Типы монтирования
# Volume (рекомендуется для данных)
docker run -d -v pgdata:/var/lib/postgresql/data postgres
# Bind mount (конкретная папка хоста)
docker run -d -v /home/user/data:/app/data myapp
# Tmpfs mount (только в RAM, не сохраняется)
docker run -d --tmpfs /tmp myapp
# Только чтение
docker run -d -v /host/config:/app/config:ro myapp
Где физически лежат volumes
# Путь на хосте
docker volume inspect pgdata --format '{{.Mountpoint}}'
# Обычно: /var/lib/docker/volumes/pgdata/_data
Backup и восстановление volume
# Создать бэкап volume в tar.gz
docker run --rm \
-v pgdata:/data \
-v $(pwd):/backup \
ubuntu tar czf /backup/pgdata-backup.tar.gz -C /data .
# Восстановить из бэкапа
docker run --rm \
-v pgdata:/data \
-v $(pwd):/backup \
ubuntu bash -c "cd /data && tar xzf /backup/pgdata-backup.tar.gz"
Команда создаёт временный контейнер ubuntu, монтирует в него volume и директорию хоста, упаковывает данные в архив. Контейнер удаляется сам после завершения — за это отвечает --rm.
Networks: связь между контейнерами
По умолчанию контейнеры могут общаться только внутри одной сети. Правильно организованные сети — это ещё и изоляция: database не должна быть доступна снаружи, только из app-контейнера.
Управление сетями
# Список сетей
docker network ls
# Создать bridge сеть
docker network create backend
# Создать с настройками подсети
docker network create --subnet=172.20.0.0/24 backend
# Инспект сети
docker network inspect backend
# Удалить сеть
docker network rm backend
# Удалить все неиспользуемые сети
docker network prune
Типы сетей
| Тип | Описание | Когда использовать |
|---|---|---|
| bridge | Изолированная сеть на хосте (по умолчанию) | Большинство сценариев |
| host | Контейнер использует сеть хоста напрямую | Высокая производительность, осторожно с безопасностью |
| none | Нет сети | Полная изоляция |
| overlay | Сеть между несколькими хостами | Docker Swarm, multi-host |
| macvlan | Контейнер получает MAC-адрес в физической сети | Legacy системы, требующие Layer 2 |
Подключение контейнеров к сети
# Запустить контейнер сразу в нужной сети
docker run -d --name db --network backend postgres
# Подключить работающий контейнер к сети
docker network connect backend web
# Отключить контейнер от сети
docker network disconnect backend web
# Контейнер в нескольких сетях
docker network connect frontend web
Связь между контейнерами
Контейнеры в одной user-defined сети могут обращаться друг к другу по имени. Это автоматический DNS внутри Docker.
# Пример: app обращается к db по имени "db"
docker run -d --name db --network backend \
-e POSTGRES_PASSWORD=secret postgres
docker run -d --name app --network backend \
-e DB_HOST=db \
-e DB_PORT=5432 \
myapp
Обрати внимание: DB_HOST=db — это именно имя контейнера. Docker внутри сети разрешает имена контейнеров в IP адреса автоматически. Никаких hosts файлов, никаких ручных IP.
Dockerfile: собрать образ
Dockerfile — текстовый файл с инструкциями для сборки образа. Каждая инструкция создаёт новый слой. Слои кэшируются — поэтому порядок инструкций влияет на скорость повторных сборок.
Базовая структура Dockerfile
# Базовый образ
FROM ubuntu:22.04
# Метаданные
LABEL maintainer="admin@example.com"
# Переменные сборки
ARG APP_VERSION=1.0
# Переменные окружения
ENV APP_HOME=/app
# Рабочая директория
WORKDIR /app
# Копировать файлы
COPY . .
# Установка зависимостей
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
# Установить зависимости Python
RUN pip3 install -r requirements.txt
# Открыть порт (документация, не реальный проброс)
EXPOSE 8000
# Пользователь для запуска (не root!)
USER nobody
# Команда запуска
CMD ["python3", "app.py"]
Сборка образа
# Собрать с тегом
docker build -t myapp:1.0 .
# Собрать из конкретного Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
# Без кэша
docker build --no-cache -t myapp:1.0 .
# С аргументами сборки
docker build --build-arg APP_VERSION=2.0 -t myapp:2.0 .
# Посмотреть слои при сборке
docker build --progress=plain -t myapp:1.0 .
Многоступенчатая сборка (multi-stage build)
Уменьшает итоговый размер образа: в первом этапе компилируем, во второй копируем только бинарник.
# Этап 1: сборка
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# Этап 2: финальный образ
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
Итоговый образ содержит только alpine и бинарник. Никаких компиляторов, исходников и зависимостей сборки. Размер падает с гигабайт до десятков мегабайт. Это не магия, это архитектура.
Docker Compose: запуск стека
Compose позволяет описать несколько сервисов в одном файле и запустить их одной командой. Версия 2 (compose plugin) встроена в Docker и вызывается через docker compose без дефиса.
Базовый docker-compose.yml
version: "3.8"
services:
web:
image: nginx:1.25
container_name: web
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html:ro
networks:
- frontend
depends_on:
- app
app:
build:
context: .
dockerfile: Dockerfile
container_name: app
restart: unless-stopped
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_NAME=mydb
- DB_USER=appuser
- DB_PASSWORD=${DB_PASSWORD}
networks:
- frontend
- backend
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
container_name: db
restart: unless-stopped
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=appuser
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
networks:
frontend:
backend:
volumes:
pgdata:
Команды Docker Compose
# Запустить стек в фоне
docker compose up -d
# Запустить с пересборкой образов
docker compose up -d --build
# Остановить стек (контейнеры останавливаются, не удаляются)
docker compose stop
# Остановить и удалить контейнеры
docker compose down
# Остановить, удалить контейнеры и volumes
docker compose down -v
# Остановить, удалить и удалить образы
docker compose down --rmi all
# Статус сервисов
docker compose ps
# Логи всех сервисов
docker compose logs
# Логи конкретного сервиса в реальном времени
docker compose logs -f app
# Зайти в контейнер сервиса
docker compose exec app bash
# Выполнить команду в сервисе
docker compose exec db psql -U appuser mydb
# Перезапустить один сервис без остановки других
docker compose restart app
# Масштабировать сервис
docker compose up -d --scale app=3
# Посмотреть переменные окружения
docker compose config
Healthcheck в Compose
Конструкция depends_on: condition: service_healthy дождётся пока база не пройдёт healthcheck перед запуском app. Без этого app стартует раньше базы и получает ошибку подключения.
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Docker для продакшена: настройки которые нельзя пропускать
Ограничения ресурсов
# Ограничение памяти и CPU через run
docker run -d \
--name app \
--memory="512m" \
--memory-swap="512m" \
--cpus="2.0" \
--restart=unless-stopped \
myapp
# Обновить лимиты работающего контейнера
docker update --memory="1g" --cpus="2.0" app
--memory-swap равный --memory означает что swap не используется. Если не задать — контейнер сможет использовать swap в два раза больше лимита памяти. На продакшене это нежелательно.
Настройка docker daemon для продакшена
Файл конфигурации: /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
},
"storage-driver": "overlay2",
"live-restore": true,
"userland-proxy": false,
"no-new-privileges": true
}
Что здесь важно:
log-driver: json-file+ ограничения — без этого логи растут бесконечно и заполняют диск. Проверенный способ убить сервер в 3 ночи.live-restore: true— контейнеры продолжают работать при перезапуске daemon. Для обновления Docker без даунтайма.no-new-privileges: true— процессы внутри контейнера не могут получить новые привилегии через setuid.
# Применить изменения daemon.json
systemctl restart docker
Безопасность: минимальный набор
# Никогда не запускать как root если можно без этого
docker run -d --user 1000:1000 myapp
# Read-only файловая система
docker run -d --read-only myapp
# Запретить запись в tmp (если приложение не требует)
docker run -d --read-only --tmpfs /tmp myapp
# Без дополнительных capabilities
docker run -d --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
# Запрет эскалации привилегий
docker run -d --security-opt no-new-privileges myapp
Firewall: порты Docker и UFW
# Привязать порт только к localhost (не публичный)
docker run -d -p 127.0.0.1:5432:5432 postgres
# Публичный порт (доступен всем)
docker run -d -p 0.0.0.0:80:80 nginx
# Или просто
docker run -d -p 80:80 nginx
Таблица портов: стандартные сервисы в Docker
| Сервис | Порт контейнера | Рекомендуемая привязка | Примечание |
|---|---|---|---|
| Nginx/Apache | 80, 443 | 0.0.0.0:80:80 | Публичный |
| PostgreSQL | 5432 | 127.0.0.1:5432:5432 | Только localhost |
| MySQL/MariaDB | 3306 | 127.0.0.1:3306:3306 | Только localhost |
| Redis | 6379 | 127.0.0.1:6379:6379 | Только localhost |
| MongoDB | 27017 | 127.0.0.1:27017:27017 | Только localhost |
| RabbitMQ | 5672, 15672 | 127.0.0.1:15672:15672 | Management только localhost |
Очистка и обслуживание
Docker накапливает мусор: остановленные контейнеры, неиспользуемые образы, осиротевшие volumes и build cache. На серверах которые работают больше года без чистки это запросто 50+ гигабайт.
# Посмотреть что занимает место
docker system df
docker system df -v
# Удалить всё неиспользуемое (контейнеры, сети, образы без тегов, build cache)
docker system prune
# То же самое но и volumes
docker system prune --volumes
# Удалить вообще всё неиспользуемое включая образы с тегами
docker system prune -a
# Только остановленные контейнеры
docker container prune
# Только образы без тегов (dangling)
docker image prune
# Все образы без запущенных контейнеров
docker image prune -a
# Только volumes без контейнеров
docker volume prune
# Только неиспользуемые сети
docker network prune
# Только build cache
docker builder prune
Автоматическая очистка через cron
# Добавить в crontab (каждое воскресенье в 3 ночи)
crontab -e
0 3 * * 0 /usr/bin/docker system prune -f >> /var/log/docker-prune.log 2>&1
Флаг -f убирает запрос подтверждения. Без него cron задача просто зависнет.
Обновление Docker и контейнеров
Обновление самого Docker
# Проверить текущую версию
docker --version
# Обновить (если ставили из официального репозитория)
apt update
apt upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Проверить что daemon работает после обновления
systemctl status docker
docker ps
Если включён live-restore: true в daemon.json — контейнеры продолжат работать во время перезапуска daemon при обновлении. Без этого флага все контейнеры остановятся.
Обновление контейнеров (вручную)
# Скачать новый образ
docker pull nginx:latest
# Остановить старый контейнер
docker stop web
# Удалить старый контейнер (данные в volumes сохранятся)
docker rm web
# Запустить с новым образом
docker run -d --name web -p 80:80 --restart=unless-stopped nginx:latest
# Удалить старый образ
docker image prune
Обновление стека через Compose
# Скачать новые образы
docker compose pull
# Пересобрать и перезапустить
docker compose up -d --build
# Только пересоздать контейнеры с новыми образами без сборки
docker compose up -d --pull always
Откат к предыдущей версии
# Посмотреть доступные теги через Hub
docker search nginx
# Запустить конкретную версию
docker run -d --name web -p 80:80 nginx:1.24
# В compose - изменить тег в файле и перезапустить
# image: nginx:1.24
docker compose up -d
Troubleshooting
Cannot connect to the Docker daemon
Daemon не запущен или нет прав.
# Проверить статус
systemctl status docker
# Запустить
systemctl start docker
# Проверить права текущего пользователя
groups $USER
# Добавить в группу docker
usermod -aG docker $USER
newgrp docker
Port is already allocated / address already in use
Порт занят другим процессом или контейнером.
# Найти что занимает порт
ss -tlnp | grep :80
lsof -i :80
# Найти контейнер который использует порт
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep 80
# Остановить конкурирующий контейнер
docker stop old_container
No space left on device
Закончилось место на диске.
# Проверить диск
df -h
du -sh /var/lib/docker/*
# Очистить Docker
docker system prune -f
# Если не помогает - смотри логи контейнеров (они могут занимать много места)
find /var/lib/docker/containers -name "*.log" -exec du -sh {} \; | sort -rh | head
Container exits immediately (exit code 1 или 137)
# Посмотреть логи остановленного контейнера
docker logs mycontainer
# Запустить интерактивно для диагностики
docker run -it --entrypoint bash myimage
# Exit code 137 - контейнер убит по OOM (нехватка памяти)
# Проверить системные сообщения
journalctl -k | grep -i "oom"
dmesg | grep -i "oom"
Контейнер не может подключиться к другому контейнеру
# Проверить что оба в одной сети
docker inspect container1 | grep -A 20 Networks
docker inspect container2 | grep -A 20 Networks
# Пинг по имени изнутри контейнера
docker exec container1 ping container2
# Проверить DNS
docker exec container1 nslookup container2
# Создать общую сеть и подключить оба
docker network create shared
docker network connect shared container1
docker network connect shared container2
Образ не собирается / COPY failed / файл не найден
# Проверить .dockerignore - возможно файл исключён
cat .dockerignore
# Собрать с verbose выводом
docker build --progress=plain -t myapp . 2>&1 | tee build.log
# Проверить context сборки (что отправляется в daemon)
docker build --no-cache -t myapp . 2>&1 | head -5
Volume не монтируется / Permission denied
# Проверить владельца директории на хосте
ls -la /host/path
# Проверить UID внутри контейнера
docker run --rm myimage id
# Исправить права
chown -R 1000:1000 /host/path
# Или запустить контейнер с нужным пользователем
docker run -d --user $(id -u):$(id -g) -v /host/path:/app/data myapp
docker compose up зависает на «Waiting for container…»
# Посмотреть статус healthcheck
docker inspect db | grep -A 10 Health
# Проверить логи сервиса
docker compose logs db
# Запустить без depends_on для диагностики
docker compose up db
Таблица: коды выхода контейнеров
| Exit Code | Причина | Что делать |
|---|---|---|
| 0 | Штатное завершение | Норма для одноразовых задач |
| 1 | Ошибка приложения | Смотреть docker logs |
| 125 | Ошибка docker run | Проверить параметры команды |
| 126 | Команда найдена, но не исполняема | Проверить права на файл |
| 127 | Команда не найдена | Проверить entrypoint/cmd |
| 137 | SIGKILL — OOM или docker kill | Проверить лимиты памяти |
| 139 | Segfault | Баг в приложении |
| 143 | SIGTERM — штатная остановка | Норма при docker stop |
Мониторинг Docker
Встроенный мониторинг
# Статистика всех контейнеров в реальном времени
docker stats
# Одиночный снимок без обновления
docker stats --no-stream
# Конкретные контейнеры
docker stats web app db --no-stream
# Форматированный вывод
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
Cadvisor для метрик
# Запустить cAdvisor для мониторинга контейнеров
docker run -d \
--name cadvisor \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:ro \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--publish=8080:8080 \
--restart=unless-stopped \
gcr.io/cadvisor/cadvisor:latest
После запуска метрики доступны на http://server:8080. Prometheus может их собирать — cAdvisor экспортирует в нужном формате из коробки.
Альтернативные инструменты
Docker — не единственный вариант. Знать альтернативы полезно.
| Инструмент | Когда использовать | Отличие от Docker |
|---|---|---|
| Podman | Rootless контейнеры, RHEL среды | Без daemon, совместимые команды |
| containerd | Kubernetes, минимальный рантайм | Только рантайм, нет CLI для людей |
| Docker Swarm | Кластер из нескольких хостов | Встроен в Docker, проще Kubernetes |
| Kubernetes | Большие кластеры, оркестрация | Гораздо сложнее, гораздо мощнее |
| Portainer | Веб-интерфейс для Docker | GUI поверх Docker API |
FAQ
Как посмотреть IP адрес контейнера?
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name
Как зайти внутрь контейнера без bash?
Многие минимальные образы не содержат bash. Используй sh:
docker exec -it container_name sh
# или
docker exec -it container_name /bin/sh
Почему docker ps не показывает мой контейнер?
docker ps без флагов показывает только запущенные контейнеры. Если контейнер упал — используй docker ps -a. Потом docker logs container_name чтобы понять почему упал.
Как передать переменные окружения безопасно?
# Через файл .env (не добавлять в git)
docker run --env-file .env myapp
# Через Docker secrets (Swarm)
docker secret create db_password ./password.txt
Как посмотреть что слушает контейнер изнутри?
docker exec container_name ss -tlnp
# или если ss нет
docker exec container_name netstat -tlnp
Как ограничить лог файлы Docker чтобы они не заполняли диск?
Глобально в /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
Или для конкретного контейнера:
docker run -d \
--log-driver json-file \
--log-opt max-size=50m \
--log-opt max-file=3 \
myapp
Как скопировать данные между двумя volumes?
docker run --rm \
-v source_volume:/source:ro \
-v target_volume:/target \
alpine cp -r /source/. /target/
Как обновить переменную окружения в работающем контейнере?
Никак. Переменные окружения устанавливаются при создании контейнера. Нужно пересоздать контейнер с новыми значениями. Это ещё одна причина использовать Compose — docker compose up -d пересоздаёт контейнеры с обновлённой конфигурацией.
Профилактика: как не сломать снова
- Всегда фиксируй версии образов (
nginx:1.25, а неnginx:latest) на продакшене.latest— это билет в ситуацию «всё работало вчера». - Настрой ротацию логов в daemon.json до того как диск заполнится.
- Базы данных — только с volume. Без исключений.
- Пароли и секреты — только через env файлы или Docker secrets. Не в Dockerfile, не в compose.yml.
- Добавь healthcheck к критичным сервисам — это даст понять когда контейнер запущен но не работает.
- Делай бэкап volumes перед обновлением. Особенно баз данных.
- Настрой
--restart=unless-stoppedдля всех продакшен контейнеров.
Итог
Ты закрыл справочник который покрывает 95% реальных сценариев работы с Docker. Образы, контейнеры, сети, тома, Compose, безопасность, очистка, мониторинг и troubleshooting — всё на одной странице.
Держи страницу в закладках. Когда что-то снова пойдёт не так — начинай с раздела Troubleshooting. Если там нет ответа — docker logs и docker inspect закроют оставшиеся 5%.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →



