Сборка и сопровождение контейнерных образов
Контейнеризация становится практически полезной не в тот момент, когда среда исполнения умеет запускать изолированные процессы, а тогда, когда приложение можно упаковать в воспроизводимый и управляемый артефакт. Таким артефактом в экосистеме Docker является образ (image). Именно образ фиксирует состав файлов, системных библиотек, прикладных зависимостей и параметры запуска, которые затем используются при создании контейнера. Поэтому качество сопровождения контейнерной среды напрямую зависит от того, насколько дисциплинированно организован процесс сборки образов.
На начальном этапе изучения контейнеров образ иногда воспринимают как «снимок» работающей системы. Такое представление отчасти полезно интуитивно, но методически оно недостаточно. Современная практика исходит не из ручного сохранения состояния, а из декларативного описания сборки. Образ должен получаться не в результате последовательности нефиксируемых действий администратора, а на основе формального сценария, который можно повторить, проверить и встроить в цепочку поставки программного обеспечения. В этой связи сборка контейнерного образа является не только технической процедурой, но и элементом инженерной дисциплины.
В рамках данной темы важно различать две взаимосвязанные задачи. Первая состоит в конструировании образа как исполнимого артефакта. Вторая связана с его сопровождением: версионированием, повторной сборкой, публикацией, обновлением и контролем состава. Если эти задачи решаются несистемно, контейнеризация теряет одно из своих главных преимуществ — предсказуемость поведения приложения в разных средах. Поэтому дальнейшее изложение сосредоточено не только на синтаксисе Dockerfile, но и на принципах, которые делают образ пригодным для учебной и производственной эксплуатации.
Dockerfile как декларативное описание образа
Основным средством описания процесса сборки в Docker является файл Dockerfile. Он задаёт последовательность инструкций, на основе которых система сборки формирует новый образ. Каждая инструкция изменяет состояние промежуточной файловой системы или задаёт метаданные, необходимые для последующего запуска контейнера. В этом смысле Dockerfile выполняет роль спецификации: он не просто перечисляет команды, а фиксирует, каким должен быть результат сборки.
Декларативный характер Dockerfile не означает отсутствия пошаговой логики. Напротив, порядок инструкций имеет принципиальное значение. Сборка обычно начинается с инструкции FROM, которая определяет базовый образ (base image). Базовый образ задаёт исходную среду: тип дистрибутива, набор системных библиотек, иногда интерпретатор языка программирования или среду выполнения. Выбор базового образа влияет на размер итогового артефакта, совместимость приложения, доступность инструментов диагностики и поверхность потенциальных уязвимостей. Поэтому выбор «самого маленького» образа не всегда является лучшим решением: чрезмерное упрощение может затруднить сопровождение и отладку.
После выбора базового образа в Dockerfile задаются операции подготовки среды. Инструкции RUN используются для выполнения команд в процессе сборки, например для установки пакетов, компиляции исходного кода или формирования каталогов приложения. Инструкция COPY переносит файлы из контекста сборки внутрь образа, а ADD выполняет сходную функцию, но обладает дополнительными возможностями, которые без необходимости часто использовать не рекомендуется, поскольку это усложняет предсказуемость сборки. Инструкции ENV, WORKDIR, EXPOSE, CMD и ENTRYPOINT определяют параметры окружения, рабочий каталог, сетевые намерения и поведение контейнера при запуске. Важно понимать, что Dockerfile объединяет как операции подготовки файловой системы, так и описание будущего режима исполнения.
Пример базового Dockerfile для веб-приложения
Рассмотрим упрощённый пример Dockerfile для небольшого приложения на Python:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8000
EXPOSE 8000
CMD ["python", "app.py"]
В этом примере инструкция FROM задаёт базовую среду с интерпретатором Python. Инструкция WORKDIR определяет рабочий каталог внутри образа и в дальнейшем делает каталог /app текущей рабочей директорией для последующих инструкций. Это означает, что относительные пути в COPY, RUN и CMD далее интерпретируются уже с учётом каталога /app, если не указан иной абсолютный путь.
Для правильного понимания примера необходимо пояснить, что такое контекст сборки. Когда пользователь запускает команду вида docker build ., точка в конце означает, что контекстом сборки становится текущий каталог на машине пользователя. Именно из этого каталога Docker может брать файлы для инструкций COPY и ADD. Следовательно, Docker не копирует файлы произвольно из всей файловой системы хоста, а работает только с тем набором данных, который был передан ему как контекст. Если нужный файл находится вне контекста, инструкция COPY не сможет его использовать без изменения структуры проекта или явного выбора другого контекста.
Инструкция COPY requirements.txt . читается слева направо: сначала указывается источник в контексте сборки, затем назначение внутри образа. В данном случае Docker берёт файл requirements.txt из корня контекста сборки на хостовой машине и копирует его в текущий рабочий каталог внутри образа, то есть в /app/requirements.txt. После этого инструкция RUN pip install --no-cache-dir -r requirements.txt выполняется уже внутри промежуточного контейнера сборки и использует только что скопированный файл зависимостей. Такое разбиение имеет практический смысл: если исходный код приложения изменится, но файл requirements.txt останется прежним, Docker сможет повторно использовать уже собранный слой с установленными пакетами.
Инструкция COPY . . требует отдельного пояснения, поскольку запись с двумя точками часто интерпретируется неверно. Первая точка обозначает источник, то есть весь текущий контекст сборки на стороне хоста, за исключением файлов, исключённых через .dockerignore. Вторая точка обозначает каталог назначения внутри образа, то есть текущий рабочий каталог /app. Иными словами, после выполнения этой инструкции файлы проекта из локального каталога пользователя переносятся внутрь файловой системы образа в каталог /app. Если в проекте присутствуют, например, app.py, каталог templates/ и файл config.yaml, то при отсутствии исключений они будут скопированы в /app/app.py, /app/templates/ и /app/config.yaml.
Наконец, CMD задаёт команду, которая будет выполнена при запуске контейнера по умолчанию. В рассматриваемом случае это команда python app.py, выполняемая в каталоге /app, где уже находятся скопированные файлы приложения и установленные зависимости.
Этот пример также показывает типичное ограничение Dockerfile. Инструкция EXPOSE не публикует порт во внешнюю сеть сама по себе, а лишь документирует намерение приложения использовать соответствующий порт внутри контейнера. Реальный проброс порта на хост задаётся уже при запуске контейнера. Если смешивать эти два уровня, можно ошибочно считать Dockerfile средством полного описания развертывания, хотя он описывает только образ и типовой режим его исполнения.
Пример минимального Dockerfile для статического содержимого
Если приложение не требует собственной среды выполнения, Dockerfile может быть значительно проще. Например, для публикации статического сайта достаточно использовать готовый веб-сервер:
FROM nginx:1.27-alpine
COPY site/ /usr/share/nginx/html/
EXPOSE 80
Здесь образ не содержит этапов установки прикладных зависимостей, потому что они не нужны. Используется готовый сервер nginx, а задача сборки сводится к копированию статических файлов в каталог, из которого сервер отдаёт содержимое. Этот пример полезен методически, поскольку показывает: Dockerfile должен описывать только те действия, которые действительно необходимы для конкретного артефакта. Попытка сделать любой Dockerfile одинаково сложным приводит не к универсальности, а к избыточности.
С методической точки зрения существенна разница между образом как продуктом сборки и контейнером как объектом времени выполнения. Dockerfile описывает образ, но не фиксирует все аспекты будущего запуска. Например, ограничения ресурсов, сетевые подключения и подключение томов обычно задаются на этапе создания контейнера, а не на этапе сборки. Если смешивать эти уровни, возникает неверная интерпретация назначения Dockerfile. Он должен содержать только те сведения, которые характеризуют сам артефакт и его типовое поведение, а не частную конфигурацию конкретного развертывания.
Практический смысл Dockerfile состоит также в том, что он превращает упаковку приложения в повторяемую процедуру. Вместо того чтобы вручную устанавливать зависимости и копировать файлы на сервер, разработчик или инженер сопровождения описывает эти действия в явном виде. Затем одна и та же инструкция сборки может быть выполнена локально, в системе непрерывной интеграции и в учебной лаборатории. Отсюда вытекает локальный вывод: Dockerfile следует рассматривать как часть исходного кода проекта, а не как вспомогательный побочный файл.
Контекст сборки и структура входных данных
Корректность образа определяется не только содержимым Dockerfile, но и тем набором файлов, который передаётся системе сборки. Этот набор называется контекстом сборки (build context). Когда запускается сборка, Docker получает доступ к каталогу проекта или другой указанной директории и может использовать находящиеся в ней файлы в инструкциях COPY и ADD. Если в контекст включены лишние данные, например локальные артефакты сборки, временные файлы, каталоги зависимостей или секреты, они могут не только увеличить размер передаваемых данных, но и случайно попасть в образ.
По этой причине важную роль играет файл .dockerignore. Его назначение сходно с назначением .gitignore, но задача иная: исключить из контекста сборки всё, что не должно участвовать в формировании образа. Типичная ошибка начинающих состоит в том, что они копируют в образ весь каталог проекта без анализа его состава. В результате в образ попадают тестовые данные, журналы, кэши пакетных менеджеров, конфиденциальные файлы среды разработки и иные объекты, не относящиеся к приложению. Такая практика противоречит принципу минимально необходимого состава артефакта.
Контекст сборки имеет и эксплуатационный аспект. Чем больше его объём, тем медленнее сборка и тем выше вероятность, что несущественные изменения в одном из файлов нарушат кэширование последующих шагов. Следовательно, сопровождение образа начинается ещё до выполнения первой инструкции Dockerfile: инженер должен определить, какие именно исходные материалы являются частью поставки, а какие должны оставаться вне её границ. Этот вопрос связывает процесс контейнеризации с общей культурой управления проектом и конфигурацией репозитория.
Слои образа и логика пошаговой сборки
Ключевая особенность контейнерного образа состоит в его слоистой структуре. Каждый существенный шаг сборки формирует новый слой файловой системы или новый набор метаданных. На практике это означает, что образ не является монолитным архивом, собранным одним действием. Он представляет собой последовательность изменений, применяемых к базовому состоянию. Такая организация позволяет повторно использовать общие фрагменты между разными образами и снижает издержки хранения и передачи.
Слоистость имеет не только инфраструктурный, но и методический смысл. Если в Dockerfile сначала устанавливаются системные зависимости, затем копируются описания зависимостей приложения, затем выполняется их установка и лишь после этого копируется остальной исходный код, то повторная сборка после изменения одного исходного файла затронет только поздние шаги. Если же в начале сборки копируется весь проект целиком, а затем выполняется установка зависимостей, любое изменение в рабочем каталоге приведёт к повторному исполнению тяжёлых операций. Следовательно, структура Dockerfile должна отражать не только логику получения результата, но и ожидаемую частоту изменения отдельных составляющих проекта.
Следует учитывать, что слой не является независимым контейнером или самостоятельной файловой системой в полном смысле. Он представляет собой набор различий по отношению к предыдущему состоянию. Поэтому удаление файла на позднем этапе не означает, что соответствующие данные физически не присутствуют в нижележащих слоях. Этот момент особенно важен для безопасности и оптимизации. Если секретный файл был скопирован в образ на одном шаге, а затем удалён на следующем, его след может сохраниться в истории слоёв. Из этого вытекает важное ограничение: нежелательные данные нельзя сначала включать в сборку с расчётом на их последующее удаление.
Понимание слоистой структуры позволяет корректно интерпретировать и размер образа. Итоговый объём определяется не только «видимым» содержимым файловой системы контейнера, но и историей изменений, накопленных в слоях. Поэтому рациональная организация шагов сборки является одновременно вопросом производительности, безопасности и эксплуатационной ясности.
Кэширование и воспроизводимость сборки
Одним из преимуществ Docker является механизм кэширования шагов сборки. Если инструкция Dockerfile и все зависящие от неё входные данные не изменились, система может повторно использовать уже сформированный слой. Это существенно ускоряет повторную сборку, особенно если в процессе участвуют длительные операции: установка пакетов, загрузка зависимостей или компиляция. Однако кэширование не является самоцелью. Быстрая, но непредсказуемая сборка не решает инженерную задачу.
Более фундаментальным требованием является воспроизводимость. Под воспроизводимостью в данном контексте понимается способность получить функционально эквивалентный образ при повторном выполнении одной и той же процедуры сборки. Абсолютная двоичная идентичность в практических условиях достигается не всегда, поскольку на результат могут влиять временные метки, внешние источники пакетов и детали компиляции. Тем не менее инженерная цель состоит в том, чтобы минимизировать число нефиксированных факторов, от которых зависит результат.
Нарушение воспроизводимости часто связано с несколькими типичными причинами. Во-первых, используются плавающие версии зависимостей, когда при каждой сборке пакетный менеджер получает потенциально новый состав библиотек. Во-вторых, в Dockerfile включаются операции, зависящие от текущего времени, внешнего состояния сети или изменяющихся удалённых ресурсов без фиксации конкретных версий. В-третьих, сборка опирается на локальные, неформализованные предпосылки: существование файла в рабочем каталоге, который не включён в репозиторий, особенности конфигурации хоста или неявные секреты среды. Подобные зависимости особенно опасны в учебных и командных проектах, поскольку делают результат неустойчивым к переносу.
Следовательно, при проектировании Dockerfile необходимо стремиться к явной фиксации входных условий. Версии базового образа должны задаваться осмысленно, зависимости приложения должны устанавливаться на основе контролируемых описаний, а контекст сборки должен содержать только то, что действительно требуется для получения результата. Практическое значение этого принципа проявляется при сопровождении: если образ можно собрать только на одном рабочем месте и только у одного участника команды, такой артефакт нельзя считать надёжно сопровождаемым.
Кэширование и воспроизводимость связаны между собой, но не совпадают. Инструкция, максимально оптимизированная для повторного использования кэша, может оставаться плохо воспроизводимой, если она опирается на изменчивый внешний ресурс. И наоборот, воспроизводимая сборка может выполняться медленнее, если её шаги организованы нерационально. Поэтому инженер должен учитывать оба критерия одновременно и принимать компромиссные решения осознанно.
Практика написания Dockerfile и типичные ошибки
Несмотря на кажущуюся простоту синтаксиса, Dockerfile легко превращается в набор механически перенесённых команд из локальной инструкции развертывания. Такой подход ошибочен, потому что контейнерная сборка требует проектирования. Необходимо различать этапы, которые относятся к подготовке приложения, и этапы, которые должны выполняться уже после запуска контейнера. Например, миграции схемы базы данных, если они зависят от доступности внешнего сервиса, не всегда корректно помещать в стадию сборки образа. Сборка должна давать переносимый артефакт, а не пытаться решать все задачи будущей эксплуатации заранее.
Одна из распространённых ошибок связана с созданием чрезмерно универсального образа, который содержит инструменты разработки, компиляторы, тестовые утилиты и средства диагностики, хотя в реальном запуске требуется только приложение и его минимальные зависимости. Такой образ удобен на коротком отрезке обучения, но в долгосрочной перспективе он увеличивает размер поставки, расширяет поверхность атаки и затрудняет анализ состава. Противоположная крайность также нежелательна: чрезмерно «обрезанный» образ может затруднить диагностику ошибок и сопровождение. Отсюда следует, что выбор состава образа должен учитывать назначение среды: разработка, тестирование, обучение или эксплуатация.
Ещё одна типичная ошибка состоит в объединении несвязанных команд в одну длинную инструкцию RUN исключительно ради уменьшения числа слоёв. Формально это может уменьшить историю изменений, но ухудшает читаемость, затрудняет сопровождение и делает диагностику сбоев менее прозрачной. Сокращение числа слоёв не должно становиться самоцелью. Более правильный подход заключается в логическом группировании действий: в пределах одного шага объединяются тесно связанные операции, а между шагами сохраняется понятная структура.
Следует также избегать помещения конфиденциальных данных непосредственно в Dockerfile или в контекст сборки. Пароли, токены доступа и приватные ключи не должны становиться частью образа, поскольку это нарушает базовые требования безопасности и создаёт долговременный риск утечки. Если в процессе сборки требуется доступ к закрытому ресурсу, должны использоваться специальные механизмы безопасной передачи секретов, а не их явное включение в текст сценария.
Локальный вывод этого раздела состоит в том, что хороший Dockerfile отличается не только корректностью, но и объяснимостью. Его структура должна позволять понять, какие шаги выполняются, зачем они нужны и какие предпосылки они предполагают. Такая прозрачность особенно важна в учебном курсе, где цель состоит не просто в получении работающего артефакта, а в формировании устойчивого инженерного мышления.
Оптимизация образов и многоэтапная сборка
После того как базовая процедура сборки становится корректной и воспроизводимой, возникает задача оптимизации образа. Чаще всего оптимизация понимается как уменьшение размера. Это действительно важный показатель: более компактный образ быстрее передаётся по сети, быстрее загружается в среду исполнения и обычно содержит меньше лишних компонентов. Однако размер не является единственным критерием. Оптимизация должна сохранять функциональную полноту, ясность состава и сопровождаемость.
Наиболее важным инструментом оптимизации является многоэтапная сборка (multi-stage build). Её идея состоит в том, что разные стадии Dockerfile выполняют разные роли. На одной стадии могут устанавливаться компиляторы и инструменты сборки, на другой — выполняться компиляция или упаковка приложения, а в итоговый образ переносятся только результаты, необходимые для запуска. Такой подход позволяет отделить среду построения артефакта от среды его исполнения. Практическое следствие очевидно: в финальном образе не остаются лишние инструменты, которые увеличивали бы размер и расширяли бы потенциальную поверхность атаки.
Многоэтапная сборка особенно полезна для компилируемых языков, однако её значение не исчерпывается ими. Даже в интерпретируемых средах она помогает отделить подготовительные шаги, тестовые зависимости и служебные файлы от финальной поставки. Вместе с тем следует учитывать ограничения. Слишком агрессивная оптимизация, ориентированная только на уменьшение размера, может привести к тому, что образ станет неудобным для диагностики, а процесс сборки — слишком сложным для сопровождения. Следовательно, инженер должен выбирать такую степень оптимизации, которая соответствует назначению конкретного артефакта.
Оптимизация затрагивает и выбор базового образа. Существуют полноценные дистрибутивные образы, минимальные варианты и специализированные среды исполнения. Полноценный образ часто проще в сопровождении, поскольку содержит стандартные инструменты и ожидаемую структуру системы. Минимальный образ уменьшает размер и снижает число потенциально уязвимых компонентов, но может осложнить разбор сбоев и потребовать более аккуратной настройки зависимостей. Выбор между этими вариантами нельзя делать абстрактно: он зависит от требований к безопасности, наблюдаемости, переносимости и удобству сопровождения.
Таким образом, оптимизация не сводится к механическому «сжатию» образа. Она предполагает инженерный анализ того, какие компоненты действительно необходимы в среде выполнения, какие шаги должны остаться на стадии сборки и какие компромиссы приемлемы для конкретного сценария использования.
Версионирование образов и управление изменениями
Образ становится полноценным артефактом поставки только тогда, когда им можно управлять как версией программного продукта. Для этого используются теги (tags), позволяющие различать варианты образа по версии, назначению или каналу поставки. Однако использование тегов нередко сопровождается методической ошибкой: тег воспринимается как абсолютная и неизменная сущность. На практике тег является лишь именованной ссылкой на конкретный образ и может быть переназначен. Поэтому теги вида latest удобны для демонстрации, но плохо подходят для управляемого сопровождения.
С инженерной точки зрения предпочтительно, чтобы версия образа была связана с версией исходного кода, конфигурации сборки и, при необходимости, с состоянием цепочки поставки. Это не означает, что в учебном курсе требуется вводить сложную схему релизного управления, но важно сформировать правильный принцип: образ должен быть однозначно соотнесён с тем исходным состоянием проекта, из которого он получен. Тогда появляется возможность анализировать изменения, воспроизводить прежние версии, выполнять откат и проверять, какой именно артефакт был развернут.
Управление изменениями включает не только присвоение имени версии, но и пересборку образов при обновлении базовых компонентов. Даже если исходный код приложения не менялся, может измениться базовый образ, исправиться уязвимость в системной библиотеке или обновиться среда исполнения. Следовательно, сопровождение контейнерного образа не заканчивается в момент первой успешной сборки. Образ требует периодического пересмотра, повторной сборки и контроля актуальности своих зависимостей.
Это обстоятельство особенно важно в облачной среде, где образы часто используются многократно и автоматически распространяются по нескольким средам. Если организация или учебная группа не контролирует происхождение и актуальность образов, она фактически теряет прозрачность поставки. Локальный вывод таков: версионирование образов должно поддерживать не только удобство использования, но и прослеживаемость жизненного цикла артефакта.
Публикация образов и роль реестров
Собранный образ приносит практическую пользу лишь тогда, когда его можно передать в среду исполнения или другой участник процесса может его получить. Для этого используются реестры контейнерных образов (registry). Реестр выполняет роль централизованного хранилища, где образам присваиваются имена, версии и правила доступа. С точки зрения архитектуры поставки реестр является связующим звеном между сборкой и развертыванием.
Публикация образа в реестр позволяет перейти от локального, разового использования к повторяемому сценарию. Один и тот же артефакт может быть собран в системе непрерывной интеграции, затем опубликован в утверждённое хранилище и оттуда использован при развертывании на тестовом или эксплуатационном контуре. Этот подход принципиально отличается от практики «собирать на каждом сервере отдельно». Локальная сборка на целевом узле увеличивает вариативность результата и делает сопровождение менее контролируемым.
Реестры могут быть публичными и частными. Публичные реестры удобны как источник типовых базовых образов и как средство распространения открытых приложений. Частные реестры важны тогда, когда требуется ограничить доступ, обеспечить контроль состава поставляемых артефактов или встроить публикацию в корпоративный контур разработки. Независимо от типа реестра, инженер должен учитывать политику именования, правила аутентификации, сроки хранения образов и процедуры удаления устаревших версий.
Публикация связана и с вопросами доверия. Образ, полученный из внешнего источника, не следует автоматически считать безопасным или корректным. Требуется понимать, кто его опубликовал, как он был собран и насколько прозрачен его состав. В учебном процессе этот вопрос особенно полезен методически: он показывает, что контейнеризация не отменяет необходимость критически оценивать происхождение программных компонентов, а, напротив, требует ещё большей дисциплины в управлении артефактами.
Сопровождение образов в цепочке поставки
На зрелом уровне контейнерный образ рассматривается как объект цепочки поставки программного обеспечения, а не как разовый технический продукт. Это означает, что сборка образа должна быть встроена в процессы проверки исходного кода, автоматического тестирования, анализа состава зависимостей и публикации результатов. Чем меньше ручных действий требуется для получения и размещения образа, тем ниже риск человеческой ошибки и тем выше воспроизводимость.
Сопровождение образов включает несколько повторяющихся операций: пересборку при изменении кода, обновление базовых компонентов, проверку корректности Dockerfile, контроль размера и состава образа, а также удаление устаревших или неподдерживаемых версий из хранилища. Эти задачи не являются внешними по отношению к контейнеризации; они составляют её нормальный эксплуатационный контур. Ошибочно считать, что после упаковки приложения в контейнер проблема сопровождения исчезает. Меняется не наличие этой проблемы, а форма её решения.
Для учебного курса принципиально важно усвоить следующее: контейнерный образ должен быть прослеживаемым, воспроизводимым и управляемым. Прослеживаемость означает возможность понять происхождение артефакта. Воспроизводимость означает возможность получить сопоставимый результат повторно. Управляемость означает возможность осознанно обновлять, публиковать, отзывать и заменять версии. Именно сочетание этих свойств превращает контейнерный образ в полноценный объект инженерной практики и подготавливает переход к дальнейшим темам курса, связанным с запуском, композицией сервисов и облачной эксплуатацией.
Итоги темы
Сборка контейнерного образа является не вспомогательной технической процедурой, а центральным элементом контейнерной модели поставки приложения. Dockerfile задаёт формальное описание артефакта, а качество этого описания определяет воспроизводимость, переносимость и сопровождаемость результата. По этой причине образ следует проектировать так же внимательно, как и само приложение.
Слоистая структура образов, кэширование и организация контекста сборки требуют осознанной инженерной логики. Нерациональный порядок шагов, включение лишних файлов и использование нефиксированных зависимостей приводят к росту размера образа, ухудшению предсказуемости и снижению безопасности. Напротив, аккуратная структура Dockerfile, применение .dockerignore и фиксация входных условий делают сборку устойчивой и объяснимой.
Оптимизация, многоэтапная сборка, версионирование и публикация в реестры показывают, что образ должен сопровождаться на всём протяжении жизненного цикла. Его необходимо не только однажды собрать, но и регулярно пересматривать, обновлять и связывать с контролируемой цепочкой поставки. Это создаёт основу для следующего этапа изучения: управления контейнерами в среде выполнения и организации повторяемого развертывания приложений.