Главная/Материалы/Материал

Слои образа

Как образ собирается из слоёв и что происходит при записи.

Стек слоёв

text
        ┌──────────────────────────────────────┐
        │  записываемый слой container'а       │  ← исчезает с container'ом
        ├──────────────────────────────────────┤
        │  COPY app/ ./app/                    │  ┐
        ├──────────────────────────────────────┤  │
   об-  │  RUN pip install -r requirements.txt │  │ слои образа:
   раз  ├──────────────────────────────────────┤  │ неизменяемы,
        │  COPY requirements.txt .             │  │ адресуются
        ├──────────────────────────────────────┤  │ по содержимому
        │  FROM python:3.13-slim               │  ┘
        └──────────────────────────────────────┘
                        │
                        ▼
             OverlayFS собирает их
             в одно дерево: merged

Как читать

Верхние слои перекрывают нижние. Приложение видит одно дерево каталогов; то, что оно собрано из шести источников, снаружи не заметно.

Записываемый слой принадлежит container'у, а не образу: два container'а из одного образа имеют разные верхние слои и общие нижние.

Что здесь неочевидно

Copy-up: запись копирует файл целиком.

text
до записи:                    после записи одного байта:

верхний слой  (пусто)         верхний слой  [файл 128 МиБ]
                                                   ▲
нижний слой   [файл 128 МиБ]  нижний слой   [файл 128 МиБ]
                                             (остался нетронутым)

Один sed -i по файлу в 128 МиБ даёт записываемый слой в 128 МиБ. chmod тоже вызывает копирование: меняются метаданные, копируется файл.

Удаление не уменьшает образ.

text
RUN rm /секрет
        │
        ▼
верхний слой: whiteout-запись «файла /секрет здесь нет»
нижний слой:  сам файл, целиком, извлекается

Проверяется распаковкой:

bash
docker save образ -o образ.tar
mkdir -p распакованное && tar -xf образ.tar -C распакованное
grep -rl 'секретная строка' распакованное/

Отсюда: секрет, скопированный и удалённый следующей инструкцией, находится в образе. Правильный способ — RUN --mount=type=secret.

Кэш сборки

text
Dockerfile              состояние кэша при изменении app/main.py
──────────────────────────────────────────────────────────────
FROM python:3.13-slim   CACHED
COPY requirements.txt   CACHED    ← файл не менялся
RUN pip install -r ...  CACHED    ← предыдущий слой тот же
COPY app/ ./app/        пересчёт  ← содержимое изменилось

Если поменять порядок:

text
FROM python:3.13-slim   CACHED
COPY . .                пересчёт  ← любое изменение любого файла
RUN pip install -r ...  пересчёт  ← предыдущий слой другой

Один недействительный слой обесценивает все последующие: слой — это состояние файловой системы после предыдущих, и при другом предыдущем результат может быть другим.

Три digest'а одного образа

text
                 index (список манифестов)
                 sha256:aaa…              ← для многоплатформенных
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
   manifest      manifest    manifest
   linux/amd64   linux/arm64  …
   sha256:bbb…                          ← этот в RepoDigests
        │
        ├──► config  sha256:ccc…        ← этот показывает docker images
        └──► слои    sha256:ddd…, …

Для закрепления версии годится тот, что показывает RepoDigests:

bash
docker image inspect образ --format '{{index .RepoDigests 0}}'

Где смотреть

Что нужноКоманда
Слои и их размерdocker history образ --human
Полные командыdocker history образ --no-trunc
Что записал containerdocker diff имя
Общий размерdocker image inspect образ --format '{{.Size}}'
Все слоиdocker image inspect образ --format '{{json .RootFS.Layers}}'

Подробнее

Урок 3.1. Устройство образа
Урок 3.2. Слои и copy-on-write
Урок 17.5. OverlayFS


Навигация

Раздел 03. Работа с Images
Диаграмма кэша сборки
Главное оглавление

Markdown на GitHub ↗