Skip to content

Слои Docker. Как устроена файловая система контейнеров

Docker — это мощная платформа для создания, доставки и запуска приложений в изолированных средах, называемых контейнерами. Одной из ключевых особенностей Docker, обеспечивающих его эффективность и гибкость, является использование слоёв (layers) в файловой системе образов. В этой статье мы подробно рассмотрим, что такое слои Docker, как они работают, зачем нужны и как влияют на производительность и управление контейнерами.


Что такое слой в Docker?

Слой (layer) в Docker — это неизменяемый (immutable) фрагмент файловой системы, представляющий собой результат выполнения одной инструкции в Dockerfile. Каждый слой содержит изменения по сравнению с предыдущим: добавленные, изменённые или удалённые файлы.

Образ Docker строится пошагово, и каждый шаг в Dockerfile создаёт новый слой поверх предыдущих. В итоге образ представляет собой стек таких слоёв, где каждый последующий слой зависит от предыдущего.


Пример: как создаются слои

Рассмотрим простой Dockerfile:

FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
COPY app.py /app/
CMD ["python", "/app/app.py"]

Каждая инструкция порождает отдельный слой:

  1. FROM ubuntu:20.04 — базовый слой, содержащий файловую систему Ubuntu.
  2. RUN apt-get update && apt-get install -y curl — новый слой с установленным curl.
  3. COPY app.py /app/ — слой, добавляющий файл app.py.
  4. CMD ["python", "/app/app.py"] — слой с метаинформацией о команде запуска (не изменяет файловую систему, но влияет на конфигурацию контейнера).

⚠️ Обратите внимание: инструкции CMD, ENTRYPOINT, ENV и другие, не изменяющие файловую систему, создают так называемые слои конфигурации, которые не добавляют данных, но влияют на поведение контейнера.


Особенности слоёв

1. Неизменяемость (Immutability)

Каждый слой — это read-only (только для чтения). Это означает, что после создания слой нельзя изменить. Если вы обновляете файл, Docker создаёт новый слой, в котором этот файл заменён, а старый остаётся в предыдущем слое (но становится "скрытым").

2. Кэширование

Docker кэширует слои. Если при сборке образа инструкция не изменилась по сравнению с предыдущей сборкой, Docker использует закэшированный слой вместо повторного выполнения. Это значительно ускоряет сборку.

Пример:

COPY requirements.txt /app/
RUN pip install -r /app/requirements.txt
COPY . /app/

Если вы меняете только код приложения (COPY . /app/), а requirements.txt остался прежним, Docker переиспользует закэшированный слой с установленными пакетами.

✅ Рекомендация: размещайте часто меняющиеся инструкции ближе к концу Dockerfile, чтобы максимизировать кэширование.

3. Общие слои между образами

Если несколько образов используют один и тот же базовый образ (например, FROM ubuntu:20.04), они делят между собой общие слои. Это экономит место на диске и ускоряет загрузку образов.


Как посмотреть слои образа?

Используйте команду:

docker image history <имя_образа>

Пример:

docker image history myapp:latest

Вывод покажет: - Размер каждого слоя - Когда был создан слой - Какая инструкция его создала

⚠️ Если образ был собран с флагом --squash (или с помощью BuildKit), слои могут быть объединены в один.


Union File System (Объединённая файловая система)

Docker использует Union File System (например, Overlay2, AUFS, ZFS), которая позволяет "накладывать" слои друг на друга, создавая иллюзию единой файловой системы.

Когда контейнер запускается, поверх read-only слоёв образа Docker добавляет тонкий writable-слой (слой на запись). Все изменения, происходящие во время работы контейнера (создание файлов, логов и т.п.), записываются именно в этот верхний слой.

После остановки и удаления контейнера writable-слой удаляется, а образ остаётся неизменным.


Преимущества архитектуры со слоями

  1. Эффективное использование дискового пространства — общие слои не дублируются.
  2. Быстрая сборка образов — за счёт кэширования.
  3. Простота распределения — при обновлении образа передаются только изменённые слои.
  4. Изоляция и воспроизводимость — каждый слой предсказуем и неизменяем.

Минусы и подводные камни

  • Накопление слоёв может привести к раздуванию образа, особенно если в одном RUN удаляются временные файлы, а в другом — создаются.
  • Каждый слой увеличивает размер образа, даже если файлы удаляются (они остаются в предыдущем слое, просто "скрыты").
  • Ограничение на количество слоёв — у некоторых систем (например, AUFS) есть лимиты, хотя современные драйверы (Overlay2) поддерживают сотни слоёв.

Советы по оптимизации слоёв

  1. Объединяйте команды в одном RUN:

```dockerfile # Плохо RUN apt-get update RUN apt-get install -y curl

# Хорошо RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* ```

  1. Удаляйте временные файлы в том же слое: Если вы устанавливаете пакеты и хотите очистить кэш, делайте это в той же команде RUN, иначе кэш останется в предыдущем слое.

  2. Используйте многоступенчатую сборку (multi-stage build): Позволяет собирать приложение в одном образе, а затем копировать только нужные артефакты в минимальный образ, исключая ненужные слои.

Пример:

```dockerfile FROM golang:1.21 AS builder COPY . /app RUN cd /app && go build -o myapp

FROM alpine:latest COPY --from=builder /app/myapp /myapp CMD ["/myapp"] ```


Освоив работу со слоями, вы сможете писать более эффективные Dockerfile и лучше понимать, как работает Docker под капотом.