Перейти к основному содержимому

Работа с Docker

Разработчику Архитектору Инженеру

Работа с Docker

Эта глава связывает теорию с практикой эксплуатации. Ниже собраны действия, которые чаще всего выполняют инженеры в реальных проектах — расчёт ресурсов, сборка образов, запуск, диагностика и безопасные обновления.


Ресурсы

Перед запуском контейнеров важно оценить ресурсы хоста. Это напрямую влияет на стабильность сервиса и скорость работы системы.

Когда вы запускаете контейнеры, они используют ресурсы хостовой системы (допустим, виртуальной машины).

Если контейнеры потребляют больше ресурсов, чем доступно на хосте, это приводит к проблемам:

  • нехватка памяти - ОС начинает использовать подкачку (swap), что сильно замедляет работу;
  • перегрузка CPU - процессор не справляется с нагрузкой, и система становится непригодной для использования;
  • завершение процессов - ОС может принудительно завершить процессы, чтобы освободить ресурсы.

Минимальные требования зависят от типа контейнеров. Рабочая формула простая:

ресурсы хоста = ресурсы ОС и фоновых процессов + ресурсы контейнеров + резерв 20-30%

Для простых и легковесных контейнеров ориентир — от 2 ГБ ОЗУ и 1 CPU.

Если запускать Docker на машине с 2 ГБ ОЗУ, хост быстро упрётся в память даже на базовых сценариях. А если, к примеру, вы разворачиваете веб-приложение, которое само по себе "кушает" 2-3 ГБ, а вдобавок ещё MongoDB, PostgreSQL/MySQL - каждый из них будет просить по 2 ГБ ОЗУ. И если добавить к ним ElasticSearch, то для него минимум 4-8 ОЗУ (для больших индексов нужно ещё больше).

В примере с веб-приложением, БД и Elasticsearch хосту обычно нужно от 10 ГБ ОЗУ и от 5 vCPU. Дополнительно закладывают резерв 20-30% для ОС и фоновых задач.

Поэтому перед запуском нескольких сервисов лучше заранее проверить суммарную нагрузку. Docker позволяет задавать лимиты для каждого контейнера, чтобы один сервис не занял всю память и CPU хоста.


Ограничение памяти.

Используйте флаг --memory для ограничения объёма ОЗУ, доступного контейнеру:

docker run -d --name my-container --memory="2g" my-image

Разбор:

  • --memory="2g" — жёсткий лимит RAM для cgroup контейнера (2 гибибайта).
  • При превышении лимита процесс в контейнере может быть убит OOM-killer'ом ядра.
  • -d — фоновый режим; --name — удобное имя для docker stats и логов.
  • my-image — образ приложения; лимит защищает хост от "утечки" памяти одним сервисом.

В таком случае, контейнер сможет использовать не более 2 ГБ ОЗУ. Если контейнер попытается использовать больше памяти, он будет остановлен.


Ограничение CPU

Используйте флаги --cpus и --cpu-shares для управления CPU:

--cpus: ограничивает количество ядер, доступных контейнеру:

docker run -d --name my-container --cpus="1.5" my-image

Разбор:

  • --cpus="1.5" — контейнеру доступно эквивалентно полутора ядрам CPU (период + quota cgroup).
  • Ограничение смягчает ситуацию, когда один сервис загружает весь хост на 100%.
  • Значение может быть дробным: 0.5, 2.0 и т.д.
  • Работает на cgroup v1/v2 в зависимости от настроек Docker Engine.

здесь контейнер сможет использовать до 1,5 ядра.

--cpu-shares задаёт относительный вес для разделения CPU между контейнерами:

docker run -d --name my-container --cpu-shares=512 my-image

Разбор:

  • --cpu-sharesотносительный вес при конкуренции за CPU, не абсолютная доля ядер.
  • По умолчанию 1024; при 512 контейнер получит примерно вдвое меньше CPU, чем сосед с 1024, если ядра заняты.
  • При простое хоста лимит shares почти не мешает использовать все ядра.
  • Пара --cpus и --cpu-shares можно комбинировать осознанно.

По умолчанию значение равно 1024. Чем меньше значение, тем меньше CPU получит контейнер.


Ограничение подкачки (swap)

Используйте флаг --memory-swap, чтобы ограничить использование подкачки:

docker run -d --name my-container --memory="2g" --memory-swap="3g" my-image

Разбор:

  • --memory="2g" — лимит RAM (без учёта swap в классической модели).
  • --memory-swap="3g" — суммарный потолок RAM+swap для cgroup (здесь до 3 ГБ всего).
  • Разница 3g - 2g — сколько контейнер может уйти в swap под нагрузкой.
  • При исчерпании лимита возможен OOM kill процесса в контейнере.

Здесь общий лимит памяти (ОЗУ+swap) равен 3 ГБ. При превышении лимита процесс в контейнере, как правило, завершается ядром (OOM).


Использование Docker Compose для управления ресурсов

В compose.yaml для обычного docker compose up (без Swarm) удобнее поля mem_limit и cpus — секция deploy.resources в Compose V3 игнорируется, если не включён режим Swarm.

docker compose up

Разбор:

  • Поднимает сервисы из compose.yaml в текущем каталоге.
  • Лимиты mem_limit и cpus из файла применяются к контейнерам при обычном docker compose (не Swarm).
  • Без -d логи всех сервисов идут в терминал; с -d — в фоне.
services:
mongodb:
image: mongo
mem_limit: 2g
cpus: 1.5
elasticsearch:
image: elasticsearch:7.10.1
mem_limit: 4g
cpus: 2

Разбор:

  • services.mongodb — контейнер MongoDB не больше 2 ГБ RAM и ~1.5 CPU.
  • services.elasticsearch — ES 7.10.1 с 4 ГБ и 2 CPU (типичный минимум для dev, в проде считают иначе).
  • mem_limit / cpus — поля Compose для standalone Engine (секция deploy.resources без Swarm игнорируется).
  • Два сервиса в одном файле стартуют вместе и разделяют ресурсы хоста по заданным потолкам.

Как проверить использование ресурсов?

Чтобы избежать перегрузки системы, важно регулярно мониторить использование ресурсов всеми запущенными контейнерами:

docker stats

Разбор:

  • Live-таблица — CONTAINER, CPU %, MEM USAGE / LIMIT, NET I/O, BLOCK I/O, PIDS.
  • Обновляется до прерывания (Ctrl+C); без аргументов — все запущенные контейнеры.
  • Сравнивают MEM USAGE с заданным --memory / mem_limit.
  • Дополняют htop/free на хосте, чтобы отделить нагрузку контейнеров от ОС.

В выводе будет указано использование CPU, ОЗУ, сети. Кроме docker stats, важно использовать и стандартные инструменты мониторинга хостовой системы:

  • Linux — htop, free -h, vmstat.
  • Windows/macOS : Диспетчер задач или Activity Monitor.

Для устойчивой работы достаточно держать простой цикл: оценка потребностей -> лимиты -> мониторинг -> корректировка. Если ресурсов не хватает даже после оптимизации, переходят к кластерному запуску (Docker Swarm или Kubernetes).


Практический чек-лист перед запуском в продакшен

  • заданы лимиты CPU и памяти для критичных контейнеров;
  • настроены healthcheck и политика перезапуска;
  • используется фиксированная версия образа вместо latest;
  • проверены логи приложения и системные метрики под нагрузкой;
  • данные вынесены в volumes, а не в writable-слой контейнера;
  • подготовлен сценарий отката на предыдущий тег.

Установка и настройка

Первым шагом в работе с контейнерами и системой контроля версий является установка и настройка таких инструментов, как Git и Docker.

Эти процессы являются фундаментальными для современных практик разработки и требуют внимательного выполнения последовательности шагов.

Прежде чем приступить к установке новых программных пакетов, обновите индекс пакетов и установленные версии (мы уже рассматривали это ранее):

sudo apt update
sudo apt upgrade

Разбор:

  • sudo apt update — обновляет списки пакетов из /etc/apt/sources.list и .list.d.
  • sudo apt upgrade — устанавливает новые версии уже установленных пакетов (без смены major, если не full-upgrade).
  • Выполняют перед установкой Docker/Git, чтобы тянуть актуальные версии из репозиториев.
  • sudo нужен для записи в системные каталоги пакетного менеджера.

apt update обновляет индекс из /etc/apt/sources.list; apt upgrade подтягивает исправления безопасности и новые версии пакетов.

Git — это распределенная система контроля версий, которая широко используется для управления исходным кодом проектов. Мы уже изучали Git ранее, но это важный момент.

Установка Git через APT и проверка версии:

sudo apt install git
git --version

Разбор:

  • apt install git — ставит Git и зависимости из репозиториев Ubuntu/Debian.
  • git --version — проверка установки и вывод номера версии в stdout.
  • После этого доступны clone, pull, commit для подготовки исходников под сборку образов.

APT автоматически загружает зависимости.

Для работы с контейнерами на Linux установите Docker Engine по официальной инструкции (актуальные версии Ubuntu/Debian). Кратко — через официальный репозиторий и ключ в /etc/apt/keyrings/:

sudo apt update
sudo apt install ca-certificates curl
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 $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Разбор:

  • curl -fsSL ... | gpg --dearmor — импорт GPG-ключа Docker в /etc/apt/keyrings/docker.gpg.
  • echo "deb [arch=... signed-by=...]" | tee ... — подключение официального APT-репозитория Docker для вашей версии Ubuntu.
  • docker-ce — демон Engine; docker-ce-cli — клиент docker.
  • containerd.io — runtime контейнеров под капотом Engine.
  • docker-buildx-plugin — расширенная сборка (multi-platform); docker-compose-plugindocker compose.
  • Цепочка повторяет официальную установку Engine на Linux без Desktop.

На Windows и macOS для разработки чаще используют Docker Desktop.

После установки Docker важно настроить его для удобства использования. По умолчанию выполнение команд Docker требует прав администратора (sudo).

Однако это ограничение можно устранить, добавив пользователя в группу Docker. Для этого используется команда:

sudo usermod -aG docker ${USER}

Разбор:

  • usermod -aG dockerдобавляет пользователя в дополнительную группу docker (-a), не удаляя другие группы.
  • ${USER} — логин текущей сессии; сокет /var/run/docker.sock станет доступен без sudo.
  • Изменение вступает в силу после нового входа в систему или newgrp docker.

После выполнения этой команды потребуется перезагрузка системы или:

newgrp docker

Разбор:

  • Запускает под-shell с активной группой docker без перезагрузки и без полного logout.
  • В этой сессии docker ps должен работать без sudo.
  • Выход из под-shell — exit.

Чтобы проверить успешность добавления пользователя в группу Docker:

groups
id

Разбор:

  • groups — список групп текущего пользователя; в нём должна быть docker.
  • id — uid, gid и все supplementary groups в числовом и текстовом виде.
  • Если docker нет — usermod не применился или нужен relogin.

Когда Docker установится, нужно получить нужный исходный код для сборки (если, конечно, мы не хотим разворачивать уже существующий контейнер).

Клонирование удалённого репозитория создаёт локальную копию со всей историей коммитов. Пример с GitHub:

$ git clone https://github.com/user/repo.git

Разбор:

  • git clone — копирует удалённый репозиторий со всей историей в каталог repo (имя из URL).
  • https://github.com/user/repo.git — плейсхолдер; подставьте URL своего проекта.
  • Символ $ в примере — приглашение shell, в команду не вводят.
  • После clone в каталоге появляются исходники для docker build.

В результате выполнения этой команды создаётся новая директория с именем репозитория, содержащая полную копию данных. Это позволяет разработчикам быстро приступить к совместной работе над проектом, минуя этапы настройки структуры файлов и метаданных.


Сборка

Работа с Dockerfile является ключевым аспектом разработки и развертывания контейнеризованных приложений. Мы отдельно рассмотрим его, конечно, но сейчас пробежимся по общему шагу работы с ним.

Приведём пример Dockerfile для простого Node.js-приложения. Этот файл демонстрирует ключевые директивы, такие как FROM, RUN, COPY и CMD.

Вот пример:

Код ITЗагрузка примера кода…

Разбор:

  • FROM node:16-alpine — базовый образ Node 16 на Alpine (малый размер).
  • WORKDIR /app — рабочая директория для последующих COPY и RUN.
  • COPY package*.json ./ — сначала манифесты npm для кэширования слоя npm install.
  • RUN npm install — установка зависимостей в образ (слой пересобирается при смене package*.json).
  • COPY . . — исходники приложения в /app.
  • CMD ["node", "index.js"] — exec-форма: при docker run стартует Node с index.js (PID 1 в контейнере).

Здесь директива FROM указывает базовый образ, который будет использоваться для создания нового образа, и используется официальный образ Node.js на основе Alpine Linux, что позволяет существенно уменьшить размер финального образа.

Директива WORKDIR задает рабочую директорию внутри контейнера, куда будут копироваться файлы и выполняться команды.

Инструкция COPY используется для копирования файлов из локальной файловой системы в контейнер. Например, COPY package*.json ./ копирует файлы package.json и package-lock.json в корневую директорию контейнера.

Инструкция RUN npm install (см. Dockerfile выше) устанавливает зависимости проекта.

CMD определяет команду, которая будет выполнена при запуске контейнера.

Для Python-приложений аналогичный Dockerfile может выглядеть следующим образом:

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Разбор:

  • FROM python:3.10-slim — урезанный образ Python без лишних пакетов.
  • COPY requirements.txt + RUN pip install --no-cache-dir -r requirements.txt — зависимости отдельным слоем; --no-cache-dir уменьшает размер слоя.
  • COPY . . — код приложения после установки зависимостей (лучше для кэша сборки).
  • CMD ["python", "app.py"] — запуск точки входа app.py интерпретатором Python.

Здесь используется базовый образ Python 3.10-slim, который также минимизирует размер образа за счет исключения ненужных компонентов.

Python 3.10-slim

Разбор:

  • Строка в блоке — напоминание имени тега базового образа (python:3.10-slim), а не команда shell.
  • В реальной сборке тег указывают в FROM Dockerfile, а не отдельной командой в bash.

После создания Dockerfile собирают образ (флаг -t — тег, . — контекст с Dockerfile):

docker build -t myapp:latest .

Разбор:

  • docker build — сборка по Dockerfile в контексте каталога ..
  • -t myapp:latest — имя репозитория myapp и тег latest для результата.
  • . — контекст: все файлы (кроме .dockerignore) отправляются демону.
  • Повторная сборка переиспользует кэш слоёв при неизменных инструкциях.

Публикация в реестр:

docker push myapp:latest

Разбор:

  • Отправляет локальный образ myapp:latest в registry, настроенный по умолчанию (часто Docker Hub после docker login).
  • Для Hub тег обычно username/myapp:latest.
  • Без предварительного login команда завершится ошибкой авторизации.

Пример многоэтапной сборки для Go-приложения:

# Стадия сборки
FROM golang:1.15 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Финальный образ
FROM gcr.io/distroless/static-debian10
COPY --from=builder /app/myapp /myapp
CMD ["/myapp"]

Разбор:

  • FROM golang:1.15 AS builder — стадия сборки с компилятором Go; имя builder для COPY --from.
  • RUN go build -o myapp . — статическая сборка бинарника в /app/myapp.
  • FROM gcr.io/distroless/static-debian10 — минимальный runtime без shell и пакетного менеджера.
  • COPY --from=builder /app/myapp /myapp — в финальный образ попадает только бинарник.
  • CMD ["/myapp"] — запуск скомпилированного файла; attack surface меньше, чем у полного golang-образа.

В этом примере первый этап использует образ golang:1.15 для компиляции исходного кода. На втором этапе создается минимальный образ на основе gcr.io/distroless/static-debian10, куда копируется только исполняемый файл.

Аналогичный метод может быть применен для Node.js-приложений. Например, на первой стадии можно установить все зависимости и выполнить сборку, а на второй — создать runtime-образ только с необходимыми файлами:

Код ITЗагрузка примера кода…

Разбор:

  • Стадия builder: npm install, npm run build — артефакты в /app/dist.
  • Финальная стадия снова node:16-alpine, но без devDependencies сборки.
  • COPY --from=builder /app/dist ./dist — только собранный фронт/бандл.
  • RUN npm install --production — runtime-зависимости без dev-пакетов.
  • Итоговый образ меньше и не содержит исходников и dev-toolchain первой стадии.

Здесь первая стадия выполняет установку зависимостей и сборку приложения, а вторая создает минималистичный образ, содержащий только скомпилированный код и runtime-зависимости.

При выполнении команды docker build Docker проходит несколько этапов:

docker build

Разбор:

  • Базовая команда сборки; в логе BuildKit/legacy builder видны этапы ниже.

  • Без -t и контекста команда не завершит полноценную сборку — в статье перечислены фазы процесса.

  • Типичный полный вызов: docker build -t name:tag ..

    • Load Definition - загрузка Dockerfile и его разбор.
    • Metadata - сбор метаданных (например, имя образа, тег).
    • Transferring Context - передача контекста (файлов и папок) на Docker-демон.
    • Resolve - разрешение зависимостей и проверка наличия кэшированных слоёв.
    • Load Context - загрузка контекста в Docker.
    • Выполнение инструкций - пошаговое выполнение инструкций Dockerfile - apt-get update, COPY, RUN;
    • Exporting to Image - создание финального образа.
    • Layers - сохранение каждого слоя как отдельного объекта.
    • Manifest - создание манифеста образа (JSON с метаданными).
    • Config - настройка конфигурации (например, переменные окружения).
    • Attestation - добавление подписей или аттестаций (если настроено).
    • Naming - присвоение имени и тега образу.
    • Unpacking - подготовка образа для использования.

Запуск и управление контейнерами

Запуск в фоне (-d) с пробросом порта (-p хост:контейнер):

docker run -d -p 8080:8080 <image_name>

Разбор:

  • -d — detached; контейнер не занимает терминал.
  • -p 8080:8080 — порт 8080 хоста → 8080 в контейнере (формат хост:контейнер).
  • <image_name> — образ с приложением, слушающим 8080 внутри.
  • Типичный шаг CI после сборки для smoke-теста HTTP.

Внешние клиенты обращаются к сервису в контейнере через порт хоста.

Важно учитывать, что при использовании брандмауэров, таких как ufw или firewalld, правила межсетевого экрана могут быть обойдены Docker. Для корректной настройки безопасности рекомендуется использовать iptables-nft или iptables-legacy и добавлять правила через цепочку DOCKER-USER.

Пример применения данной команды можно наблюдать в CI/CD-пайплайне, где после сборки образа Docker выполняется его запуск с пробросом портов для тестирования функциональности. После успешного тестирования контейнер может быть остановлен, а образ отправлен в реестр для дальнейшего использования в Kubernetes или других системах оркестрации.

Одним из важнейших аспектов управления контейнерами является возможность анализа их логов для диагностики проблем и мониторинга состояния.

Для просмотра логов используется docker logs, где <container_id> — уникальный идентификатор запущенного контейнера. Команда выводит стандартные потоки (stdout и stderr) и позволяет отслеживать события внутри контейнера в реальном времени.

Если контейнер работает некорректно:

docker logs <container_id>

Разбор:

  • Читает объединённый поток stdout и stderr контейнера.
  • <container_id> — ID или имя из docker ps.
  • Без флагов — весь доступный лог с момента старта контейнера.

Для ограничения вывода или потокового просмотра:

docker logs --tail 100 <container_id>
docker logs -f <container_id>

Разбор:

  • --tail 100 — только последние 100 строк (быстрый просмотр при больших логах).
  • -f (follow) — потоковый вывод новых строк, как tail -f.
  • Удобно в CI и при отладке падений сразу после docker run.

Эти возможности особенно полезны при мониторинге производительности CI/CD-пайплайнов, где время сборки и частота развертываний являются ключевыми показателями эффективности.


Частые эксплуатационные сценарии

Ниже команды, которые регулярно используются в поддержке и инцидентах:

Код ITЗагрузка примера кода…

Разбор:

  • docker ps -a — все контейнеры, включая Exited; видно время и код выхода.
  • docker inspect — JSON — Env, Mounts, NetworkSettings, State, HostConfig.
  • docker top — процессы PID в namespace контейнера с хоста.
  • docker exec -it ... sh — интерактивная отладка внутри работающего контейнера.
  • docker system prune — удаляет остановленные контейнеры, неиспользуемые сети, образы без контейнеров; кэш сборки может пострадать.

Важно запускать docker system prune осознанно: команда удаляет неиспользуемые объекты и может затронуть кэш, который нужен для быстрых сборок.


Связанные статьи