← Назад к вопросам

Как хранится приложение в Docker image

2.0 Middle🔥 211 комментариев
#Docker, Kubernetes и DevOps#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как хранится приложение в Docker image

Docker image состоит из слоёв (layers), каждый слой представляет собой инкрементальные изменения файловой системы.

1. Слои (Layers) в Docker image

Kаждая строка в Dockerfile создаёт новый слой:

FROM openjdk:17-slim               # слой 1 — базовый image
WORKDIR /app                       # слой 2 — установка рабочей директории
COPY pom.xml .                     # слой 3 — копирование файлов
RUN mvn clean install -DskipTests  # слой 4 — установка зависимостей
COPY src ./src                     # слой 5 — копирование исходного кода
RUN mvn package                    # слой 6 — компиляция приложения
EXPOSE 8080                        # слой 7 — metadata
ENTRYPOINT ["java", "-jar", "target/app.jar"]

Визуально:

┌─────────────────────────────────────┐
│ Слой 7: ENTRYPOINT metadata         │ ← writable layer (во время запуска)
├─────────────────────────────────────┤
│ Слой 6: /target/app.jar (6 MB)      │ ← read-only
├─────────────────────────────────────┤
│ Слой 5: /app/src (200 MB)           │ ← read-only
├─────────────────────────────────────┤
│ Слой 4: ~/.m2 (dependencies 500 MB) │ ← read-only
├─────────────────────────────────────┤
│ Слой 3: pom.xml (2 KB)              │ ← read-only
├─────────────────────────────────────┤
│ Слой 2: /app directory              │ ← read-only
├─────────────────────────────────────┤
│ Слой 1: openjdk:17-slim base (500 MB) │ ← read-only
└─────────────────────────────────────┘

2. Union File System (UnionFS)

Docker использует Union File System для объединения слоёв:

# Файлы видны как единая файловая система
docker run -it myapp:latest ls -la /app

# На самом деле:
# /app — виртуальное объединение слоёв
# /app/pom.xml — из слоя 3
# /app/src — из слоя 5
# /app/target — из слоя 6

3. Copy-on-Write (CoW)

Если контейнер изменяет файл из read-only слоя, создаётся копия:

# container layer (writable, создаётся при docker run)
┌───────────────────────────────────┐
│ пользовательские изменения        │ ← новый файл
├───────────────────────────────────┤
│ /tmp/logs (контейнер пишет)       │ ← CoW копия
├───────────────────────────────────┤
│ read-only слои из image           │
└───────────────────────────────────┘
// Если приложение пишет в лог
public class App {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("/tmp/app.log");
        fw.write("Hello");  // Docker создаёт CoW копию файла
        fw.close();
    }
}

4. Размер и кэширование слоёв

# Проверить слои image
$ docker history myapp:latest
IMAGE               CREATED             CREATED BY                                      SIZE
<image-id>          2 hours ago         /bin/sh -c #(nop)  ENTRYPOINT ["java" "-jar"…   0B
<image-id>          2 hours ago         /bin/sh -c mvn package                          200MB
<image-id>          2 hours ago         /bin/sh -c mvn clean install -DskipTests         500MB
<image-id>          2 hours ago         /bin/sh -c #(nop) COPY file:pom.xml /app         2KB
<image-id>          2 weeks ago         /bin/sh -c #(nop) WORKDIR /app                   0B
<image-id>          2 weeks ago         /bin/sh -c #(nop)  FROM openjdk:17-slim:…       500MB

# Общий размер: ~1.2 GB (слои)   

5. Слои кэшируются при сборке

# Dockerfile
FROM openjdk:17-slim               # слой 1 (кэш из registry)
WORKDIR /app                       # слой 2 (новый)
COPY pom.xml .                     # слой 3 (новый)
RUN mvn clean install -DskipTests  # слой 4 (кэш, если pom.xml не изменился)
COPY src ./src                     # слой 5 (новый, исходный код изменился)
RUN mvn package                    # слой 6 (пересчитывается, т.к. src новый)

При docker build:

  1. Прочитай pom.xml
  2. Проверь кэш — есть ли слой с этим pom.xml?
  3. Если да — используй кэш (быстро)
  4. Если нет — выполни RUN (медленно)

6. Оптимизация Dockerfile для слоёв

❌ Плохо — изменяет много слоёв:

FROM openjdk:17-slim
WORKDIR /app
COPY . .                     # копирует всё
RUN mvn clean install && mvn package && rm -rf /tmp/*

✅ Хорошо — оптимизирует слои:

FROM openjdk:17-slim
WORKDIR /app

# слой 1: зависимости (кэшируется хорошо)
COPY pom.xml .
RUN mvn dependency:resolve

# слой 2: код (меняется часто)
COPY src ./src
RUN mvn package -DskipTests

# слой 3: финальный (минимальный)
FROM openjdk:17-slim
COPY --from=0 /app/target/app.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Multi-stage build (рекомендуется):

# Stage 1: builder
FROM openjdk:17-slim as builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:resolve
COPY src ./src
RUN mvn package -DskipTests

# Stage 2: runtime (маленький образ)
FROM openjdk:17-slim
WORKDIR /app
COPY --from=builder /build/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

7. Где хранятся слои на диске

# В Docker daemon
/var/lib/docker/
├── image/
│   └── overlay2/
│       └── layerdb/
│           └── sha256/  # индексы слоёв по hash
├── overlay2/  # файловая система слоёв
│   ├── a3b2c1d4.../
│   │   ├── diff/  # изменения в этом слое
│   │   ├── work/  # рабочая директория
│   │   └── merged/  # объединённая FS
│   ├── b4c3d2e5.../
│   └── ...
└── containers/
    └── <container-id>/
        ├── layer/
        └── ...

8. Docker image в registry

# Когда пушишь image в Docker Hub
$ docker push myrepo/myapp:1.0

# Docker разделяет на слои
Digest: sha256:abc123...  # hash всего image
Config: sha256:def456...  # metadata

Layers:
- sha256:layer1... (500 MB)  — base image
- sha256:layer2... (200 KB)  — dependencies
- sha256:layer3... (150 MB)  — code
- sha256:layer4... (10 MB)   — binary

# При pull загружаются только новые слои

9. Размер контейнера vs image

# Image size (read-only слои)
$ docker images
REPOSITORY  TAG     SIZE
myapp       latest  706MB

# Размер контейнера при запуске
$ docker ps -s
CONTAINER ID  SIZE
abc123...     50MB  # 50 MB новых файлов (CoW слой)

# Общее:
# - image: 706 MB (на диске, кэшируется)
# - контейнер: 706 + 50 = 756 MB (при запуске)

10. Inspect layers

# Посмотреть конфиг image
$ docker inspect myapp:latest
[
  {
    "RootFS": {
      "Type": "layers",
      "Layers": [
        "sha256:abc123...",  # слой 1
        "sha256:def456...",  # слой 2
        "sha256:ghi789..."   # слой 3
      ]
    }
  }
]

# Или через dive (инструмент для анализа)
$ dive myapp:latest

Итого

  • Docker image = стек read-only слоёв + metadata
  • Каждая команда Dockerfile = новый слой
  • Union File System объединяет слои в один
  • Copy-on-Write позволяет контейнеру менять файлы
  • Кэширование слоёв ускоряет сборку
  • Multi-stage builds уменьшают размер итогового image
  • При docker push слои отправляются отдельно (экономия трафика)
Как хранится приложение в Docker image | PrepBro