[{"data":1,"prerenderedAt":2480},["ShallowReactive",2],{"topic:aidt-bac-cloud_tech:topic-05":3},{"content":4,"pz":417,"lr":429,"additional":417,"courseMeta":2438},{"id":5,"title":6,"body":7,"course_slug":416,"description":17,"env_label":417,"env_url":417,"extension":418,"group":417,"is_course_project":419,"is_index":419,"level":417,"meta":420,"navigation":421,"path":422,"section":423,"seo":424,"stem":425,"topic_number":426,"topic_slug":427,"__hash__":428},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content.md","Управление контейнерами в среде выполнения",{"type":8,"value":9,"toc":384},"minimark",[10,14,18,21,24,29,32,37,40,43,51,55,58,61,64,68,71,78,81,85,88,91,94,98,101,104,116,120,123,127,130,134,137,140,143,147,158,171,193,201,205,208,211,214,217,220,224,227,231,237,240,243,247,254,260,264,270,276,280,286,292,300,304,307,311,314,318,321,324,328,331,334,342,346,352,355,358,362,365,368,372,375,378,381],[11,12,6],"h1",{"id":13},"управление-контейнерами-в-среде-выполнения",[15,16,17],"p",{},"Предыдущие темы курса были посвящены созданию контейнерного образа как воспроизводимого артефакта поставки. Однако образ сам по себе не является работающим приложением. Он представляет собой инертное описание файловой системы и параметров запуска, которое приобретает практический смысл лишь тогда, когда на его основе создаётся и запускается контейнер. Именно на этом этапе начинается управление контейнером в среде выполнения: инженер должен определить, как приложение будет запущено, каким образом оно взаимодействует с внешним окружением, как контролировать его поведение и как корректно обновлять.",[15,19,20],{},"Управление контейнерами на этапе выполнения принципиально отличается от управления образами на этапе сборки. Если сборка отвечает на вопрос «что содержит артефакт», то среда выполнения отвечает на вопрос «как именно этот артефакт функционирует в конкретных условиях». Один и тот же образ может быть запущен с различными переменными окружения, ограничениями ресурсов, сетевыми настройками и политиками перезапуска. Поэтому корректная эксплуатация контейнера требует не только умения собрать образ, но и понимания того, какие параметры определяют поведение контейнера после его создания.",[15,22,23],{},"В рамках данной темы рассматриваются четыре взаимосвязанных аспекта. Во-первых, параметры запуска контейнера и их влияние на поведение приложения. Во-вторых, роль основного процесса и механизм завершения контейнера. В-третьих, средства инспекции и диагностики. В-четвёртых, принцип неизменяемой инфраструктуры и модель обновления контейнерных приложений. Вместе эти аспекты формируют представление о контейнере не как о «чёрном ящике», а как об управляемом объекте с определённым жизненным циклом.",[25,26,28],"h2",{"id":27},"параметры-запуска-контейнера","Параметры запуска контейнера",[15,30,31],{},"Запуск контейнера — это не просто создание экземпляра образа. Это конфигурирование среды выполнения, в которой приложение будет работать. Параметры запуска определяют, какие ресурсы доступны контейнеру, как он взаимодействует с сетью, какие данные получает из внешнего окружения и каким образом среда выполнения реагирует на его остановку или аварийное завершение. Понимание этих параметров необходимо для перехода от разового экспериментального запуска к воспроизводимому и контролируемому развёртыванию.",[33,34,36],"h3",{"id":35},"переменные-окружения-и-аргументы-команды","Переменные окружения и аргументы команды",[15,38,39],{},"Одним из основных способов передачи конфигурации контейнеру являются переменные окружения (environment variables). Они задаются при запуске и доступны процессу внутри контейнера точно так же, как переменные окружения в обычной операционной системе. Переменные окружения удобны тем, что позволяют отделить конфигурацию от образа: один и тот же артефакт может быть запущен с разными параметрами подключения к базе данных, адресами внешних сервисов или режимами работы приложения.",[15,41,42],{},"Вместе с тем переменные окружения имеют ограничения. Они не предназначены для передачи сложных структурированных данных и не обеспечивают конфиденциальности значений без дополнительных мер. Если приложение требует передачи секретов, таких как пароли или ключи доступа, использование переменных окружения в явном виде создаёт риск: значения могут быть видны при инспекции контейнера или в журналах среды выполнения. Этот аспект становится особенно важным при переходе к эксплуатационным средам, где управление секретами требует отдельных механизмов.",[15,44,45,46,50],{},"Помимо переменных окружения, при запуске контейнера можно передавать аргументы командной строки, которые переопределяют команду, заданную в образе инструкцией ",[47,48,49],"code",{},"CMD",". Это позволяет, например, запустить контейнер с тем же образом, но в диагностическом режиме, с альтернативной конфигурацией или с выполнением вспомогательной задачи. Такое разделение между образом и параметрами запуска подчёркивает принципиальную идею: образ описывает «что может быть запущено», а параметры запуска определяют «как именно это запускается в данный момент».",[33,52,54],{"id":53},"точка-входа-и-команда-по-умолчанию","Точка входа и команда по умолчанию",[15,56,57],{},"Для правильного понимания параметров запуска необходимо различать два механизма, определяющих поведение контейнера при старте: точку входа (entrypoint) и команду по умолчанию (cmd). Точка входа задаёт основной исполняемый файл или сценарий, который будет вызван при запуске контейнера. Команда по умолчанию определяет аргументы, передаваемые точке входа. Если точка входа не задана явно, среда выполнения использует стандартную оболочку операционной системы.",[15,59,60],{},"На практике разделение между entrypoint и cmd позволяет создавать образы, которые ведут себя как «параметризуемые команды». Например, образ утилиты может использовать в качестве точки входа саму утилиту, а аргументы командной строки, переданные при запуске контейнера, будут подставлены как параметры. Это удобно для инструментов, выполняющих конкретные задачи, но нежелательно для сервисных приложений, где точка входа и команда обычно фиксируются на этапе сборки образа.",[15,62,63],{},"Типичной ошибкой является смешение ролей entrypoint и cmd. Если обе инструкции используются одновременно, но их взаимодействие не продумано, результат может оказаться неожиданным. Например, если пользователь передаёт аргументы при запуске контейнера, они заменяют значение cmd, но не entrypoint. Непонимание этой логики приводит к трудно диагностируемым сбоям при запуске, особенно если поведение образа не документировано.",[33,65,67],{"id":66},"проброс-портов-и-сетевые-параметры","Проброс портов и сетевые параметры",[15,69,70],{},"Контейнер по умолчанию работает в изолированном сетевом пространстве. Приложение внутри контейнера может слушать сетевой порт, но это не означает, что порт доступен извне. Для предоставления доступа к сервису из внешней сети используется проброс портов (port mapping): при запуске контейнера указывается соответствие между портом хостовой машины и портом внутри контейнера.",[15,72,73,74,77],{},"Важно различать два уровня описания. Инструкция ",[47,75,76],{},"EXPOSE"," в Dockerfile лишь документирует намерение приложения использовать определённый порт. Она не выполняет реального проброса. Реальное сетевое подключение формируется только при запуске контейнера, когда указывается конкретное соответствие портов. Смешение этих двух уровней является распространённой ошибкой, которая приводит к тому, что разработчик ожидает сетевой доступности, не выполнив необходимого действия при запуске.",[15,79,80],{},"Сетевые параметры запуска не ограничиваются пробросом портов. Контейнер может быть подключён к пользовательской сети, в которой действуют собственные правила разрешения имён и маршрутизации. Эти вопросы подробно рассматриваются в последующей теме, посвящённой сетевому взаимодействию контейнерных приложений. Однако уже на данном этапе необходимо понимать, что сетевая конфигурация является частью параметров запуска, а не свойством образа.",[33,82,84],{"id":83},"ограничение-ресурсов","Ограничение ресурсов",[15,86,87],{},"Среда выполнения контейнеров позволяет задавать ограничения на использование вычислительных ресурсов: процессорного времени и оперативной памяти. Эти ограничения реализуются через механизм групп управления ресурсами (control groups, cgroups), который был рассмотрен в теме, посвящённой механизмам изоляции.",[15,89,90],{},"Ограничение ресурсов выполняет несколько функций. Во-первых, оно защищает хостовую систему от ситуации, в которой один контейнер потребляет все доступные ресурсы и вызывает деградацию соседних процессов. Во-вторых, оно позволяет моделировать условия, приближённые к реальной эксплуатации, где приложение работает в рамках выделенного бюджета ресурсов. В-третьих, ограничения служат инструментом раннего обнаружения проблем: если приложение не способно работать в заданных рамках, это сигнализирует о необходимости оптимизации или пересмотра архитектуры.",[15,92,93],{},"Следует учитывать, что поведение контейнера при превышении лимита зависит от типа ресурса. Превышение лимита памяти обычно приводит к принудительному завершению контейнера операционной системой (механизм OOM Killer). Превышение лимита процессорного времени приводит к замедлению, но не к завершению. Это различие имеет практическое значение: приложение, не рассчитанное на ограниченный объём памяти, может аварийно завершаться без видимой ошибки в журналах приложения, поскольку завершение инициировано не самим процессом, а ядром операционной системы.",[33,95,97],{"id":96},"политика-перезапуска","Политика перезапуска",[15,99,100],{},"При запуске контейнера можно задать политику перезапуска (restart policy), которая определяет поведение среды выполнения при завершении контейнера. Существуют несколько вариантов: контейнер не перезапускается; перезапускается всегда; перезапускается только в случае ошибки; перезапускается всегда, кроме явной остановки пользователем.",[15,102,103],{},"Политика перезапуска является простейшим механизмом обеспечения живучести сервиса на уровне одного узла. Она полезна для длительно работающих приложений, которые должны автоматически восстанавливаться после кратковременного сбоя. Однако этот механизм имеет существенные ограничения. Он не способен обнаружить ситуацию, когда приложение формально работает, но функционально неработоспособно. Он не позволяет распределить нагрузку между несколькими экземплярами. Он не обеспечивает управляемого обновления. Все эти задачи требуют более развитых средств, которые предоставляются оркестраторами и рассматриваются в последующих темах курса.",[105,106,107,108,107,113],"figure",{},"\n  ",[109,110],"img",{"src":111,"alt":112},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fcontainer_run_parameters.svg","Параметры запуска контейнера и их связь с образом",[114,115,112],"figcaption",{},[33,117,119],{"id":118},"связь-параметров-запуска-с-воспроизводимостью","Связь параметров запуска с воспроизводимостью",[15,121,122],{},"Совокупность параметров запуска определяет не только текущее поведение контейнера, но и воспроизводимость развёртывания. Если параметры задаются вручную при каждом запуске, высока вероятность расхождения между средами разработки, тестирования и эксплуатации. Этот риск нарастает по мере увеличения числа параметров и числа контейнеров в составе приложения. Поэтому уже на данном этапе следует осознавать необходимость фиксации параметров запуска в формализованном описании. Средства такой фиксации, в первую очередь Docker Compose, рассматриваются в последующих темах. Однако важно понимать, что потребность в декларативном описании развёртывания возникает не из абстрактного стремления к порядку, а из конкретных проблем, связанных с ручным управлением параметрами запуска.",[25,124,126],{"id":125},"основной-процесс-и-завершение-контейнера","Основной процесс и завершение контейнера",[15,128,129],{},"Контейнер в среде Docker не является самостоятельной операционной системой и не содержит полноценного менеджера процессов. Он представляет собой оболочку для выполнения одного основного процесса, который определяет жизненный цикл контейнера. Понимание этого принципа является ключевым для корректной эксплуатации контейнерных приложений.",[33,131,133],{"id":132},"процесс-pid-1","Процесс PID 1",[15,135,136],{},"При запуске контейнера среда выполнения создаёт в изолированном пространстве имён процесс, который получает идентификатор PID 1. Этот процесс является корневым для всего дерева процессов внутри контейнера. В обычной операционной системе PID 1 выполняет особую роль: он является процессом инициализации (init), который отвечает за порождение и контроль дочерних процессов, а также за корректную обработку сигналов.",[15,138,139],{},"В контейнерной среде процесс PID 1 обычно является самим приложением, а не специализированным менеджером инициализации. Это означает, что приложение принимает на себя обязанности, которые в традиционной системе выполнялись бы системным процессом init. В частности, оно должно корректно обрабатывать сигналы завершения и, при необходимости, управлять дочерними процессами.",[15,141,142],{},"Связь между PID 1 и жизненным циклом контейнера является прямой. Когда процесс с PID 1 завершается, контейнер прекращает существование, вне зависимости от того, продолжают ли работать какие-либо дочерние процессы. Код завершения процесса PID 1 становится кодом завершения контейнера. Следовательно, проектирование основного процесса контейнера — это не только задача разработки приложения, но и задача корректной интеграции с контейнерной средой.",[33,144,146],{"id":145},"обработка-сигналов-и-корректное-завершение","Обработка сигналов и корректное завершение",[15,148,149,150,153,154,157],{},"Когда пользователь или среда выполнения инициирует остановку контейнера, Docker отправляет процессу PID 1 сигнал ",[47,151,152],{},"SIGTERM",". Этот сигнал предлагает процессу завершиться корректно: закрыть сетевые соединения, сбросить буферы данных, освободить ресурсы. Если процесс не завершается в течение установленного времени ожидания (по умолчанию 10 секунд), Docker отправляет сигнал ",[47,155,156],{},"SIGKILL",", который принудительно прекращает выполнение процесса без возможности корректного завершения.",[15,159,160,161,163,164,167,168,170],{},"Проблема состоит в том, что не все приложения корректно обрабатывают ",[47,162,152],{},". Если приложение запущено через оболочку (shell), например через ",[47,165,166],{},"CMD [\"sh\", \"-c\", \"python app.py\"]",", то PID 1 получает оболочка, а не приложение. Оболочка, как правило, не передаёт полученный сигнал дочернему процессу. В результате приложение не получает уведомления о необходимости завершения и продолжает работать до истечения тайм-аута, после чего принудительно завершается сигналом ",[47,169,156],{},". Такое поведение приводит к потере данных, незавершённым транзакциям и увеличению времени остановки контейнера.",[15,172,173,174,176,177,180,181,184,185,188,189,192],{},"Для избежания этой проблемы рекомендуется использовать exec-форму инструкций ",[47,175,49],{}," и ",[47,178,179],{},"ENTRYPOINT"," в Dockerfile (например, ",[47,182,183],{},"CMD [\"python\", \"app.py\"]"," вместо ",[47,186,187],{},"CMD python app.py","). В exec-форме приложение становится процессом PID 1 непосредственно и получает сигналы напрямую. Альтернативный подход — использование легковесного процесса инициализации (init process), который берёт на себя роль PID 1 и корректно передаёт сигналы основному приложению. Docker предоставляет встроенную поддержку такого механизма через параметр запуска ",[47,190,191],{},"--init",".",[105,194,107,195,107,199],{},[109,196],{"src":197,"alt":198},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fpid1_signal_handling.svg","Процесс PID 1 и обработка сигналов в контейнере",[114,200,198],{},[33,202,204],{"id":203},"типичные-причины-немедленного-завершения-контейнера","Типичные причины немедленного завершения контейнера",[15,206,207],{},"Одна из наиболее частых проблем при начальном знакомстве с контейнерами — контейнер, который завершается сразу после запуска. Это поведение, как правило, объясняется одной из нескольких причин.",[15,209,210],{},"Во-первых, основной процесс может завершаться, потому что он предназначен для выполнения одноразовой задачи, а не для длительной работы. Например, если в качестве команды указана утилита, выводящая текст и завершающаяся, контейнер прекратит существование после её выполнения. Контейнер существует ровно столько, сколько работает его основной процесс.",[15,212,213],{},"Во-вторых, процесс может завершаться с ошибкой из-за отсутствия необходимых файлов, переменных окружения или зависимостей. В этом случае код завершения контейнера будет ненулевым, а журналы могут содержать диагностическое сообщение.",[15,215,216],{},"В-третьих, приложение может быть запущено в фоновом режиме (как демон), после чего основной процесс, породивший демон, завершается. С точки зрения контейнерной модели это означает завершение PID 1 и, следовательно, остановку контейнера. Приложения, предназначенные для работы в контейнере, должны выполняться на переднем плане (foreground), а не уходить в фоновый режим.",[15,218,219],{},"Понимание этих причин позволяет избежать типичной ошибки начинающих, которые пытаются решить проблему немедленного завершения добавлением искусственных задержек или бесконечных циклов ожидания. Такие «решения» маскируют реальную проблему и создают контейнеры, которые формально работают, но не выполняют полезной функции.",[25,221,223],{"id":222},"инспекция-и-сопровождение-контейнера","Инспекция и сопровождение контейнера",[15,225,226],{},"Работающий контейнер является объектом наблюдения и диагностики. Способность получить информацию о состоянии контейнера, его конфигурации и поведении приложения внутри него — это обязательная часть эксплуатационной дисциплины. Docker предоставляет несколько инструментов для решения этой задачи, каждый из которых отвечает на определённый класс вопросов.",[33,228,230],{"id":229},"журналы-приложения","Журналы приложения",[15,232,233,234,192],{},"Основным источником информации о поведении приложения в контейнере являются журналы (logs). В контейнерной модели стандартной практикой считается направление вывода приложения в стандартные потоки: стандартный поток вывода (stdout) и стандартный поток ошибок (stderr). Docker перехватывает эти потоки и сохраняет их содержимое, предоставляя доступ через команду ",[47,235,236],{},"docker logs",[15,238,239],{},"Такой подход имеет несколько достоинств. Он не требует настройки файлового журналирования внутри контейнера. Он позволяет централизованно собирать журналы из множества контейнеров. Он совместим с различными драйверами журналирования, которые могут перенаправлять вывод во внешние системы хранения и анализа.",[15,241,242],{},"Вместе с тем следует учитывать ограничения. Не все приложения по умолчанию пишут в стандартные потоки. Некоторые пишут журналы в файлы внутри контейнера, что требует дополнительной настройки для их извлечения. Кроме того, объём журналов, хранимых Docker, по умолчанию не ограничен, что при интенсивном журналировании может привести к исчерпанию дискового пространства на хосте. Поэтому в эксплуатационных средах политика ротации и ограничения размера журналов является обязательной мерой.",[33,244,246],{"id":245},"инспекция-конфигурации-контейнера","Инспекция конфигурации контейнера",[15,248,249,250,253],{},"Команда ",[47,251,252],{},"docker inspect"," предоставляет детальную информацию о конфигурации контейнера: его параметрах запуска, сетевых настройках, подключённых томах, переменных окружения, состоянии и метаданных. Эта информация полезна для диагностики расхождений между ожидаемой и фактической конфигурацией. Например, если приложение не подключается к базе данных, инспекция может показать, что переменная окружения с адресом подключения содержит неверное значение или не задана.",[15,255,256,257,259],{},"Инспекция также позволяет проверить фактические ограничения ресурсов, назначенные контейнеру, его сетевой адрес в контейнерной сети, политику перезапуска и другие параметры, которые могли быть заданы при запуске. Это делает ",[47,258,252],{}," инструментом верификации: инженер может убедиться, что контейнер запущен именно с теми параметрами, которые были запланированы.",[33,261,263],{"id":262},"выполнение-команд-внутри-контейнера","Выполнение команд внутри контейнера",[15,265,249,266,269],{},[47,267,268],{},"docker exec"," позволяет запустить дополнительный процесс внутри работающего контейнера. Чаще всего это интерактивная оболочка, с помощью которой можно исследовать файловую систему, проверить наличие файлов, просмотреть сетевую конфигурацию или выполнить диагностическую команду. Этот инструмент полезен при отладке и локализации проблем.",[15,271,272,273,275],{},"Однако использование ",[47,274,268],{}," требует осознанного подхода. Выполнение команд внутри контейнера допустимо как средство диагностики, но не должно становиться инструментом ручной модификации среды. Если инженер вручную устанавливает пакет, изменяет конфигурационный файл или перезапускает процесс внутри контейнера, эти изменения не отражаются в образе и будут потеряны при пересоздании контейнера. Такой подход противоречит принципу неизменяемой инфраструктуры, который рассматривается далее.",[33,277,279],{"id":278},"мониторинг-потребления-ресурсов","Мониторинг потребления ресурсов",[15,281,249,282,285],{},[47,283,284],{},"docker stats"," предоставляет информацию о текущем потреблении ресурсов контейнерами: использование процессора, оперативной памяти, сетевого ввода-вывода и дискового ввода-вывода. Эти данные полезны для оценки фактической нагрузки и обнаружения аномалий: утечек памяти, чрезмерного потребления процессора или неожиданной сетевой активности.",[15,287,288,289,291],{},"На уровне единичного контейнера ",[47,290,284],{}," является инструментом оперативного наблюдения. Для систематического мониторинга, хранения истории метрик и настройки оповещений требуются внешние системы наблюдаемости, которые рассматриваются в теме, посвящённой безопасности и наблюдаемости контейнерных приложений.",[105,293,107,294,107,298],{},[109,295],{"src":296,"alt":297},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fcontainer_inspection.svg","Средства инспекции и диагностики контейнера",[114,299,297],{},[33,301,303],{"id":302},"границы-допустимого-вмешательства","Границы допустимого вмешательства",[15,305,306],{},"Инструменты инспекции и диагностики формируют важную дисциплинарную границу. Наблюдение за контейнером, чтение его журналов, проверка конфигурации и анализ потребления ресурсов — это нормальные эксплуатационные действия. Однако ручное изменение содержимого работающего контейнера, установка дополнительного программного обеспечения, модификация конфигурационных файлов внутри контейнера — это действия, нарушающие воспроизводимость среды и создающие расхождение между артефактом (образом) и реальным состоянием системы. Разграничение между допустимой диагностикой и нежелательной модификацией является одним из ключевых навыков инженера, работающего с контейнерами.",[25,308,310],{"id":309},"неизменяемость-среды-и-обновление-приложений","Неизменяемость среды и обновление приложений",[15,312,313],{},"Контейнерная модель развёртывания основана на принципе неизменяемой инфраструктуры (immutable infrastructure). Этот принцип утверждает, что компоненты среды выполнения не модифицируются после создания. Вместо внесения изменений в работающий экземпляр создаётся новый экземпляр на основе обновлённого артефакта. Применительно к контейнерам это означает, что обновление приложения производится не путём изменения содержимого работающего контейнера, а путём пересборки образа и пересоздания контейнера.",[33,315,317],{"id":316},"суть-принципа-неизменяемости","Суть принципа неизменяемости",[15,319,320],{},"В традиционной модели эксплуатации серверов обновление приложения нередко выполнялось путём ручного копирования файлов, установки пакетов и изменения конфигурации на работающем сервере. Такой подход порождает ряд проблем. Состояние сервера постепенно отклоняется от первоначального описания (явление, известное как «дрейф конфигурации», configuration drift). Воспроизвести точное состояние системы на другом узле становится невозможно без детальной документации каждого выполненного действия. Откат к предыдущей версии затруднён, поскольку изменения накапливаются без формальной фиксации.",[15,322,323],{},"Принцип неизменяемости устраняет эти проблемы путём жёсткого разделения двух операций. Первая операция — конструирование артефакта (образа), которая выполняется один раз и фиксируется. Вторая операция — запуск экземпляра (контейнера) на основе этого артефакта. Если требуется изменение, создаётся новый артефакт, а прежний экземпляр заменяется новым. Контейнер не модифицируется; он заменяется.",[33,325,327],{"id":326},"цикл-обновления-приложения","Цикл обновления приложения",[15,329,330],{},"Корректный цикл обновления контейнерного приложения состоит из нескольких последовательных этапов. Сначала вносятся изменения в исходный код, конфигурацию сборки или зависимости приложения. Затем выполняется пересборка образа, в результате которой создаётся новый артефакт с обновлённым содержимым. После этого существующий контейнер останавливается, и на его месте создаётся новый контейнер на основе обновлённого образа.",[15,332,333],{},"Этот цикл обеспечивает несколько важных свойств. Во-первых, каждая версия приложения представлена конкретным образом с определённым тегом, что позволяет точно знать, какой артефакт развёрнут в данный момент. Во-вторых, откат к предыдущей версии сводится к запуску контейнера на основе прежнего образа. В-третьих, устраняется проблема дрейфа конфигурации, поскольку состояние контейнера определяется исключительно его образом и параметрами запуска.",[105,335,107,336,107,340],{},[109,337],{"src":338,"alt":339},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fimmutable_infrastructure.svg","Принцип неизменяемой инфраструктуры и цикл обновления",[114,341,339],{},[33,343,345],{"id":344},"ошибки-ручной-модификации-контейнера","Ошибки ручной модификации контейнера",[15,347,348,349,351],{},"Несмотря на ясность принципа неизменяемости, на практике возникает соблазн внести «быстрое исправление» непосредственно в работающий контейнер. Типичные примеры: ручная правка конфигурационного файла через ",[47,350,268],{},", установка дополнительного пакета для решения текущей проблемы, изменение прав доступа к файлам внутри контейнера.",[15,353,354],{},"Такие действия создают несколько проблем. Изменение не зафиксировано в образе и будет потеряно при следующем пересоздании контейнера. Другие экземпляры, запущенные из того же образа, не получат данного изменения, что приведёт к несогласованности между средами. Откат к предыдущему состоянию невозможен, поскольку изменение не является частью управляемого жизненного цикла артефакта. Кроме того, ручные вмешательства затрудняют диагностику: если система ведёт себя иначе, чем ожидается на основании образа, инженер не может исключить, что причиной является незафиксированное ручное изменение.",[15,356,357],{},"Корректный подход требует возвращения к этапу сборки. Если в приложении обнаружена ошибка, она исправляется в исходном коде, образ пересобирается, и контейнер пересоздаётся. Если требуется изменение конфигурации, оно вносится в параметры запуска или конфигурационные файлы, передаваемые контейнеру извне. Этот подход может казаться более длительным, но именно он обеспечивает прослеживаемость, воспроизводимость и управляемость, которые составляют основу контейнерной модели эксплуатации.",[33,359,361],{"id":360},"границы-применимости-принципа-неизменяемости","Границы применимости принципа неизменяемости",[15,363,364],{},"Принцип неизменяемости распространяется на программную среду контейнера, но не на все аспекты его работы. Данные, создаваемые приложением в процессе работы, по определению изменяются. Если приложение работает с базой данных, принимает загружаемые пользователями файлы или формирует кэш, эти данные не могут быть частью неизменяемого образа. Для их хранения используются тома и другие механизмы внешнего хранения, рассматриваемые в теме, посвящённой хранению данных в контейнерных средах.",[15,366,367],{},"Таким образом, неизменяемость образа и контейнера не означает неизменяемости всех данных приложения. Она означает неизменяемость программной среды: исполняемых файлов, библиотек, конфигурации, встроенной в образ. Разделение между изменяемыми данными и неизменяемой средой является фундаментальным архитектурным решением, которое должно быть принято на этапе проектирования контейнерного приложения.",[25,369,371],{"id":370},"итоги-темы","Итоги темы",[15,373,374],{},"Управление контейнерами в среде выполнения является самостоятельной инженерной задачей, не сводимой к знанию команд запуска и остановки. Параметры запуска — переменные окружения, проброс портов, ограничения ресурсов и политика перезапуска — определяют поведение контейнера и должны задаваться осознанно, с учётом требований воспроизводимости и сопровождаемости.",[15,376,377],{},"Контейнер существует ровно столько, сколько работает его основной процесс. Понимание роли PID 1, механизма обработки сигналов и причин немедленного завершения контейнера позволяет избежать распространённых ошибок и проектировать приложения, корректно интегрированные с контейнерной средой.",[15,379,380],{},"Инструменты инспекции и диагностики — журналы, команды инспекции, выполнение команд внутри контейнера и мониторинг потребления ресурсов — обеспечивают наблюдаемость и контроль. Однако диагностика должна оставаться наблюдением, а не ручной модификацией: изменение содержимого работающего контейнера нарушает принцип неизменяемой инфраструктуры и создаёт неуправляемое расхождение между артефактом и реальным состоянием среды.",[15,382,383],{},"Принцип неизменяемости связывает этап сборки образа с этапом эксплуатации контейнера. Обновление приложения выполняется через пересборку образа и пересоздание контейнера, а не через модификацию работающего экземпляра. Этот подход обеспечивает прослеживаемость версий, управляемый откат и согласованность между средами. Вместе с разделением изменяемых данных и неизменяемой программной среды он формирует основу для перехода к более сложным сценариям развёртывания: сетевому взаимодействию, хранению данных и композиции многоконтейнерных приложений.",{"title":385,"searchDepth":386,"depth":386,"links":387},"",2,[388,397,402,409,415],{"id":27,"depth":386,"text":28,"children":389},[390,392,393,394,395,396],{"id":35,"depth":391,"text":36},3,{"id":53,"depth":391,"text":54},{"id":66,"depth":391,"text":67},{"id":83,"depth":391,"text":84},{"id":96,"depth":391,"text":97},{"id":118,"depth":391,"text":119},{"id":125,"depth":386,"text":126,"children":398},[399,400,401],{"id":132,"depth":391,"text":133},{"id":145,"depth":391,"text":146},{"id":203,"depth":391,"text":204},{"id":222,"depth":386,"text":223,"children":403},[404,405,406,407,408],{"id":229,"depth":391,"text":230},{"id":245,"depth":391,"text":246},{"id":262,"depth":391,"text":263},{"id":278,"depth":391,"text":279},{"id":302,"depth":391,"text":303},{"id":309,"depth":386,"text":310,"children":410},[411,412,413,414],{"id":316,"depth":391,"text":317},{"id":326,"depth":391,"text":327},{"id":344,"depth":391,"text":345},{"id":360,"depth":391,"text":361},{"id":370,"depth":386,"text":371},"aidt-bac-cloud_tech",null,"md",false,{},true,"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content","content",{"title":6,"description":17},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content",5,"topic-05","tKoOX7e-UUjViBNxfVB0zdA68XCptlbJHJO3ET9W8cI",{"id":430,"title":431,"body":432,"course_slug":416,"description":385,"env_label":417,"env_url":417,"extension":418,"group":417,"is_course_project":419,"is_index":419,"level":417,"meta":2432,"navigation":421,"path":2433,"section":2434,"seo":2435,"stem":2436,"topic_number":426,"topic_slug":427,"__hash__":2437},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-lr.md","Лабораторная работа 5. Управление контейнерами в среде выполнения",{"type":8,"value":433,"toc":2418},[434,437,454,458,466,469,473,476,479,505,509,512,521,578,583,629,657,677,681,724,727,731,738,773,777,782,1291,1295,1311,1315,1318,1620,1623,1638,1642,1645,1941,1944,1956,1960,1963,2297,2300,2306,2310,2317,2320,2346,2357,2361,2414],[11,435,431],{"id":436},"лабораторная-работа-5-управление-контейнерами-в-среде-выполнения",[438,439,440,448],"ul",{},[441,442,443,447],"li",{},[444,445,446],"strong",{},"Объём:"," 4 академических часа",[441,449,450,453],{},[444,451,452],{},"Раздел курса:"," учебное пособие, тема 5 «Управление контейнерами в среде выполнения»",[25,455,457],{"id":456},"введение","Введение",[15,459,460,461,465],{},"В предыдущей лабораторной работе образ был рассмотрен как воспроизводимый артефакт поставки: студент сосредоточился на том, ",[462,463,464],"em",{},"что"," упаковано в контейнер. Настоящая работа смещает фокус на следующий этап жизненного цикла — управление контейнером в среде выполнения. Один и тот же образ ведёт себя по-разному в зависимости от переменных окружения, ограничений ресурсов, политики перезапуска, способа завершения основного процесса. Освоение этих параметров — необходимое условие для перехода от разового экспериментального запуска к управляемой эксплуатации.",[15,467,468],{},"Результаты работы готовят студента к последующим темам: сетевому взаимодействию контейнеров, хранению данных и многоконтейнерной композиции. На этом этапе важно увидеть контейнер не как «чёрный ящик», а как управляемый объект со своим жизненным циклом и набором эксплуатационных характеристик.",[25,470,472],{"id":471},"цель-работы","Цель работы",[15,474,475],{},"Освоить управление контейнером на этапе выполнения: настройку параметров запуска, диагностику состояния и реализацию принципа неизменяемой инфраструктуры на материале учебного веб-приложения.",[15,477,478],{},"После выполнения работы студент сможет:",[438,480,481,484,490,499,502],{},[441,482,483],{},"запускать контейнер с заданными переменными окружения, ограничениями процессора и памяти, политикой перезапуска и пробросом портов;",[441,485,486,487,489],{},"определять процесс с PID 1 в контейнере и обосновывать выбор формы инструкции ",[47,488,49],{}," (exec form vs shell form) с точки зрения обработки сигналов;",[441,491,492,493,495,496,498],{},"наблюдать поведение контейнера при штатной остановке (",[47,494,152],{},") и принудительном завершении (",[47,497,156],{},") и интерпретировать результат;",[441,500,501],{},"собирать диагностические сведения о работающем контейнере штатными средствами среды выполнения: журналы, конфигурация, метрики, выполнение команд внутри контейнера;",[441,503,504],{},"обновлять приложение через пересоздание контейнера из нового образа без ручной модификации работающего экземпляра.",[25,506,508],{"id":507},"теоретический-минимум","Теоретический минимум",[15,510,511],{},"Теоретический материал, необходимый для выполнения работы, раскрыт в учебном пособии, тема 5: разделы «Параметры запуска контейнера», «Основной процесс и завершение контейнера», «Инспекция и сопровождение контейнера», «Неизменяемость среды и обновление приложений». Ниже приведены только справочные сведения об используемых командах и опциях, нужные непосредственно по ходу работы.",[15,513,514],{},[444,515,516,517,520],{},"Опции запуска ",[47,518,519],{},"docker run",", используемые в задании:",[438,522,523,529,535,545,551,572],{},[441,524,525,528],{},[47,526,527],{},"-e KEY=VALUE"," — задать переменную окружения внутри контейнера;",[441,530,531,534],{},[47,532,533],{},"-p HOST:CONTAINER"," — пробросить порт хоста на порт контейнера;",[441,536,537,540,541,544],{},[47,538,539],{},"--cpus=N"," — ограничить доступное процессорное время эквивалентом ",[47,542,543],{},"N"," ядер;",[441,546,547,550],{},[47,548,549],{},"--memory=SIZE"," — ограничить максимальный объём оперативной памяти;",[441,552,553,556,557,560,561,564,565,564,568,571],{},[47,554,555],{},"--restart=POLICY"," — политика перезапуска: ",[47,558,559],{},"no"," (по умолчанию), ",[47,562,563],{},"on-failure[:N]",", ",[47,566,567],{},"unless-stopped",[47,569,570],{},"always",";",[441,573,574,577],{},[47,575,576],{},"--name NAME"," — присвоить контейнеру имя, удобное для последующих команд.",[15,579,580],{},[444,581,582],{},"Команды инспекции:",[438,584,585,595,605,611,617,623],{},[441,586,587,590,591,594],{},[47,588,589],{},"docker ps [-a]"," — список запущенных (",[47,592,593],{},"-a"," — всех) контейнеров;",[441,596,597,600,601,604],{},[47,598,599],{},"docker logs [-f] NAME"," — журнал вывода stdout\u002Fstderr основного процесса (",[47,602,603],{},"-f"," — потоковый режим);",[441,606,607,610],{},[47,608,609],{},"docker inspect NAME"," — полный JSON-документ конфигурации и состояния контейнера;",[441,612,613,616],{},[47,614,615],{},"docker stats [NAME ...]"," — потоковые метрики потребления ресурсов;",[441,618,619,622],{},[47,620,621],{},"docker exec [-it] NAME CMD"," — выполнение команды внутри уже работающего контейнера;",[441,624,625,628],{},[47,626,627],{},"docker top NAME"," — список процессов контейнера с точки зрения хоста.",[15,630,631,634,635,638,639,641,642,644,645,648,649,651,652,654,655,192],{},[444,632,633],{},"Сигналы остановки."," ",[47,636,637],{},"docker stop"," посылает основному процессу ",[47,640,152],{}," и через таймаут (по умолчанию 10 с) — ",[47,643,156],{},". ",[47,646,647],{},"docker kill"," посылает ",[47,650,156],{}," немедленно, без возможности корректного завершения. Поведение приложения на ",[47,653,152],{}," определяется самим приложением: если оно не установило обработчик, процесс будет завершён только после ",[47,656,156],{},[15,658,659,664,665,667,668,670,671,674,675,192],{},[444,660,661,662,192],{},"Формы инструкции ",[47,663,49],{}," Exec form (",[47,666,183],{},") запускает процесс напрямую — он становится PID 1 и получает сигналы от среды выполнения. Shell form (",[47,669,187],{},") запускает процесс через ",[47,672,673],{},"\u002Fbin\u002Fsh -c",", который и становится PID 1; сигналы при этом не пробрасываются дочернему процессу, и приложение не видит ",[47,676,152],{},[25,678,680],{"id":679},"перечень-оснащения","Перечень оснащения",[438,682,683,692,698,705,708,715],{},[441,684,685,686,564,689,192],{},"Локальная машина с установленным Docker Engine 24.0+ или Docker Desktop, проверка готовности — ",[47,687,688],{},"docker version",[47,690,691],{},"docker info",[441,693,694,695,192],{},"Сетевой доступ к Docker Hub для загрузки базовых образов ",[47,696,697],{},"python:3.12-slim",[441,699,700,701,704],{},"Свободные TCP-порты ",[47,702,703],{},"8090–8093"," на хосте.",[441,706,707],{},"Не менее 1 ГБ свободного места на диске и не менее 2 ГБ свободной оперативной памяти (для проверки лимитов памяти).",[441,709,710,711,714],{},"Утилита ",[47,712,713],{},"curl"," для проверки HTTP-доступности приложения.",[441,716,717,718,176,721,192],{},"Текстовый редактор для подготовки ",[47,719,720],{},"app.py",[47,722,723],{},"Dockerfile",[15,725,726],{},"Эталонные решения и индивидуальные варианты не предусмотрены: задание выполняется всеми студентами в общей формулировке.",[25,728,730],{"id":729},"порядок-выполнения-работы","Порядок выполнения работы",[15,732,733,734,737],{},"Работа состоит из четырёх частей. Все команды выполняются в каталоге ",[47,735,736],{},"~\u002Flab05",", который создаётся в начале работы:",[739,740,744],"pre",{"className":741,"code":742,"language":743,"meta":385,"style":385},"language-bash shiki shiki-themes github-light github-dark","mkdir -p ~\u002Flab05 && cd ~\u002Flab05\n","bash",[47,745,746],{"__ignoreMap":385},[747,748,751,755,759,763,767,770],"span",{"class":749,"line":750},"line",1,[747,752,754],{"class":753},"sScJk","mkdir",[747,756,758],{"class":757},"sj4cs"," -p",[747,760,762],{"class":761},"sZZnC"," ~\u002Flab05",[747,764,766],{"class":765},"sVt8B"," && ",[747,768,769],{"class":757},"cd",[747,771,772],{"class":761}," ~\u002Flab05\n",[33,774,776],{"id":775},"часть-1-подготовка-учебного-приложения-и-параметры-запуска","Часть 1. Подготовка учебного приложения и параметры запуска",[778,779,781],"h4",{"id":780},"задание","Задание",[783,784,785,989,1070,1095,1160,1256],"ol",{},[441,786,787,788,790,791,793,794],{},"В каталоге ",[47,789,736],{}," создайте файл ",[47,792,720],{},":",[739,795,799],{"className":796,"code":797,"language":798,"meta":385,"style":385},"language-python shiki shiki-themes github-light github-dark","from http.server import HTTPServer, SimpleHTTPRequestHandler\nimport os\nimport signal\nimport sys\nimport time\n\nport = int(os.environ.get(\"PORT\", 8000))\ngreeting = os.environ.get(\"GREETING\", \"default\")\n\nclass Handler(SimpleHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200)\n        self.send_header(\"Content-Type\", \"text\u002Fplain; charset=utf-8\")\n        self.end_headers()\n        self.wfile.write(f\"{greeting}, порт {port}\\n\".encode())\n\n    def log_message(self, format, *args):\n        sys.stderr.write(\"%s - %s\\n\" % (self.address_string(), format % args))\n\nserver = HTTPServer((\"0.0.0.0\", port), Handler)\n\ndef shutdown(signum, frame):\n    sys.stderr.write(f\"получен сигнал {signum}, завершаюсь корректно\\n\")\n    sys.stderr.flush()\n    server.server_close()\n    sys.exit(0)\n\nsignal.signal(signal.SIGTERM, shutdown)\nsignal.signal(signal.SIGINT, shutdown)\n\nsys.stderr.write(f\"запуск, PID={os.getpid()}, порт={port}\\n\")\nsys.stderr.flush()\nserver.serve_forever()\n","python",[47,800,801,806,811,816,822,827,833,839,845,850,856,862,868,874,880,886,891,897,903,908,914,919,925,931,937,943,949,954,960,966,971,977,983],{"__ignoreMap":385},[747,802,803],{"class":749,"line":750},[747,804,805],{},"from http.server import HTTPServer, SimpleHTTPRequestHandler\n",[747,807,808],{"class":749,"line":386},[747,809,810],{},"import os\n",[747,812,813],{"class":749,"line":391},[747,814,815],{},"import signal\n",[747,817,819],{"class":749,"line":818},4,[747,820,821],{},"import sys\n",[747,823,824],{"class":749,"line":426},[747,825,826],{},"import time\n",[747,828,830],{"class":749,"line":829},6,[747,831,832],{"emptyLinePlaceholder":421},"\n",[747,834,836],{"class":749,"line":835},7,[747,837,838],{},"port = int(os.environ.get(\"PORT\", 8000))\n",[747,840,842],{"class":749,"line":841},8,[747,843,844],{},"greeting = os.environ.get(\"GREETING\", \"default\")\n",[747,846,848],{"class":749,"line":847},9,[747,849,832],{"emptyLinePlaceholder":421},[747,851,853],{"class":749,"line":852},10,[747,854,855],{},"class Handler(SimpleHTTPRequestHandler):\n",[747,857,859],{"class":749,"line":858},11,[747,860,861],{},"    def do_GET(self):\n",[747,863,865],{"class":749,"line":864},12,[747,866,867],{},"        self.send_response(200)\n",[747,869,871],{"class":749,"line":870},13,[747,872,873],{},"        self.send_header(\"Content-Type\", \"text\u002Fplain; charset=utf-8\")\n",[747,875,877],{"class":749,"line":876},14,[747,878,879],{},"        self.end_headers()\n",[747,881,883],{"class":749,"line":882},15,[747,884,885],{},"        self.wfile.write(f\"{greeting}, порт {port}\\n\".encode())\n",[747,887,889],{"class":749,"line":888},16,[747,890,832],{"emptyLinePlaceholder":421},[747,892,894],{"class":749,"line":893},17,[747,895,896],{},"    def log_message(self, format, *args):\n",[747,898,900],{"class":749,"line":899},18,[747,901,902],{},"        sys.stderr.write(\"%s - %s\\n\" % (self.address_string(), format % args))\n",[747,904,906],{"class":749,"line":905},19,[747,907,832],{"emptyLinePlaceholder":421},[747,909,911],{"class":749,"line":910},20,[747,912,913],{},"server = HTTPServer((\"0.0.0.0\", port), Handler)\n",[747,915,917],{"class":749,"line":916},21,[747,918,832],{"emptyLinePlaceholder":421},[747,920,922],{"class":749,"line":921},22,[747,923,924],{},"def shutdown(signum, frame):\n",[747,926,928],{"class":749,"line":927},23,[747,929,930],{},"    sys.stderr.write(f\"получен сигнал {signum}, завершаюсь корректно\\n\")\n",[747,932,934],{"class":749,"line":933},24,[747,935,936],{},"    sys.stderr.flush()\n",[747,938,940],{"class":749,"line":939},25,[747,941,942],{},"    server.server_close()\n",[747,944,946],{"class":749,"line":945},26,[747,947,948],{},"    sys.exit(0)\n",[747,950,952],{"class":749,"line":951},27,[747,953,832],{"emptyLinePlaceholder":421},[747,955,957],{"class":749,"line":956},28,[747,958,959],{},"signal.signal(signal.SIGTERM, shutdown)\n",[747,961,963],{"class":749,"line":962},29,[747,964,965],{},"signal.signal(signal.SIGINT, shutdown)\n",[747,967,969],{"class":749,"line":968},30,[747,970,832],{"emptyLinePlaceholder":421},[747,972,974],{"class":749,"line":973},31,[747,975,976],{},"sys.stderr.write(f\"запуск, PID={os.getpid()}, порт={port}\\n\")\n",[747,978,980],{"class":749,"line":979},32,[747,981,982],{},"sys.stderr.flush()\n",[747,984,986],{"class":749,"line":985},33,[747,987,988],{},"server.serve_forever()\n",[441,990,991,992,994,995,793,997,1059,1062,1063,1065,1066,1069],{},"Создайте ",[47,993,723],{}," в каталоге ",[47,996,736],{},[739,998,1002],{"className":999,"code":1000,"language":1001,"meta":385,"style":385},"language-dockerfile shiki shiki-themes github-light github-dark","FROM python:3.12-slim\n\nWORKDIR \u002Fapp\n\nCOPY app.py .\n\nENV PORT=8000\nENV GREETING=\"контейнерное приложение\"\n\nEXPOSE 8000\n\nCMD [\"python\", \"-u\", \"app.py\"]\n","dockerfile",[47,1003,1004,1009,1013,1018,1022,1027,1031,1036,1041,1045,1050,1054],{"__ignoreMap":385},[747,1005,1006],{"class":749,"line":750},[747,1007,1008],{},"FROM python:3.12-slim\n",[747,1010,1011],{"class":749,"line":386},[747,1012,832],{"emptyLinePlaceholder":421},[747,1014,1015],{"class":749,"line":391},[747,1016,1017],{},"WORKDIR \u002Fapp\n",[747,1019,1020],{"class":749,"line":818},[747,1021,832],{"emptyLinePlaceholder":421},[747,1023,1024],{"class":749,"line":426},[747,1025,1026],{},"COPY app.py .\n",[747,1028,1029],{"class":749,"line":829},[747,1030,832],{"emptyLinePlaceholder":421},[747,1032,1033],{"class":749,"line":835},[747,1034,1035],{},"ENV PORT=8000\n",[747,1037,1038],{"class":749,"line":841},[747,1039,1040],{},"ENV GREETING=\"контейнерное приложение\"\n",[747,1042,1043],{"class":749,"line":847},[747,1044,832],{"emptyLinePlaceholder":421},[747,1046,1047],{"class":749,"line":852},[747,1048,1049],{},"EXPOSE 8000\n",[747,1051,1052],{"class":749,"line":858},[747,1053,832],{"emptyLinePlaceholder":421},[747,1055,1056],{"class":749,"line":864},[747,1057,1058],{},"CMD [\"python\", \"-u\", \"app.py\"]\n",[1060,1061],"br",{},"Обратите внимание на форму ",[47,1064,49],{}," (exec form, JSON-массив) и флаг ",[47,1067,1068],{},"-u"," интерпретатора Python: они потребуются в части 2 для корректной работы с сигналами и журналом.",[441,1071,1072,1073],{},"Соберите образ:",[739,1074,1076],{"className":741,"code":1075,"language":743,"meta":385,"style":385},"docker build -t lab05-app:v1 .\n",[47,1077,1078],{"__ignoreMap":385},[747,1079,1080,1083,1086,1089,1092],{"class":749,"line":750},[747,1081,1082],{"class":753},"docker",[747,1084,1085],{"class":761}," build",[747,1087,1088],{"class":757}," -t",[747,1090,1091],{"class":761}," lab05-app:v1",[747,1093,1094],{"class":761}," .\n",[441,1096,1097,1098,1154,1156,1157,192],{},"Запустите контейнер с переопределением переменной окружения и пробросом порта:",[739,1099,1101],{"className":741,"code":1100,"language":743,"meta":385,"style":385},"docker run -d --name lab05-run1 \\\n    -e GREETING=\"лабораторная 5\" \\\n    -p 8090:8000 \\\n    lab05-app:v1\ncurl http:\u002F\u002Flocalhost:8090\n",[47,1102,1103,1122,1132,1142,1147],{"__ignoreMap":385},[747,1104,1105,1107,1110,1113,1116,1119],{"class":749,"line":750},[747,1106,1082],{"class":753},[747,1108,1109],{"class":761}," run",[747,1111,1112],{"class":757}," -d",[747,1114,1115],{"class":757}," --name",[747,1117,1118],{"class":761}," lab05-run1",[747,1120,1121],{"class":757}," \\\n",[747,1123,1124,1127,1130],{"class":749,"line":386},[747,1125,1126],{"class":757},"    -e",[747,1128,1129],{"class":761}," GREETING=\"лабораторная 5\"",[747,1131,1121],{"class":757},[747,1133,1134,1137,1140],{"class":749,"line":391},[747,1135,1136],{"class":757},"    -p",[747,1138,1139],{"class":761}," 8090:8000",[747,1141,1121],{"class":757},[747,1143,1144],{"class":749,"line":818},[747,1145,1146],{"class":761},"    lab05-app:v1\n",[747,1148,1149,1151],{"class":749,"line":426},[747,1150,713],{"class":753},[747,1152,1153],{"class":761}," http:\u002F\u002Flocalhost:8090\n",[1060,1155],{},"Зафиксируйте вывод и сравните его с тем, что было бы при запуске без ",[47,1158,1159],{},"-e GREETING=...",[441,1161,1162,1163,1217,1219,1220,1244,1246,1247,564,1250,564,1253,192],{},"Запустите второй контейнер с ограничениями ресурсов и политикой перезапуска:",[739,1164,1166],{"className":741,"code":1165,"language":743,"meta":385,"style":385},"docker run -d --name lab05-run2 \\\n    --cpus=0.5 \\\n    --memory=128m \\\n    --restart=on-failure:3 \\\n    -p 8091:8000 \\\n    lab05-app:v1\n",[47,1167,1168,1183,1190,1197,1204,1213],{"__ignoreMap":385},[747,1169,1170,1172,1174,1176,1178,1181],{"class":749,"line":750},[747,1171,1082],{"class":753},[747,1173,1109],{"class":761},[747,1175,1112],{"class":757},[747,1177,1115],{"class":757},[747,1179,1180],{"class":761}," lab05-run2",[747,1182,1121],{"class":757},[747,1184,1185,1188],{"class":749,"line":386},[747,1186,1187],{"class":757},"    --cpus=0.5",[747,1189,1121],{"class":757},[747,1191,1192,1195],{"class":749,"line":391},[747,1193,1194],{"class":757},"    --memory=128m",[747,1196,1121],{"class":757},[747,1198,1199,1202],{"class":749,"line":818},[747,1200,1201],{"class":757},"    --restart=on-failure:3",[747,1203,1121],{"class":757},[747,1205,1206,1208,1211],{"class":749,"line":426},[747,1207,1136],{"class":757},[747,1209,1210],{"class":761}," 8091:8000",[747,1212,1121],{"class":757},[747,1214,1215],{"class":749,"line":829},[747,1216,1146],{"class":761},[1060,1218],{},"Проверьте сохранённые параметры:",[739,1221,1223],{"className":741,"code":1222,"language":743,"meta":385,"style":385},"docker inspect lab05-run2 \\\n    --format '{{.HostConfig.NanoCpus}} {{.HostConfig.Memory}} {{.HostConfig.RestartPolicy}}'\n",[47,1224,1225,1236],{"__ignoreMap":385},[747,1226,1227,1229,1232,1234],{"class":749,"line":750},[747,1228,1082],{"class":753},[747,1230,1231],{"class":761}," inspect",[747,1233,1180],{"class":761},[747,1235,1121],{"class":757},[747,1237,1238,1241],{"class":749,"line":386},[747,1239,1240],{"class":757},"    --format",[747,1242,1243],{"class":761}," '{{.HostConfig.NanoCpus}} {{.HostConfig.Memory}} {{.HostConfig.RestartPolicy}}'\n",[1060,1245],{},"Зафиксируйте полученные значения и поясните, как они соотносятся с переданными ключами ",[47,1248,1249],{},"--cpus",[47,1251,1252],{},"--memory",[47,1254,1255],{},"--restart",[441,1257,1258,1259,1262,1263,1266,1267],{},"Оставьте контейнер ",[47,1260,1261],{},"lab05-run1"," запущенным до конца части 2; контейнер ",[47,1264,1265],{},"lab05-run2"," остановите и удалите:",[739,1268,1270],{"className":741,"code":1269,"language":743,"meta":385,"style":385},"docker stop lab05-run2 && docker rm lab05-run2\n",[47,1271,1272],{"__ignoreMap":385},[747,1273,1274,1276,1279,1281,1283,1285,1288],{"class":749,"line":750},[747,1275,1082],{"class":753},[747,1277,1278],{"class":761}," stop",[747,1280,1180],{"class":761},[747,1282,766],{"class":765},[747,1284,1082],{"class":753},[747,1286,1287],{"class":761}," rm",[747,1289,1290],{"class":761}," lab05-run2\n",[778,1292,1294],{"id":1293},"результат","Результат",[15,1296,1297,1298,1300,1301,1304,1305,1307,1308,1310],{},"Работающий контейнер ",[47,1299,1261],{},", доступный по ",[47,1302,1303],{},"http:\u002F\u002Flocalhost:8090",", и зафиксированные параметры ",[47,1306,1265],{},": значения CPU\u002Fпамяти\u002Fполитики перезапуска, прочитанные через ",[47,1309,252],{},", с пояснением соответствия исходным ключам командной строки.",[33,1312,1314],{"id":1313},"часть-2-pid-1-и-обработка-сигналов","Часть 2. PID 1 и обработка сигналов",[778,1316,781],{"id":1317},"задание-1",[783,1319,1320,1359,1404,1483,1535,1601],{},[441,1321,1322,1323,1356,1358],{},"Определите процесс с PID 1 в работающем контейнере:",[739,1324,1326],{"className":741,"code":1325,"language":743,"meta":385,"style":385},"docker top lab05-run1\ndocker exec lab05-run1 ps -o pid,ppid,cmd\n",[47,1327,1328,1338],{"__ignoreMap":385},[747,1329,1330,1332,1335],{"class":749,"line":750},[747,1331,1082],{"class":753},[747,1333,1334],{"class":761}," top",[747,1336,1337],{"class":761}," lab05-run1\n",[747,1339,1340,1342,1345,1347,1350,1353],{"class":749,"line":386},[747,1341,1082],{"class":753},[747,1343,1344],{"class":761}," exec",[747,1346,1118],{"class":761},[747,1348,1349],{"class":761}," ps",[747,1351,1352],{"class":757}," -o",[747,1354,1355],{"class":761}," pid,ppid,cmd\n",[1060,1357],{},"Зафиксируйте имя процесса с PID 1 и поясните, почему именно он получает сигналы от Docker.",[441,1360,1361,1362,1394,1396,1397,1400,1401,1403],{},"Выполните корректную остановку с измерением времени реакции:",[739,1363,1365],{"className":741,"code":1364,"language":743,"meta":385,"style":385},"time docker stop lab05-run1\ndocker logs lab05-run1 | tail -5\n",[47,1366,1367,1376],{"__ignoreMap":385},[747,1368,1369,1373],{"class":749,"line":750},[747,1370,1372],{"class":1371},"szBVR","time",[747,1374,1375],{"class":765}," docker stop lab05-run1\n",[747,1377,1378,1380,1383,1385,1388,1391],{"class":749,"line":386},[747,1379,1082],{"class":753},[747,1381,1382],{"class":761}," logs",[747,1384,1118],{"class":761},[747,1386,1387],{"class":1371}," |",[747,1389,1390],{"class":753}," tail",[747,1392,1393],{"class":757}," -5\n",[1060,1395],{},"В журнале должна появиться строка вида ",[47,1398,1399],{},"получен сигнал 15, завершаюсь корректно"," — это отклик обработчика ",[47,1402,152],{}," в приложении. Зафиксируйте время остановки.",[441,1405,1406,1407,1472,1474,1475,1478,1479,1482],{},"Удалите контейнер и пересоздайте его с альтернативным запуском в shell form (без exec form):",[739,1408,1410],{"className":741,"code":1409,"language":743,"meta":385,"style":385},"docker rm lab05-run1\n\ndocker run -d --name lab05-shell \\\n    -p 8090:8000 \\\n    --entrypoint sh \\\n    lab05-app:v1 \\\n    -c \"python -u app.py\"\n",[47,1411,1412,1420,1424,1439,1447,1457,1464],{"__ignoreMap":385},[747,1413,1414,1416,1418],{"class":749,"line":750},[747,1415,1082],{"class":753},[747,1417,1287],{"class":761},[747,1419,1337],{"class":761},[747,1421,1422],{"class":749,"line":386},[747,1423,832],{"emptyLinePlaceholder":421},[747,1425,1426,1428,1430,1432,1434,1437],{"class":749,"line":391},[747,1427,1082],{"class":753},[747,1429,1109],{"class":761},[747,1431,1112],{"class":757},[747,1433,1115],{"class":757},[747,1435,1436],{"class":761}," lab05-shell",[747,1438,1121],{"class":757},[747,1440,1441,1443,1445],{"class":749,"line":818},[747,1442,1136],{"class":757},[747,1444,1139],{"class":761},[747,1446,1121],{"class":757},[747,1448,1449,1452,1455],{"class":749,"line":426},[747,1450,1451],{"class":757},"    --entrypoint",[747,1453,1454],{"class":761}," sh",[747,1456,1121],{"class":757},[747,1458,1459,1462],{"class":749,"line":829},[747,1460,1461],{"class":761},"    lab05-app:v1",[747,1463,1121],{"class":757},[747,1465,1466,1469],{"class":749,"line":835},[747,1467,1468],{"class":757},"    -c",[747,1470,1471],{"class":761}," \"python -u app.py\"\n",[1060,1473],{},"Аргументы ",[47,1476,1477],{},"--entrypoint sh ... -c \"...\""," намеренно эмулируют типичную ошибку: запуск приложения через оболочку, при которой PID 1 — это ",[47,1480,1481],{},"sh",", а не Python.",[441,1484,1485,1486,1526,1528,1529,1531,1532,1534],{},"Проверьте PID 1 нового контейнера и попытайтесь его остановить:",[739,1487,1489],{"className":741,"code":1488,"language":743,"meta":385,"style":385},"docker exec lab05-shell ps -o pid,ppid,cmd\ntime docker stop lab05-shell\ndocker logs lab05-shell | tail -5\n",[47,1490,1491,1505,1512],{"__ignoreMap":385},[747,1492,1493,1495,1497,1499,1501,1503],{"class":749,"line":750},[747,1494,1082],{"class":753},[747,1496,1344],{"class":761},[747,1498,1436],{"class":761},[747,1500,1349],{"class":761},[747,1502,1352],{"class":757},[747,1504,1355],{"class":761},[747,1506,1507,1509],{"class":749,"line":386},[747,1508,1372],{"class":1371},[747,1510,1511],{"class":765}," docker stop lab05-shell\n",[747,1513,1514,1516,1518,1520,1522,1524],{"class":749,"line":391},[747,1515,1082],{"class":753},[747,1517,1382],{"class":761},[747,1519,1436],{"class":761},[747,1521,1387],{"class":1371},[747,1523,1390],{"class":753},[747,1525,1393],{"class":757},[1060,1527],{},"Зафиксируйте: имя процесса с PID 1, время выполнения ",[47,1530,637],{},", наличие или отсутствие строки об обработке ",[47,1533,152],{}," в журнале. Объясните, почему оболочка не пробросила сигнал дочернему процессу и какие последствия это имеет в эксплуатации.",[441,1536,1537,1538,1593,1595,1596,176,1598,1600],{},"Выполните принудительное завершение для сравнения:",[739,1539,1541],{"className":741,"code":1540,"language":743,"meta":385,"style":385},"docker run -d --name lab05-kill -p 8092:8000 lab05-app:v1\nsleep 1\ntime docker kill lab05-kill\ndocker logs lab05-kill | tail -5\n",[47,1542,1543,1564,1572,1579],{"__ignoreMap":385},[747,1544,1545,1547,1549,1551,1553,1556,1558,1561],{"class":749,"line":750},[747,1546,1082],{"class":753},[747,1548,1109],{"class":761},[747,1550,1112],{"class":757},[747,1552,1115],{"class":757},[747,1554,1555],{"class":761}," lab05-kill",[747,1557,758],{"class":757},[747,1559,1560],{"class":761}," 8092:8000",[747,1562,1563],{"class":761}," lab05-app:v1\n",[747,1565,1566,1569],{"class":749,"line":386},[747,1567,1568],{"class":753},"sleep",[747,1570,1571],{"class":757}," 1\n",[747,1573,1574,1576],{"class":749,"line":391},[747,1575,1372],{"class":1371},[747,1577,1578],{"class":765}," docker kill lab05-kill\n",[747,1580,1581,1583,1585,1587,1589,1591],{"class":749,"line":818},[747,1582,1082],{"class":753},[747,1584,1382],{"class":761},[747,1586,1555],{"class":761},[747,1588,1387],{"class":1371},[747,1590,1390],{"class":753},[747,1592,1393],{"class":757},[1060,1594],{},"Зафиксируйте время завершения и наличие строки об обработке сигнала. Поясните разницу между ",[47,1597,637],{},[47,1599,647],{}," с точки зрения приложения.",[441,1602,1603,1604],{},"Удалите все контейнеры этой части:",[739,1605,1607],{"className":741,"code":1606,"language":743,"meta":385,"style":385},"docker rm lab05-shell lab05-kill\n",[47,1608,1609],{"__ignoreMap":385},[747,1610,1611,1613,1615,1617],{"class":749,"line":750},[747,1612,1082],{"class":753},[747,1614,1287],{"class":761},[747,1616,1436],{"class":761},[747,1618,1619],{"class":761}," lab05-kill\n",[778,1621,1294],{"id":1622},"результат-1",[15,1624,1625,1626,1628,1629,1631,1632,1634,1635,1637],{},"Сравнительная таблица из трёх запусков (exec form + ",[47,1627,637],{},", shell form + ",[47,1630,637],{},", exec form + ",[47,1633,647],{},") с тремя столбцами: PID 1, время завершения, наличие записи об обработке сигнала в журнале. Текстовое объяснение наблюдаемых различий со ссылкой на форму ",[47,1636,49],{}," и тип сигнала.",[33,1639,1641],{"id":1640},"часть-3-инспекция-и-диагностика-работающего-контейнера","Часть 3. Инспекция и диагностика работающего контейнера",[778,1643,781],{"id":1644},"задание-2",[783,1646,1647,1697,1755,1803,1860,1883,1917],{},[441,1648,1649,1650],{},"Запустите контейнер заново для использования в этой части:",[739,1651,1653],{"className":741,"code":1652,"language":743,"meta":385,"style":385},"docker run -d --name lab05-diag \\\n    -e GREETING=\"диагностика\" \\\n    --memory=128m \\\n    -p 8090:8000 \\\n    lab05-app:v1\n",[47,1654,1655,1670,1679,1685,1693],{"__ignoreMap":385},[747,1656,1657,1659,1661,1663,1665,1668],{"class":749,"line":750},[747,1658,1082],{"class":753},[747,1660,1109],{"class":761},[747,1662,1112],{"class":757},[747,1664,1115],{"class":757},[747,1666,1667],{"class":761}," lab05-diag",[747,1669,1121],{"class":757},[747,1671,1672,1674,1677],{"class":749,"line":386},[747,1673,1126],{"class":757},[747,1675,1676],{"class":761}," GREETING=\"диагностика\"",[747,1678,1121],{"class":757},[747,1680,1681,1683],{"class":749,"line":391},[747,1682,1194],{"class":757},[747,1684,1121],{"class":757},[747,1686,1687,1689,1691],{"class":749,"line":818},[747,1688,1136],{"class":757},[747,1690,1139],{"class":761},[747,1692,1121],{"class":757},[747,1694,1695],{"class":749,"line":426},[747,1696,1146],{"class":761},[441,1698,1699,1700],{},"Сгенерируйте искусственную нагрузку запросами:",[739,1701,1703],{"className":741,"code":1702,"language":743,"meta":385,"style":385},"for i in $(seq 1 20); do curl -s http:\u002F\u002Flocalhost:8090 > \u002Fdev\u002Fnull; done\n",[47,1704,1705],{"__ignoreMap":385},[747,1706,1707,1710,1713,1716,1719,1722,1725,1728,1731,1734,1737,1740,1743,1746,1749,1752],{"class":749,"line":750},[747,1708,1709],{"class":1371},"for",[747,1711,1712],{"class":765}," i ",[747,1714,1715],{"class":1371},"in",[747,1717,1718],{"class":765}," $(",[747,1720,1721],{"class":753},"seq",[747,1723,1724],{"class":757}," 1",[747,1726,1727],{"class":757}," 20",[747,1729,1730],{"class":765},"); ",[747,1732,1733],{"class":1371},"do",[747,1735,1736],{"class":753}," curl",[747,1738,1739],{"class":757}," -s",[747,1741,1742],{"class":761}," http:\u002F\u002Flocalhost:8090",[747,1744,1745],{"class":1371}," >",[747,1747,1748],{"class":761}," \u002Fdev\u002Fnull",[747,1750,1751],{"class":765},"; ",[747,1753,1754],{"class":1371},"done\n",[441,1756,1757,1758,1789,1791,1792,1795,1796,1799,1800,192],{},"Изучите журнал приложения двумя способами:",[739,1759,1761],{"className":741,"code":1760,"language":743,"meta":385,"style":385},"docker logs lab05-diag\ndocker logs --tail 5 --timestamps lab05-diag\n",[47,1762,1763,1772],{"__ignoreMap":385},[747,1764,1765,1767,1769],{"class":749,"line":750},[747,1766,1082],{"class":753},[747,1768,1382],{"class":761},[747,1770,1771],{"class":761}," lab05-diag\n",[747,1773,1774,1776,1778,1781,1784,1787],{"class":749,"line":386},[747,1775,1082],{"class":753},[747,1777,1382],{"class":761},[747,1779,1780],{"class":757}," --tail",[747,1782,1783],{"class":757}," 5",[747,1785,1786],{"class":757}," --timestamps",[747,1788,1771],{"class":761},[1060,1790],{},"Зафиксируйте, какие потоки попадают в журнал (",[47,1793,1794],{},"stdout","\u002F",[47,1797,1798],{},"stderr",") и какую информацию даёт флаг ",[47,1801,1802],{},"--timestamps",[441,1804,1805,1806,1857,1859],{},"Снимите конфигурацию и состояние контейнера:",[739,1807,1809],{"className":741,"code":1808,"language":743,"meta":385,"style":385},"docker inspect lab05-diag \\\n    --format '{{.State.Status}} {{.State.Pid}} {{.NetworkSettings.IPAddress}}'\n\ndocker inspect lab05-diag --format '{{json .Config.Env}}' | python3 -m json.tool\n",[47,1810,1811,1821,1828,1832],{"__ignoreMap":385},[747,1812,1813,1815,1817,1819],{"class":749,"line":750},[747,1814,1082],{"class":753},[747,1816,1231],{"class":761},[747,1818,1667],{"class":761},[747,1820,1121],{"class":757},[747,1822,1823,1825],{"class":749,"line":386},[747,1824,1240],{"class":757},[747,1826,1827],{"class":761}," '{{.State.Status}} {{.State.Pid}} {{.NetworkSettings.IPAddress}}'\n",[747,1829,1830],{"class":749,"line":391},[747,1831,832],{"emptyLinePlaceholder":421},[747,1833,1834,1836,1838,1840,1843,1846,1848,1851,1854],{"class":749,"line":818},[747,1835,1082],{"class":753},[747,1837,1231],{"class":761},[747,1839,1667],{"class":761},[747,1841,1842],{"class":757}," --format",[747,1844,1845],{"class":761}," '{{json .Config.Env}}'",[747,1847,1387],{"class":1371},[747,1849,1850],{"class":753}," python3",[747,1852,1853],{"class":757}," -m",[747,1855,1856],{"class":761}," json.tool\n",[1060,1858],{},"Зафиксируйте состояние контейнера, PID процесса с точки зрения хоста, IP-адрес во внутренней сети и список переменных окружения.",[441,1861,1862,1863,1880,1882],{},"Получите потоковые метрики потребления ресурсов одной выборкой:",[739,1864,1866],{"className":741,"code":1865,"language":743,"meta":385,"style":385},"docker stats --no-stream lab05-diag\n",[47,1867,1868],{"__ignoreMap":385},[747,1869,1870,1872,1875,1878],{"class":749,"line":750},[747,1871,1082],{"class":753},[747,1873,1874],{"class":761}," stats",[747,1876,1877],{"class":757}," --no-stream",[747,1879,1771],{"class":761},[1060,1881],{},"Зафиксируйте значения CPU %, MEM USAGE\u002FLIMIT, NET I\u002FO. Сопоставьте лимит памяти со значением, переданным при запуске.",[441,1884,1885,1886,1910,1912,1913,1916],{},"Войдите внутрь контейнера для диагностики и осмотрите файловую систему:",[739,1887,1889],{"className":741,"code":1888,"language":743,"meta":385,"style":385},"docker exec -it lab05-diag sh -c \"ls \u002Fapp && cat \u002Fetc\u002Fos-release | head -3\"\n",[47,1890,1891],{"__ignoreMap":385},[747,1892,1893,1895,1897,1900,1902,1904,1907],{"class":749,"line":750},[747,1894,1082],{"class":753},[747,1896,1344],{"class":761},[747,1898,1899],{"class":757}," -it",[747,1901,1667],{"class":761},[747,1903,1454],{"class":761},[747,1905,1906],{"class":757}," -c",[747,1908,1909],{"class":761}," \"ls \u002Fapp && cat \u002Fetc\u002Fos-release | head -3\"\n",[1060,1911],{},"Зафиксируйте имя приложения, найденное в ",[47,1914,1915],{},"\u002Fapp",", и базовый дистрибутив, использованный в образе.",[441,1918,1919,1920],{},"Завершите контейнер:",[739,1921,1923],{"className":741,"code":1922,"language":743,"meta":385,"style":385},"docker stop lab05-diag && docker rm lab05-diag\n",[47,1924,1925],{"__ignoreMap":385},[747,1926,1927,1929,1931,1933,1935,1937,1939],{"class":749,"line":750},[747,1928,1082],{"class":753},[747,1930,1278],{"class":761},[747,1932,1667],{"class":761},[747,1934,766],{"class":765},[747,1936,1082],{"class":753},[747,1938,1287],{"class":761},[747,1940,1771],{"class":761},[778,1942,1294],{"id":1943},"результат-2",[15,1945,1946,1947,1949,1950,1952,1953,1955],{},"Журнал контейнера с временными метками, выдержки ",[47,1948,252],{}," (статус, PID, IP, переменные окружения), снимок ",[47,1951,284],{},", результат ",[47,1954,268],{}," с подтверждением содержимого образа. Краткое пояснение, какой инструмент следует выбирать для каждой типовой задачи диагностики: «приложение упало», «приложение медленно отвечает», «приложение не видит конфигурацию».",[33,1957,1959],{"id":1958},"часть-4-неизменяемое-обновление-приложения","Часть 4. Неизменяемое обновление приложения",[778,1961,781],{"id":1962},"задание-3",[783,1964,1965,2006,2058,2103,2145,2207,2259],{},[441,1966,1967,1968,793,1971,2003,2005],{},"Запустите контейнер версии ",[47,1969,1970],{},"v1",[739,1972,1974],{"className":741,"code":1973,"language":743,"meta":385,"style":385},"docker run -d --name lab05-prod -p 8093:8000 lab05-app:v1\ncurl http:\u002F\u002Flocalhost:8093\n",[47,1975,1976,1996],{"__ignoreMap":385},[747,1977,1978,1980,1982,1984,1986,1989,1991,1994],{"class":749,"line":750},[747,1979,1082],{"class":753},[747,1981,1109],{"class":761},[747,1983,1112],{"class":757},[747,1985,1115],{"class":757},[747,1987,1988],{"class":761}," lab05-prod",[747,1990,758],{"class":757},[747,1992,1993],{"class":761}," 8093:8000",[747,1995,1563],{"class":761},[747,1997,1998,2000],{"class":749,"line":386},[747,1999,713],{"class":753},[747,2001,2002],{"class":761}," http:\u002F\u002Flocalhost:8093\n",[1060,2004],{},"Зафиксируйте ответ приложения.",[441,2007,2008,2009,2012,2013,2055,2057],{},"Сначала примените ",[444,2010,2011],{},"антипаттерн"," — измените файл прямо внутри работающего контейнера:",[739,2014,2016],{"className":741,"code":2015,"language":743,"meta":385,"style":385},"docker exec lab05-prod sh -c \"sed -i 's\u002Fdefault\u002Fизменено вручную\u002F' \u002Fapp\u002Fapp.py\"\ndocker exec lab05-prod cat \u002Fapp\u002Fapp.py | grep greeting\n",[47,2017,2018,2033],{"__ignoreMap":385},[747,2019,2020,2022,2024,2026,2028,2030],{"class":749,"line":750},[747,2021,1082],{"class":753},[747,2023,1344],{"class":761},[747,2025,1988],{"class":761},[747,2027,1454],{"class":761},[747,2029,1906],{"class":757},[747,2031,2032],{"class":761}," \"sed -i 's\u002Fdefault\u002Fизменено вручную\u002F' \u002Fapp\u002Fapp.py\"\n",[747,2034,2035,2037,2039,2041,2044,2047,2049,2052],{"class":749,"line":386},[747,2036,1082],{"class":753},[747,2038,1344],{"class":761},[747,2040,1988],{"class":761},[747,2042,2043],{"class":761}," cat",[747,2045,2046],{"class":761}," \u002Fapp\u002Fapp.py",[747,2048,1387],{"class":1371},[747,2050,2051],{"class":753}," grep",[747,2053,2054],{"class":761}," greeting\n",[1060,2056],{},"Поясните в отчёте, почему такое изменение не приведёт к фактическому обновлению поведения приложения (даже без перезапуска контейнера) и какие последствия оно имеет для воспроизводимости.",[441,2059,2060,2061,2097,2099,2100,2102],{},"Удалите изменённый контейнер и проверьте, что в образе ничего не сохранилось:",[739,2062,2064],{"className":741,"code":2063,"language":743,"meta":385,"style":385},"docker rm -f lab05-prod\ndocker run --rm lab05-app:v1 grep greeting \u002Fapp\u002Fapp.py\n",[47,2065,2066,2078],{"__ignoreMap":385},[747,2067,2068,2070,2072,2075],{"class":749,"line":750},[747,2069,1082],{"class":753},[747,2071,1287],{"class":761},[747,2073,2074],{"class":757}," -f",[747,2076,2077],{"class":761}," lab05-prod\n",[747,2079,2080,2082,2084,2087,2089,2091,2094],{"class":749,"line":386},[747,2081,1082],{"class":753},[747,2083,1109],{"class":761},[747,2085,2086],{"class":757}," --rm",[747,2088,1091],{"class":761},[747,2090,2051],{"class":761},[747,2092,2093],{"class":761}," greeting",[747,2095,2096],{"class":761}," \u002Fapp\u002Fapp.py\n",[1060,2098],{},"Зафиксируйте: образ остался прежним; правки ",[47,2101,268],{}," затрагивали только слой записи удалённого контейнера.",[441,2104,2105,2106,2108,2109,2112,2113,2116,2117],{},"Теперь выполните корректное обновление через пересборку. Внесите изменение в ",[47,2107,720],{},": замените значение по умолчанию переменной ",[47,2110,2111],{},"greeting"," на ",[47,2114,2115],{},"\"приложение v2\"",", после чего соберите новый образ с новой версией:",[739,2118,2120],{"className":741,"code":2119,"language":743,"meta":385,"style":385},"docker build -t lab05-app:v2 .\ndocker images lab05-app\n",[47,2121,2122,2135],{"__ignoreMap":385},[747,2123,2124,2126,2128,2130,2133],{"class":749,"line":750},[747,2125,1082],{"class":753},[747,2127,1085],{"class":761},[747,2129,1088],{"class":757},[747,2131,2132],{"class":761}," lab05-app:v2",[747,2134,1094],{"class":761},[747,2136,2137,2139,2142],{"class":749,"line":386},[747,2138,1082],{"class":753},[747,2140,2141],{"class":761}," images",[747,2143,2144],{"class":761}," lab05-app\n",[441,2146,2147,2148,2204,2206],{},"Выполните управляемое обновление: остановите старую версию, удалите контейнер и запустите новый из обновлённого образа:",[739,2149,2151],{"className":741,"code":2150,"language":743,"meta":385,"style":385},"docker stop lab05-prod 2>\u002Fdev\u002Fnull\ndocker rm lab05-prod 2>\u002Fdev\u002Fnull\ndocker run -d --name lab05-prod -p 8093:8000 lab05-app:v2\ncurl http:\u002F\u002Flocalhost:8093\n",[47,2152,2153,2167,2179,2198],{"__ignoreMap":385},[747,2154,2155,2157,2159,2161,2164],{"class":749,"line":750},[747,2156,1082],{"class":753},[747,2158,1278],{"class":761},[747,2160,1988],{"class":761},[747,2162,2163],{"class":1371}," 2>",[747,2165,2166],{"class":761},"\u002Fdev\u002Fnull\n",[747,2168,2169,2171,2173,2175,2177],{"class":749,"line":386},[747,2170,1082],{"class":753},[747,2172,1287],{"class":761},[747,2174,1988],{"class":761},[747,2176,2163],{"class":1371},[747,2178,2166],{"class":761},[747,2180,2181,2183,2185,2187,2189,2191,2193,2195],{"class":749,"line":391},[747,2182,1082],{"class":753},[747,2184,1109],{"class":761},[747,2186,1112],{"class":757},[747,2188,1115],{"class":757},[747,2190,1988],{"class":761},[747,2192,758],{"class":757},[747,2194,1993],{"class":761},[747,2196,2197],{"class":761}," lab05-app:v2\n",[747,2199,2200,2202],{"class":749,"line":818},[747,2201,713],{"class":753},[747,2203,2002],{"class":761},[1060,2205],{},"Зафиксируйте, что ответ приложения изменился. Поясните, чем такая модель обновления (пересоздание контейнера из нового образа) отличается от ручной правки внутри контейнера и в чём её преимущество для эксплуатации.",[441,2208,2209,2210,2256,2258],{},"Проверьте, что обе версии образа продолжают существовать локально и можно вернуться к предыдущей:",[739,2211,2213],{"className":741,"code":2212,"language":743,"meta":385,"style":385},"docker stop lab05-prod && docker rm lab05-prod\ndocker run -d --name lab05-prod-rollback -p 8093:8000 lab05-app:v1\ncurl http:\u002F\u002Flocalhost:8093\n",[47,2214,2215,2231,2250],{"__ignoreMap":385},[747,2216,2217,2219,2221,2223,2225,2227,2229],{"class":749,"line":750},[747,2218,1082],{"class":753},[747,2220,1278],{"class":761},[747,2222,1988],{"class":761},[747,2224,766],{"class":765},[747,2226,1082],{"class":753},[747,2228,1287],{"class":761},[747,2230,2077],{"class":761},[747,2232,2233,2235,2237,2239,2241,2244,2246,2248],{"class":749,"line":386},[747,2234,1082],{"class":753},[747,2236,1109],{"class":761},[747,2238,1112],{"class":757},[747,2240,1115],{"class":757},[747,2242,2243],{"class":761}," lab05-prod-rollback",[747,2245,758],{"class":757},[747,2247,1993],{"class":761},[747,2249,1563],{"class":761},[747,2251,2252,2254],{"class":749,"line":391},[747,2253,713],{"class":753},[747,2255,2002],{"class":761},[1060,2257],{},"Зафиксируйте: откат сводится к запуску предыдущего тегированного образа без каких-либо «отмен» правок.",[441,2260,2261,2262],{},"Выполните очистку:",[739,2263,2265],{"className":741,"code":2264,"language":743,"meta":385,"style":385},"docker stop lab05-prod-rollback && docker rm lab05-prod-rollback\ndocker image rm lab05-app:v1 lab05-app:v2\n",[47,2266,2267,2284],{"__ignoreMap":385},[747,2268,2269,2271,2273,2275,2277,2279,2281],{"class":749,"line":750},[747,2270,1082],{"class":753},[747,2272,1278],{"class":761},[747,2274,2243],{"class":761},[747,2276,766],{"class":765},[747,2278,1082],{"class":753},[747,2280,1287],{"class":761},[747,2282,2283],{"class":761}," lab05-prod-rollback\n",[747,2285,2286,2288,2291,2293,2295],{"class":749,"line":386},[747,2287,1082],{"class":753},[747,2289,2290],{"class":761}," image",[747,2292,1287],{"class":761},[747,2294,1091],{"class":761},[747,2296,2197],{"class":761},[778,2298,1294],{"id":2299},"результат-3",[15,2301,2302,2303,2305],{},"Зафиксированная пара ответов приложения «до» и «после» обновления, подтверждение неизменности образа после ручной правки внутри контейнера, выполненный откат к версии ",[47,2304,1970],{}," без модификаций образа. Текстовое объяснение принципа неизменяемой инфраструктуры в одном-двух абзацах.",[25,2307,2309],{"id":2308},"форма-отчёта","Форма отчёта",[15,2311,2312,2313,2316],{},"Базовые требования к оформлению отчёта (титульный лист, структура файлов в репозитории, порядок сдачи через MR) — в ",[47,2314,2315],{},"shared\u002Fsubmission.md",". Здесь указано только специфичное для данной работы.",[15,2318,2319],{},"В отчёт включить:",[438,2321,2322,2328,2331,2334,2340],{},[441,2323,2324,2325,2327],{},"сведения об окружении: версия Docker Engine (",[47,2326,688],{},"), операционная система, объём оперативной памяти хоста;",[441,2329,2330],{},"по каждой из четырёх частей — последовательность выполненных команд, их вывод (как текст в блоках кода) и фиксацию указанного в разделе «Результат» части;",[441,2332,2333],{},"сравнительную таблицу из части 2 (3 строки × 3 столбца: PID 1, время завершения, обработка сигнала);",[441,2335,2336,2337,2339],{},"снимок ",[47,2338,284],{}," из части 3;",[441,2341,2342,2343,2345],{},"общий вывод (5–10 предложений): какие параметры запуска оказались определяющими для воспроизводимости поведения контейнера, в чём состоит роль PID 1 и почему форма ",[47,2344,49],{}," критична для корректной остановки, как соотносятся принципы неизменяемой инфраструктуры с задачами обновления и отката.",[15,2347,2348,2349,176,2351,2353,2354,2356],{},"Файлы ",[47,2350,720],{},[47,2352,723],{}," приложить к отчёту в неизменном виде. Скриншоты — только при необходимости подтвердить визуальные особенности (например, цвет\u002Fсимвол статуса в ",[47,2355,284],{},").",[25,2358,2360],{"id":2359},"контрольные-вопросы","Контрольные вопросы",[783,2362,2363,2366,2373,2379,2387,2395,2405,2411],{},[441,2364,2365],{},"Чем переменные окружения отличаются от аргументов командной строки как способ передачи конфигурации контейнеру? В каких случаях каждый из них предпочтителен?",[441,2367,2368,2369,2372],{},"Что происходит, если контейнер запущен с ограничением ",[47,2370,2371],{},"--memory=128m",", а приложение пытается выделить 200 МБ? В чём отличие этого поведения от системы без cgroups?",[441,2374,2375,2376,2378],{},"Почему процесс с PID 1 в контейнере играет особую роль? Что произойдёт, если этот процесс не установит обработчик сигнала ",[47,2377,152],{},"?",[441,2380,2381,2382,2384,2385,2378],{},"Сравните exec form и shell form инструкции ",[47,2383,49],{},". Почему shell form может приводить к тому, что приложение завершается только по таймауту ",[47,2386,637],{},[441,2388,2389,2390,176,2392,2394],{},"В чём принципиальное отличие команд ",[47,2391,637],{},[47,2393,647],{}," с точки зрения возможности корректного завершения работы приложения?",[441,2396,2397,2398,564,2400,176,2402,2404],{},"Какие задачи диагностики решаются с помощью ",[47,2399,236],{},[47,2401,252],{},[47,2403,284],{},"? Приведите по одному эксплуатационному сценарию для каждого инструмента.",[441,2406,2407,2408,2410],{},"Почему изменение файлов приложения внутри работающего контейнера через ",[47,2409,268],{}," считается антипаттерном? Что произойдёт с этими изменениями при пересоздании контейнера?",[441,2412,2413],{},"В чём состоит принцип неизменяемой инфраструктуры? Каким образом он реализуется при обновлении контейнерного приложения и при откате к предыдущей версии?",[2415,2416,2417],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":385,"searchDepth":386,"depth":386,"links":2419},[2420,2421,2422,2423,2424,2430,2431],{"id":456,"depth":386,"text":457},{"id":471,"depth":386,"text":472},{"id":507,"depth":386,"text":508},{"id":679,"depth":386,"text":680},{"id":729,"depth":386,"text":730,"children":2425},[2426,2427,2428,2429],{"id":775,"depth":391,"text":776},{"id":1313,"depth":391,"text":1314},{"id":1640,"depth":391,"text":1641},{"id":1958,"depth":391,"text":1959},{"id":2308,"depth":386,"text":2309},{"id":2359,"depth":386,"text":2360},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-lr","lr",{"title":431,"description":385},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-05-lr","aQ7J1QO8PVsqjbCtex0q8WnFSNYA2zHtltrgTKz8ldM",{"id":2439,"title":2440,"body":2441,"course_slug":416,"description":385,"env_label":417,"env_url":417,"extension":418,"group":2471,"is_course_project":419,"is_index":421,"level":2472,"meta":2473,"navigation":421,"path":2476,"section":417,"seo":2477,"stem":2478,"topic_number":417,"topic_slug":417,"__hash__":2479},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Findex.md","Облачные технологии",{"type":8,"value":2442,"toc":2469},[2443,2446],[11,2444,2440],{"id":2445},"облачные-технологии",[438,2447,2448,2456,2463],{},[441,2449,2450,2451],{},"Требования к материалам: ",[2452,2453,2455],"a",{"href":2454},".\u002Fshared\u002FSTYLEGUIDE","shared\u002FSTYLEGUIDE.md",[441,2457,2458,2459],{},"Содержание курса: ",[2452,2460,2462],{"href":2461},".\u002Ftopics","topics.md",[441,2464,2465,2466],{},"Инструкция по сдаче работ: ",[2452,2467,2315],{"href":2468},".\u002Fshared\u002Fsubmission",{"title":385,"searchDepth":386,"depth":386,"links":2470},[],"bachelor","бакалавриат",{"topics_count":864,"has_lr":421,"has_pz":419,"has_course_project":421,"final_assessment":2474,"tech_focus":2475},"зачет","Облачные технологии, контейнеризация, Docker","\u002Fcourses\u002Faidt-bac-cloud_tech",{"title":2440,"description":385},"courses\u002Faidt-bac-cloud_tech\u002Findex","uo9ftIk0ye9G2gRrb5M7uFNZQgYvuCJ8j7yJSmoQuGo",1779455410875]