Skip to content

Контейнер. Что это?

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

Структура Docker контейнера основана на слоях файловой системы и дополнительных метаданных, которые описывают контейнер и его настройки. Вот основные компоненты структуры контейнера:

  • Образ контейнера: Образ контейнера - это основа контейнера, которая состоит из нескольких файловых слоев (layers). Каждый слой содержит файлы, директории, библиотеки, исполняемые файлы и другие ресурсы, необходимые для работы приложения. Эти слои хранятся в образе в виде дифференциальных изменений относительно предыдущих слоев, что позволяет экономить место на диске.
  • Метаданные: Метаданные контейнера хранят информацию о контейнере, такую как имя контейнера, используемые порты, переменные окружения, команды для выполнения и т. д. Эти метаданные хранятся в JSON-файле и используются для конфигурирования и запуска контейнера.
  • Файловая система: Каждый слой образа добавляет или изменяет файлы и директории в файловой системе контейнера. Когда контейнер запускается, все слои объединяются в единую файловую систему, и приложение работает в этой изолированной среде.
  • Пространство имен: Контейнеры изолированы друг от друга с помощью пространств имен. Это означает, что каждый контейнер имеет свое уникальное пространство имен для процессов, сети, файловых систем и других ресурсов.
  • Процессы: Каждый контейнер запускает свои собственные процессы в изолированной среде. Это позволяет контейнерам быть независимыми и изолированными, даже если они работают на одном хосте.
  • Сеть: Контейнеры могут иметь свои собственные сетевые интерфейсы и IP-адреса, что обеспечивает изоляцию и позволяет контейнерам общаться друг с другом и с внешним миром.

Слои контейнера

Слои контейнера - это основные строительные блоки, из которых состоит Docker образ. Когда вы создаете образ, Docker использует концепцию слоев для оптимизации хранения, передачи и обновления образов. Каждый слой представляет собой набор изменений относительно предыдущего слоя, что позволяет сэкономить место на диске и ускорить процесс создания и обновления образов.

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

  • Базовый образ: Каждый Docker образ начинается с базового образа. Этот базовый образ может быть операционной системой, такой как Ubuntu, Alpine Linux или другой. Базовый образ представляет собой первый слой контейнера.
  • Изменения слоев: После базового слоя создаются последующие слои, каждый из которых представляет собой изменения в файловой системе, добавленные к предыдущему слою. Это могут быть новые файлы, измененные файлы или даже удаленные файлы.
  • Слои на чтение и запись: Важно понимать, что каждый слой на чтение остается неизменным и не может быть изменен после создания. Когда вы создаете контейнер из образа, Docker создает новый слой на запись поверх образа для изменений, внесенных во время работы контейнера. Это обеспечивает изоляцию и возможность создания множества контейнеров на основе одного образа.
  • Инкрементальность: Поскольку каждый слой представляет собой набор изменений, образы могут делить общие слои между собой. Это означает, что если несколько образов используют один и тот же слой, он будет сохранен только один раз на диске, что сэкономит место.
  • Кэширование: Docker также использует кэширование слоев. Если слой был создан ранее и файлы не изменились, Docker просто повторно использует этот слой из кэша, вместо того чтобы создавать его заново. Это ускоряет процесс создания образов.

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

Главное отличие docker-image от контейнера - это доступный для записи верхний слой контейнера. Все, что контейнер будет писать и менять, происходит в этом верхнем слое, и никак не влияет на нижние слои docker-образа, на основы которого создан контейнер.

Процесс в контейнере

Один контейнер - один процесс! Это классическая и рекомендуемая схема применения контейнеров. Приложение, запушенное в контейнере, стартует с помощью ENRYPOINT и/или CMD, дальше этот процесс "единолично" работает в контейнере, он может "форкнуться" при необходимости, но изначально это один главный процесс. Если необходимо запустить в своем контейнере несколько процессов

Ограничение доступных ресурсов

По умолчанию, контейнер никак не ограничен в использовании ресурсов хостового компьютера. Но в Докере есть механизм который позволяет задавать лимиты на используюмую память и CPU для отдельно взятого контейнера.

Memory limits

  • Жесткие ограничения, не дают контейнеру использовать больше, чем указано в ограничении
  • Мягкие ограничения, позволяют процессу выходить за лимиты, пока на хосте не сработают ред-флаги, например: ядро обнаружет, что осталось мало памяти или за нее началась конкуренция.

Подробно про лимиты памяти

CPU limits

Конфигурация лимитов CPU осуществляется через CFS scheduler

Метрики в реальном времени

Самый простой способ снять метрики с контейнеров - команда docker stats, в режиме реального времени показывает сколько каких ресурсов потребляет docker.

docer stats
docker stats <container1> container2> # позволяет указывать метрики каких контейнеров выводить

docker stats --no-stream # отменяет потоковый вывод метрик
CONTAINER ID   NAME                        CPU %     MEM USAGE / LIMIT    MEM %     NET I/O           BLOCK I/O         PIDS
00a1fdecdca6   cadvisor                    10.71%    91.57MiB / 3.82GiB   2.34%     1.8GB / 114GB     86.4MB / 0B       19
f09289fd17bb   prometheus                  0.00%     166.4MiB / 3.82GiB   4.26%     119GB / 2.13GB    2.65GB / 34.5GB   9
e58a8379b9f4   alertmanager                0.23%     19.29MiB / 3.82GiB   0.49%     1.38MB / 454kB    158MB / 696kB     9
68aa594554ae   grafana                     0.12%     80.17MiB / 3.82GiB   2.05%     123MB / 473MB     794MB / 493MB     15
997ba88d455f   node-exporter               0.00%     12.45MiB / 3.82GiB   0.32%     312MB / 5.56GB    43.2MB / 0B       9

Подробно про docker stats

Подробно про сбор метрик

Размер контейнера

Что бы узнать сколько места занимает запущенный контейнер используется команда docker ps -s

Форматированный вывод команды docker ps
docker ps -s --format "table {{.Names}}\t{{.ID}}\t{{.Size}}"

# -size: это все, что контейнер записал в верхний слой
# -virtual size: все место которое занимает контейнер в ФС, включая ro-слои образа

Важно понимать, что два разных контейнера, созданных из одного и того же образа будут использовать read-only слои совместно, что существенно экономит место на хосте.