Які основні інструкції використовуються в 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.
Продуктивність
| Оптимізація | Вплив |
|---|---|
| Розділення 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]] — оптимізація образу