Шари образу незмінні. Кожна інструкція додає новий шар поверх попередніх, і видалення файлу в пізнішому шарі не прибирає його з попереднього - лише позначає як видалений (так званий whiteout-файл).
RUN curl -o /tmp/sdk.tar.gz https://example.com/sdk.tar.gz # шар +500 МБ
RUN tar -xzf /tmp/sdk.tar.gz -C /opt && rm /tmp/sdk.tar.gz # архів «видалено»
У контейнері /tmp/sdk.tar.gz не видно, але 500 МБ залишилися в першому шарі. Образ завантажується з усіма шарами - і з видаленими файлами теж.
Як працює файлова система контейнера (OverlayFS):
- шари образу - лише для читання, складені один на одного;
- контейнер отримує тонкий записуваний шар зверху;
- зміна файлу з нижнього шару - копіювання при записі (copy-on-write): файл копіюється у верхній шар і змінюється там;
- видалення - позначка у верхньому шарі, що ховає файл нижче.
Як правильно - створення і видалення в одному шарі:
RUN curl -o /tmp/sdk.tar.gz https://example.com/sdk.tar.gz \
&& tar -xzf /tmp/sdk.tar.gz -C /opt \
&& rm /tmp/sdk.tar.gz
Або, краще, multi-stage build: усе тимчасове - у стадії збирання, у фінальний образ копіюється лише результат:
FROM debian:bookworm-slim AS sdk
RUN ... завантажити й розпакувати
FROM php:8.5-fpm
COPY --from=sdk /opt/sdk /opt/sdk
Типові «невидимі» витрати:
apt-get updateв одномуRUN, а очищення/var/lib/apt/lists- в іншому;COPY . ., а потімRUN rm -rf tests node_modules- краще не копіювати їх через.dockerignore;RUN chown -R www-data /appпісляCOPY- повна копія всіх файлів у новому шарі (замість цього -COPY --chown);- секрети: файл з ключем, скопійований і потім видалений, лишається в шарі - його можна дістати з образу. Для секретів -
RUN --mount=type=secret.
Записуваний шар контейнера теж варто тримати малим: логи, кеш, завантажені файли - у томах чи зовнішніх сховищах. Запис у шар контейнера повільніший (copy-on-write) і зникає разом з контейнером.