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

За счет чего достигается легковесность контейнера в Docker

2.3 Middle🔥 231 комментариев
#Docker, Kubernetes и DevOps

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

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

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

# За счет чего достигается легковесность контейнера в Docker

Краткий ответ

Докер контейнеры легче чем виртуальные машины благодаря использованию общего ядра ОС вместо эмуляции всей ОС. Docker использует Linux kernel features (namespaces и cgroups) для изоляции процессов без полной виртуализации.

Архитектура Docker vs Виртуальные машины

Виртуальная машина (тяжелая)

Хост ОС
├─ Hypervisor (KVM, VirtualBox)
│  ├─ ОС 1 (200MB) + ядро + драйверы
│  │  └─ Приложение
│  ├─ ОС 2 (200MB) + ядро + драйверы
│  │  └─ Приложение
│  └─ ОС 3 (200MB) + ядро + драйверы
│     └─ Приложение

Время запуска: несколько минут
Объем диска: ГБ (100+)
Потребление памяти: ГБ

Docker контейнер (легкий)

Хост ОС (Linux kernel)
├─ Docker Daemon
│  ├─ Контейнер 1 (процесс)
│  │  ├─ Приложение
│  │  └─ Зависимости (МБ)
│  ├─ Контейнер 2 (процесс)
│  │  ├─ Приложение
│  │  └─ Зависимости (МБ)
│  └─ Контейнер 3 (процесс)
│     ├─ Приложение
│     └─ Зависимости (МБ)

Время запуска: несколько миллисекунд
Объем диска: МБ (5-100)
Потребление памяти: МБ

Ключевые технологии Linux Kernel

1. Namespaces (изоляция)

Namespaces позволяют контейнерам иметь независимые виды на системные ресурсы:

# PID namespace — контейнер видит свои процессы
Containers видят разные PID:
Контейнер 1: PID 1 (init), PID 2 (app) → но это PID 100, 101 на хосте
Контейнер 2: PID 1 (init), PID 2 (app) → но это PID 200, 201 на хосте

# Network namespace — отдельная сетевая стек
Каждый контейнер имеет свой:
- IP адрес
- Port space
- Маршруты
- Файерволл

# Mount namespace — отдельная файловая система
Контейнер видит свой корень /
- /bin → слой контейнера
- /lib → базовый образ
- /app → приложение

# User namespace — изоляция прав
Пользователь root в контейнере может быть непривилегированным пользователем на хосте

# IPC namespace — изоляция межпроцессного взаимодействия
Контейнеры не могут видеть shared memory другого контейнера

# UTS namespace — отдельные hostname и domainname

2. Cgroups (ограничение ресурсов)

Control Groups позволяют ограничивать потребление ресурсов:

docker run --memory="512m" nginx
  ↓
  Контейнер не может использовать больше 512MB памяти

docker run --cpus="0.5" nginx
  ↓
  Контейнер использует максимум 50% одного ядра процессора

docker run --pids-limit=10 nginx
  ↓
  Контейнер может создать максимум 10 процессов

Cgroups отслеживают и ограничивают:

  • Память (RSS, swap)
  • CPU
  • I/O дискового ввода-вывода
  • Сеть
  • Количество процессов

Многоуровневая файловая система (Layers)

Copy-on-Write (CoW)

Докер использует многоуровневую файловую систему, где слои переиспользуются:

FROM ubuntu:20.04          # Слой 1: 72 MB базовая ОС
RUN apt-get install -y java # Слой 2: +200 MB
COPY app.jar .             # Слой 3: +50 MB (приложение)
ENTRYPOINT java -jar app.jar

Результат

Образ Java приложения:
├─ Слой 1 (ubuntu базе): 72 MB (переиспользуется всеми образами)
├─ Слой 2 (Java): 200 MB (переиспользуется всеми Java образами)
└─ Слой 3 (приложение): 50 MB (уникально для этого образа)

Общий размер одного образа: 322 MB
Если создать ещё 5 контейнеров: 322 MB + 5*(дельта слоя контейнера)

Copy-on-Write механизм

Базовый образ (read-only):
/usr/bin/java
/lib/libc.so
/app/config.yml

Контейнер A изменяет /app/config.yml:
1. Копируется в слой контейнера (write layer)
2. Остальные файлы остаются в read-only слое
3. Размер слоя = только изменённые файлы

Сравнение размеров

Без Docker

Установка 3 экземпляров Java приложений:
- ОС 1: 2 GB + Java + app
- ОС 2: 2 GB + Java + app
- ОС 3: 2 GB + Java + app
Итого: 6+ GB + Java*3 + app*3

С Docker

Ubuntu образ: 72 MB (общий)
Java слой: 200 MB (переиспользуется)
Приложение слой: 50 MB × 3 контейнера
Итого: 72 + 200 + 150 = 422 MB

Экономия: в 14+ раз меньше места!

Пример: Alpine Linux

# ❌ Большой образ
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
# Размер: 100+ MB

# ✅ Легкий образ
FROM alpine:latest
RUN apk add --no-cache curl
# Размер: 5-10 MB

Alpine базирована на муслинке (миниатюрная libc), а не на полной glibc.

Процесс изоляции в действии

// Java приложение в контейнере
public class DockerApp {
    public static void main(String[] args) {
        // Это процесс на хосте, но изолирован
        System.out.println(ManagementFactory.getRuntimeMXBean().getPid());
        // Выведет: 1 (в контейнере)
        // На хосте это может быть PID 45678
    }
}
# На хосте видно
$ ps aux | grep java
root     45678 99.5  0.5  2563480 45088 ?  Sl  10:00 java -jar app.jar

# В контейнере видно
$ ps aux
root     1 99.5  0.5  2563480 45088 ?  Sl  10:00 java -jar app.jar

Потребление ресурсов

Память

Типичный контейнер: 5-50 MB (базовая система)
Java приложение: +200-500 MB (JVM + app)
Всего на контейнер: 250-600 MB

Вирт. машина: 2+ GB минимум
Вирт. машина с Java: 3+ GB

Время запуска

Вирт. машина: 30-60 секунд (boot ОС)
Докер контейнер: 100-500 миллисекунд (start process)

Почему контейнеры легче (итоговая таблица)

АспектВМDocker
Ядро ОССвое (эмулируется)Общее (Linux kernel)
ОСПолная (2+ GB)Только необходимое (5-100 MB)
Файловая системаЦельная копияСлои с переиспользованием (CoW)
ПроцессыЭмулируютсяНастоящие процессы ОС
ПамятьВыделяется заранееПо требованию
ЗапускBoot цикл (минуты)Fork процесса (мс)
РезультатГБМБ

Заключение

Докер контейнеры легче виртуальных машин потому что:

  1. Используют общее ядро ОС вместо полной виртуализации (namespaces, cgroups)
  2. Не требуют полной ОС в образе (только зависимости)
  3. Используют многоуровневую ФС с переиспользованием слоев (Copy-on-Write)
  4. Изоляция на уровне процессов, а не эмуляция железа
  5. Быстрый запуск — просто fork процесса вместо boot ОС

Это достигается на уровне Linux kernel features, что делает контейнеры идеальными для микросервисной архитектуры и CI/CD конвейеров.

За счет чего достигается легковесность контейнера в Docker | PrepBro