Питання 3 · Розділ 14

Що таке Dockerfile?

Це декларативний файл: ви описуєте, яким має бути образ, а Docker сам вирішує, як його зібрати. Це як рецепт: ви пишете кроки, а Docker збирає з них готовий образ, який можна за...

Мовні версії: English Russian Ukrainian

🟢 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 .:

  1. Docker-клієнт передає вміст поточної папки (Build Context) Docker-демону.
  2. Демон покроково виконує інструкції з Dockerfile.
  3. Кожна інструкція створює новий шар (layer) в образі.
  4. Шари кешуються — якщо інструкція не змінилася, 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 -- інвалідація всіх наступних шарів.
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

  1. Єдина відповідальність — один контейнер, один процес.
  2. Мінімізація розміру — використовуйте легкі базові образи (alpine, slim, distroless).

Alpine – мінімальний Linux-дистрибутив (~5 МБ), часто використовується як база для образів.

  1. Безпека — не запускайте додаток від root. Використовуйте USER.
  2. Конкретні версії — завжди вказуйте точні версії базових образів.

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).

Кеш інвалідація відбувається коли: змінилася інструкція, змінилися файли, що копіюються, змінився хеш батьківського шару.

Патерн “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+): паралельне виконання незалежних кроків, secret-менеджмент (--mount=type=secret), SSH forwarding (--mount=type=ssh), cache mounts (--mount=type=cache).

Продуктивність

Базовий образ Розмір Час 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

Моніторинг

  • 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

Часті уточнюючі питання:

  • «Чому важливий порядок інструкцій?» — Зміна інструкції інвалідує всі наступні шари кешу
  • «Що таке .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]] — запуск контейнера