Гид по технологиям

Как работает COPY --link в Docker BuildKit

• 8 min read • DevOps • Обновлено 30 Nov 2025
COPY --link в Docker BuildKit: ускорение сборок
COPY --link в Docker BuildKit: ускорение сборок

Схема слоёв Docker: сравнение обычного COPY и COPY --link

Быстрые ссылки

  • Что такое –link?
  • Как COPY –link изменяет поведение COPY
  • Как добавить COPY –link в сборки
  • Почему linked copies важны
  • Когда не стоит использовать COPY –link
  • Чеклист миграции и тесты
  • Итог и рекомендации

Что такое –link?

--link — это необязательный флаг для существующей инструкции Dockerfile COPY, введённый в BuildKit. Он меняет модель добавления файлов: вместо наложения новых файлов на предыдущий слой создаётся отдельный автономный файловый снапшот (слой) для каждого вызова COPY. Далее эти независимые слои связываются в цепочку, формируя финальный образ.

Пояснение в одну строку: стандартный COPY добавляет файлы в предыдущий слой; COPY --link создаёт новый отдельный слой, содержащий только скопированные файлы.

Пример привычного поведения (без –link):

FROM alpine

COPY my-file /my-file

COPY another-file /another-file

Обычный COPY записывает файлы в слой, который создаётся поверх предыдущего контекста образа. В результате слои строятся друг над другом, и на этапе сборки требуется содержимое предыдущих слоёв, чтобы корректно выполнить операцию копирования.

Как COPY –link изменяет поведение COPY

С --link каждая команда COPY создаёт отдельную файловую систему (снапшот), содержащую лишь те файлы, которые вы копируете в этом шаге. На этапе сборки эти снапшоты не мёрджатся в предыдущий слой: создаются независимые архивы-слои (tarball), которые потом связываются в манифесте.

Пример того же Dockerfile с --link:

FROM alpine

COPY –link my-file /my-file

COPY –link another-file /another-file

Теперь итоговый образ будет состоять из трёх независимых снапшотов: базового слоя Alpine и двух отдельных слоёв с my-file и another-file. В файловой системе контейнера при запуске вы получите привычную картину: все файлы окажутся на своих местах, но процесс сборки и кэширования изменится.

Схема: отличия между COPY и COPY --link в Docker

Important: Поведение контейнера при запуске остаётся прежним — конечная файловая система идентична. Изменяется только способ формирования слоёв во время сборки.

Как добавить COPY –link в сборки

Предусловия:

  • BuildKit должен быть включён (Buildx либо DOCKER_BUILDKIT=1).
  • В Dockerfile необходимо явно указать синтаксис v1.4: # syntax=docker/dockerfile:1.4.

Минимальный Dockerfile с --link:

# syntax=docker/dockerfile:1.4
FROM alpine:latest

COPY --link my-file /my-file

COPY --link another-file /another-file

Сборка с включённым BuildKit:

DOCKER_BUILDKIT=1 docker build -t my-image:latest .

Альтернативы запуска BuildKit:

  • docker buildx –create && docker buildx build …
  • локальное окружение CI/CDE с включённым BuildKit (многие CI уже поддерживают)

Важно: образы с COPY --link работают как обычные образы — их можно запускать, пушить в реестр, экспортировать. Флаг влияет только на процесс формирования слоёв.

Почему linked copies важны

Ключевые преимущества:

  • Ускорение повторных сборок: независимые слои легче кешируются и повторно используются.
  • Ребейзинг образов: можно «пересадить» независимый снапшот (сбилженный артефакт) на другой базовый образ без загрузки содержимого предыдущего базового образа.
  • Улучшенная устойчивость к изменениям в промежуточных этапах: если изменился один артефакт, не придётся перезаписывать или перерасчитывать файлы в других слоях.

Иллюстрация сценария ребейза (без лишней загрузки базовых слоёв):

  • У вас есть образ example-image:20.04, в нём сохранён inline-кэш и слои с собранным бинарником /bin/app.
  • Вы хотите выпустить новую версию, основанную на ubuntu:22.04.
  • Если бинарник был сохранён как независимый слой с COPY --link, BuildKit может связать этот слой в манифест Ubuntu 22.04 без подтягивания полного содержимого ubuntu:20.04.

Пример команд:

# Сборка с встраиваемым кэшем
docker buildx build --cache-to type=inline -t example-image:20.04 .

# Ребейз с использованием кэша
docker buildx build --cache-from example-image:20.04 -t example-image:22.04 .

Такой подход особенно полезен для многоплатформенных CI и для распределённых сборок, где экономия трафика и времени критична.

Ментальная модель

Представьте слои как стопку карточек: обычный COPY рисует новый рисунок на верхней карточке, а COPY --link вкладывает новую карточку с рисунком в стопку, не меняя содержимое соседних карточек. Это упрощает замену одной карточки без перерисовки всех предыдущих.

Примеры многослойных сборок и преимущества

Многослойный Dockerfile с генерацией конфигурации и билдом бинара:

FROM golang AS build

RUN go build -o /app .

FROM config-builder AS config

RUN generate-config --out /config.yaml

FROM ubuntu:latest

COPY --link --from=config /config.yaml build.conf

COPY --link --from=build /app /bin/app

Поведение:

  • Если изменился только config.yaml, с --link системы может заменить только слой с конфигом; образ Ubuntu не нужно подтягивать и бинарник не нужно перекомпилировать.
  • Без --link изменение config.yaml часто приводит к invalidation кэша и побочным пересборкам.

Когда не стоит использовать COPY –link

Ограничения и ситуации, где --link не работает или ведёт себя иначе:

  1. Неоднозначные пути назначения
COPY --link my-file /data
  • В обычном COPY если /data уже существует и является директорией в образе, my-file попадёт в /data/my-file.
  • С --link целевая файловая система для этой команды всегда пуста, поэтому my-file окажется по /data — это может сломать ожидания.
  1. Разрешение символьных ссылок
  • Обычный COPY учитывает существующие симлинки в предыдущем слое и может следовать им.
  • При --link симлинки в предыдущих слоях физически отсутствуют в независимом слое, поэтому поведение не совпадает.
  1. Ожидание побочных эффектов от наличия файлов в родительских слоях
  • Если ваша логика полагается на том, что в момент COPY уже существуют определённые файлы/папки в предыдущих слоях, --link может нарушить эти предположения.
  1. Инструменты, которые анализируют содержимое отдельных слоёв во время сборки
  • Некоторые внутренние build-скрипты могут предполагать наличие файлов в одном слое и модифицировать их; --link делает слои изолированными, поэтому такие сценарии потребуют правок.

Important: Из-за этих несовместимостей --link реализован как опциональный флаг, а не как новое поведение по умолчанию.

Когда --link даёт наибольшую пользу

  • CI/CD с частыми пересборками и ограниченным сетевым трафиком.
  • Многоплатформенные сборки и удалённые билд-сервера.
  • Сценарии, где артефакты (binaries, конфиги) стабильны и могут быть переиспользованы между базовыми образами.

Миграция: пошаговый чеклист

  1. Подготовка
  • Включите BuildKit в локальном окружении и в CI.
  • Обновите Docker CLI / Buildx до версии, поддерживающей --link (Buildx v0.8+).
  • Добавьте # syntax=docker/dockerfile:1.4 в Dockerfiles, где будете использовать --link.
  1. Анализ Dockerfile
  • Найдите все COPY с неоднозначными целевыми путями (например, /data вместо /data/ или COPY foo /opt).
  • Найдите места, где destination — симлинк или зависит от предыдущего содержимого образа.
  1. Исправление проблемных мест
  • Явно указывайте конечные пути: используйте слэши для директорий (/data/) и явные имена файлов (/data/my-file).
  • Избегайте копирования в существующие симлинки; разрешите симлинки в отдельном шаге или устраните их.
  1. Инкрементальная замена
  • Внедряйте --link по одному COPY в отдельной ветке.
  • Пишите тесты и прогоняйте CI для проверки запуска контейнеров и поведений приложений.
  1. Мониторинг и откат
  • Наблюдайте за временем сборки и размером слоёв.
  • Если обнаружите регрессию, откатите изменения в Dockerfile и задокументируйте причину.

Ролевые чеклисты

Разработчик:

  • Убедиться, что Dockerfile содержит # syntax=docker/dockerfile:1.4.
  • Проверить каждый COPY на неоднозначные пути и симлинки.
  • Добавить --link для стабильных артефактов (бинарники, конфиги).

CI-инженер:

  • Включить DOCKER_BUILDKIT=1 в конвейере.
  • Поддерживать кеширование (--cache-to, --cache-from).
  • Добавить тесты контейнера после сборки (smoke tests).

DevOps / оператор реестра:

  • Проверить политику слоя и хранения в реестре (хранение новых слоёв и перевод на pulls базовых слоёв при запуске).
  • Оценить использование сетевого трафика при первоначальной загрузке образов.

Тесты и критерии приёмки

Критерии приёмки для Dockerfile с --link:

  • Сборка проходит локально и в CI с BuildKit (DOCKER_BUILDKIT=1).
  • Контейнер на основе финального образа запускается и проходит набор smoke-тестов (процессы стартуют, приложения отвечают).
  • Изменение несущественного файла не приводит к повторной загрузке/перекомпиляции независимых артефактов.
  • Поведение файловых путей соответствует ожиданиям (особенно для целевых директорий и симлинков).

Минимальные тест-кейсы:

  • Сборка образа с COPY --link и запуск контейнера — проходит.
  • Проверка размеров слоёв/архивов (необязательная метрика).
  • Ребейз: сборка нового образа на другом базовом образе с --cache-from — завершается быстро и без подтягивания прежнего базового слоя.

Risk matrix и способы смягчения

  • Риск: сломанная логика из-за неоднозначных путей. Митигирование: явная нормализация путей и статический анализ Dockerfile.
  • Риск: несовместимость с инструментами, предполагающими модификацию предыдущих слоёв. Митигирование: поэтапная миграция и тестирование.
  • Риск: люди ожидали, что COPY ведёт себя как раньше. Митигирование: документировать изменения, проводить внутренние обзоры Dockerfile.

Шпаргалка команд и примеры

Включение BuildKit локально:

# временно для одной команды
DOCKER_BUILDKIT=1 docker build -t my-image:latest .

# или использовать buildx
docker buildx create --use
docker buildx build -t my-image:latest .

Кэширующие опции Buildx:

# записать кеш внутрь образа (inline)
docker buildx build --cache-to type=inline -t example-image:20.04 .

# использовать кеш из другого образа
docker buildx build --cache-from example-image:20.04 -t example-image:22.04 .

Полезный сниппет: явное указание директорий при копировании

# некорректно для --link, если /data существовал
COPY --link my-file /data

# корректнее: явно указываем имя файла
COPY --link my-file /data/my-file

# или явно указываем каталог с завершающим слэшем, если хотим именно каталог
RUN mkdir -p /data
COPY --link my-file /data/

Решение: стоит ли включать COPY –link сейчас?

Рекомендуется вводить --link постепенно там, где:

  • вы контролируете Dockerfile и можете явно указать целевые пути;
  • файлы, которые копируются, являются стабильными артефактами (binaries, конфиги);
  • вы хотите ускорить CI сборки и снизить сетевой трафик при ребейзах.

Если у вас много Dockerfile в наследии, начните с критичных ветвей и автоматизированных тестов.

Диаграмма принятия решения

flowchart TD
  A[Начало: хотим ускорить сборки?] --> B{Копируем ли вы стабильные артефакты?}
  B -- Да --> C{Есть ли неоднозначные целевые пути или симлинки?}
  B -- Нет --> E[Оставить обычный COPY; оптимизируйте сборку по-другому]
  C -- Нет --> D[Включить COPY --link и протестировать]
  C -- Да --> F[Исправить пути/симлинки, затем включить COPY --link]
  D --> G[Мониторинг и откат при проблемах]
  F --> D
  E --> G

Глоссарий (1 строка на термин)

  • BuildKit: современный движок сборки образов Docker, обеспечивающий улучшенное кэширование и параллелизм.
  • Снапшот (snapshot): архивное представление файловой системы шага сборки, используемое в слое образа.
  • Ребейз (rebase): перенос независимого слоя или артефакта на другой базовый образ без перекомпиляции.

Частые ошибки и способы их избежать

  • Ошибка: ожидание, что COPY --link my-file /data поместит my-file в /data/my-file. Решение: указывать /data/my-file явно.
  • Ошибка: полагаться на симлинки в целевом образе. Решение: разрешить симлинки в отдельном шаге, либо заменить их на явные пути.
  • Ошибка: предположение, что --link уменьшит размер конечного образа. Пояснение: итоговый размер образа как правило совпадает; преимущество в скорости сборки и трафике при пересборках.

Edge-case gallery (варианты, когда поведение может удивлять)

  • Копирование директории в путь, который в базовом образе является файлом (и наоборот).
  • Ожидание, что порядок COPY влияет на момент присоединения слоёв: с --link слои независимы и их порядок важен только для переопределения файлов в итоге.
  • Инструменты, вычисляющие контрольные суммы слоёв на этапе сборки, могут вернуть другой результат из‑за разницы в архивировании слоёв.

Итог

BuildKit COPY --link — мощный инструмент для ускорения Docker‑сборок, улучшения кэширования и облегчения ребейзинга артефактов между базовыми образами. Внедряйте постепенно: исправляйте неоднозначные пути и симлинки, покрывайте изменения тестами и отслеживайте CI‑метрики. В большинстве современных CI‑сетапов --link даёт заметное улучшение времени сборки и устойчивости кэша.

Коротко:

  • Используйте # syntax=docker/dockerfile:1.4 и включайте BuildKit.
  • Явно задавайте пути назначения при копировании.
  • Тестируйте и мигрируйте по шагам.

Спасибо за внимание — если нужно, могу подготовить готовый сканер Dockerfile, который находит потенциально проблемные COPY для миграции на --link.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Несколько аккаунтов Skype: Multi Skype Launcher
Программное обеспечение

Несколько аккаунтов Skype: Multi Skype Launcher

Журнал для работы: повысить продуктивность
Productivity

Журнал для работы: повысить продуктивность

Персональные звуки уведомлений на Android
Android.

Персональные звуки уведомлений на Android

Скачивание шоу Hulu для офлайн‑просмотра
Стриминг

Скачивание шоу Hulu для офлайн‑просмотра

Microsoft Start: персонализированная новостная лента
Новости

Microsoft Start: персонализированная новостная лента

Как изменить имя в Epic Games быстро
Гайды

Как изменить имя в Epic Games быстро