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

Быстрые ссылки
- Что такое –link?
- Как COPY –link изменяет поведение COPY
- Как добавить COPY –link в сборки
- Почему linked copies важны
- Когда не стоит использовать COPY –link
- Чеклист миграции и тесты
- Итог и рекомендации
Что такое –link?
--link — это необязательный флаг для существующей инструкции Dockerfile COPY, введённый в BuildKit. Он меняет модель добавления файлов: вместо наложения новых файлов на предыдущий слой создаётся отдельный автономный файловый снапшот (слой) для каждого вызова COPY. Далее эти независимые слои связываются в цепочку, формируя финальный образ.
Пояснение в одну строку: стандартный COPY добавляет файлы в предыдущий слой; COPY --link создаёт новый отдельный слой, содержащий только скопированные файлы.
Пример привычного поведения (без –link):
FROM alpineCOPY my-file /my-file
COPY another-file /another-file
Обычный COPY записывает файлы в слой, который создаётся поверх предыдущего контекста образа. В результате слои строятся друг над другом, и на этапе сборки требуется содержимое предыдущих слоёв, чтобы корректно выполнить операцию копирования.
Как COPY –link изменяет поведение COPY
С --link каждая команда COPY создаёт отдельную файловую систему (снапшот), содержащую лишь те файлы, которые вы копируете в этом шаге. На этапе сборки эти снапшоты не мёрджатся в предыдущий слой: создаются независимые архивы-слои (tarball), которые потом связываются в манифесте.
Пример того же Dockerfile с --link:
FROM alpineCOPY –link my-file /my-file
COPY –link another-file /another-file
Теперь итоговый образ будет состоять из трёх независимых снапшотов: базового слоя Alpine и двух отдельных слоёв с my-file и another-file. В файловой системе контейнера при запуске вы получите привычную картину: все файлы окажутся на своих местах, но процесс сборки и кэширования изменится.

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 не работает или ведёт себя иначе:
- Неоднозначные пути назначения
COPY --link my-file /data- В обычном
COPYесли/dataуже существует и является директорией в образе,my-fileпопадёт в/data/my-file. - С
--linkцелевая файловая система для этой команды всегда пуста, поэтомуmy-fileокажется по/data— это может сломать ожидания.
- Разрешение символьных ссылок
- Обычный
COPYучитывает существующие симлинки в предыдущем слое и может следовать им. - При
--linkсимлинки в предыдущих слоях физически отсутствуют в независимом слое, поэтому поведение не совпадает.
- Ожидание побочных эффектов от наличия файлов в родительских слоях
- Если ваша логика полагается на том, что в момент
COPYуже существуют определённые файлы/папки в предыдущих слоях,--linkможет нарушить эти предположения.
- Инструменты, которые анализируют содержимое отдельных слоёв во время сборки
- Некоторые внутренние build-скрипты могут предполагать наличие файлов в одном слое и модифицировать их;
--linkделает слои изолированными, поэтому такие сценарии потребуют правок.
Important: Из-за этих несовместимостей
--linkреализован как опциональный флаг, а не как новое поведение по умолчанию.
Когда --link даёт наибольшую пользу
- CI/CD с частыми пересборками и ограниченным сетевым трафиком.
- Многоплатформенные сборки и удалённые билд-сервера.
- Сценарии, где артефакты (binaries, конфиги) стабильны и могут быть переиспользованы между базовыми образами.
Миграция: пошаговый чеклист
- Подготовка
- Включите BuildKit в локальном окружении и в CI.
- Обновите Docker CLI / Buildx до версии, поддерживающей
--link(Buildx v0.8+). - Добавьте
# syntax=docker/dockerfile:1.4в Dockerfiles, где будете использовать--link.
- Анализ Dockerfile
- Найдите все
COPYс неоднозначными целевыми путями (например,/dataвместо/data/илиCOPY foo /opt). - Найдите места, где destination — симлинк или зависит от предыдущего содержимого образа.
- Исправление проблемных мест
- Явно указывайте конечные пути: используйте слэши для директорий (
/data/) и явные имена файлов (/data/my-file). - Избегайте копирования в существующие симлинки; разрешите симлинки в отдельном шаге или устраните их.
- Инкрементальная замена
- Внедряйте
--linkпо одномуCOPYв отдельной ветке. - Пишите тесты и прогоняйте CI для проверки запуска контейнеров и поведений приложений.
- Мониторинг и откат
- Наблюдайте за временем сборки и размером слоёв.
- Если обнаружите регрессию, откатите изменения в 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.
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента