Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «У чому різниця між COPY і ADD у Dockerfile?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`COPY` і `ADD`** обидва копіюють файли з build context у image, але `ADD` робить додаткову магію: автоматично розпаковує локальні tar-архіви і може тягти з URL. Для звичайного копіювання беремо `COPY`. ```dockerfile COPY package.json /app/ # звичайне копіювання, передбачуване ADD app.tar.gz /app/ # ще й розпакує tarball ADD https://example.com/x.deb / # ще й скачає з URL (уникай) ``` **Головне:** за замовчуванням `COPY`. До `ADD` тягнися лише коли реально треба tar-розпакування або fetch з URL. Офіційний best-practices guide прямо це каже.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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`. ### Таблиця порівняння | Можливість | `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` для звичайного копіювання, бо звучить потужніше** ```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.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.