Перейти к содержимому

6. Слои и кэш

Каждый Docker-образ состоит из слоёв — неизменяемых (иммутабельных) файловых систем, объединённых с помощью Union File System. Понимание механизма слоёв и кеширования позволяет значительно ускорить сборку образов.

Каждая инструкция RUN, COPY и ADD в Dockerfile создаёт новый слой. Инструкции FROM, ENV, ARG, EXPOSE, CMD, ENTRYPOINT, WORKDIR — создают мета-слои, не занимающие дискового пространства.

FROM node:20-alpine # Слой 1: базовый образ (~130 MB)
WORKDIR /app # Мета-данные
COPY package*.json ./ # Слой 2: файлы зависимостей (~10 KB)
RUN npm ci # Слой 3: node_modules (~50 MB)
COPY . . # Слой 4: исходный код (~5 MB)
CMD ["node", "server.js"] # Мета-данные

Docker кеширует каждый слой. При повторной сборке Docker проверяет каждую инструкцию:

  1. Если инструкция и её контекст не изменились → используется кеш
  2. Если что-то изменилось → пересобираются этот и все последующие слои

Это ключевое правило: как только кеш инвалидируется, все последующие слои пересобираются заново.

docker build .
Step 1/6 : FROM node:20-alpine
---> 3a1e2b23a4f1 # кеш
Step 2/6 : WORKDIR /app
---> Using cache # кеш
Step 3/6 : COPY package*.json ./
---> Using cache # кеш (package.json не изменился)
Step 4/6 : RUN npm ci
---> Using cache # кеш (зависимости не изменились!)
Step 5/6 : COPY . .
---> abc123def456 # пересборка (код изменился)
Step 6/6 : CMD ["node", "server.js"]
---> Rebuilt # пересборка

Порядок инструкций критически важен для эффективного кеширования:

❌ Плохой Dockerfile — долгая пересборка:

FROM node:20-alpine
WORKDIR /app
COPY . . # Копируем всё сразу — кеш сбивается при любом изменении кода!
RUN npm install # Пересобирается каждый раз
CMD ["node", "app.js"]

✅ Хороший Dockerfile — быстрое кеширование:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./ # Сначала только зависимости
RUN npm ci # Этот слой кешируется, если package.json не менялся
COPY . . # Код копируем ПОСЛЕ установки зависимостей
CMD ["node", "app.js"]

Теперь при изменении только кода приложения npm install не нужно запускать заново — его результат уже закеширован.

Окно терминала
# История слоёв образа с размерами
docker history nginx
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 3 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon... 0B
<missing> 3 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 3 weeks ago /bin/sh -c set -x && ... install nginx 45.4MB
# Просмотр с полными командами
docker history --no-trunc nginx
# Дайвер для детального анализа (инструмент dive)
dive nginx

Объединение RUN-команд:

# ❌ Три слоя по 10 MB каждый = 30 MB
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# ✅ Один слой = ~10 MB (кеш apt удаляется в том же слое)
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*

Использование .dockerignore:

node_modules/
.git/
.env
*.log
dist/
coverage/
.DS_Store

Docker BuildKit (включён по умолчанию с Docker 23+) улучшает производительность:

  • Параллельная сборка независимых слоёв
  • Улучшенное кеширование (включая удалённый кеш)
  • Поддержка монтирования секретов и SSH
Окно терминала
# Включить BuildKit для старых версий
DOCKER_BUILDKIT=1 docker build .
# Сборка с отображением прогресса
docker build --progress=plain .