Что такое Dockerfile?
Это декларативный файл: вы описываете, каким должен быть образ, а Docker сам решает, как его собрать. Это как рецепт: вы пишете шаги, а Docker собирает из них готовый образ, кот...
🟢 Junior Level
Простое объяснение
Dockerfile — это текстовый файл с инструкциями для создания Docker-образа.
Это декларативный файл: вы описываете, каким должен быть образ, а Docker сам решает, как его собрать. Это как рецепт: вы пишете шаги, а Docker собирает из них готовый образ, который можно запустить как контейнер.
Аналогия
Dockerfile — это кулинарный рецепт. FROM — это основа (например, тесто), COPY — добавление ингредиентов, RUN — приготовление, ENTRYPOINT — способ подачи блюда. Из одного рецепта можно приготовить сколько угодно одинаковых блюд (контейнеров).
Пример
# 1. Базовый образ
FROM openjdk:17-jdk-slim
# 2. Рабочая директория
WORKDIR /app
# 3. Копирование файла
COPY myapp.jar app.jar
# 4. Порт приложения
EXPOSE 8080
# 5. Команда запуска
ENTRYPOINT ["java", "-jar", "app.jar"]
# Собрать образ из Dockerfile
docker build -t myapp .
# Запустить контейнер
docker run -p 8080:8080 myapp
Основные инструкции
| Инструкция | Что делает |
|---|---|
FROM |
Указывает базовый образ (например, openjdk:17) |
WORKDIR |
Задаёт рабочую директорию |
COPY |
Копирует файлы в образ |
RUN |
Выполняет команду при сборке |
EXPOSE |
Указывает порт приложения |
ENTRYPOINT |
Команда запуска приложения |
Что запомнить
- Dockerfile — это рецепт для создания Docker-образа
- Каждая инструкция создаёт новый слой в образе
FROM— всегда первая инструкция- Используйте конкретные версии образов, а не
latest docker buildсобирает образ,docker runзапускает контейнер
Когда НЕ нужен Dockerfile
Если используете platform-as-a-service (Heroku, Railway, Render), Dockerfile может не понадобиться – платформа сама собирает образ из кода.
🟡 Middle Level
Как работает сборка образа
Когда вы запускаете docker build .:
- Docker-клиент передаёт содержимое текущей папки (Build Context) Docker-демону.
- Демон пошагово выполняет инструкции из Dockerfile.
- Каждая инструкция создаёт новый слой (layer) в образе.
- Слои кэшируются — если инструкция не изменилась, Docker использует кэш.
Build Context и .dockerignore
Build Context — это всё содержимое директории, переданное Docker. Чтобы не передавать лишние файлы (.git, target/, логи), используйте .dockerignore:
.git
target/
*.log
.idea/
Понятие слоёв (Layers) и кэширование
Docker использует слоистую файловую систему (UnionFS/Overlay2):
- Каждая команда (
RUN,COPY,ADD) создаёт новый слой - Слои кэшируются — если инструкция и файлы не изменились, Docker берёт готовый слой из кэша
Правило оптимизации: Располагайте редко меняющиеся инструкции в начале, часто меняющиеся — в конце.
# ПЛОХО: при каждом изменении кода кэш зависимостей сбрасывается
COPY src /app/src
COPY pom.xml /app
RUN mvn -f /app/pom.xml clean package
# ХОРОШО: зависимости кэшируются отдельно
COPY pom.xml /app
RUN mvn -f /app/pom.xml dependency:go-offline
// Docker кэширует слой, если инструкция и ВСЕ предыдущие слои не изменились.
// Изменился pom.xml -- invalidate всех последующих слоёв.
COPY src /app/src
RUN mvn -f /app/pom.xml clean package
Типичные ошибки
| Ошибка | Последствие | Как избежать |
|---|---|---|
FROM openjdk:latest |
Недетерминированные сборки | FROM openjdk:17-jdk-slim |
Нет .dockerignore |
Медленная сборка, большие образы | Создайте .dockerignore |
| Все команды в одном RUN | Невозможно использовать кэш частично | Разделяйте логические шаги |
| Запуск приложения от root | Риск безопасности | USER appuser |
| Один огромный слой | Нет кэширования, медленный rebuild | Разделяйте зависимости и код |
Основные принципы хорошего Dockerfile
- Единственная ответственность — один контейнер, один процесс.
- Минимизация размера — используйте лёгкие базовые образы (
alpine,slim,distroless).
Alpine – минимальный Linux-дистрибутив (~5 МБ), часто используется как база для образов.
- Безопасность — не запускайте приложение от
root. ИспользуйтеUSER. - Конкретные версии — всегда указывайте точные версии базовых образов.
Multi-stage build
Позволяет компилировать код в одном временном образе, а в итоговый копировать только артефакт:
# Stage 1: Build
FROM maven:3.8-openjdk-17 AS build
COPY src /app/src
COPY pom.xml /app
RUN mvn -f /app/pom.xml clean package -DskipTests
# Stage 2: Runtime
FROM openjdk:17-jdk-slim
COPY --from=build /app/target/app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
Что запомнить
- Каждая инструкция = новый слой
- Кэширование — ключ к быстрой сборке
- Порядок инструкций важен для производительности
- Используйте
.dockerignoreдля исключения лишних файлов - Multi-stage build — стандарт для продакшен-образов
🔴 Senior Level
Dockerfile как Infrastructure as Code
Dockerfile — это не просто скрипт сборки, это фундамент Infrastructure as Code (IaC) для уровня приложений. Он определяет воспроизводимость, безопасность и эффективность доставки ПО.
Глубокий анализ процесса сборки
Build Context и его влияние на CI/CD
docker build . → tar-архив всей директории → отправка Docker daemon
Проблемы: большой контекст = медленная отправка (особенно в remote daemon). .dockerignore работает как .gitignore, но для Docker. В CI/CD контекст может включать артефакты предыдущих сборок.
Best Practice:
# Исключаем всё, кроме нужного
**
!src/
!pom.xml
!Dockerfile
Механизм кэширования: глубокое понимание
Docker вычисляет хэш каждого слоя на основе: самой инструкции, хэшей предыдущих слоёв, содержимого файлов (для COPY и ADD).
Кэш invalidation происходит когда: изменилась инструкция, изменились копируемые файлы, изменился хэш родительского слоя.
Паттерн “Dependency Layer” для Java:
FROM maven:3.8-openjdk-17 AS build
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B # редко меняется → кэшируется
COPY src ./src
RUN mvn package -DskipTests # часто меняется
Это даёт 90%+ hit rate кэша при сборках без изменения зависимостей.
Trade-offs
| Решение | Плюс | Минус |
|---|---|---|
| Alpine-образы | Минимальный размер (~5MB base) | musl libc ≠ glibc, проблемы с native libraries |
| Slim-образы | glibc совместимость, маленький размер | Больше alpine |
| Distroless | Минимальная поверхность атаки | Нет shell для отладки, нужны ephemeral debug containers |
| Много слоёв | Лучшее кэширование | Больше мета-данных, медленнее pull |
| Мало слоёв | Быстрый pull | Хуже кэширование |
Edge Cases
- Alpine + native libraries: Alpine использует musl libc. JNI-библиотеки (Netty epoll, PostgreSQL native) могут требовать glibc. Решение: используйте
debian-slimили собирайте под musl. - Таймауты при сборке:
RUN apt-get updateможет зависнуть из-за сетевых проблем. Решение: используйте зеркала, retry-логику. - Non-deterministic builds:
apt-get updateбез фиксации версий пакетов даёт разные результаты. Решение:apt-get install -y package=1.2.3-1. - Cross-platform builds: Сборка amd64 образа на ARM (Apple Silicon). Решение:
docker buildxс QEMU эмуляцией или remote builders.
Безопасность Dockerfile
# Production-ready Dockerfile для Spring Boot
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
WORKDIR /app
COPY --from=build /build/target/*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:+UseG1GC", "-jar", "app.jar"]
Критические правила:
- Не используйте
latest - Не запускайте от root
- Read-only filesystem где возможно
- Минимизируйте поверхность атаки
- Используйте
HEALTHCHECK
Влияние на CI/CD pipeline
developer → git push → CI запускает docker build →
cache hit? → быстро (секунды) : медленно (минуты) →
docker push → CD деплоит
Оптимизация CI: registry cache (pull предыдущего образа), BuildKit cache, layer sharing между образами.
BuildKit (Docker 23+): параллельное выполнение независимых шагов, секрет-менеджмент (--mount=type=secret), SSH forwarding (--mount=type=ssh), cache mounts (--mount=type=cache).
Performance
| Базовый образ | Размер | Время pull | Время start |
|---|---|---|---|
openjdk:17 |
~500 МБ | ~30s | ~5s |
openjdk:17-slim |
~300 МБ | ~15s | ~5s |
eclipse-temurin:17-jre-alpine |
~100 МБ | ~5s | ~4s |
distroless/java17 |
~80 МБ | ~4s | ~4s |
Monitoring
docker history <image>— анализ слоёв и их размеровdocker build --progress=plain— детальный вывод сборкиdive <image>— интерактивный анализатор слоёв- BuildKit
--progress=trace— tracing каждого шага
Production Story
Команда из 50 разработчиков столкнулась с тем, что CI-сборка занимала 12 минут. Анализ показал: каждый раз загружались все Maven-зависимости заново. Внедрение multi-stage build с раздельным кэшированием pom.xml сократило время до 2 минут (83% improvement). Дополнительно: переход на slim-образ уменьшил размер с 650MB до 280MB, что ускорило деплой в 2.3 раза и сэкономило 40% storage в registry.
Резюме
- Dockerfile — рецепт создания иммутабельного артефакта. Понимание кэширования слоёв — ключ к быстрым CI/CD.
- Всегда стремитесь к минимизации слоёв и Multi-stage сборке.
- Dockerfile должен быть детерминированным (конкретные версии, не
latest). - Безопасность: non-root пользователь, минимальный базовый образ, health checks.
- На масштабе оптимизация Dockerfile экономит сотни часов CI/CD и гигабайты storage.
🎯 Шпаргалка для интервью
Обязательно знать:
- Dockerfile — декларативный рецепт создания иммутабельного Docker-образа
- Каждая инструкция (RUN, COPY, ADD) создаёт новый read-only слой
- Кэширование слоёв — ключ к быстрой сборке: порядок инструкций критичен
- Multi-stage build — стандарт для production: сборка в одном образе, runtime в другом
- Exec form
["cmd", "arg"]обязателен для CMD/ENTRYPOINT (signal handling) - Безопасность: non-root пользователь, конкретные теги, минимальный базовый образ
- BuildKit: секреты (
--mount=type=secret), SSH forwarding, cache mounts
Частые уточняющие вопросы:
- «Почему важен порядок инструкций?» — Изменение инструкции invalidates все последующие слои кэша
- «Что такое .dockerignore?» — Исключает файлы из build context (как .gitignore для Docker)
- «Почему Alpine может быть проблемой?» — musl libc ≠ glibc; JNI-библиотеки могут не работать
- «Чем COPY отличается от ADD?» — ADD умеет распаковывать архивы и скачивать по URL, но COPY предпочтительнее
Красные флаги (НЕ говорить):
- «Использую
latestтег для удобства» (недетерминированные сборки) - «Секреты передаю через ARG» (видны в
docker history) - «Запускаю приложение от root в контейнере» (риск безопасности)
- «ADD лучше чем COPY» (COPY — best practice в 95% случаев)
Связанные темы:
- [[Какие основные инструкции используются в Dockerfile]] — детальный разбор инструкций
- [[Что такое multi-stage build]] — оптимизация размера образа
- [[В чём разница между CMD и ENTRYPOINT]] — запуск контейнера