Какие основные инструкции используются в Dockerfile?
Dockerfile состоит из инструкций, каждая из которых создаёт новый слой в образе. Вот основные из них:
🟢 Junior Level
Основные инструкции
Dockerfile состоит из инструкций, каждая из которых создаёт новый слой в образе. Вот основные из них:
Инструкции определения среды
| Инструкция | Что делает | Пример |
|---|---|---|
FROM |
Задаёт базовый образ | FROM openjdk:17-slim |
WORKDIR |
Устанавливает рабочую директорию | WORKDIR /app |
ENV |
Устанавливает переменные окружения | ENV APP_PORT=8080 |
Инструкции работы с файлами
| Инструкция | Что делает |
|---|---|
COPY |
Копирует файлы с хоста в образ |
ADD |
Как COPY, но умеет распаковывать архивы |
Инструкции выполнения команд
| Инструкция | Что делает |
|---|---|
RUN |
Выполняет команду при сборке образа |
CMD |
Команда по умолчанию при запуске контейнера |
ENTRYPOINT |
Основная команда запуска контейнера |
Инструкции настройки
| Инструкция | Что делает |
|---|---|
EXPOSE |
Документирует порт приложения |
USER |
Указывает пользователя для запуска |
Простой пример
FROM openjdk:17-jdk-slim # Базовый образ
WORKDIR /app # Рабочая директория
COPY myapp.jar app.jar # Копируем jar-файл
EXPOSE 8080 # Порт (документация)
ENTRYPOINT ["java", "-jar", "app.jar"] # Команда запуска
COPY vs ADD
- COPY – предпочтительный вариант (95% случаев).
- ADD – если нужна автораспаковка tar-архивов или скачивание по URL (но для URL лучше RUN curl).
Exec form ["cmd", "arg"] – команда запускается напрямую. Shell form cmd arg – через оболочку /bin/sh -c.
ENV – переменная доступна и при сборке, и в запущенном контейнере. ARG – только при сборке.
Что запомнить
FROM— всегда первая инструкцияRUN— выполняется при сборке,CMD/ENTRYPOINT— при запускеCOPY– предпочтительный вариант (95% случаев).ADD– если нужна автораспаковка tar-архивов или скачивание по URL.EXPOSEне открывает порт, только документирует его- Используйте
WORKDIRвместоRUN cd ...
🟡 Middle Level
Классификация инструкций
1. Инструкции определения среды
FROM — задаёт базовый образ. С этого начинается любой Dockerfile. Best Practice: указывайте конкретную версию (openjdk:17-slim), а не latest.
ENV — устанавливает переменные окружения. Доступны и при сборке, и в запущенном контейнере.
ENV JAVA_OPTS="-Xmx512m"
ENV APP_ENV=production
ARG — определяет переменные, доступные только в процессе сборки.
ARG APP_VERSION=1.0.0
RUN echo "Building version $APP_VERSION"
WORKDIR — устанавливает рабочую директорию. Все последующие команды выполняются относительно неё.
WORKDIR /app # лучше, чем RUN cd /app
2. Инструкции работы с файлами
COPY — копирует файлы с хоста в образ.
COPY pom.xml /app/
COPY src/ /app/src/
ADD — расширенная версия COPY. Умеет распаковывать архивы (.tar.gz) и скачивать файлы по URL.
ADD app.tar.gz /app/ # автоматически распакует
3. Инструкции выполнения команд
RUN — выполняет команду при сборке и фиксирует результат в новом слое.
# Объединяйте команды для уменьшения слоёв
RUN apt-get update && \
apt-get install -y git curl && \
rm -rf /var/lib/apt/lists/*
// Удаление в том же RUN критично: если удалить в отдельном RUN,
// файлы останутся в нижнем слое и будут в образе.
CMD — задаёт команду по умолчанию при старте контейнера. Легко переопределить.
CMD ["--server.port=8080"]
ENTRYPOINT — определяет основную команду запуска. Сложнее переопределить.
ENTRYPOINT ["java", "-jar", "/app.jar"]
4. Инструкции настройки доступа
EXPOSE — документирует порт. Не публикует его реально (для этого нужен -p при запуске).
USER — указывает пользователя для запуска.
RUN useradd -r appuser
USER appuser
VOLUME — создаёт точку монтирования для постоянных данных.
VOLUME ["/data"]
Типичные ошибки
| Ошибка | Последствие | Как избежать |
|---|---|---|
RUN apt-get update в отдельном слое |
Кэш устаревает, пакеты не находятся | Объединяйте update && install в один RUN |
Использование shell form CMD java -jar |
Сигналы не доходят до приложения | Используйте exec form ["java", "-jar"] |
| Передача секретов через ARG | Пароли видны в docker history |
Используйте BuildKit secrets (--mount=type=secret) |
| Удаление файлов в отдельном RUN | Файлы остаются в нижних слоях | Удаляйте в том же RUN, где создали |
| Несколько CMD/ENTRYPOINT | Учитывается только последняя | Одна CMD, одна ENTRYPOINT на файл |
Сравнение CMD vs ENTRYPOINT
| Инструкция | Можно переопределить? | Основная цель |
|---|---|---|
| ENTRYPOINT | С трудом (--entrypoint) |
Фиксированная команда |
| CMD | Очень легко | Параметры по умолчанию |
Best Practices
- Объединяйте
RUNкоманды через&&для уменьшения слоёв - Очищайте кэш пакетов в том же
RUNслое - Используйте
WORKDIRвместо цепочекRUN cd - Всегда используйте Multi-stage build для разделения сборки и runtime
Что запомнить
- Каждая
RUN,COPY,ADDсоздаёт новый слой - Порядок инструкций влияет на кэширование
- Используйте Exec form
["cmd", "arg"]вместо Shell form - Очищайте кэш в том же слое, где установка
ENTRYPOINT+CMDвместе — лучшая практика
🔴 Senior Level
Архитектура инструкций и их влияние на образ
Понимание нюансов инструкций критично для создания безопасных, компактных и быстрых в сборке образов.
Глубокий анализ: слоистая модель
Каждая инструкция RUN, COPY, ADD создаёт новый слой (layer). Слои — read-only файловые системы, объединённые через UnionFS (Overlay2).
Критическое последствие: удаление файла в новом слое не удаляет его из образа — создаётся лишь «whiteout» запись. Размер файла в образе = сумма всех слоёв, в которых он присутствует.
# ПЛОХО: файл остаётся в нижнем слое
RUN apt-get update && apt-get install -y package
RUN rm -rf /var/lib/apt/lists/*
# ХОРОШО: один слой, кэш очищен сразу
RUN apt-get update && \
apt-get install -y package && \
rm -rf /var/lib/apt/lists/*
Trade-offs
| Решение | Плюс | Минус |
|---|---|---|
| Shell form | Удобство (pipes, переменные) | PID 1 проблема, сигналы не доходят |
| Exec form | Правильная обработка сигналов | Нет shell-функциональности |
| ARG для конфигурации | Просто | Видно в docker history, не runtime |
| ENV для конфигурации | Доступно runtime | Видно в docker inspect |
| ADD для URL | Не нужен RUN curl/wget | Непредсказуемый кэш, нет retry |
| RUN curl/wget | Контроль, retry, checksum | Дополнительный слой |
ARG vs ENV: тонкости
| Характеристика | ARG | ENV |
|---|---|---|
| Доступна при сборке | Да | Да |
| Доступна в контейнере | Нет | Да |
Виден в docker inspect |
Нет | Да |
Виден в docker history |
Да (значение!) | Да |
Security warning: значения ARG видны в docker history. Не передавайте секреты через ARG!
# ПЛОХО: пароль виден в docker history
ARG DB_PASSWORD=secret123
# ХОРОШО: BuildKit secrets
RUN --mount=type=secret,id=db_pass cat /run/secrets/db_pass
Shell form vs Exec form: критический нюанс
Exec form (рекомендуемая):
ENTRYPOINT ["java", "-jar", "/app.jar"]
Запускается напрямую как процесс с PID 1. Правильно обрабатывает сигналы (SIGTERM, SIGKILL). Критично для graceful shutdown в Kubernetes.
Shell form:
ENTRYPOINT java -jar /app.jar
Запускается как подпроцесс /bin/sh -c. Сигналы ОС приходят в оболочку sh, а не в приложение. Приложение может быть «убито» жёстко без завершения транзакций.
Проблема PID 1: в Linux процесс с PID 1 имеет особое поведение — он не получает SIGTERM по умолчанию. Решение: exec form, tini, или docker run --init.
Edge Cases
- ONBUILD в multi-stage:
ONBUILDинструкции выполняются при использовании образа как базового. В multi-stage это может привести к неожиданным побочным эффектам. - Glob patterns в COPY:
COPY target/*.jar— если файлов нет, сборка падает. Если файлов несколько, копируются все в указанную директорию. - Символьные ссылки:
COPYследует за symlink’ами на хосте. Это может привести к включению неожиданных файлов. - Временные метки:
COPYсохраняет mtime файлов. Это влияет на детерминизм сборки. BuildKit--metadata-fileпомогает отслеживать. - ENV и escaping:
ENV FOO=bar\ baz— пробел в значении.ENV FOO="bar baz"— кавычки включаются в значение.
HEALTHCHECK: production-обязанность
HEALTHCHECK --interval=30s --timeout=3s --retries=3 --start-period=60s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
Без HEALTHCHECK оркестратор не знает, живо ли приложение. Контейнер может быть Running, но приложение внутри — мёртвым (deadlock, out of memory).
BuildKit: продвинутые возможности
# Синтаксис BuildKit
# syntax=docker/dockerfile:1
# Секреты
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm install
# SSH forwarding (для private git repos)
RUN --mount=type=ssh git clone git@github.com:org/private-repo.git
# Cache mount (для package managers)
RUN --mount=type=cache,target=/root/.m2 mvn package
# TMPFS mount
RUN --mount=type=tmpfs,target=/tmp make
ONBUILD: инструкция для родительских образов
# В базовом образе
ONBUILD COPY . /app
ONBUILD RUN mvn package
Используется для создания родительских образов, автоматизирующих шаги для потомков. Опасно в multi-stage: ONBUILD не выполняется для subsequent stages.
LABEL: метаданные для CI/CD
LABEL maintainer="team@example.com"
LABEL version="1.0.0"
LABEL org.opencontainers.image.source="https://github.com/org/repo"
LABEL org.opencontainers.image.revision="abc123"
Полезно для отслеживания образов в registry, автоматизации, compliance.
Performance
| Оптимизация | Влияние |
|---|---|
| Разделение pom.xml и src | Кэш hit rate > 90% |
| Объединение RUN команд | На 20-40% меньше слоёв |
| Alpine вместо full | На 60-80% меньше размер |
| Multi-stage build | На 50-70% меньше финальный образ |
| BuildKit cache mount | На 50% быстрее повторные сборки |
Резюме
- Каждая команда
RUN,COPY,ADDсоздаёт новый слой — удаляйте временные файлы в том же слое. - Используйте Exec form для
CMDиENTRYPOINT— критично для обработки сигналов. - ARG виден в
docker history— не передавайте секреты. HEALTHCHECK— обязательный элемент production-образа.- BuildKit (
--mount=type=secret/cache/ssh) — современный стандарт сборки. - Оптимизация слоёв = экономия места в registry + ускорение деплоя.
🎯 Шпаргалка для интервью
Обязательно знать:
- RUN выполняется при сборке, CMD/ENTRYPOINT — при запуске контейнера
- Каждая RUN/COPY/ADD создаёт новый слой; удаление в отдельном RUN не удаляет файл из образа
- Exec form
["cmd", "arg"]критичен для signal handling (graceful shutdown) - ARG виден в
docker history— не передавайте секреты; используйте BuildKit secrets - ENV доступна в runtime, ARG — только при сборке
- HEALTHCHECK обязателен для production-образов
- BuildKit:
--mount=type=secret/cache/ssh— современный стандарт сборки
Частые уточняющие вопросы:
- «Почему shell form плоха?» — Запускается через
/bin/sh -c, сигналы ОС не доходят до приложения (PID 1 проблема) - «Зачем объединять RUN команды через &&?» — Каждая инструкция = слой; объединение уменьшает число слоёв
- «Что делает ONBUILD?» — Инструкции выполняются при использовании образа как базового (для родительских образов)
- «EXPOSE открывает порт?» — Нет, только документирует; реальный маппинг через
-pпри запуске
Красные флаги (НЕ говорить):
- «EXPOSE делает порт доступным снаружи» (только документирует, нужен
-p) - «Передаю пароли через ARG» (видно в
docker history) - «Удаляю файлы в отдельном RUN слое» (файлы остаются в нижнем слое)
- «Использую shell form для ENTRYPOINT» (проблема PID 1, сигналы теряются)
Связанные темы:
- [[Что такое Dockerfile]] — основы Dockerfile
- [[В чём разница между CMD и ENTRYPOINT]] — детально о запуске
- [[Что такое multi-stage build]] — оптимизация образа