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

В этой статье я расскажу о некоторых функциях Docker, которые редко попадают в учебные руководства и официальные туториалы, но которые могут облегчить жизнь разработчикам и понять Docker чуть глубже. По ходу статьи фичи будут переходить от более известных к менее известным (подобно когда-то популярному формату айсбергов).


Игнорирование файлов при сборке:

Когда вы запускаете сборку образа, Docker-клиент ищет в корне контекста файл .dockerignore. Если он найден, все файлы и каталоги, подпадающие под его шаблоны, исключаются из контекста ещё до того, как будут переданы билдеру.

Если у вас несколько Dockerfile, можно задать для каждого свой файл игнорирования. Для этого: файл игнорирования кладётся в ту же папку, что и сам Dockerfile, и его имя начинается с названия Dockerfile. Вот пример такого подхода:

├── index.ts
├── src/
├── docker
│  ├── build.Dockerfile
│  ├── build.Dockerfile.dockerignore
│  ├── lint.Dockerfile
│  ├── lint.Dockerfile.dockerignore
│  ├── test.Dockerfile
│  └── test.Dockerfile.dockerignore
├── package.json
└── package-lock.json

С шаблонами составления .dockerignore вы можете ознакомиться тут.

Пример составления .dockerignore

# комментарий. будет проигнорен докером
**/.git
**/node_modules/ # любые папки node_modules на любом уровне
**/LICENSE
**/.vscode
**/npm-debug.log
**/coverage
**/logs
**/.editorconfig
**/.aws
temp? # исключит файлы и папки в корне, имена которых отличаются на 1 символ ( tempa или /tempb )
*.md # все markdown в корне(но не в сабдерикториях)
!README*.md # пощадить все файлы, начинающиеся с README и формата markdown 

Многоступенчатая сборка (multi-stage builds)

Многие знают про возможность использовать несколько FROM в одном Dockerfile, но далеко не все понимают, насколько полезным инструментом это является. Представьте, что у вас есть приложение на Go или C++, которое сначала нужно скомпилировать, а затем запустить в контейнере. Если вы используете один контейнер для всего процесса, образ получается тяжёлым: там присутствуют компиляторы, библиотеки для разработки и прочее.

Multi-stage позволяет в одном Dockerfile отделить стадию сборки (с компиляторами, dev-зависимостями) от стадии рантайма. В результате финальный образ содержит только необходимые двоичные файлы и библиотеки — меньше места и быстрее старт.

Multi-stage позволяет разделять сборку и рантайм в одном Dockerfile. В первом контейнере в FROM ставится образ с компилятором (golang:1.22.2), во втором — минимальный рантайм (alpinedistroless), ==куда копируется готовый исполняемый файл==. В итоге финальный образ весит считаные мегабайты.

FROM golang:1.24 AS build
WORKDIR /app
COPY . .
RUN go build -o /app/bin/app

FROM alpine:3.20
COPY --from=build /app/bin/app /usr/local/bin/app
CMD ["app"]

Простой шаблон (Go):

FROM golang:1.24 AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/bin/app ./cmd/app

FROM alpine:3.20
RUN adduser -D appuser
COPY --from=build /app/bin/app /usr/local/bin/app
USER appuser
CMD ["/usr/local/bin/app"]

AS build даёт именованный этап, откуда потом COPY --from=build берёт файлы.

Используйте явные теги образов — (golang:1.24alpine:3.20) гарантируют воспроизводимость; не используйте latest в проде.

Обязательно используйте .dockerignore, чтобы не копировать большие/лишние каталоги в контекст (node_modules, .git, tmp). Это ускоряет сборку и уменьшает шанс утечки секретов.

Копируйте только нужные файлы, не копируйте целые папки, если нужен только бинарник.

Типы Mounts

BuildKit позволяет в инструкциях RUN использовать дополнительные монтирования (mounts), чтобы облегчить сборку образов. Типы монтирования:

Тип

Назначение

bind

погрузить директорию или файл из файловой системы хоста в контейнер, для использования в RUN.

cache

кэшировать директории между сборками, ускорить установку зависимостей, компиляцию и др.

ssh

давать доступ к SSH-агенту или ключам для клонирования приватных репозиториев или других SSH-операций.

secret

безопасно передавать секреты (API-ключи, пароли и др.), не включать их в итоговый образ.

Подробнее по каждому типу

1. Bind (type=bind)

Назначение:
Подключает (mount) файлы или директории внутрь контейнера в момент RUN, без копирования в образ. То есть, содержимое доступно для команды, но не сохраняется после окончания RUN, если только команда явно не скопирует его куда-то.

Выполните следующую команду, чтобы запустить bash в контейнере ubuntu с помощью bind mount.

Пример:

# syntax=docker/dockerfile:1.3
FROM node:16

RUN --mount=type=bind,source=./tools,target=/app/tools \
    ./tools/setup.sh

Опции:

  • target / dst — путь внутри контейнера, куда монтируется.

  • source / src — путь внутри from. Значение по умолчанию — корень from.

  • from — Build stage, context, или имя образа для установки корня source.

  • rw / readwrite — разрешить запись на mount. Записанные данные не сохраняются.

Пример:

docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash

или

docker run -it \
  -v "$(pwd):/src" \
  ubuntu bash

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

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

2. Cache (type=cache)

Назначение:
Дать возможность накапливать результат каких-то операций между сборками, чтобы не запускать полную работу заново. Часто используется для пакетных менеджеров, компиляторов, apt/dpkg, pip, npm, go, ccache и т. д.

Постоянный кэш помогает ускорить этапы сборки. Даже если вы перестраиваете слой, вы загружаете только новые или изменённые пакеты.

Пример:

# syntax=docker/dockerfile:1.3
FROM python:3.11

WORKDIR /app

COPY requirements.txt .

RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

COPY . .

RUN python setup.py install

Опции:

  • target — где внутри контейнера будет располагаться кэш.

  • id — идентификатор кэша, чтобы отделять разные кэши, если нужно. По умолчанию часто равен пути target.

  • sharing — как кэш может быть использован: sharedprivatelocked.

    • shared — разные билд-команды (писатели) могут одновременно писать.

    • private — каждый писатель получит «свой» кэш mount.

    • locked — второй писатель ждёт, пока первый освободит mount.

  • from — Создание stage, context, или имени изображения для использования в качестве базы для монтирования кэша. По умолчанию — пустая директория.

  • ro / readonly — если нужно только чтение из кэша.

  • source — путь внутри from. Значение по умолчанию — корень from.

  • mode — права доступа для новой кэш-директории в восьмеричном формате. По умолчанию: 0755.

  • uid — идентификатор пользователя для новой кэш-директории. По умолчанию: 0.

  • gid — идентификатор группы для новой кэш-директории. По умолчанию: 0.

3. SSH (type=ssh)

Если учётные данные, которые вы хотите использовать в своей сборке, являются сокетом или ключом SSH, вы можете использовать mount SSH вместо secret mount. Клонирование частных репозиториев Git является распространённым вариантом использования для mount SSH.

SSH ключи / агент передаётся временно, не остаётся в образе.
Опции:

  • id — идентификатор ssh.

  • target — куда в контейнере монтировать ssh сокет / ключ.

Пример:

# syntax=docker/dockerfile:1.4
FROM ubuntu:22.04

RUN --mount=type=ssh git clone git@github.com:myorg/private.git /src/private

4. Secret (type=secret)

Назначение:
Когда нужны конфиденциальные данные во время сборки: API-ключи, токены, credentials, приватные конфигурации. Нужно, чтобы они были доступны только во время RUN, и не остались в конечном образе.

На стадии сборки нужно передать секрет через команду docker build

```bash
docker build --secret id=mysecret,src=/local/path/to/secret.txt .
```

В Dockerfile

```dockerfile
RUN --mount=type=secret,id=mysecret \
    some-command-that-needs-secret /run/secrets/mysecret
```

Опции:

  • id — имя секрета.

  • target — куда монтировать (если не дефолтное /run/secrets/).

  • required=true/false — если секрет обязателен, и сборка должна провалиться, если его нет.

  • modeuidgid — права доступа к файлу.


Здоровье контейнеров: HEALTHCHECK

Иногда сервис формально запущен, но внутри него процесс повис или работает некорректно. Docker позволяет описать внутри образа команду для проверки состояния контейнера.

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

HEALTHCHECK в Dockerfile даёт Docker команду для периодической проверки состояния контейнера; статусы: startinghealthyunhealthy.

HEALTHCHECK [OPTIONS] CMD command

Пример для веб-сервиса:

HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
  CMD curl -f http://localhost:8080/ || exit 1
  • --timeout — если ответ на запрос не приходит в течение этого времени, то запрос считается неуспешным.

  • --start-period — даёт службе время на старт, прежде чем начинать проверки.

  • --interval — время между проверками.

  • --retries — число неудачных попыток до перехода в unhealthy.

Статус выхода команды указывает на состояние здоровья контейнера. Возможные значения:

0: success — контейнер работоспособен и готов к использованию.
1: unhealthy — контейнер работает некорректно.

Команда должна быть лёгкой, детерминированной и возвращать код 0 при OK. Не делайте тяжёлых end-to-end тестов внутри HEALTHCHECK — это нагружает контейнер.

В Dockerfile может быть только одна инструкция HEALTHCHECK. Если указано несколько, действовать будет только последняя.

Теперь docker ps показывает STATUS и health (если проверка добавлена).
Отдельно здоровье можно проверить так: docker inspect --format='{{json .State.Health}}'

Более того, оркестраторы вроде Docker Swarm или Kubernetes могут использовать эту информацию для перезапуска контейнера. Оркестраторы (Swarm, ECS, Kubernetes) и другие инструменты могут использовать этот статус для перезапуска или маршрутизации трафика.


Log drivers: управление логами

По умолчанию Docker сохраняет логи в JSON-файл. Но это далеко не всегда удобно. Для интеграции с системами мониторинга можно выбрать другие драйверы

Docker поддерживает несколько драйверов логирования (json-file по умолчанию, syslogjournaldfluentdgelf и т. д.)

Пример запуска контейнера с syslog:

docker run --log-driver=syslog nginx:1.27

Или задать дефолт для всех контейнеров в /etc/docker/daemon.json:

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Docker поддерживает несколько встроенных драйверов:

  • json-file — стандартный вариант, пишет логи в локальные JSON-файлы.

  • local — похож на json-file, но использует более компактное бинарное хранение и лучше масштабируется.

  • syslog — отправляет логи в системный журнал.

  • journald — интеграция с systemd и journalctl.

  • fluentd — пересылает логи в Fluentd-агент.

  • gelf — передаёт сообщения в Graylog или Logstash через протокол GELF.

  • awslogssplunkdatadoggcplogslogentries — отправляют логи в облачные или коммерческие системы.

Каждый драйвер имеет свои особенности, параметры и требования. Например, fluentd требует установленного и запущенного агента Fluentd на хосте, а syslog — доступного демона syslog.

Docker login

Когда вы вводите docker login и видите предупреждение

WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credential-stores

— Docker честно говорит, что по умолчанию сохраняет ваши учётные данные в файл ~/.docker/config.json в виде, который не шифруется (только кодируется base64).

Это означает, что любой base64 --decode вернёт user:password

Что такое credential helper и зачем он нужен

Credential helper (помощник по учётным данным) — это небольшая программа, которая умеет прятать/доставать пароли в/из платформенного хранилища (macOS Keychain, Windows Credential Manager, GNOME Keyring / secretservice, pass (password-store) и т. п.)

Docker сам не шифрует, но может делегировать хранение этим помощникам: тогда в config.json вместо auth будет ссылка на helper, а сам пароль хранится в безопасном хранилище ОС.

Репозиторий официальных helper-ов — docker-credential-helpers на GitHub.

Там написана подробная инструкция по установке.

Хранилище учётных данных указывается в файле $HOME/.docker/config.json, чтобы сообщить Docker Engine о его использовании. Значением параметра credsStore должен быть суффикс используемой программы (то есть часть, идущая после docker-credential-).

Примеры конфигурации

macOS (Keychain). Самый простой вариант на Mac — использовать osxkeychain. В ~/.docker/config.json добавить:

{ "credsStore": "osxkeychain" }

После этого docker login положит пароль в Keychain, а в config.json не будет явных паролей.

В Linux часто используют pass.

Пример использования pass:

# установить pass и gpg (в Debian/Ubuntu)
sudo apt-get update
sudo apt-get install -y pass gpg

# инициализировать хранилище pass (создать/указать GPG-ключ)
gpg --full-generate-key # `RSA and RSA`; 4096; 0;
gpg --list-keys
pass init "ваш pub GPG_KEY_ID"

# скачать helper docker-credential-pass (пример: релиз v0.9.3)
curl -L -o /usr/local/bin/docker-credential-pass \
  https://github.com/docker/docker-credential-helpers/releases/download/v0.9.3/docker-credential-pass-v0.9.3.linux-amd64
chmod +x /usr/local/bin/docker-credential-pass

# настроить Docker:
mkdir -p ~/.docker
cat > ~/.docker/config.json <<'EOF'
{ "credsStore": "pass" }
EOF

После этого docker login будет сохранять данные в pass, а не в config.json. (Релизы helper-ов см. репозиторий — текущая версия релиза на момент написания: v0.9.3)

В Windows Docker Desktop по умолчанию интегрируется с Windows Credential Manager / Docker Desktop store (credsStore обычно = desktop или wincred в зависимости от сборки).

На Windows настройки доступны в GUI Docker Desktop — опция «Securely store docker logins»; либо вручную в config.json


Многие используют только базовые команды вроде docker build и docker run, не догадываясь, сколько возможностей скрыто внутри. Docker за 12 лет наполнился стольким количеством функций, что документация стала похожа на бесконечный океан.

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

Ваш фидбек повлияет на выпуск 2 части. Жду ваших комментариев :)

© 2025 ООО «МТ ФИНАНС»

Only registered users can participate in poll. Log in, please.
Узнали что-нибудь новое из статьи
5.13%Да, всё новое2
74.36%Да, некоторые фичи знал до этого29
20.51%Нет8
39 users voted. 3 users abstained.