Тема 03

Контейнеризация и архитектура Docker

Контейнеризация и архитектура Docker

Контейнер как модель изоляции процессов

Контейнеризация решает практическую задачу: запускать приложения изолированно и воспроизводимо, не создавая для каждого приложения отдельную виртуальную машину. В отличие от VM, контейнер не содержит собственного ядра операционной системы. Контейнер — это набор процессов, которые выполняются на ядре хоста, но «видят» ограниченную картину системы: свои процессы, свою файловую систему, свои сетевые интерфейсы и свои квоты ресурсов. Эта изоляция достигается механизмами ядра Linux, а Docker предоставляет удобную модель упаковки и запуска.

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

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

Сопоставление с виртуализацией

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

Виртуальная машина представляет собой полноценную вычислительную среду с собственной операционной системой. Гипервизор обеспечивает аппаратную изоляцию: гостевая система не имеет прямого доступа к ресурсам хоста или других виртуальных машин. Границы изоляции проходят на уровне виртуального оборудования, что обеспечивает высокую степень безопасности. Даже если гостевая операционная система подвергнется компрометации, злоумышленнику потребуется преодолеть дополнительный барьер в виде гипервизора, чтобы получить доступ к хостовой системе.

Контейнер не содержит собственной операционной системы и не требует гипервизора. Изоляция обеспечивается средствами ядра хостовой системы — пространствами имён и группами управления ресурсами, рассмотренными в предыдущей теме. С точки зрения хостовой системы контейнер представляет собой процесс (или группу процессов) в ограниченном окружении. Это даёт ряд практических преимуществ: минимальные накладные расходы (нет необходимости загружать дополнительную операционную систему), быстрый запуск (секунды вместо минут) и компактность артефакта (контейнерный образ содержит только приложение и его зависимости).

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

Сценарии предпочтительного использования

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

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

На практике виртуализация и контейнеризация часто применяются совместно. Типичная архитектура облачной платформы предполагает, что контейнеры исполняются внутри виртуальных машин. Виртуальная машина обеспечивает аппаратную изоляцию между потребителями облачного сервиса, а контейнеры внутри неё обеспечивают эффективное размещение и управление прикладными компонентами.

Сравнительная характеристика

По уровню изоляции виртуальные машины обеспечивают аппаратную изоляцию через гипервизор, тогда как контейнеры полагаются на механизмы ядра операционной системы. По объёму потребляемых ресурсов виртуальная машина включает полную операционную систему и потребляет от сотен мегабайт до нескольких гигабайт оперативной памяти; контейнер потребляет только ресурсы, необходимые для исполнения прикладного процесса. По времени запуска виртуальная машина загружается от десятков секунд до нескольких минут, контейнер — за секунды. По размеру артефакта образ виртуальной машины обычно измеряется гигабайтами, контейнерный образ — десятками или сотнями мегабайт. По совместимости виртуальная машина может исполнять произвольную операционную систему, тогда как контейнер ограничен ядром хостовой системы.

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

Контейнер и образ: назначение и взаимосвязь

В Docker используются две взаимосвязанные сущности: образ и контейнер. Образ — это подготовленный артефакт, который описывает, что именно будет запущено. Контейнер — это экземпляр выполнения, который определяет, как именно это будет запущено в конкретный момент времени.

Образ (image) представляет собой файловую систему приложения и набор метаданных запуска. В образ не входит ядро операционной системы и драйверы устройств: контейнеры используют ядро системы-хоста. Назначение образа — обеспечить переносимость и воспроизводимость: если используется один и тот же образ, приложение стартует в одинаковом наборе файлов и зависимостей (при условии совместимости архитектуры и платформы). Образ хранится локально или в реестре и может быть версионирован.

Контейнер (container) создаётся на основе образа и является объектом времени выполнения. При создании контейнера формируется отдельный слой файловой системы с возможностью записи, а также фиксируются параметры запуска: команда, переменные окружения, ограничения ресурсов, подключаемые хранилища и сетевые настройки. Контейнер “живёт” пока выполняется основной процесс внутри контейнера; при завершении этого процесса контейнер переходит в остановленное состояние. Поэтому контейнер — это не “отдельная операционная система”, а управляемая среда для выполнения процесса(ов) с заданной изоляцией и ресурсными ограничениями.

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

Из этой модели следует ключевая практика эксплуатации. Изменения, выполненные внутри работающего контейнера, относятся к его слою записи и не являются надёжным способом сопровождения: при пересоздании контейнера они исчезают. Поэтому корректный процесс обновления приложения заключается в создании новой версии образа и запуске нового контейнера, а не во внесении изменений в уже запущенный экземпляр.

Изоляция контейнеров: пространства имён и группы управления ресурсами

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

Изоляция обеспечивается пространствами имён (namespaces). Пространство имён можно понимать как механизм, который заставляет процесс «видеть» только выделенный ему набор системных объектов. Например, в контейнере может быть собственная нумерация процессов, собственная конфигурация сети и отдельное дерево монтирования файловых систем. Для контейнеризации чаще всего используются несколько типов пространств имён.

PID namespace определяет, какие процессы видимы и как они нумеруются внутри контейнера. Благодаря этому процессы контейнера не видят процессы хоста и других контейнеров, а внутри контейнера основной процесс получает идентификатор PID 1. Этот факт имеет практические последствия: PID 1 в Linux обладает особыми обязанностями по обработке сигналов и «сборке» завершившихся дочерних процессов; поэтому некоторые приложения в роли PID 1 требуют корректной настройки, а в реальных системах нередко используют специализированные минимальные процессы-инициализаторы.

Network namespace предоставляет контейнеру отдельный сетевой стек: сетевые интерфейсы, таблицы маршрутизации, правила фильтрации. С точки зрения процессов внутри контейнера сеть выглядит «своей», хотя физически пакеты всё равно проходят через подсистему хоста. Именно на базе network namespaces Docker строит модели виртуальных сетей и изоляции взаимодействия между контейнерами.

Mount namespace формирует отдельное дерево монтирования. Это позволяет контейнеру иметь свою корневую файловую систему, собранную из слоёв образа, а также подключать дополнительные хранилища (например, тома) в заданные точки. Mount namespace тесно связан с механизмом слоистой файловой системы контейнера: процессы контейнера видят единое дерево каталогов, хотя на уровне реализации оно составлено из нескольких слоёв.

UTS namespace изолирует такие атрибуты, как имя хоста и доменное имя узла, что полезно для корректного поведения приложений, которые используют эти значения. IPC namespace изолирует механизмы межпроцессного взаимодействия (например, разделяемую память и семафоры), снижая риск нежелательных пересечений между контейнерами.

User namespace позволяет отображать идентификаторы пользователей и групп контейнера на другие идентификаторы на хосте. Это важно для безопасности: процесс может иметь высокие привилегии внутри контейнера, но соответствовать непривилегированному пользователю на хосте. На этой возможности основаны режимы запуска без прав суперпользователя (rootless), которые уменьшают последствия возможной компрометации приложения.

Одной из целей контейнеризации является также управляемое распределение ресурсов. Для этого ядро предоставляет механизм групп управления ресурсами (cgroups). Через cgroups можно ограничивать и учитывать потребление процессорного времени и оперативной памяти, а также собирать статистику использования ресурсов. Практическая ценность cgroups проявляется в мультисервисных системах: без ограничений один контейнер способен занять всю память или создать избыточную нагрузку на CPU и тем самым нарушить работу других сервисов. В эксплуатационной практике задавать лимиты — это способ сделать поведение системы предсказуемым и снизить влияние ошибок и пиков нагрузки.

Механизмы изоляции контейнера: пространства имён обеспечивают изоляцию видимости, cgroups ограничивают потребление ресурсов
Механизмы изоляции контейнера: пространства имён обеспечивают изоляцию видимости, cgroups ограничивают потребление ресурсов

Важно понимать, что namespaces и cgroups решают разные задачи. Пространства имён отвечают за границы видимости и изоляцию, тогда как cgroups — за количественные ограничения и учёт. Docker объединяет оба механизма в единый объект контейнера, что и создаёт эффект «изолированной среды выполнения».

Файловая система контейнера: слои образа, слой записи и внешнее хранение данных

Когда контейнер создаётся из образа, Docker должен предоставить процессам контейнера корневую файловую систему, которая выглядит как обычное дерево каталогов. При этом образ состоит из слоёв «только чтение», а контейнер должен иметь возможность изменять файлы в ходе работы. Эта задача решается при помощи объединённой (слоистой) файловой системы, где к слоям образа добавляется верхний слой с возможностью записи.

Слой записи контейнера является индивидуальным для каждого контейнера. Любые изменения файлов — создание новых, модификация существующих, удаление — фиксируются именно в этом верхнем слое. При чтении данных система формирует итоговую картину: если файл изменён, используется версия из слоя записи; если не изменён, берётся из нижних слоёв образа. Такое устройство обеспечивает быстрое развёртывание и экономию дискового пространства.

Вместе с тем слой записи связан с жизненным циклом контейнера. При удалении контейнера этот слой исчезает, а значит, исчезают и изменения, которые хранились только в нём. Поэтому слой записи следует рассматривать как место для временных данных и внутренних изменений, которые допускается потерять при пересоздании контейнера. Если приложению требуется постоянное хранение (данные базы, загруженные пользователями файлы, результаты вычислений), такие данные должны быть вынесены во внешнее хранилище — механизмы постоянного хранения данных в контейнерных средах подробно рассматриваются в соответствующей теме курса.

Слоистая файловая система: контейнеры разделяют общие слои образа, каждый имеет собственный слой записи
Слоистая файловая система: контейнеры разделяют общие слои образа, каждый имеет собственный слой записи

Слоистая файловая система влияет и на производительность. Для большинства приложений накладные расходы невелики, однако при интенсивной записи слой записи и механизмы copy-on-write могут создавать дополнительные издержки. Это обстоятельство учитывается при проектировании политики хранения данных контейнерных приложений.

Архитектура Docker: клиент, демон и компоненты исполнения

Docker включает клиентскую утилиту (CLI) и серверный компонент Docker Engine (демон). Клиент направляет запросы демону, а демон управляет объектами Docker: образами, контейнерами, сетями, томами и процессом сборки. Этот подход отделяет интерфейс управления от реализации операций и позволяет централизованно вести состояние объектов.

На более низком уровне выполнение контейнеров делегируется специализированным компонентам. Компонент containerd отвечает за управление жизненным циклом контейнеров и взаимодействие с хранилищами образов, а runc выполняет непосредственный запуск контейнера, настраивая изоляцию через пространства имён и ограничения ресурсов через cgroups. Такое разделение упрощает поддержку и развитие системы: высокоуровневые функции управления (образы, сети, тома) отделены от механизма запуска процессов.

Архитектура Docker: клиент, демон, containerd и runc
Архитектура Docker: клиент, демон, containerd и runc

Совместимость контейнерной экосистемы обеспечивается стандартами OCI (Open Container Initiative). OCI определяет формат образов и спецификацию выполнения контейнеров. Благодаря этому образы и средства запуска могут быть взаимозаменяемыми в пределах стандарта, а инфраструктура не оказывается жестко привязанной к одному программному продукту.

Жизненный цикл и базовые операции

Контейнер создаётся из образа, получает параметры запуска и запускает основной процесс. Контейнер считается запущенным, пока выполняется основной процесс. Это объясняет типичную ситуацию, когда контейнер “сразу останавливается”: обычно причиной является корректное завершение основного процесса, а не ошибка платформы.

Жизненный цикл контейнера: создание, запуск, остановка, перезапуск и удаление
Жизненный цикл контейнера: создание, запуск, остановка, перезапуск и удаление

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

Журналы и инспекция конфигурации

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

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

Реестры образов

Образы хранятся в реестрах — публичных или частных. Реестр предоставляет интерфейсы для публикации и получения образов и позволяет использовать образы как типовые артефакты поставки. Для сопровождения существенна политика версионирования: в учебных работах часто применяются теги, однако в эксплуатационных сценариях предпочтительнее исключать неоднозначность и фиксировать конкретные версии (включая возможность использования идентификатора содержимого).

В реальных системах реестр выступает частью цепочки поставки: он позволяет централизованно хранить утверждённые версии образов, управлять доступом и проводить дополнительные проверки (например, сканирование на уязвимости). Эти вопросы будут подробно рассмотрены в главах, посвящённых безопасности и сопровождению.

Итоги темы

Контейнеризация представляет собой модель изоляции процессов, основанную на механизмах ядра операционной системы — пространствах имён и группах управления ресурсами. В отличие от виртуальных машин, контейнеры разделяют ядро хоста, что обеспечивает существенное сокращение времени запуска и накладных расходов. Docker предоставляет единую модель управления контейнерами, образами, сетями и томами, опираясь на стандарты OCI для обеспечения переносимости. Образ выступает неизменяемым артефактом поставки, а контейнер — его экземпляром времени выполнения с эфемерным слоем записи. Понимание границ контейнерной изоляции, слоистой файловой системы и жизненного цикла контейнера является необходимым основанием для последующего изучения сборки образов, сетевого взаимодействия и оркестрации.

Лабораторная работа 3. Контейнеризация и архитектура Docker

Цель работы

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

Предварительные сведения

Для выполнения лабораторной работы потребуется компьютер с операционной системой Windows 10/11 (с WSL 2), macOS 12+ или Linux (Ubuntu 22.04+ или аналогичный дистрибутив). На рабочем месте должен быть установлен Docker Engine версии 24.0 или новее (на Linux) либо Docker Desktop (на Windows и macOS).

Проверка готовности среды выполняется командой:

docker version

Команда должна вывести информацию о клиенте и сервере Docker. Если сервер недоступен, убедитесь, что демон Docker запущен.

Работа выполняется на локальной машине студента и не требует подключения к облачным платформам. Основным результатом является отчёт, содержащий описание проделанных действий, вывод команд, наблюдения и ответы на поставленные вопросы.

Задание

Работа состоит из четырёх последовательных частей.

Часть 1. Установка Docker и проверка среды контейнеризации

Порядок выполнения:

  1. Установите Docker, следуя официальной документации для вашей операционной системы. Зафиксируйте в отчёте версию Docker Engine и версию операционной системы хоста.
  2. Выполните следующие команды и зафиксируйте их вывод:
    # Версия Docker и сведения о среде
    docker version
    
    # Подробная информация о конфигурации Docker Engine
    docker info
    
  3. В выводе команды docker info найдите и зафиксируйте в отчёте:
    • используемый драйвер хранилища (Storage Driver);
    • среду выполнения контейнеров (Default Runtime);
    • корневой каталог Docker (Docker Root Dir);
    • количество контейнеров и образов в системе.
  4. Запустите тестовый контейнер для проверки работоспособности среды:
    docker run hello-world
    

    Убедитесь, что вывод содержит сообщение об успешном выполнении. Объясните в отчёте, какие шаги выполнил Docker при обработке этой команды (откуда был получен образ, что произошло после его загрузки, почему контейнер завершился).

Результат: работающая среда Docker, зафиксированные параметры конфигурации, объяснение выполнения docker run hello-world.

Часть 2. Жизненный цикл контейнера

Цель части — исследовать состояния контейнера, роль основного процесса (PID 1) и механизмы управления жизненным циклом.

Порядок выполнения:

  1. Запустите контейнер с веб-сервером Nginx в фоновом режиме:
    docker run -d --name web-server -p 8080:80 nginx:1.27
    
  2. Убедитесь, что контейнер работает, и зафиксируйте его состояние:
    docker ps
    

    Откройте в браузере адрес http://localhost:8080 и убедитесь, что отображается стартовая страница Nginx.
  3. Исследуйте информацию о контейнере:
    # Подробная конфигурация контейнера
    docker inspect web-server
    
    # Журналы контейнера
    docker logs web-server
    
    # Потребление ресурсов в реальном времени
    docker stats web-server --no-stream
    

    Из вывода docker inspect найдите и зафиксируйте в отчёте: используемый образ, IP-адрес контейнера, проброшенные порты, команду запуска (Cmd или Entrypoint).
  4. Исследуйте переходы между состояниями контейнера:
    # Остановка контейнера
    docker stop web-server
    docker ps -a
    
    # Повторный запуск
    docker start web-server
    docker ps
    
    # Перезапуск
    docker restart web-server
    docker logs web-server --tail 5
    

    Зафиксируйте, как изменяется состояние контейнера (столбец STATUS) после каждой операции. Проверьте, сохраняется ли доступность http://localhost:8080 после перезапуска.
  5. Исследуйте поведение контейнера при завершении основного процесса. Запустите контейнер, основной процесс которого завершается немедленно:
    docker run --name instant-exit ubuntu:24.04 echo "Процесс завершён"
    docker ps -a --filter name=instant-exit
    

    Зафиксируйте статус контейнера. Объясните в отчёте, почему контейнер перешёл в состояние Exited, и сопоставьте это поведение с контейнером Nginx, который продолжает работать.
  6. Выполните очистку:
    docker stop web-server
    docker rm web-server instant-exit
    

Результат: зафиксированные переходы между состояниями, вывод команд инспекции, объяснение роли основного процесса.

Часть 3. Образ и контейнер: слоистая файловая система и эфемерность

Цель части — убедиться на практике в различии между образом (неизменяемый артефакт) и контейнером (эфемерный экземпляр выполнения со слоем записи).

Порядок выполнения:

  1. Загрузите образ и исследуйте его слои:
    docker pull nginx:1.27
    
    # Список слоёв образа
    docker history nginx:1.27
    
    # Размер образа
    docker images nginx:1.27
    

    Зафиксируйте количество слоёв и общий размер образа.
  2. Создайте два контейнера из одного образа и убедитесь, что они разделяют слои:
    docker run -d --name container-a nginx:1.27
    docker run -d --name container-b nginx:1.27
    
  3. Внесите изменение в файловую систему первого контейнера и проверьте, затронут ли второй:
    # Создайте файл в контейнере A
    docker exec container-a bash -c "echo 'Контейнер A' > /tmp/test.txt"
    
    # Проверьте наличие файла в контейнере A
    docker exec container-a cat /tmp/test.txt
    
    # Проверьте наличие файла в контейнере B
    docker exec container-b cat /tmp/test.txt
    

    Зафиксируйте результат. Объясните, почему файл, созданный в контейнере A, не виден в контейнере B, хотя оба контейнера созданы из одного образа.
  4. Проверьте эфемерность слоя записи:
    # Модифицируйте стартовую страницу Nginx в контейнере A
    docker exec container-a bash -c "echo '<h1>Изменённая страница</h1>' > /usr/share/nginx/html/index.html"
    
    # Убедитесь, что изменение действует
    docker exec container-a cat /usr/share/nginx/html/index.html
    
    # Удалите контейнер A и создайте новый из того же образа
    docker stop container-a
    docker rm container-a
    docker run -d --name container-a-new nginx:1.27
    
    # Проверьте содержимое стартовой страницы в новом контейнере
    docker exec container-a-new cat /usr/share/nginx/html/index.html
    

    Зафиксируйте, сохранилось ли изменение. Объясните результат, опираясь на понятия слоя записи и неизменяемости образа.
  5. Исследуйте разницу в дисковом пространстве между образом и контейнерами:
    docker system df
    

    Зафиксируйте, сколько дискового пространства занимают образы и сколько — контейнеры. Объясните, почему два контейнера из одного образа занимают существенно меньше места, чем два копии образа.
  6. Выполните очистку:
    docker stop container-b container-a-new
    docker rm container-b container-a-new
    

Результат: зафиксированные эксперименты с файловой системой, объяснение механизма слоёв, ответы на поставленные вопросы.

Часть 4. Механизмы изоляции контейнера

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

Порядок выполнения:

  1. Запустите контейнер в интерактивном режиме и исследуйте его представление о системе:
    docker run -it --name isolation-test ubuntu:24.04 bash
    

    Внутри контейнера выполните следующие команды и зафиксируйте вывод:
    # Имя хоста (UTS namespace)
    hostname
    
    # Список процессов (PID namespace)
    ps aux
    
    # Сетевые интерфейсы (Network namespace)
    ip address show
    # (если ip недоступна: cat /proc/net/if_inet6 или hostname -I)
    
    # Идентификатор текущего пользователя (User namespace)
    id
    whoami
    
    # Корневая файловая система (Mount namespace)
    ls /
    cat /etc/os-release
    

    Выйдите из контейнера командой exit.
  2. Сопоставьте наблюдения с хостовой системой. На хосте выполните:
    hostname
    ps aux | head -20
    ip address show
    

    Зафиксируйте, какие различия видны между представлением контейнера и хоста. Объясните, какие пространства имён отвечают за каждое из наблюдаемых различий.
  3. Исследуйте ограничение ресурсов через cgroups. Запустите два контейнера с различными лимитами памяти:
    docker run -d --name limited-256 --memory=256m nginx:1.27
    docker run -d --name limited-128 --memory=128m nginx:1.27
    

    Проверьте действующие ограничения:
    docker stats --no-stream
    

    Зафиксируйте столбцы MEM USAGE / LIMIT для каждого контейнера. Убедитесь, что ограничения соответствуют заданным параметрам.
  4. Проверьте реакцию на превышение лимита памяти:
    # Запустите контейнер с жёстким ограничением памяти в 8 МБ
    docker run -d --name oom-test --memory=8m nginx:1.27
    
    # Через несколько секунд проверьте состояние контейнера
    docker ps -a --filter name=oom-test
    docker inspect oom-test --format='{{.State.OOMKilled}}'
    

    Зафиксируйте, завершился ли контейнер аварийно (OOMKilled). Если контейнер продолжает работать, постепенно уменьшайте лимит. Объясните в отчёте механизм OOM Killer и его связь с cgroups.
  5. Заполните сравнительную таблицу на основании наблюдений из текущей и предыдущей лабораторной работы:
    ХарактеристикаВиртуальная машина (VirtualBox)Контейнер (Docker)
    Наличие собственного ядра ОС
    Время запуска среды
    Механизм изоляции процессов
    Механизм изоляции сети
    Видимость процессов с хоста
    Накладные расходы на дисковое пространство
    Ограничение потребления памяти
  6. Выполните очистку:
    docker rm -f isolation-test limited-256 limited-128 oom-test
    docker image prune -f
    

Результат: зафиксированные наблюдения по каждому типу изоляции, заполненная сравнительная таблица, объяснение механизмов pространств имён и cgroups.

Требования к оформлению отчёта

Отчёт должен содержать:

  • титульный лист с указанием номера лабораторной работы, темы, имени студента и номера группы;
  • краткое описание конфигурации рабочего места: характеристики хостовой машины, версия операционной системы, версия Docker Engine;
  • результаты каждой из четырёх частей задания: выполненные команды и их вывод, наблюдения, ответы на поставленные вопросы;
  • заполненную сравнительную таблицу из части 4;
  • общий вывод по работе (5–10 предложений): что было изучено, какие свойства контейнеризации проявились на практике, в чём контейнер оказался удобнее и в чём ограниченнее виртуальной машины.

Скриншоты прилагаются по необходимости для подтверждения выполненных шагов. Вывод команд допускается вставлять как текст в блоках кода.

Контрольные вопросы

  1. Какие действия выполняет Docker при обработке команды docker run, если указанный образ отсутствует локально?
  2. Почему контейнер завершает работу сразу после выполнения команды echo? Каким образом нужно запустить контейнер, чтобы он оставался активным?
  3. Объясните различие между образом и контейнером. Можно ли изменить содержимое образа, записав файл внутри работающего контейнера?
  4. Два контейнера созданы из одного образа. Изменение файла в первом контейнере не отражается во втором. Какой механизм это обеспечивает?
  5. Перечислите пространства имён Linux, используемые при контейнеризации, и поясните роль каждого. Какое пространство имён отвечает за то, что контейнер видит собственный PID 1?
  6. Чем ограничение ресурсов через cgroups отличается от изоляции через пространства имён? Приведите пример ситуации, когда отсутствие лимитов cgroups приводит к проблемам в многоконтейнерной среде.
  7. Что произойдёт с данными, записанными в файловую систему контейнера, если контейнер будет удалён? Каким образом можно сохранить данные между пересозданиями контейнера?