
Как сделать Docker наладить более быстро
25 июля 2025 г.Введение
Вы когда-нибудь смотрели на свой терминал, ожидая сборки Docker, и задавались вопросом, почему крошечное изменение кода вызвало 10-минутное перекомпиляцию всего вашего проекта? Или почему ваше последнее изображение сотни мегабайт больше, чем вы думаете, это должно быть? Это не причуды таинственной системы; Это предсказуемые результаты понятной механики. Разница между разочаровывающим медленным рабочим процессом и эффективным быстрым часто сводится к пониманию двигателяdocker build
Полем
Эта статья является руководством по этому инженерному отделению. Мы демистифицируем процесс сборки, освоив три столпа эффективности: система кэширования слоя, искусствоRUN
командование и роль.dockerignore
Файл в качестве привратника в вашу сборку. К концу вы не будете знатьчтокоманды для запуска, нопочемуОни работают, позволяя вам создавать по -настоящему профессиональные и оптимизированные контейнеры. Этот проект имеет простое приложение для ИИ, которое использует модель BERT для классификации текста, и мы будем использовать его DockerFile из нашегоlayered_imageПроект как тематическое исследование, чтобы проиллюстрировать эти основные принципы.
Фундамент: слои Docker и кеш сборки - неизменная книга
Представьте себе, что изображение Docker не является единственным монолитным файлом, а в виде стопки точно определенных изменений, например, неизменной бухгалтерской книги, где каждая транзакция записывается на новой странице. Это суть слоистой файловой системы Docker. Каждая инструкция в вашемDockerfile
-FROM
ВCOPY
ВRUN
ВCMD
и т. д. обычно создает новый слой. Этот слой не содержит полной копии файловой системы; Вместо этого он записывает толькоразличиявведено этой конкретной инструкцией по сравнению со слоем под ним. Если аRUN apt-get install curl
Команда добавляетcurl
Этот слой по сути говорит «+ curl и его зависимости». Если последующийCOPY my_script.py /app/
Добавляет сценарий, этот новый слой говорит «+ /apps/my_script.py».
Этот многослойный подход гениальен для эффективности. Когда вы тянете изображение, Docker загружает только слои, которых у него еще нет. Когда вы строите изображения, которые разделяют общие базовые слои (например,python:3.10-slim
) эти базовые слои хранятся один раз и разделяются.
Опираясь на эту многослойную файловую системуDocker строить кешПолем Это память Docker о прошлых операциях. Когда вы выпускаетеdocker build
команда, Docker проходит через вашDockerfile
инструкция по инструкции. Для каждой инструкции это проверяет три вещи:
- Точная инструкция (например,
COPY my_file.txt /dest/
) - Содержание любых файлов, связанных с этой инструкцией (например, контрольная сумма
my_file.txt
) - Родительский слой изображения, на котором основана эта инструкция.
Если Docker находит существующий слой в своем кэше, который был создан източно такой же родительский слойиспользуяточно та же инструкциясточно такие же входные файлы, это повторно использует этот кэшированный слой мгновенно. Это удар в кеш.
Однако, еслилюбойИз этих условий изменяются для, например, Если инструкция отличается, если содержимое копированного файла изменилось, или если родительский слой отличается (потому что предыдущая инструкция была промахом кэша), то Docker испытаетКэш БюстПолем Когда происходит бюст в кеш, Docker должен выполнить эту инструкцию с нуля, создав новый слой. Критически,Все последующие инструкции вDockerfile
также будет выполнен с нуля, независимо от того, могли ли они соответствовать кешу самостоятельно. Кэш недействителен с этого момента вниз.
Это приводит к золотому правилу кэширования:Заказ инструкции с наименьшего часто изменяются на наиболее часто изменяемые.Подумайте об этом, как о организации своего стола: вещи, которые вы редко касаетесь, идут в задние ящики; Вещи, которые вы используете, постоянно остаются на вершине.
Интерактивный эксперимент, чтобы почувствовать кеш:
- Во -первых, построитьLayered_image(который имеет порядок, удобный в кэше), используя такую команду, как
time docker build -t bert-classifier:layered -f layered_image/Dockerfile layered_image/
Полем Для нас эта первоначальная сборка взяла на себя23 секундыПолем - Теперь открыт
layered_image/app/predictor.py
и сделать тривиальное изменение, например, добавить комментарий. Восстановите изображение:time docker build -t bert-classifier:layered -f layered_image/Dockerfile layered_image/
Полем Сборка должна завершитьМенее чем секундуПолем Почему? Докер видитFROM
ВWORKDIR
ВCOPY runtime_requirements.txt
не изменяются и повторно используют свои слои. Он видитRUN pip install
инструкция такая же, и ее вход (runtime_requirements.txt
) не изменил свой контент, поэтому он повторно использует массовый слой, созданныйpip install
Полем Только когда он достигаетCOPY layered_image/app/ ./app/
Обнаружает ли это изменение (ваш модифицированныйpredictor.py
), так что это восстанавливает этот слой и последующие. Если вам нужны доказательства, идите вперед и добавьте—progress=plain
Флаг до конца команда сборки. Docker CLI покажет вам кэшированные слои. - Далее, решающий тест для понимания недействительной: отредактируйте свой
layered_image/Dockerfile
Полем Переместить линиюCOPY layered_image/app/ ./app/
кдоаRUN pip install ...
линия. Сделать еще одно тривиальное изменениеlayered_image/app/predictor.py
и восстановить. Что происходит? Сборка снова займет 23 секунды! Изменение наapp/predictor.py
разбил кеш в (теперь ранее)COPY ./app/
шаг. Потому чтоpip install
Шаг приходитпослеЭтот бюст в кеш, он также вынужден повторно запустить с нуля, хотяruntime_requirements.txt
не изменился.
Этот эксперимент мощно демонстрирует, как каскады бюста кэша и почему порядок ваших инструкций DockerFile имеет первостепенное значение для быстрого цикла развития. Вот структура, благоприятная для кэша, мы защищаем из нашихlayered_imageпроект:
# Cache-Friendly Order (from layered_image/Dockerfile runtime stage)
FROM python:3.10-slim AS runtime
WORKDIR /app
# 1. Copy requirements first (changes less often than app code)
COPY layered_image/runtime_requirements.txt ./runtime_requirements.txt
# 2. Install dependencies (slow step, now cached if requirements.txt doesn't change)
RUN pip install --no-cache-dir -r runtime_requirements.txt # (Full command shown later)
# 3. Copy app code last (changes most often)
COPY layered_image/app/ ./app/
COPY layered_image/sample_data/ ./sample_data/
CMD ["python", "app/predictor.py", "sample_data/sample_text.txt"]
ИскусствоRUN
Команда: цепочка для микроскопических слоев
Стремление к эффективномуDockerfile
Имеет параллель в физическом мире: попытка минимизировать объем коллекции предметов. КаждыйRUN
Команда в вашем Dockerfile создает новый слой. Если вы загружаете инструмент, используйте его, а затем удалите его в отдельномRUN
Команды, вы похожи на то, что кто -то помещает элемент в коробку, а затем положил пустую обертку для этого элемента в другой поле сверху. Оригинальный предмет все еще там, в нижней коробке, занимающий место, даже если в верхней коробке говорится: «Это ушло».
В частности, файлы, созданные в одном слое, не могут бытьПоистине удаленоот общего размера изображения по команде в последующем слое. Последующий слой просто записывает, что эти файлы «удалены» или «скрыты», но биты, включающие эти файлы, все еще существуют в исторических слоях изображения. Вот что подобно инструментамdiveчасто сообщают как «потраченное впустую пространство».
Рассмотрим этот анти-паттерн:
# Anti-Pattern: Separate RUN commands leading to bloat
FROM python:3.10-slim
WORKDIR /app
COPY runtime_requirements.txt .
RUN pip install --no-cache-dir -r runtime_requirements.txt # Step 1: Install
RUN pip cache purge # Step 2: Cleanup attempt 1
RUN rm -rf /tmp/* /var/tmp/* # Step 3: Cleanup attempt 2
# ... (further cleanup attempts)
Если бы вы построили изображение и пробегdocker history bert-classifier-layers
, вы бы соблюдали вывод для каждогоRUN
шаг. ПервыйRUN pip install...
Шаг покажет значительный объем написанных данных (около 679 МБ). ПоследующееRUN pip cache purge
иRUN rm -rf /tmp/
Шаги показывают очень мало данных, написанных для их слоев, возможно, всего несколько килобитов. Это потому, что они неудаление данных из предыдущего уровня 679 МБ; Они просто добавляют новые маленькие слои сверху, которые отмечают эти файлы как удаленные. Слой 679 МБ остается частью истории изображения.
docker history bert-classifier-layers
IMAGE CREATED CREATED BY SIZE COMMENT
f09d44f97ab4 34 minutes ago CMD ["python" "app/predictor.py" "sample_dat… 0B buildkit.dockerfile.v0
<missing> 34 minutes ago COPY layered_image/sample_data/ ./sample_dat… 376B buildkit.dockerfile.v0
<missing> 34 minutes ago COPY layered_image/app/ ./app/ # buildkit 5.51kB buildkit.dockerfile.v0
<missing> 34 minutes ago RUN /bin/sh -c rm -rf /tmp/* /var/tmp/* && … 0B buildkit.dockerfile.v0
<missing> 34 minutes ago RUN /bin/sh -c pip cache purge # buildkit 6.21kB buildkit.dockerfile.v0
<missing> 34 minutes ago RUN /bin/sh -c pip install --no-cache-dir -r… 679MB buildkit.dockerfile.v0
<missing> 34 minutes ago COPY layered_image/runtime_requirements.txt … 141B buildkit.dockerfile.v0
<missing> 3 hours ago WORKDIR /app 0B buildkit.dockerfile.v0
<missing> 11 days ago CMD ["python3"] 0B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; for src in idle3 p… 36B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; savedAptMark="$(a… 46.4MB buildkit.dockerfile.v0
<missing> 11 days ago ENV PYTHON_SHA256=ae665bc678abd9ab6a6e1573d2… 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV PYTHON_VERSION=3.10.18 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV GPG_KEY=A035C8C19219BA821ECEA86B64E628F8… 0B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; apt-get update; a… 9.17MB buildkit.dockerfile.v0
<missing> 11 days ago ENV LANG=C.UTF-8 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV PATH=/usr/local/bin:/usr/local/sbin:/usr… 0B buildkit.dockerfile.v0
<missing> 11 days ago # debian.sh --arch 'arm64' out/ 'bookworm' '… 97.2MB debuerreotype 0.15
Решение состоит в том, чтобы выполнить все связанные операции, особенно создание и очистку временных файлов или инструментов, в рамкаходинокийRUN
командование, цепляя их с помощью&&
Полем Это гарантирует, что существуют любые временные артефактыэфемерноВо время исполнения этого синглаRUN
командование и прошел до того, как слой будет завершен и совершен.
Давайте посмотрим на агрессивную очисткуRUN
командование из нашегоlayered_image/Dockerfile.:
RUN pip install --no-cache-dir -r runtime_requirements.txt && \
pip cache purge && \
rm -rf /tmp/* /var/tmp/* && \
find /usr/local/lib/python*/site-packages/ -name "*.pyc" -delete && \
find /usr/local/lib/python*/site-packages/ -name "__pycache__" -type d -exec rm -rf {} + || true
Эта команда - тщательно хореографический танец:
pip install --no-cache-dir -r runtime_requirements.txt
: Устанавливает пакеты Python, не оставляя загруженные колесные файлы в PIP's HTTP Cache.pip cache purge
: Явно очищает любой другой кэш -PIP.rm -rf /tmp/* /var/tmp/
: Удаляет файлы из стандартных временных каталогов.find ... -name “
.pyc" -delete
: Удаляет скомпилированные файлы кода Python Byte.find ... -name “
pycache
" -type d -exec rm -rf {} +
: Удаляетpycache
каталоги.|| true
: ОбеспечиваетRUN
команда преуспевает, даже еслиfind
Не находит никаких файлов (которые могут вернуть ненулевой код выхода).
Воздействие (демонстрируется сdocker history
):
С этим синглом, цепьюRUN
командование, полученный слой для нашегоlayered_image
Проект572 МБПолем Если эти шаги не были раскрыты, начальныеpip install
создаст слой приблизительно 679 МБ. Аdocker history
Команда будет отражать это:
docker history bert-classifier-layers
IMAGE CREATED CREATED BY SIZE COMMENT
17d0319094f4 2 minutes ago CMD ["python" "app/predictor.py" "sample_dat… 0B buildkit.dockerfile.v0
<missing> 2 minutes ago COPY layered_image/sample_data/ ./sample_dat… 376B buildkit.dockerfile.v0
<missing> 2 minutes ago COPY layered_image/app/ ./app/ # buildkit 5.51kB buildkit.dockerfile.v0
<missing> 2 minutes ago RUN /bin/sh -c pip install --no-cache-dir -r… 572MB buildkit.dockerfile.v0
<missing> 2 minutes ago COPY layered_image/runtime_requirements.txt … 141B buildkit.dockerfile.v0
<missing> 3 hours ago WORKDIR /app 0B buildkit.dockerfile.v0
<missing> 11 days ago CMD ["python3"] 0B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; for src in idle3 p… 36B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; savedAptMark="$(a… 46.4MB buildkit.dockerfile.v0
<missing> 11 days ago ENV PYTHON_SHA256=ae665bc678abd9ab6a6e1573d2… 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV PYTHON_VERSION=3.10.18 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV GPG_KEY=A035C8C19219BA821ECEA86B64E628F8… 0B buildkit.dockerfile.v0
<missing> 11 days ago RUN /bin/sh -c set -eux; apt-get update; a… 9.17MB buildkit.dockerfile.v0
<missing> 11 days ago ENV LANG=C.UTF-8 0B buildkit.dockerfile.v0
<missing> 11 days ago ENV PATH=/usr/local/bin:/usr/local/sbin:/usr… 0B buildkit.dockerfile.v0
<missing> 11 days ago # debian.sh --arch 'arm64' out/ 'bookworm' '… 97.2MB debuerreotype 0.15
Это прямое сравнение в размере слоя демонстрирует сохранение107 МБПросто путем правильно структурирования очистки в пределах того жеRUN
инструкция
Привратник: мастерство.dockerignore
Наши окончательные принципы: самое начало процесса сборки. Когда вы выполняетеdocker build .
,.
(или любой путь, который вы указали) Определяет «контекст построения». Docker тщательно упаковывает все в этом пути (с уважением.dockerignore
Файл, конечно) в архив и передает его в Docker Daemon. Демон затем распаковывает этот контекст и использует его в качестве единственного источника локальных файлов для любыхCOPY
илиADD
инструкции в вашемDockerfile
Полем Он не имеет доступа ни к чему в вашей файловой системе вне этого контекста.
Проблема, особенно для проектов искусственного интеллекта, заключается в том, что наши каталоги проекта часто представляют собой сокровищники файлов, совершенно не относящиеся к окончательному изображению времени выполнения: локальные наборы данных, модель контрольно -пропускных пунктов, ноутбуки Jupyter, виртуальные среды Python и все.git
история Отправка контекста с несколькими гигабайтами не просто медленная (особенно если ваш демон удален, как во многих системах CI), это также проблема безопасности и чистоты. Вы рискуете случайноCOPY
конфиденциальная информация или артефакты развития в ваш образ.
А.dockerignore
Файл - ваш бдительный привратник. Это простой текстовый файл, помещенный в корень вашего контекста сборки, который использует шаблоны (очень похоже на.gitignore
) указать, какие файлы и каталоги должны бытьисключеноиз контекста, прежде чем он когда -либо упаковался и отправлен в демон.
Всеобъемлющий.dockerignore
Для проекта искусственного интеллекта может выглядеть так:
# .dockerignore
# Python virtual environments
.venv/
env/
venv/
# Python caches and compiled files
__pycache__/
*.py[cod] # .pyc, .pyo, .pyd
*.egg-info/
dist/
build/
*.so # Compiled shared objects, unless explicitly needed and copied
# IDE and OS specific
.vscode/
.idea/
*.swp
*.swo
.DS_Store
Thumbs.db
# Notebooks and exploratory artifacts
notebooks/
*.ipynb_checkpoints
# Test-related files (if not run inside the container build)
tests/
.pytest_cache/
htmlcov/
.coverage
# Large data or model files not intended for baking into the image
data/
models/
model_checkpoints/
*.pt
*.onnx
*.h5
# Log files
*.log
# Dockerfile itself (usually not needed to be COPIED into the image)
# Dockerfile
# Version control (see note below)
# .git
# .gitignore
Тщательно определяя, что игнорировать, вы гарантируете контекст сборки. Это ускоряет начальный шаг «отправка контекста сборки в Docker Daemon ...», снижает вероятность случайного включения данных и делает вашCOPY . .
команды безопаснее и более предсказуемо.
Заключение
В некотором смыслеDockerfile
это просто еще один инструмент. Тем не менее, углубляясь в свою механику, понимаякакОн превращает ваши инструкции в изображение, вы получаете контроль ремесленника. Мы видели, что преднамеренный заказ инструкций в честь кэша сборки может превратить минуты ожидания в секунды. Мы узнали, что искусная цепочка внутриRUN
Команды - это не только синтаксис; Речь идет о скульптуре худых, эффективных слоев. И мы узнали.dockerignore
Файл не как незначительные детали, а как решающий опекун целостности и скорости нашего процесса сборки.
Эти принципы, слои, кэширование, цепочка и управление контекстом являются фундаментальными. Освоение их является ключом к тому, чтобы выходить за рамки простого создания изображений Docker, чтобы по -настоящему разработать их для эффективности, скорости и чистоты, особенно в требовательном мире ИИ.
Твоя очередь
Теперь, когда вы понимаете эту механику, пересмотрите свои собственные Dockerfiles. Можете ли вы переупорядочить слои для лучшего кэширования? Вы можете цепоритьRUN
команды для более агрессивной очистки? Внедрить надежный.dockerignore
Полем Поделитесь своими выводами или вопросами в комментариях ниже!
Оригинал