🐛 Типичные ошибки при написании Dockerfile

⚡ Топ-5 ошибок

  • Потеря кеша слоёв — код скопирован раньше зависимостей → при каждом изменении кода пересобирается pip install
  • Данные БД исчезают — запущена БД без volume → удалили контейнер = потеряли все данные
  • Пароль в Dockerfile — ENV POSTGRES_PASSWORD=secret в Dockerfile виден через docker history
  • CMD вместо ENTRYPOINT — docker run myimage bash перекроет CMD, приложение не запустится
  • Dockerfile не найден — запуск docker build из неправильной директории или опечатка в имени файла

Ошибка 1: Неправильный порядок слоёв — каждый раз пересобирается pip install

Симптом: docker build каждый раз устанавливает зависимости заново, даже если вы изменили только одну строку кода. Сборка занимает несколько минут вместо секунд.
# Dockerfile — НЕПРАВИЛЬНО
FROM python:3.12-slim
WORKDIR /app
COPY . .                               # копируем ВСЁ сразу
RUN pip install -r requirements.txt    # кеш сбрасывается при любом изменении кода!
CMD ["python3", "app.py"]
# Dockerfile — ПРАВИЛЬНО
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .               # сначала только requirements
RUN pip install --no-cache-dir -r requirements.txt  # кешируется!
COPY . .                              # потом код — меняется часто, слой лёгкий
CMD ["python3", "app.py"]

Почему: Docker кешируется до первого изменения. Если COPY . . раньше RUN pip install, то любое изменение в любом файле проекта сбрасывает кеш pip install.


Ошибка 2: Запуск БД без volume — потеря всех данных

Симптом: остановили или удалили контейнер с базой данных — все таблицы и записи исчезли.
# Терминал — НЕПРАВИЛЬНО
docker run -d --name mydb -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
# Данные хранятся ВНУТРИ контейнера!
# docker rm mydb → все данные потеряны навсегда
# Терминал — ПРАВИЛЬНО
docker run -d \
  --name mydb \
  -e MYSQL_ROOT_PASSWORD=secret \
  -v mysql-data:/var/lib/mysql \       # данные на хосте!
  mysql:8.0
# docker rm mydb → данные в volume mysql-data, не теряются
# docker run ... -v mysql-data:/var/lib/mysql mysql:8.0 → данные восстановятся

Ошибка 3: Пароль в Dockerfile

Симптом: секретные данные (пароли, API-ключи) видны через docker history myimage и docker inspect myimage. Если образ выгружен на Docker Hub — данные утекли.
# Dockerfile — ОПАСНО!
FROM postgres:16-alpine
ENV POSTGRES_PASSWORD=my-super-secret-password
# Теперь ЛЮБОЙ может узнать пароль:
# docker history myimage
# docker inspect myimage
# Dockerfile — ПРАВИЛЬНО: пустой Dockerfile
FROM postgres:16-alpine
EXPOSE 5432
# Паролей нет!
# Терминал — пароль передаём при запуске
docker run -d \
  -e POSTGRES_PASSWORD=my-secret-pw \
  postgres:16-alpine

# Ещё лучше: использовать .env файл с Docker Compose
# (не хранить пароль даже в истории терминала)

Ошибка 4: Путаница CMD и ENTRYPOINT

Симптом: docker run myimage bash переопределяет CMD и приложение не запускается, или наоборот — ENTRYPOINT нельзя переопределить без специального флага.
# Dockerfile с CMD — команда при запуске переопределяется
FROM python:3.12-slim
WORKDIR /app
COPY . .
CMD ["python3", "app.py"]

# Результат:
# docker run myimage               → python3 app.py  (OK)
# docker run myimage bash          → bash  (CMD заменён — app.py НЕ запустится!)
# docker run myimage python3 other.py → python3 other.py
# Dockerfile с ENTRYPOINT — команда фиксирована
FROM python:3.12-slim
WORKDIR /app
COPY . .
ENTRYPOINT ["python3", "app.py"]

# Результат:
# docker run myimage               → python3 app.py  (OK)
# docker run myimage --port 8080   → python3 app.py --port 8080 (аргументы добавились!)
# docker run --entrypoint bash myimage → bash  (только через --entrypoint)

Правило: используйте ENTRYPOINT, если контейнер — это «утилита» с фиксированной командой. Используйте CMD, если хотите разрешить переопределение команды при запуске.


Ошибка 5: «Cannot find Dockerfile» — неправильная директория или имя

Симптом: docker build выдаёт ошибку "unable to prepare context: unable to evaluate symlinks in Dockerfile path: ... no such file or directory"
# Терминал — частые причины
# 1. Запуск из неправильной директории
cd C:\projects
docker build -t myapp .            # ищет Dockerfile в C:\projects, а он в C:\projects\myapp!

# 2. Опечатка в имени — Dockerfile с маленькой буквы
# (на Linux регистр важен, Windows не чувствителен, но по стандарту — заглавная D)
docker build -t myapp .            # ищет ./Dockerfile (заглавная D)
# Терминал — исправление
# 1. Перейти в нужную директорию
cd C:\projects\myapp
docker build -t myapp .

# 2. Или указать путь явно через -f
docker build -t myapp -f ./myapp/Dockerfile ./myapp

Ошибка 6: Большой контекст сборки из-за отсутствия .dockerignore

Симптом: docker build запускается медленно — сообщение "Sending build context to Docker daemon X.XX MB" с большим числом. В образ попадают .git, node_modules, виртуальные окружения.
# Терминал — без .dockerignore
docker build -t myapp .
# Sending build context to Docker daemon  500MB  ← плохо!
# .dockerignore — добавьте этот файл в корень проекта
.git
.gitignore
.env
.env.*
__pycache__
*.pyc
*.pyo
*.log
node_modules
.DS_Store
*.tar.gz
dist/
build/
.venv/
venv/
# Терминал — с .dockerignore
docker build -t myapp .
# Sending build context to Docker daemon  2.048kB  ← хорошо!

Ошибка 7: chmod после COPY — не срабатывает из-за отдельного слоя

На практике это иногда встречается при сложных многопользовательских конфигурациях. Права, установленные в отдельном RUN после COPY, всё равно сохраняются в слое — это работает корректно в Docker. Проблема обычно возникает при Bind Mounts: права файлов на хосте могут перекрываться правами монтирования.

# Dockerfile — ПРАВИЛЬНО: chmod в том же RUN что и остальные операции с файлом
COPY scripts/ /usr/local/bin/
RUN chmod +x /usr/local/bin/*.sh
← К оглавлению урока