Что такое слой (layer) в Docker-образе?

Ответ

Слой в Docker-образе — это неизменяемый (read-only) файловый снимок (snapshot), который представляет собой результат выполнения одной инструкции в Dockerfile. Образ формируется как стек таких слоёв.

Как это работает:

  1. Каждая команда в Dockerfile (FROM, RUN, COPY, ADD, ENV и др.) создаёт новый слой поверх предыдущих.
  2. Слои кешируются. Если Dockerfile и контекст сборки не изменились, Docker повторно использует кешированные слои, что значительно ускоряет сборку.
  3. Разные образы могут совместно использовать одни и те же базовые слои (например, alpine:latest), что экономит место на диске.
  4. Контейнер запускается с добавлением тонкого записываемого слоя (container layer) поверх стека слоёв образа.

Пример Dockerfile и соответствующих слоёв:

# Слой 1: Базовый образ (например, alpine:latest)
FROM alpine:latest
# Слой 2: Установка пакетов
RUN apk add --no-cache python3 py3-pip
# Слой 3: Копирование файлов приложения
COPY app.py requirements.txt /app/
# Слой 4: Установка зависимостей Python
RUN pip3 install -r /app/requirements.txt
# Слой 5: Определение команды запуска
CMD ["python3", "/app/app.py"]

Просмотр истории образа:

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

Практические следствия и best practices:

  • Оптимизация размера: Объединяйте связанные команды RUN с помощью && и обратной косой черты, чтобы минимизировать количество слоёв и удалять временные файлы в одном слое.
    # Плохо: 2 слоя
    RUN apt-get update
    RUN apt-get install -y package
    # Хорошо: 1 слой, кеш очищен
    RUN apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/*
  • Порядок инструкций: Меняющиеся реже инструкции (например, COPY зависимостей) стоит размещать выше, чтобы использовать кеш для слоёв с установкой.
  • Многоступенчатая сборка (multi-stage): Позволяет использовать промежуточные образы для компиляции, а в итоговый образ копировать только необходимые артефакты, исключая инструменты сборки и промежуточные слои.

Ответ 18+ 🔞

Смотри, объясню на пальцах, как это всё работает, а то звучит страшно, а на деле — проще пареной репы.

Представь, что Docker-образ — это твой любимый многослойный бутерброд, только его нельзя есть, а можно только смотреть. Каждый слой — это результат одной команды в этом самом Dockerfile. Сделал RUN apt update — положил кусок колбасы. Сделал COPY — добавил слой сыра. И так далее.

Суть в чём:

  1. Каждая строчка в Dockerfile — это новый, намертво зафиксированный слой. Раз положил — не переделаешь. Это как фотография состояния файлов после каждой команды.
  2. Вот это самое важное: слои кешируются, ёпта! Если ты второй раз собираешь тот же Dockerfile и ничего не менял, Docker не будет всё делать заново, а возьмёт готовые слои из кеша. Скорость сборки вырастает в разы, волнение ебать как падает.
  3. Разные образы могут делить общие слои. У тебя десять сервисов на alpine:latest? На диске он будет лежать в одном экземпляре, а не в десяти. Экономия места — просто овердохуища.
  4. Когда ты из образа запускаешь контейнер, поверх этой стопки неизменяемых слоёв кладётся ещё один, тонкий, но записываемый. Всё, что меняется в работающем контейнере (логи, временные файлы), пишется туда. Остановил контейнер — этот слой накрылся медным тазом.

Вот смотри на пример, прям как в жизни:

# Слой 1: Берём голый alpine. Хуй с горы, самый базовый.
FROM alpine:latest
# Слой 2: Ставим питон и pip. Слой уже посолиднее.
RUN apk add --no-cache python3 py3-pip
# Слой 3: Копируем наш код. Тут уже подозрение ебать чувствую — а вдруг не скопируется.
COPY app.py requirements.txt /app/
# Слой 4: Ставим зависимости из requirements.txt. Вот тут обычно всё идёт по пизде, если зависимости кривые.
RUN pip3 install -r /app/requirements.txt
# Слой 5: Говорим, что запускать, когда контейнер стартанёт.
CMD ["python3", "/app/app.py"]

Хочешь посмотреть, из какого дерьма твой образ слеплен? Жми:

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

А теперь лайфхаки, чтобы не быть распиздяем:

  • Размер имеет значение: Не плоди кучу слоёв RUN. Объединяй команды в одну цепочку через && и чисти за собой мусор в этом же слое! Иначе останутся временные файлы от установки пакетов, и образ раздует как корову.

    # Делает два слоя и оставляет хлам. Пиздец как плохо.
    RUN apt-get update
    RUN apt-get install -y package
    
    # Делает один слой и убирает за собой. Красота!
    RUN apt-get update && apt-get install -y package && rm -rf /var/lib/apt/lists/*
  • Порядок — всё: Ставь в начале Dockerfile команды, которые меняются редко (типа копирования зависимостей requirements.txt или package.json). А то скопируешь свой код app.py в самом начале, будешь его каждый раз править, и при каждой сборке Docker будет переустанавливать все зависимости, потому что слой с COPY изменился и кеш дальше не работает. Терпения ноль ебать после третьей такой сборки.
  • Многоступенчатая сборка — маст хэв для умных: Это когда ты в одном Dockerfile используешь один образ (тяжёлый, со всеми компиляторами), чтобы собрать своё приложение, а потом копируешь готовый бинарник в другой, чистенький и лёгкий образ. Все инструменты сборки остаются в первом образе, а в итоговый попадает только твоя готовая программа. Разница в размере может быть в десятки раз. Просто ебушки-воробушки, а не технология.