У чому різниця між 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 фічу.
Швидкий приклад
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.
Таблиця порівняння
| Можливість | COPY | ADD |
|---|---|---|
| Локальне копіювання файлу/директорії | Так | Так |
Флаг --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 для звичайного копіювання, бо звучить потужніше
# НЕПРАВИЛЬНО: бентежить майбутніх читачів
ADD package.json /app/
# ПРАВИЛЬНО: звичайне копіювання звичайною командою
COPY package.json /app/ADD тут функціонально те саме, що й COPY, але тіммейт, читаючи файл, мусить думати «це tarball, що розпакується?». Знижуй когнітивне навантаження. Бери COPY.
Використовувати ADD <URL> і довіряти йому у проді
# НЕПРАВИЛЬНО: без 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 / розпаковує
# Несподіванка: ЦЕ НЕ кладе tarball у /opt/
ADD vendor.tar.gz /opt/
# Розпаковує вміст у /opt/.
# Якщо хотів файл як є, бери COPY.Класична гра при міграції Dockerfile.
Реальне застосування
- Більшість прод-Dockerfile використовують
COPYвиключно. Лінії зADD, що бачиш, це зазвичайADD some-rootfs.tar.gz /у мінімальнихFROM scratchimage. - 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 для всього
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
FROM scratch
ADD alpine-minirootfs-3.21.0-x86_64.tar.gz /
CMD ["/bin/sh"]Тут ADD робить свою справжню роботу: розпаковує tar у image. Саме так upstream будується офіційний alpine image.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів