Skip to main content

У чому різниця між COPY і ADD у Dockerfile?

COPY і ADD у Dockerfile на перший погляд виглядають майже однаково: обидва копіюють файли з build context у image. Різниця в тому, що ADD робить понад копіювання.

Теорія

TL;DR

  • COPY = звичайне копіювання файлу/директорії. Передбачуване. Дефолтний вибір.
  • ADD = COPY + автоматично розпаковує локальні tar-архіви + тягне з URL.
  • Обидва підтримують флаги --chown, --chmod, --from=stage ідентично.
  • Офіційний best-practices guide каже: «Хоча ADD і COPY функціонально схожі, у загальному випадку COPY кращий вибір.»
  • До ADD тягнися лише коли реально треба його tar або URL фічу.

Швидкий приклад

dockerfile
FROM alpine:3.21 # COPY: звичайне копіювання. Що бачиш, те і отримуєш. COPY package.json /app/ COPY src/ /app/src/ # ADD: копіювання + розпакування tar (tar.gz, tar.bz2, tar.xz, tar) ADD app.tar.gz /app/ # ↑ Tarball розпакується у /app/, а НЕ покладеться туди як файл. # ADD: ще й тягне URL (працює, але не рекомендується) ADD https://example.com/file.deb /tmp/ # ↑ Без перевірки checksum, без кешу, без retry.

Чотири рядки, дві несподіванки, якщо не знав про додатки ADD.

Таблиця порівняння

МожливістьCOPYADD
Локальне копіювання файлу/директоріїТакТак
Флаг --chown=user:groupТакТак
Флаг --chmod=755ТакТак
--from=stage (multi-stage)ТакТак
Авто-розпакування локального tarНіТак
Fetch з URLНіТак
Передбачуване, легко читаєтьсяТакНі (залежить від input)
Рекомендований дефолтТакНі

Коли ADD дійсно правильний вибір

  • У тебе локальний tarball, який ти хочеш розпакувати одним кроком. Економить рядок RUN tar xf ....
  • Будуєш з нуля і потрібно затягнути base rootfs як tar (FROM scratch + ADD rootfs.tar.gz /).

Для fetch з URL краще RUN curl ... && перевір-checksum && rm в одному RUN. Отримуєш явний контроль над верифікацією і можеш почистити в тому самому шарі.

Типові помилки

Використовувати ADD для звичайного копіювання, бо звучить потужніше

dockerfile
# НЕПРАВИЛЬНО: бентежить майбутніх читачів ADD package.json /app/ # ПРАВИЛЬНО: звичайне копіювання звичайною командою COPY package.json /app/

ADD тут функціонально те саме, що й COPY, але тіммейт, читаючи файл, мусить думати «це tarball, що розпакується?». Знижуй когнітивне навантаження. Бери COPY.

Використовувати ADD <URL> і довіряти йому у проді

dockerfile
# НЕПРАВИЛЬНО: без checksum, без retry, лишає сміття ADD https://example.com/binary.deb /tmp/ RUN dpkg -i /tmp/binary.deb # ПРАВИЛЬНО: явний fetch з верифікацією RUN curl -fsSL -o /tmp/binary.deb https://example.com/binary.deb && \ echo "<sha256> /tmp/binary.deb" | sha256sum -c - && \ dpkg -i /tmp/binary.deb && \ rm /tmp/binary.deb

Варіант з RUN верифікований, чистить за собою у тому ж шарі і голосно падає, якщо upstream-бінар тихо змінився.

Забути, що ADD some.tar.gz / розпаковує

dockerfile
# Несподіванка: ЦЕ НЕ кладе tarball у /opt/ ADD vendor.tar.gz /opt/ # Розпаковує вміст у /opt/. # Якщо хотів файл як є, бери COPY.

Класична гра при міграції Dockerfile.

Реальне застосування

  • Більшість прод-Dockerfile використовують COPY виключно. Лінії з ADD, що бачиш, це зазвичай ADD some-rootfs.tar.gz / у мінімальних FROM scratch image.
  • Distroless і Alpine base image самі будуються через ADD <rootfs>.tar.gz /, бо це і є точне призначення ADD.
  • Лінтери Dockerfile у Google Cloud Build, GitHub Actions позначають ADD <URL> і рекомендують патерн RUN curl.

Питання для поглиблення

Q: Якщо ADD робить більше, чому це не дефолтна рекомендація?


A: Бо «більше» означає «менш передбачувано». Читаючи COPY foo /bar, ти знаєш точно, що відбудеться. Читаючи ADD foo /bar, мусиш знати тип foo, щоб зрозуміти що буде. Нудне і явне перемагає розумне.

Q: Чи розпаковує ADD .zip?


A: Ні. Тільки tar-варіанти: .tar, .tar.gz (або .tgz), .tar.bz2, .tar.xz. Для .zip бери COPY плюс RUN unzip.

Q: Чи можна COPY з URL?


A: Напряму ні. З BuildKit (# syntax=docker/dockerfile:1.7+) можна ADD <URL> з верифікацією checksum через --checksum=sha256:..., це сучасний безпечний спосіб тягти URL. Звичайний COPY лишається local-only.

Q: (Senior) Коли б ти зараз свідомо обрав ADD для URL?


A: З BuildKit-ою ADD --checksum=sha256:abc... https://example.com/file, що дає верифіковане, cache-friendly завантаження в одному рядку. Це єдиний патерн ADD <URL>, який я б поклав у прод.

Приклади

Кейс 99 відсотків: COPY для всього

dockerfile
FROM node:22-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . USER node CMD ["node", "server.js"]

Жодного ADD. Жодних несподіванок. Чіткі межі кешу. Так виглядає більшість прод-Dockerfile.

Легітимний ADD: збірка base image з rootfs

dockerfile
FROM scratch ADD alpine-minirootfs-3.21.0-x86_64.tar.gz / CMD ["/bin/sh"]

Тут ADD робить свою справжню роботу: розпаковує tar у image. Саме так upstream будується офіційний alpine image.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів