Есть ли принципиальная разница между инструкциями в Dockerfile и в каком порядке их писать
Что проверяют
Понимаете ли вы, что инструкции работают в разное время, при сборке или при запуске, и что порядок определяет, какая часть кэша слоёв переживёт следующую сборку. Следом часто спрашивают про разницу CMD и ENTRYPOINT или просят разобрать чужой Dockerfile.
Как отвечать
Разницу удобно показать по группам:
- меняют файловую систему и дают слой:
RUN,COPY,ADD.ADDумеет распаковывать локальные архивы и качать по URL, поэтому по умолчанию берут предсказуемыйCOPY; - задают метаданные:
ENV,WORKDIR,USER,EXPOSE,LABEL.ARGвиден лишь при сборке,ENVдоживает до запущенного контейнера; - описывают запуск:
CMDзадаёт команду по умолчанию и заменяется аргументамиdocker run.ENTRYPOINTзадаёт исполняемый файл, аCMDстановится его аргументами. ПереопределитьENTRYPOINTтоже можно, флагом--entrypoint.
Порядок идёт от редко меняющегося к часто меняющемуся: изменение слоя сбрасывает кэш всех следующих. Для Python-сервиса:
- база:
FROM python:3.13-slimиENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1; - системные пакеты: один
RUNсapt-get update, установкой через--no-install-recommendsи очисткой/var/lib/apt/lists; - зависимости:
COPYтолькоrequirements.txtилиpyproject.tomlс lock-файлом, затем установка; - код и запуск:
COPYисходников,USERбез root,CMDв exec-форме.
Для компактного образа нужна многоэтапная сборка: зависимости ставятся в виртуальное окружение на этапе builder, в финальный образ копируется только оно. С BuildKit кэш pip или uv подключают через RUN --mount=type=cache, а .dockerignore не пускает в контекст .git и .venv.
Где спотыкаются
Копируют весь проект до установки зависимостей. Любая правка в коде заново ставит все пакеты.
Пишут CMD в shell-форме. Команда запускается через /bin/sh -c, и если оболочка остаётся первым процессом, SIGTERM до приложения не доходит: через 10 секунд docker stop убивает его принудительно. Exec-форма CMD ["gunicorn", "app:app"] от этого избавляет.
Передают токены через ARG или ENV. Они остаются в истории образа, для секретов есть RUN --mount=type=secret.