[{"data":1,"prerenderedAt":5910},["ShallowReactive",2],{"course:aidt-bac-cloud_tech":3,"course:aidt-bac-cloud_tech:topics":58,"course:aidt-bac-cloud_tech:project":2490},{"id":4,"title":5,"body":6,"course_slug":43,"description":40,"env_label":44,"env_url":44,"extension":45,"group":46,"is_course_project":47,"is_index":48,"level":49,"meta":50,"navigation":48,"path":54,"section":44,"seo":55,"stem":56,"topic_number":44,"topic_slug":44,"__hash__":57},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Findex.md","Облачные технологии",{"type":7,"value":8,"toc":39},"minimark",[9,13],[10,11,5],"h1",{"id":12},"облачные-технологии",[14,15,16,25,32],"ul",{},[17,18,19,20],"li",{},"Требования к материалам: ",[21,22,24],"a",{"href":23},".\u002Fshared\u002FSTYLEGUIDE","shared\u002FSTYLEGUIDE.md",[17,26,27,28],{},"Содержание курса: ",[21,29,31],{"href":30},".\u002Ftopics","topics.md",[17,33,34,35],{},"Инструкция по сдаче работ: ",[21,36,38],{"href":37},".\u002Fshared\u002Fsubmission","shared\u002Fsubmission.md",{"title":40,"searchDepth":41,"depth":41,"links":42},"",2,[],"aidt-bac-cloud_tech",null,"md","bachelor",false,true,"бакалавриат",{"topics_count":51,"has_lr":48,"has_pz":47,"has_course_project":48,"final_assessment":52,"tech_focus":53},12,"зачет","Облачные технологии, контейнеризация, Docker","\u002Fcourses\u002Faidt-bac-cloud_tech",{"title":5,"description":40},"courses\u002Faidt-bac-cloud_tech\u002Findex","uo9ftIk0ye9G2gRrb5M7uFNZQgYvuCJ8j7yJSmoQuGo",[59,398,716,954,1496,1894],{"id":60,"title":61,"body":62,"course_slug":43,"description":70,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":390,"navigation":48,"path":391,"section":392,"seo":393,"stem":394,"topic_number":395,"topic_slug":396,"__hash__":397},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-01-content.md","Основы облачных технологий",{"type":7,"value":63,"toc":364},[64,67,71,74,77,82,87,90,93,96,100,103,106,109,112,115,118,130,133,137,140,143,146,149,153,156,160,163,166,169,173,176,179,182,186,189,192,195,199,202,210,213,216,220,223,227,230,233,236,240,243,246,249,253,256,259,262,266,274,277,281,284,288,291,294,302,305,309,312,320,323,326,330,333,336,339,342,345,349,352,355,358,361],[10,65,61],{"id":66},"основы-облачных-технологий",[68,69,70],"p",{},"На протяжении нескольких десятилетий вычислительная инфраструктура организаций строилась преимущественно на локальных ресурсах. Серверы размещались в собственных или арендованных помещениях, закупались с запасом на пиковые нагрузки, требовали физического обслуживания и планирования замены оборудования. Такая модель обеспечивала полный контроль над средой, но порождала целый ряд устойчивых проблем: длительные сроки ввода мощностей в эксплуатацию, низкий средний коэффициент использования оборудования и необходимость содержать штат специалистов для обслуживания физической инфраструктуры.",[68,72,73],{},"Облачные вычисления представляют собой принципиально иной подход к организации доступа к вычислительным ресурсам. Вместо приобретения и сопровождения оборудования потребитель получает ресурсы по запросу через сеть, оплачивая фактическое потребление. Эта модель изменяет не только экономику владения инфраструктурой, но и способ проектирования, развертывания и сопровождения программных систем. Поэтому понимание основ облачной модели является обязательной предпосылкой для дальнейшего изучения виртуализации, контейнеризации и оркестрации, составляющих технологическое ядро данного курса.",[68,75,76],{},"Настоящая тема раскрывает фундаментальные понятия облачных вычислений: сущность облачной модели и её ключевые признаки, модели предоставления сервисов, модели развертывания инфраструктуры, а также экономические и эксплуатационные эффекты, определяющие практический смысл перехода к облаку. Каждый из этих вопросов рассматривается не только в описательном ключе, но и с позиции ограничений, допущений и типичных ошибок интерпретации, которые возникают при первом знакомстве с облачной тематикой.",[78,79,81],"h2",{"id":80},"сущность-облачной-модели-вычислений","Сущность облачной модели вычислений",[83,84,86],"h3",{"id":85},"переход-от-локальной-инфраструктуры-к-модели-предоставления-ресурсов-по-запросу","Переход от локальной инфраструктуры к модели предоставления ресурсов по запросу",[68,88,89],{},"Традиционная модель организации вычислительной инфраструктуры предполагает, что организация самостоятельно приобретает, размещает и обслуживает аппаратные ресурсы. Сервер, закупленный для конкретного проекта, физически устанавливается в серверном помещении или центре обработки данных (ЦОД), подключается к сети, настраивается, а затем эксплуатируется на протяжении нескольких лет до момента замены. Этот подход принято обозначать термином «on-premises» (локальное размещение). Его ключевая особенность состоит в том, что ответственность за все уровни инфраструктуры — от электропитания до прикладного программного обеспечения — лежит на самой организации.",[68,91,92],{},"Облачная модель вычислений меняет этот принцип. Вычислительные, сетевые и дисковые ресурсы предоставляются сторонним поставщиком через программный интерфейс или веб-консоль. Потребитель не владеет оборудованием, не отвечает за его физическое размещение и не планирует его жизненный цикл. Вместо этого он формулирует требования к ресурсам, получает их в необходимом объёме и освобождает по мере завершения потребности. Такая модель делает вычислительные ресурсы аналогичными коммунальным услугам: они доступны по требованию, потребляются в необходимом количестве и оплачиваются по факту использования.",[68,94,95],{},"Важно подчеркнуть, что переход к облаку не сводится к замене физического оборудования виртуальным. Облачная модель меняет саму логику взаимодействия с инфраструктурой. Если при локальном размещении решение о приобретении ресурсов принимается заблаговременно, с учётом прогнозируемой нагрузки, то в облачной среде ресурсы могут быть получены в момент возникновения потребности и возвращены сразу после её удовлетворения. Это различие имеет не только экономические, но и архитектурные следствия: приложения могут проектироваться с расчётом на динамическое изменение объёма доступных ресурсов, а не на фиксированную аппаратную конфигурацию.",[83,97,99],{"id":98},"ключевые-признаки-облачной-среды","Ключевые признаки облачной среды",[68,101,102],{},"Для формального определения облачных вычислений в мировой практике принято опираться на характеристики, сформулированные Национальным институтом стандартов и технологий США (National Institute of Standards and Technology, NIST). Согласно этому определению, облачная среда обладает пятью существенными признаками.",[68,104,105],{},"Первый признак — самообслуживание по требованию (on-demand self-service). Потребитель может самостоятельно выделять вычислительные ресурсы — серверные мощности, хранилища, сетевые компоненты — без необходимости обращения к представителю поставщика. Это означает, что процесс предоставления ресурсов автоматизирован и доступен через программный интерфейс или панель управления.",[68,107,108],{},"Второй признак — широкая сетевая доступность (broad network access). Облачные ресурсы доступны через сеть с использованием стандартных протоколов и механизмов доступа. Это позволяет потребителям работать с инфраструктурой из различных сред: рабочих станций, мобильных устройств, удалённых терминалов.",[68,110,111],{},"Третий признак — объединение ресурсов (resource pooling). Поставщик объединяет физические ресурсы в общий пул и динамически распределяет их между несколькими потребителями. Конкретный потребитель, как правило, не контролирует и не знает точного физического расположения предоставленных ему ресурсов, хотя может задать ограничения на уровне региона или зоны доступности.",[68,113,114],{},"Четвёртый признак — быстрая эластичность (rapid elasticity). Ресурсы могут масштабироваться — увеличиваться или уменьшаться — в соответствии с текущими потребностями потребителя. С точки зрения пользователя доступные ресурсы кажутся практически неограниченными и могут быть получены в любом объёме в любой момент.",[68,116,117],{},"Пятый признак — измеряемость потребления (measured service). Облачная система автоматически контролирует и оптимизирует использование ресурсов, предоставляя механизмы мониторинга, отчётности и прозрачного учёта потребления. Это обеспечивает возможность оплаты строго за использованные ресурсы и позволяет обеим сторонам — поставщику и потребителю — контролировать объём предоставленных услуг.",[119,120,121,122,121,127],"figure",{},"\n  ",[123,124],"img",{"src":125,"alt":126},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-01\u002Fnist_cloud_characteristics.svg","Пять существенных признаков облачной среды по NIST",[128,129,126],"figcaption",{},[68,131,132],{},"Следует оговориться, что перечисленные признаки описывают идеальную модель. На практике конкретные облачные платформы могут реализовывать эти свойства в различной степени. Например, эластичность может быть ограничена квотами, время выделения ресурсов может составлять не секунды, а минуты, а механизмы учёта потребления могут существенно различаться по прозрачности и детализации. Тем не менее именно совокупность этих пяти признаков позволяет отличить облачную среду от традиционного хостинга или обычной аренды виртуальных серверов, где степень автоматизации и эластичности существенно ниже.",[83,134,136],{"id":135},"отличие-облачных-вычислений-от-традиционного-хостинга-и-классической-серверной-инфраструктуры","Отличие облачных вычислений от традиционного хостинга и классической серверной инфраструктуры",[68,138,139],{},"Разграничение облачных вычислений и традиционного хостинга важно не только терминологически, но и практически. Услуги традиционного хостинга — аренда выделенного сервера, виртуального сервера с фиксированной конфигурацией, размещение оборудования в ЦОД — существуют на рынке давно и продолжают использоваться. Однако они, как правило, не обладают свойствами эластичности и автоматического самообслуживания. Арендованный сервер предоставляется в заранее согласованной конфигурации, его масштабирование требует ручного вмешательства или нового договора, а управление осуществляется через ограниченный набор инструментов.",[68,141,142],{},"Облачная модель отличается тем, что инфраструктура программно определяема (software-defined). Это означает, что создание, конфигурирование, масштабирование и уничтожение ресурсов осуществляется через программный интерфейс (API), а не посредством физических операций или переговоров с поставщиком. Программная определяемость делает инфраструктуру пригодной для автоматизации: ресурсы могут создаваться и удаляться скриптами, описываться в конфигурационных файлах и управляться как часть программного проекта.",[68,144,145],{},"Ещё одно существенное отличие касается модели тарификации. Традиционный хостинг чаще всего предполагает фиксированную абонентскую плату за заранее определённый объём ресурсов, независимо от фактической загрузки. Облачная модель в типичном случае предполагает оплату по факту потребления (pay-as-you-go): потребитель платит за количество использованных процессорных часов, объём хранимых данных, объём переданного сетевого трафика. Это различие создаёт как преимущества, так и риски, которые подробнее рассматриваются в разделе об экономике облака.",[68,147,148],{},"Таким образом, облачные вычисления не следует воспринимать как простое продолжение услуг хостинга. Это качественно иная модель, в основе которой лежат программная определяемость, эластичность, автоматизированное управление и учёт потребления. Понимание этих различий необходимо для корректной оценки возможностей и ограничений облачной инфраструктуры.",[78,150,152],{"id":151},"модели-предоставления-сервисов","Модели предоставления сервисов",[68,154,155],{},"Облачная модель вычислений не является однородной. В зависимости от того, какой именно уровень инфраструктуры предоставляется потребителю в виде управляемого сервиса, принято различать несколько моделей обслуживания. Каждая из них определяет границу ответственности между поставщиком облачных услуг и потребителем: чем выше уровень абстракции сервиса, тем меньше задач ложится на потребителя и тем больше контроля он передаёт поставщику.",[83,157,159],{"id":158},"инфраструктура-как-услуга-infrastructure-as-a-service-iaas","Инфраструктура как услуга (Infrastructure as a Service, IaaS)",[68,161,162],{},"Модель IaaS предоставляет потребителю базовые вычислительные ресурсы: виртуальные машины, дисковые хранилища, сетевые компоненты. Потребитель получает доступ к виртуализированной инфраструктуре, на которой он самостоятельно устанавливает операционную систему, настраивает среду выполнения, развёртывает приложения и отвечает за их сопровождение. Поставщик при этом обеспечивает работоспособность физического оборудования, гипервизора, сетевой инфраструктуры и базовых служб платформы.",[68,164,165],{},"Граница ответственности в IaaS проходит на уровне операционной системы. Всё, что находится выше — системное администрирование, установка обновлений, настройка безопасности, развертывание приложений — является задачей потребителя. Это создаёт максимальную гибкость: потребитель может использовать любые операционные системы, любые программные стеки и любые конфигурации. Однако эта же гибкость порождает значительную эксплуатационную нагрузку, поскольку требует квалификации в области системного администрирования, сетевой безопасности и сопровождения инфраструктуры.",[68,167,168],{},"IaaS является наиболее близкой к традиционной серверной модели формой облачного сервиса. Потребитель работает с виртуальными машинами примерно так же, как с физическими серверами, но получает преимущества облачной модели: эластичность, автоматизируемость и оплату по факту потребления. Примерами IaaS-сервисов являются Amazon EC2, Google Compute Engine, Azure Virtual Machines, Yandex Compute Cloud.",[83,170,172],{"id":171},"платформа-как-услуга-platform-as-a-service-paas","Платформа как услуга (Platform as a Service, PaaS)",[68,174,175],{},"Модель PaaS поднимает уровень абстракции выше. Потребитель получает не виртуальную машину, а готовую платформу для развертывания приложений. Поставщик берёт на себя управление операционной системой, средой выполнения, базовыми службами и инфраструктурными компонентами. Потребитель сосредоточен на разработке, развертывании и сопровождении прикладного кода, не занимаясь вопросами конфигурации серверов и системного программного обеспечения.",[68,177,178],{},"Практический смысл PaaS состоит в сокращении эксплуатационной нагрузки. Разработчик загружает код приложения, а платформа обеспечивает его исполнение, масштабирование, балансировку нагрузки и базовый мониторинг. Это позволяет существенно ускорить цикл разработки и развертывания, особенно для типовых веб-приложений и сервисов. Однако у этой модели есть и ограничения. PaaS накладывает требования на архитектуру приложения: поддерживаемые языки программирования, среды выполнения, способы хранения данных и механизмы масштабирования определяются поставщиком платформы. Если приложение не вписывается в предложенные рамки, его адаптация может оказаться трудоёмкой или невозможной.",[68,180,181],{},"Ещё одним следствием модели PaaS является более выраженная зависимость от поставщика (vendor lock-in). Приложение, спроектированное под конкретную платформу, может использовать специфические интерфейсы, механизмы хранения и способы конфигурации, которые не переносятся напрямую на другие платформы. Поэтому выбор PaaS требует осознанной оценки баланса между удобством и степенью привязки. Примерами PaaS-сервисов являются Google App Engine, Heroku, Azure App Service.",[83,183,185],{"id":184},"программное-обеспечение-как-услуга-software-as-a-service-saas","Программное обеспечение как услуга (Software as a Service, SaaS)",[68,187,188],{},"Модель SaaS представляет собой наивысший уровень абстракции в классификации облачных сервисов. Потребитель получает доступ к готовому приложению, работающему в облачной инфраструктуре поставщика. Вся ответственность за инфраструктуру, платформу, среду выполнения и само приложение лежит на поставщике. Потребитель использует приложение через веб-интерфейс или API, не занимаясь ни его установкой, ни обновлением, ни обеспечением доступности.",[68,190,191],{},"SaaS является наиболее распространённой формой облачного потребления для конечных пользователей. Примеры SaaS-решений хорошо знакомы: почтовые сервисы, системы управления проектами, офисные пакеты, платформы для совместной работы. С точки зрения курса облачных технологий модель SaaS интересна прежде всего как граничный случай, демонстрирующий предельную степень передачи контроля поставщику. Потребитель SaaS не имеет доступа ни к инфраструктуре, ни к платформе, ни к исходному коду приложения. Его возможности ограничены конфигурацией, предусмотренной поставщиком.",[68,193,194],{},"Ограничения SaaS проявляются в невозможности глубокой настройки, в зависимости от политик поставщика в отношении хранения данных, доступности и функциональных обновлений, а также в риске потери доступа к данным при прекращении оказания услуги. Для инженерных задач, связанных с проектированием и эксплуатацией инфраструктуры, SaaS является объектом изучения, но не основным инструментом работы.",[83,196,198],{"id":197},"сравнение-моделей","Сравнение моделей",[68,200,201],{},"Три рассмотренные модели образуют спектр, на одном конце которого находится максимальный контроль при высокой эксплуатационной нагрузке (IaaS), а на другом — минимальный контроль при минимальной нагрузке (SaaS). PaaS занимает промежуточную позицию.",[119,203,121,204,121,208],{},[123,205],{"src":206,"alt":207},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-01\u002Fiaas_paas_saas_responsibility.svg","Распределение ответственности между потребителем и поставщиком в моделях On-Premises, IaaS, PaaS и SaaS",[128,209,207],{},[68,211,212],{},"Выбор модели определяется не абстрактными предпочтениями, а конкретными требованиями. Если организации необходимо полное управление операционной системой и сетевой конфигурацией, IaaS является единственным подходящим вариантом. Если задача сводится к быстрому развертыванию типового приложения, PaaS может оказаться более рациональным выбором. Если потребность состоит в использовании готового продукта без необходимости его сопровождения, SaaS удовлетворяет её наиболее полно.",[68,214,215],{},"Важно понимать, что границы между моделями не являются абсолютно жёсткими. Современные облачные платформы предлагают сервисы, которые сочетают признаки нескольких моделей. Например, управляемые базы данных предоставляют инфраструктурный ресурс (хранилище, вычисления), но освобождают потребителя от задач системного администрирования, что сближает их с PaaS. Управляемые сервисы оркестрации контейнеров (такие как Amazon EKS или Google GKE) располагаются на границе IaaS и PaaS. Поэтому классификация моделей полезна как концептуальная рамка, но не должна восприниматься как строгая таксономия с непроницаемыми границами.",[78,217,219],{"id":218},"модели-развертывания-облачной-инфраструктуры","Модели развертывания облачной инфраструктуры",[68,221,222],{},"Помимо модели предоставления сервисов, облачная инфраструктура различается по способу развертывания. Эта классификация отвечает на вопрос о том, кому принадлежит и кем эксплуатируется облачная среда, а также кто имеет к ней доступ. Выбор модели развертывания определяется требованиями к безопасности, нормативному соответствию, стоимости и масштабируемости.",[83,224,226],{"id":225},"публичное-облако-public-cloud","Публичное облако (public cloud)",[68,228,229],{},"Публичное облако представляет собой инфраструктуру, которая принадлежит и управляется сторонним поставщиком облачных услуг и предоставляется широкому кругу потребителей. Физические ресурсы являются общими для множества арендаторов (multi-tenancy), хотя каждый потребитель работает в логически изолированной среде. Публичное облако обеспечивает наибольшую эластичность и наименьшие начальные затраты, поскольку потребителю не требуется приобретать оборудование и содержать инфраструктуру.",[68,231,232],{},"К основным преимуществам публичного облака относятся практически неограниченная масштабируемость, глобальная географическая доступность, широкий спектр предоставляемых сервисов и модель оплаты по факту потребления. Крупнейшими поставщиками публичных облачных услуг являются Amazon Web Services (AWS), Microsoft Azure, Google Cloud Platform (GCP), а также региональные провайдеры, такие как Yandex Cloud.",[68,234,235],{},"Вместе с тем публичное облако имеет ограничения. Потребитель не контролирует физическое расположение данных с точностью до конкретного сервера, не может влиять на политику обновления платформы и вынужден полагаться на гарантии поставщика в отношении доступности и безопасности. Для отдельных отраслей — финансового сектора, здравоохранения, государственного управления — нормативные требования могут ограничивать или полностью исключать размещение определённых категорий данных в публичном облаке.",[83,237,239],{"id":238},"частное-облако-private-cloud","Частное облако (private cloud)",[68,241,242],{},"Частное облако представляет собой облачную инфраструктуру, выделенную для использования одной организацией. Оно может размещаться как на собственных площадках организации, так и на площадках стороннего поставщика, но в обоих случаях ресурсы не разделяются с другими потребителями. Частное облако позволяет реализовать те же принципы — самообслуживание, эластичность, измеряемость, — но в контролируемой среде с более жёстким управлением доступом и политиками безопасности.",[68,244,245],{},"Мотивация к построению частного облака обычно связана с требованиями безопасности и нормативного соответствия. Организация сохраняет полный контроль над физическим расположением данных, сетевой инфраструктурой и политиками доступа. Это особенно важно в тех случаях, когда обрабатываемые данные подпадают под регуляторные ограничения, требующие их локализации или исключения доступа третьих лиц.",[68,247,248],{},"Однако частное облако имеет существенные ограничения. Построение и сопровождение такой инфраструктуры требует значительных капитальных вложений, квалифицированного персонала и непрерывных затрат на эксплуатацию. Масштабируемость частного облака ограничена объёмом имеющихся физических ресурсов: при исчерпании ёмкости необходимо закупать новое оборудование, что занимает время и требует дополнительных инвестиций. Поэтому частное облако, как правило, экономически оправдано для крупных организаций с устойчивыми и предсказуемыми нагрузками.",[83,250,252],{"id":251},"гибридное-облако-hybrid-cloud","Гибридное облако (hybrid cloud)",[68,254,255],{},"Гибридное облако объединяет элементы публичного и частного облака, обеспечивая возможность переноса нагрузок между ними. Типичный сценарий состоит в том, что базовая нагрузка обрабатывается в частном облаке, а пиковые запросы перенаправляются в публичное. Другой распространённый вариант предполагает размещение критически важных данных в частном облаке с использованием публичного облака для менее чувствительных задач.",[68,257,258],{},"Привлекательность гибридной модели состоит в возможности сочетать контроль частного облака с эластичностью публичного. Однако на практике реализация гибридного облака порождает значительные инженерные сложности. Необходимо обеспечить надёжную сетевую связность между средами, единообразные политики безопасности и управления доступом, совместимость форматов данных и механизмов развертывания. Переносимость нагрузок между частным и публичным сегментами требует продуманной архитектуры приложений и инфраструктуры, а не является свойством, возникающим автоматически.",[68,260,261],{},"Типичная ошибка при обсуждении гибридного облака состоит в предположении, что простое одновременное использование частной и публичной инфраструктуры уже образует гибридное облако. На самом деле гибридная модель предполагает интеграцию сред: оркестрацию нагрузок, единый контур управления, согласованные политики. Без этих элементов речь идёт о параллельном использовании двух разрозненных инфраструктур, а не о гибридном облаке в строгом смысле.",[83,263,265],{"id":264},"выбор-модели-развертывания","Выбор модели развертывания",[119,267,121,268,121,272],{},[123,269],{"src":270,"alt":271},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-01\u002Fcloud_deployment_models.svg","Модели развертывания облачной инфраструктуры: публичное, частное и гибридное облако",[128,273,271],{},[68,275,276],{},"Выбор между публичным, частным и гибридным облаком определяется совокупностью факторов: требованиями к безопасности и нормативному соответствию, характером нагрузок, бюджетными ограничениями, наличием квалифицированных кадров и стратегическими приоритетами организации. Универсального решения не существует. Публичное облако оптимально для стартапов, исследовательских проектов и нагрузок с высокой вариативностью. Частное облако подходит для организаций с жёсткими регуляторными требованиями и стабильной нагрузкой. Гибридное облако позволяет совместить достоинства обоих подходов, но требует зрелой инженерной практики и дополнительных затрат на интеграцию.",[78,278,280],{"id":279},"экономика-и-эксплуатационные-эффекты-облака","Экономика и эксплуатационные эффекты облака",[68,282,283],{},"Переход к облачной модели затрагивает не только техническую архитектуру, но и экономику владения инфраструктурой. Понимание экономических эффектов облака важно не менее, чем знание его технических характеристик, поскольку именно экономические соображения часто определяют решение о миграции в облако или отказе от неё.",[83,285,287],{"id":286},"капитальные-и-операционные-затраты","Капитальные и операционные затраты",[68,289,290],{},"В традиционной модели организация несёт капитальные затраты (Capital Expenditure, CapEx) на приобретение оборудования. Сервер приобретается единовременно, ставится на баланс и амортизируется в течение нескольких лет. Помимо стоимости самого оборудования, капитальные затраты включают расходы на проектирование серверного помещения, системы охлаждения, электроснабжения и физической безопасности. Эти расходы возникают до момента начала эксплуатации и не зависят от фактического уровня использования ресурсов.",[68,292,293],{},"Облачная модель трансформирует капитальные затраты в операционные (Operational Expenditure, OpEx). Потребитель не приобретает оборудование, а оплачивает использование ресурсов на регулярной основе. Это изменение имеет два важных следствия. Во-первых, снижается барьер входа: для запуска проекта не требуется крупных первоначальных инвестиций. Во-вторых, затраты становятся более пропорциональными реальному потреблению, что в теории позволяет избежать оплаты простаивающих ресурсов.",[119,295,121,296,121,300],{},[123,297],{"src":298,"alt":299},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-01\u002Fcapex_vs_opex.svg","Сравнение структуры затрат: традиционная модель (CapEx) и облачная модель (OpEx)",[128,301,299],{},[68,303,304],{},"Однако замена CapEx на OpEx не является безусловным преимуществом. При устойчивых и предсказуемых нагрузках облачное размещение может оказаться дороже, чем эксплуатация собственного оборудования. Это связано с тем, что в стоимость облачных ресурсов включена маржа поставщика, затраты на обеспечение избыточности и расходы на программную инфраструктуру платформы. Поэтому экономическая оценка облачной модели должна учитывать не только прямые затраты на ресурсы, но и полную стоимость владения (Total Cost of Ownership, TCO), включающую расходы на персонал, лицензии, сетевой трафик, хранение данных и обеспечение безопасности.",[83,306,308],{"id":307},"эластичное-выделение-ресурсов-как-инструмент-оптимизации-затрат","Эластичное выделение ресурсов как инструмент оптимизации затрат",[68,310,311],{},"Одним из центральных экономических преимуществ облака является эластичность — возможность динамически изменять объём потребляемых ресурсов в зависимости от текущей нагрузки. В традиционной модели оборудование закупается с расчётом на пиковые нагрузки. Если пиковая нагрузка составляет условные 100 единиц, а средняя — 30, то 70% ёмкости большую часть времени простаивает. Организация платит за полный объём оборудования вне зависимости от того, сколько из него используется.",[119,313,121,314,121,318],{},[123,315],{"src":316,"alt":317},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-01\u002Felasticity_resource_utilization.svg","Сравнение использования ресурсов: фиксированная ёмкость традиционной модели и эластичное выделение в облаке",[128,319,317],{},[68,321,322],{},"Эластичность облака позволяет приблизить фактическое потребление ресурсов к текущей потребности. В идеальном сценарии при нагрузке в 30 единиц потребитель оплачивает только 30 единиц, а при кратковременном росте до 100 единиц ресурсы масштабируются автоматически и возвращаются к прежнему уровню после снижения нагрузки. Это снижает избыточное резервирование и позволяет более рационально использовать бюджет.",[68,324,325],{},"На практике реализация эластичности требует инженерной подготовки. Приложение должно быть спроектировано таким образом, чтобы поддерживать горизонтальное масштабирование — добавление новых экземпляров обработки. Если архитектура приложения предполагает единственный сервер с фиксированным состоянием, эластичность облака не может быть использована в полной мере. Следовательно, эластичность является не только инфраструктурным свойством, но и архитектурным требованием к приложению.",[83,327,329],{"id":328},"риски-облачной-модели","Риски облачной модели",[68,331,332],{},"При всех преимуществах облачная модель несёт ряд рисков, которые должны учитываться при принятии решения о миграции.",[68,334,335],{},"Первый риск — неконтролируемый рост потребления. Простота выделения ресурсов в облаке может приводить к тому, что отдельные команды или проекты потребляют значительно больше, чем планировалось. Если в организации отсутствуют механизмы мониторинга и контроля расходов, общий счёт за облачные услуги может оказаться существенно выше ожидаемого. Эта проблема обозначается термином «cloud sprawl» (неконтролируемое разрастание облачных ресурсов).",[68,337,338],{},"Второй риск — зависимость от поставщика (vendor lock-in). Чем глубже организация интегрируется с конкретной облачной платформой, тем труднее перенести нагрузки на другую платформу или вернуть их в собственную инфраструктуру. Зависимость может проявляться на уровне специфических API, форматов данных, механизмов управления и ценовых моделей. Снижение зависимости возможно за счёт использования открытых стандартов, контейнеризации приложений и абстрагирования от платформенно-специфических сервисов, однако полное её устранение на практике достигается редко.",[68,340,341],{},"Третий риск — сложность прогнозирования полной стоимости владения. Облачные тарифы формируются из множества компонентов: стоимость вычислений, хранения, сетевого трафика, операций ввода-вывода, использования дополнительных сервисов. Прозрачное прогнозирование итоговой стоимости требует понимания модели тарификации каждого используемого компонента. Неверный прогноз может привести к тому, что реальные затраты значительно превысят бюджет, а обратная миграция окажется сложной и дорогостоящей.",[68,343,344],{},"Перечисленные риски не обесценивают преимуществ облачной модели, но требуют осознанного подхода. Решение о переходе в облако должно основываться на анализе конкретных условий, а не на предположении о безусловной экономической выгоде.",[78,346,348],{"id":347},"итоги-темы","Итоги темы",[68,350,351],{},"Облачные вычисления представляют собой модель предоставления вычислительных ресурсов по запросу, основанную на принципах самообслуживания, эластичности, широкой сетевой доступности, объединения ресурсов и измеряемости потребления. Эта модель принципиально отличается от традиционного хостинга и локальной серверной инфраструктуры прежде всего программной определяемостью ресурсов и динамическим характером их выделения.",[68,353,354],{},"Модели предоставления сервисов — IaaS, PaaS и SaaS — образуют спектр от максимального контроля при высокой эксплуатационной нагрузке до минимального контроля при минимальной нагрузке. Выбор модели определяется конкретными требованиями к гибкости, управляемости и степени делегирования ответственности поставщику. Границы между моделями подвижны, а современные облачные платформы предлагают сервисы, сочетающие признаки нескольких уровней.",[68,356,357],{},"Модели развертывания — публичное, частное и гибридное облако — различаются по критериям принадлежности инфраструктуры, уровня контроля и нормативного соответствия. Каждая из них имеет свою область применимости, и выбор должен основываться на анализе требований к безопасности, стоимости, масштабируемости и зрелости инженерных практик организации.",[68,359,360],{},"Экономические эффекты облака связаны с трансформацией капитальных затрат в операционные, с возможностью эластичного выделения ресурсов и со снижением избыточного резервирования. Однако эти преимущества сопровождаются рисками неконтролируемого роста потребления, зависимости от поставщика и сложности прогнозирования полной стоимости владения. Осознанный переход к облачной модели требует не только технической компетенции, но и понимания экономических и организационных последствий принимаемых решений.",[68,362,363],{},"Рассмотренные в данной теме основы создают концептуальную базу для дальнейшего изучения технологий, составляющих ядро облачной инфраструктуры. Следующая тема посвящена виртуализации — фундаментальному механизму, обеспечивающему абстрагирование физических ресурсов и формирование управляемых вычислительных сред, на которых строится большинство облачных платформ.",{"title":40,"searchDepth":41,"depth":41,"links":365},[366,372,378,384,389],{"id":80,"depth":41,"text":81,"children":367},[368,370,371],{"id":85,"depth":369,"text":86},3,{"id":98,"depth":369,"text":99},{"id":135,"depth":369,"text":136},{"id":151,"depth":41,"text":152,"children":373},[374,375,376,377],{"id":158,"depth":369,"text":159},{"id":171,"depth":369,"text":172},{"id":184,"depth":369,"text":185},{"id":197,"depth":369,"text":198},{"id":218,"depth":41,"text":219,"children":379},[380,381,382,383],{"id":225,"depth":369,"text":226},{"id":238,"depth":369,"text":239},{"id":251,"depth":369,"text":252},{"id":264,"depth":369,"text":265},{"id":279,"depth":41,"text":280,"children":385},[386,387,388],{"id":286,"depth":369,"text":287},{"id":307,"depth":369,"text":308},{"id":328,"depth":369,"text":329},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-01-content","content",{"title":61,"description":70},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-01-content",1,"topic-01","_-ng4y7ZULsC5fiHJ07rpBeT2Ty1kfMY5U87W42ocg4",{"id":399,"title":400,"body":401,"course_slug":43,"description":408,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":710,"navigation":48,"path":711,"section":392,"seo":712,"stem":713,"topic_number":41,"topic_slug":714,"__hash__":715},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-02-content.md","Виртуализация как основа облачной инфраструктуры",{"type":7,"value":402,"toc":685},[403,406,409,412,415,419,423,426,429,432,440,444,447,450,458,461,465,468,471,474,478,482,485,488,492,495,498,501,505,508,511,514,522,526,529,532,535,538,542,545,548,552,556,559,562,565,568,571,579,583,586,589,592,595,599,602,605,608,611,614,618,621,625,628,631,634,637,640,643,646,650,653,657,660,663,671,673,676,679,682],[10,404,400],{"id":405},"виртуализация-как-основа-облачной-инфраструктуры",[68,407,408],{},"Облачные вычисления, рассмотренные в предыдущей теме, опираются на способность предоставлять вычислительные ресурсы по запросу, динамически распределять их между потребителями и обеспечивать логическую изоляцию нагрузок. Все эти свойства предполагают наличие промежуточного программного уровня, который отделяет прикладные задачи от физического оборудования. Таким уровнем является виртуализация.",[68,410,411],{},"Виртуализация не является изобретением облачной эпохи. Первые практические реализации виртуальных машин появились ещё в 1960-х годах в контексте мэйнфреймов IBM, где требовалось одновременно исполнять несколько независимых операционных сред на одном дорогостоящем вычислительном узле. Однако именно в сочетании с облачной моделью виртуализация приобрела массовое промышленное значение: она стала технологической основой, на которой строятся центры обработки данных, платформы IaaS и большинство управляемых облачных сервисов.",[68,413,414],{},"Настоящая тема раскрывает назначение виртуализации, типы гипервизоров и подходов к виртуализации, ресурсную модель виртуальной машины, а также механизмы изоляции и управления ресурсами, реализуемые на уровне ядра операционной системы. Каждый из этих вопросов важен не только теоретически, но и практически: без понимания механизмов виртуализации невозможно корректно оценить ни возможности облачных платформ, ни ограничения различных подходов к изоляции вычислительных сред.",[78,416,418],{"id":417},"назначение-виртуализации","Назначение виртуализации",[83,420,422],{"id":421},"абстрагирование-физических-ресурсов-и-формирование-управляемых-вычислительных-сред","Абстрагирование физических ресурсов и формирование управляемых вычислительных сред",[68,424,425],{},"Основная идея виртуализации состоит в создании программной абстракции над физическим оборудованием. Вместо того чтобы предоставлять приложению прямой доступ к процессору, оперативной памяти, дисковому хранилищу и сетевому интерфейсу конкретного сервера, виртуализация формирует промежуточный уровень, который представляет эти ресурсы в виде логических сущностей. Приложение и операционная система, работающие внутри виртуальной машины, взаимодействуют с виртуальным оборудованием так, как если бы оно было физическим, но в действительности все обращения транслируются программным слоем виртуализации.",[68,427,428],{},"Такое абстрагирование решает несколько фундаментальных задач. Во-первых, оно позволяет разместить на одном физическом сервере несколько независимых вычислительных сред, каждая из которых содержит собственную операционную систему, собственный набор приложений и собственную конфигурацию. Во-вторых, оно обеспечивает логическую изоляцию между этими средами: сбой или компрометация одной виртуальной машины не затрагивает другие, размещённые на том же оборудовании. В-третьих, абстрагирование делает вычислительные ресурсы управляемыми программно: виртуальную машину можно создать, остановить, клонировать, перенести на другой физический узел или уничтожить через программный интерфейс, без физического доступа к оборудованию.",[68,430,431],{},"Именно программная управляемость превращает виртуализацию из механизма разделения ресурсов в инструмент построения масштабируемой инфраструктуры. Без неё облачная модель — с её требованиями к самообслуживанию, эластичности и измеряемости — была бы практически нереализуема. Каждый раз, когда потребитель облачного сервиса создаёт виртуальный сервер через веб-консоль или API, он фактически инициирует процесс создания виртуальной машины на одном из физических узлов центра обработки данных.",[119,433,121,434,121,438],{},[123,435],{"src":436,"alt":437},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-02\u002Fvirtualization_abstraction_layers.svg","Архитектурные уровни виртуализации: гипервизор как промежуточный слой между физическим оборудованием и виртуальными машинами",[128,439,437],{},[83,441,443],{"id":442},"консолидация-нагрузок-и-повышение-коэффициента-использования-оборудования","Консолидация нагрузок и повышение коэффициента использования оборудования",[68,445,446],{},"Одним из наиболее осязаемых практических эффектов виртуализации является консолидация нагрузок (workload consolidation). В традиционной модели каждому приложению или сервису нередко выделяется отдельный физический сервер. Такой подход мотивирован соображениями изоляции и совместимости: различные приложения могут требовать разных версий операционной системы, конфликтующих библиотек или несовместимых настроек среды. Однако следствием этого является крайне низкий коэффициент использования оборудования. Исследования показывают, что средняя загрузка физических серверов в традиционных центрах обработки данных составляет от 5 до 15 процентов вычислительной мощности. Остальные ресурсы простаивают, но продолжают потреблять электроэнергию и занимать место.",[68,448,449],{},"Виртуализация позволяет разместить на одном физическом сервере десятки виртуальных машин, каждая из которых функционирует как изолированная вычислительная среда. Это повышает коэффициент использования оборудования до 60–80 процентов и более, что даёт значительный экономический эффект: сокращается количество физических серверов, снижаются затраты на электроэнергию, охлаждение, размещение и обслуживание.",[119,451,121,452,121,456],{},[123,453],{"src":454,"alt":455},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-02\u002Fworkload_consolidation.svg","Консолидация нагрузок: сравнение использования оборудования без виртуализации и с виртуализацией",[128,457,455],{},[68,459,460],{},"Вместе с тем консолидация нагрузок не является безусловно позитивным явлением. Размещение слишком большого числа виртуальных машин на одном физическом узле приводит к конкуренции за ресурсы: процессорное время, пропускную способность памяти и дисковую подсистему. Если совокупная потребность виртуальных машин систематически превышает возможности оборудования, возникает деградация производительности, которая может затронуть все размещённые нагрузки. Поэтому планирование ёмкости (capacity planning) является обязательной дисциплиной при эксплуатации виртуализированной инфраструктуры.",[83,462,464],{"id":463},"роль-виртуализации-в-построении-центров-обработки-данных-и-облачных-платформ","Роль виртуализации в построении центров обработки данных и облачных платформ",[68,466,467],{},"Современный центр обработки данных (ЦОД), обслуживающий облачную платформу, представляет собой не просто совокупность серверов, а управляемую среду, в которой физические ресурсы объединены в единый пул и предоставляются потребителям через программные интерфейсы. Виртуализация является ключевым компонентом этой архитектуры. Она обеспечивает абстрагирование оборудования, изоляцию потребителей друг от друга (multi-tenancy), управление жизненным циклом виртуальных сред и динамическое перераспределение ресурсов.",[68,469,470],{},"На уровне облачной платформы виртуализация реализует несколько критически важных функций. Миграция виртуальных машин между физическими узлами (live migration) позволяет проводить обслуживание оборудования без остановки пользовательских нагрузок. Моментальные снимки состояния (snapshots) обеспечивают возможность отката к предыдущему состоянию в случае сбоя. Шаблонизация ускоряет массовое развертывание однотипных сред. Автоматизированное управление ресурсами позволяет платформе перераспределять нагрузки в зависимости от текущего потребления.",[68,472,473],{},"Следует, однако, учитывать, что виртуализация не является единственной технологией, используемой в облачных платформах. Современные платформы дополняют её программно-определяемыми сетями (Software-Defined Networking, SDN), программно-определяемым хранилищем (Software-Defined Storage, SDS) и системами оркестрации, которые координируют работу всех компонентов. Тем не менее именно виртуализация вычислительных ресурсов остаётся фундаментальным слоем, без которого остальные надстройки не имеют основания.",[78,475,477],{"id":476},"гипервизоры-и-типы-виртуализации","Гипервизоры и типы виртуализации",[83,479,481],{"id":480},"понятие-гипервизора","Понятие гипервизора",[68,483,484],{},"Гипервизор (hypervisor), или монитор виртуальных машин (Virtual Machine Monitor, VMM), представляет собой программный компонент, обеспечивающий создание и управление виртуальными машинами. Гипервизор располагается между физическим оборудованием и гостевыми операционными системами, выполняя функции распределения ресурсов, изоляции и трансляции обращений гостевых систем к аппаратуре. Именно гипервизор определяет, какие ресурсы доступны каждой виртуальной машине, каким образом обращения к оборудованию транслируются и как обеспечивается взаимная независимость виртуальных сред.",[68,486,487],{},"С точки зрения архитектуры гипервизоры принято разделять на два типа, различающихся способом взаимодействия с аппаратным обеспечением.",[83,489,491],{"id":490},"гипервизор-первого-типа-type-1-hypervisor","Гипервизор первого типа (type 1 hypervisor)",[68,493,494],{},"Гипервизор первого типа, называемый также «bare-metal hypervisor», устанавливается непосредственно на физическое оборудование, без промежуточной операционной системы общего назначения. Он берёт на себя функции управления аппаратными ресурсами и предоставляет их гостевым операционным системам. Фактически гипервизор первого типа сам является минимальной операционной средой, специализированной на задачах виртуализации.",[68,496,497],{},"Примерами гипервизоров первого типа являются VMware ESXi, Microsoft Hyper-V (в режиме серверной роли), Xen и KVM. Последний заслуживает отдельного пояснения. KVM (Kernel-based Virtual Machine) встроен в ядро Linux и превращает операционную систему Linux в гипервизор первого типа. Хотя Linux при этом продолжает функционировать как операционная система общего назначения, с точки зрения архитектуры виртуализации KVM классифицируется как гипервизор первого типа, поскольку модуль виртуализации работает на уровне ядра и взаимодействует с аппаратными средствами виртуализации процессора напрямую.",[68,499,500],{},"Гипервизоры первого типа применяются в центрах обработки данных и облачных платформах, где требуется высокая производительность, надёжная изоляция и возможность управления большим количеством виртуальных машин. Отсутствие промежуточной операционной системы общего назначения снижает накладные расходы и уменьшает поверхность атаки.",[83,502,504],{"id":503},"гипервизор-второго-типа-type-2-hypervisor","Гипервизор второго типа (type 2 hypervisor)",[68,506,507],{},"Гипервизор второго типа устанавливается как обычное приложение поверх существующей операционной системы. Хостовая операционная система управляет аппаратными ресурсами, а гипервизор работает в её пользовательском пространстве, создавая виртуальные машины как процессы хостовой среды. Обращения гостевых систем к аппаратуре проходят через два программных уровня: сначала через гипервизор, затем через хостовую операционную систему.",[68,509,510],{},"Примерами гипервизоров второго типа являются Oracle VirtualBox и VMware Workstation. Такие решения широко используются для разработки, тестирования и обучения, поскольку не требуют выделенного оборудования и позволяют запускать виртуальные машины на обычном рабочем компьютере наряду с другими приложениями.",[68,512,513],{},"Основным недостатком гипервизоров второго типа являются более высокие накладные расходы. Дополнительный программный уровень в виде хостовой операционной системы вносит задержки при обработке обращений к аппаратуре. Кроме того, гостевые операционные системы конкурируют за ресурсы не только друг с другом, но и с приложениями хостовой системы. Поэтому для задач промышленной эксплуатации гипервизоры второго типа, как правило, не используются.",[119,515,121,516,121,520],{},[123,517],{"src":518,"alt":519},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-02\u002Fhypervisor_type1_vs_type2.svg","Архитектура гипервизоров первого и второго типа: сравнение программных стеков",[128,521,519],{},[83,523,525],{"id":524},"полная-виртуализация-паравиртуализация-и-аппаратная-поддержка","Полная виртуализация, паравиртуализация и аппаратная поддержка",[68,527,528],{},"Помимо архитектурной классификации гипервизоров по типу размещения, существует классификация по способу обработки привилегированных инструкций гостевых операционных систем. Этот вопрос связан с тем, что операционная система, исполняющаяся внутри виртуальной машины, полагает себя единственным владельцем аппаратных ресурсов и пытается выполнять привилегированные операции — управление таблицами страниц памяти, обращения к устройствам ввода-вывода, управление прерываниями, — которые в действительности должны быть перехвачены гипервизором.",[68,530,531],{},"При полной виртуализации (full virtualization) гостевая операционная система не модифицируется. Она исполняется в неизменённом виде, а гипервизор перехватывает и эмулирует привилегированные инструкции. Это обеспечивает максимальную совместимость: внутри виртуальной машины можно запустить практически любую операционную систему без её адаптации. Однако эмуляция привилегированных операций создаёт вычислительные накладные расходы, которые могут быть заметны при интенсивных операциях ввода-вывода или при работе с памятью.",[68,533,534],{},"Паравиртуализация (paravirtualization) предполагает модификацию гостевой операционной системы. Вместо выполнения привилегированных инструкций напрямую гостевая система использует специальные программные интерфейсы (hypercalls) для обращения к гипервизору. Это позволяет избежать затратного перехвата и эмуляции, повышая производительность. Однако паравиртуализация требует изменений в ядре гостевой операционной системы, что ограничивает набор совместимых систем и усложняет сопровождение. Наиболее известной реализацией паравиртуализации является гипервизор Xen в его раннем варианте.",[68,536,537],{},"Аппаратная поддержка виртуализации (hardware-assisted virtualization) появилась в процессорах Intel (технология VT-x) и AMD (технология AMD-V) в середине 2000-х годов. Она вводит специальный режим работы процессора, в котором гостевые операционные системы могут безопасно исполнять привилегированные инструкции, а процессор аппаратно обеспечивает перехват тех операций, которые должны обрабатываться гипервизором. Аппаратная поддержка существенно сократила накладные расходы полной виртуализации, сделав её производительность сопоставимой с паравиртуализацией. В результате аппаратная виртуализация стала доминирующим подходом в современных системах. KVM, VMware ESXi и Microsoft Hyper-V опираются на аппаратную поддержку процессора как на обязательное условие работы.",[83,539,541],{"id":540},"влияние-выбранного-подхода-на-практику","Влияние выбранного подхода на практику",[68,543,544],{},"Выбор типа гипервизора и подхода к виртуализации определяется конкретными требованиями. Для промышленной облачной инфраструктуры стандартом де-факто являются гипервизоры первого типа с аппаратной поддержкой виртуализации, обеспечивающие оптимальное соотношение производительности, совместимости и изоляции. Для учебных и исследовательских задач гипервизоры второго типа остаются удобным и доступным инструментом.",[68,546,547],{},"Важно осознавать, что производительность виртуальной машины всегда несколько ниже, чем производительность приложения, исполняющегося непосредственно на физическом оборудовании. Даже при наличии аппаратной поддержки виртуализации существуют накладные расходы на трансляцию обращений к памяти, эмуляцию устройств ввода-вывода и управление переключениями контекста между гостевыми системами. В большинстве прикладных сценариев эти расходы составляют единицы процентов и несущественны, однако для задач с высокими требованиями к производительности — высоконагруженные базы данных, системы реального времени, ресурсоёмкие вычисления — они могут стать значимым фактором.",[78,549,551],{"id":550},"ресурсная-модель-виртуальной-машины","Ресурсная модель виртуальной машины",[83,553,555],{"id":554},"виртуальные-процессоры-оперативная-память-дисковые-образы-и-виртуальные-сетевые-интерфейсы","Виртуальные процессоры, оперативная память, дисковые образы и виртуальные сетевые интерфейсы",[68,557,558],{},"Виртуальная машина представляется гостевой операционной системе как полноценный компьютер с определённым набором аппаратных ресурсов. Эти ресурсы являются виртуальными, то есть предоставляются гипервизором на основе физических ресурсов хоста, но с точки зрения гостевой системы они неотличимы от реального оборудования.",[68,560,561],{},"Виртуальные процессоры (virtual CPU, vCPU) соответствуют логическим ядрам физического процессора, выделяемым виртуальной машине. Количество виртуальных процессоров определяет, сколько потоков вычислений гостевая система может выполнять параллельно. Гипервизор планирует исполнение виртуальных процессоров на физических ядрах, обеспечивая разделение процессорного времени между виртуальными машинами. При этом суммарное количество виртуальных процессоров всех виртуальных машин на одном хосте может превышать количество физических ядер — это называется переподпиской (overcommitment) процессорных ресурсов. Умеренная переподписка допустима, если нагрузки не являются одновременно интенсивными, но при чрезмерной переподписке неизбежна деградация производительности.",[68,563,564],{},"Оперативная память виртуальной машины выделяется из общего объёма физической памяти хоста. Гипервизор обеспечивает изоляцию адресного пространства: каждая гостевая система видит собственный непрерывный диапазон адресов, хотя в действительности эти адреса могут быть размещены в различных физических областях. Некоторые гипервизоры поддерживают механизмы оптимизации памяти, такие как дедупликация одинаковых страниц (memory deduplication) и динамическое перераспределение памяти (memory ballooning), однако эти технологии имеют свои ограничения и не всегда применимы.",[68,566,567],{},"Дисковые ресурсы виртуальной машины представлены в виде образов дисков (disk images) — файлов или блочных устройств, которые гипервизор предоставляет гостевой системе как виртуальные жёсткие диски. Распространёнными форматами образов являются VMDK (VMware), VHD и VHDX (Microsoft), QCOW2 (QEMU\u002FKVM) и RAW. Выбор формата влияет на производительность, возможность создания снимков состояния и эффективность использования дискового пространства. Формат RAW обеспечивает наименьшие накладные расходы, но занимает полный объём выделенного пространства. Формат QCOW2 поддерживает динамическое выделение (thin provisioning), при котором файл образа увеличивается по мере фактической записи данных, а также позволяет создавать снимки состояния.",[68,569,570],{},"Виртуальные сетевые интерфейсы обеспечивают подключение виртуальной машины к сети. Гипервизор эмулирует сетевой адаптер, который гостевая система использует для передачи и приёма данных. Виртуальные интерфейсы могут быть подключены к различным типам виртуальных сетей: мостовым (bridged), обеспечивающим доступ к физической сети хоста, внутренним (internal), связывающим только виртуальные машины на одном хосте, или изолированным (host-only), ограничивающим связность виртуальной машиной и хостом. Для повышения производительности сетевого ввода-вывода используются паравиртуализированные сетевые драйверы (например, virtio в среде KVM), которые снижают накладные расходы на эмуляцию полноценного аппаратного адаптера.",[119,572,121,573,121,577],{},[123,574],{"src":575,"alt":576},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-02\u002Fvm_resource_model.svg","Ресурсная модель виртуальной машины: виртуальные компоненты и их отображение на физическое оборудование через гипервизор",[128,578,576],{},[83,580,582],{"id":581},"шаблоны-снимки-состояния-и-клонирование","Шаблоны, снимки состояния и клонирование",[68,584,585],{},"Помимо основных вычислительных ресурсов, виртуализация предоставляет ряд инструментов, существенно упрощающих управление вычислительными средами.",[68,587,588],{},"Шаблон (template) виртуальной машины представляет собой предварительно настроенный образ, содержащий операционную систему, базовое программное обеспечение и конфигурацию. Шаблоны используются для быстрого развертывания новых виртуальных машин: вместо установки операционной системы с нуля администратор создаёт экземпляр на основе шаблона, что сокращает время подготовки среды с десятков минут до нескольких секунд. В контексте облачных платформ шаблоны лежат в основе каталогов готовых образов (например, Amazon Machine Images в AWS или образов в Yandex Cloud).",[68,590,591],{},"Снимок состояния (snapshot) фиксирует полное состояние виртуальной машины в определённый момент времени: содержимое дисков, состояние оперативной памяти и конфигурацию виртуального оборудования. Снимки позволяют оперативно вернуться к зафиксированному состоянию, что особенно полезно при тестировании обновлений, экспериментах с конфигурацией и в учебных сценариях. Однако снимки не являются полноценной заменой резервного копирования. Длительное накопление снимков увеличивает объём занимаемого дискового пространства, усложняет управление и может негативно сказаться на производительности дисковой подсистемы, поскольку каждая операция записи должна учитывать цепочку зависимых снимков.",[68,593,594],{},"Клонирование (cloning) создаёт полную копию существующей виртуальной машины, включая её дисковые образы и конфигурацию. Клон представляет собой независимый экземпляр, который после создания существует и развивается отдельно от оригинала. Клонирование используется для масштабирования однотипных сред, создания тестовых копий и подготовки учебных лабораторий.",[83,596,598],{"id":597},"ограничения-виртуальной-машины","Ограничения виртуальной машины",[68,600,601],{},"При всех достоинствах виртуализации виртуальная машина обладает рядом ограничений, которые необходимо учитывать при проектировании инфраструктуры.",[68,603,604],{},"Первое ограничение — накладные расходы на гостевую операционную систему. Каждая виртуальная машина содержит полноценный экземпляр операционной системы со всеми её компонентами: ядром, системными службами, библиотеками и конфигурационными файлами. Это означает, что значительная доля ресурсов — оперативной памяти и дискового пространства — расходуется не на прикладную задачу, а на поддержание операционной среды. Если на одном физическом сервере размещено двадцать виртуальных машин с одной и той же операционной системой, ядро этой системы загружено в память двадцать раз, и двадцать экземпляров системных служб потребляют процессорное время.",[68,606,607],{},"Второе ограничение — длительность запуска. Создание и загрузка виртуальной машины включает инициализацию виртуального оборудования, загрузку ядра операционной системы и запуск системных служб. Этот процесс занимает от десятков секунд до нескольких минут в зависимости от конфигурации. Для задач, требующих быстрого масштабирования и мгновенного развертывания экземпляров, такая задержка может быть неприемлемой.",[68,609,610],{},"Третье ограничение — объём артефакта. Образ виртуальной машины, включающий операционную систему и прикладное программное обеспечение, может занимать гигабайты дискового пространства. Передача таких образов по сети, их хранение и версионирование требуют значительных ресурсов.",[68,612,613],{},"Перечисленные ограничения не делают виртуализацию непригодной — напротив, для широкого спектра задач она остаётся оптимальным решением. Однако именно эти ограничения стали одной из предпосылок появления контейнеризации как альтернативного, более легковесного подхода к изоляции приложений.",[78,615,617],{"id":616},"механизмы-изоляции-на-уровне-ядра-операционной-системы","Механизмы изоляции на уровне ядра операционной системы",[68,619,620],{},"Помимо аппаратной виртуализации, реализуемой гипервизорами, современные ядра операционных систем предоставляют собственные механизмы изоляции и управления ресурсами. Эти механизмы позволяют разделять процессы и группы процессов на уровне операционной системы, обеспечивая логическое разграничение без создания полноценных виртуальных машин. Понимание этих механизмов важно в контексте виртуализации, поскольку они составляют основу легковесных форм изоляции, широко применяемых в современных облачных платформах.",[83,622,624],{"id":623},"пространства-имён-namespaces","Пространства имён (namespaces)",[68,626,627],{},"Пространства имён (namespaces) обеспечивают логическое разделение глобальных ресурсов ядра между процессами. Каждое пространство имён создаёт для входящих в него процессов изолированное представление определённого ресурса, скрывая существование одноимённых ресурсов в других пространствах. Linux реализует несколько типов пространств имён, каждый из которых изолирует отдельную категорию ресурсов.",[68,629,630],{},"Пространство имён процессов (PID namespace) скрывает от группы процессов все остальные процессы системы. Внутри пространства имён нумерация процессов начинается заново с единицы. Первый процесс, запущенный в изолированной среде, получает идентификатор PID 1 в пределах своего пространства имён, хотя хостовая система видит его под иным глобальным идентификатором.",[68,632,633],{},"Сетевое пространство имён (network namespace) предоставляет изолированный сетевой стек: собственные сетевые интерфейсы, таблицы маршрутизации, правила сетевого экрана и диапазон портов. Благодаря этому несколько групп процессов на одном хосте могут одновременно использовать одни и те же номера портов, не конфликтуя друг с другом.",[68,635,636],{},"Пространство имён точек монтирования (mount namespace) изолирует файловую систему: каждая группа процессов видит собственное дерево каталогов, независимое от основной файловой системы хоста и от файловых систем других изолированных сред.",[68,638,639],{},"Пространство имён узла (UTS namespace) позволяет группе процессов иметь собственное имя хоста (hostname) и доменное имя, независимое от хостовой системы. Это важно для приложений, которые используют имя хоста для идентификации узла в распределённой системе.",[68,641,642],{},"Пространство имён межпроцессного взаимодействия (IPC namespace) изолирует механизмы взаимодействия между процессами: разделяемую память, очереди сообщений и семафоры POSIX. Процессы внутри изолированной среды могут взаимодействовать через эти механизмы, не затрагивая процессы за её пределами.",[68,644,645],{},"Пространство имён пользователей (user namespace) позволяет отображать идентификаторы пользователей и групп внутри изолированной среды на иные идентификаторы снаружи. Практически значимым следствием этого является возможность предоставить процессу привилегии суперпользователя (UID 0) в пределах пространства имён, тогда как на уровне хостовой системы он будет работать с ограниченными правами непривилегированного пользователя. Данный механизм снижает риски эскалации привилегий при нарушении изоляции.",[83,647,649],{"id":648},"группы-управления-ресурсами-control-groups-cgroups","Группы управления ресурсами (control groups, cgroups)",[68,651,652],{},"Группы управления ресурсами (control groups, cgroups) решают задачу, принципиально отличную от пространств имён. Если пространства имён определяют, какие ресурсы видит процесс, то cgroups определяют, сколько ресурсов он может потребить. С их помощью можно задать верхние пределы потребления процессорного времени, оперативной памяти, пропускной способности дисковой подсистемы и сетевого интерфейса. При достижении лимита памяти, например, ядро может завершить наиболее ресурсоёмкий процесс группы или ограничить его выполнение — в зависимости от настроенной политики. Без количественных ограничений одна неконтролируемая группа процессов способна исчерпать все ресурсы хоста, нарушая работу остальных.",[83,654,656],{"id":655},"соотношение-аппаратной-и-программной-изоляции","Соотношение аппаратной и программной изоляции",[68,658,659],{},"Механизмы изоляции ядра и гипервизорная виртуализация решают схожую задачу — разграничение вычислительных сред — но на разных уровнях абстракции и с различными гарантиями. Гипервизор создаёт аппаратную границу между гостевой и хостовой системой, опираясь на поддержку процессора. Пространства имён и cgroups — это программные средства одного и того же ядра операционной системы, а не аппаратные барьеры. Уязвимость в ядре хостовой системы потенциально затрагивает все изолированные среды, работающие на этом ядре. Поэтому изоляция средствами ядра считается менее строгой по сравнению с изоляцией, обеспечиваемой гипервизором.",[68,661,662],{},"В сценариях с высокими требованиями к безопасности дополнительно используются специализированные механизмы: seccomp-профили, ограничивающие набор доступных системных вызовов; политики AppArmor и SELinux, реализующие мандатный контроль доступа; а также изолирующие среды исполнения, добавляющие промежуточный уровень между процессами и ядром.",[119,664,121,665,121,669],{},[123,666],{"src":667,"alt":668},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-02\u002Fnamespaces_cgroups.svg","Механизмы изоляции на уровне ядра: пространства имён обеспечивают логическое разделение ресурсов, cgroups ограничивают потребление",[128,670,668],{},[78,672,348],{"id":347},[68,674,675],{},"Виртуализация является фундаментальной технологией, обеспечивающей абстрагирование физических ресурсов и формирование управляемых вычислительных сред. Она позволяет размещать на одном физическом сервере множество независимых виртуальных машин, каждая из которых содержит полноценную операционную систему и работает в изолированном окружении. Консолидация нагрузок повышает коэффициент использования оборудования, а программная управляемость виртуальных сред делает возможным построение облачных платформ с их требованиями к эластичности, самообслуживанию и автоматизации.",[68,677,678],{},"Гипервизоры первого типа, работающие непосредственно на оборудовании, обеспечивают производительность и изоляцию, необходимые для промышленной эксплуатации. Гипервизоры второго типа остаются удобным инструментом для разработки и обучения. Аппаратная поддержка виртуализации в современных процессорах существенно сократила накладные расходы и стала обязательным компонентом всех актуальных решений.",[68,680,681],{},"Ресурсная модель виртуальной машины включает виртуальные процессоры, оперативную память, дисковые образы и сетевые интерфейсы, а инструменты шаблонизации, создания снимков и клонирования ускоряют управление вычислительными средами. Вместе с тем виртуальные машины обладают ограничениями: значительные накладные расходы на гостевую операционную систему, длительное время запуска и большой объём артефактов.",[68,683,684],{},"Помимо аппаратной виртуализации, ядро операционной системы предоставляет собственные механизмы изоляции — пространства имён и группы управления ресурсами, — которые позволяют разграничивать процессы и ограничивать их потребление ресурсов на программном уровне. Эти механизмы обеспечивают менее строгую, но значительно более легковесную изоляцию по сравнению с гипервизором и составляют технологическую основу для подходов, рассматриваемых в последующих темах курса.",{"title":40,"searchDepth":41,"depth":41,"links":686},[687,692,699,704,709],{"id":417,"depth":41,"text":418,"children":688},[689,690,691],{"id":421,"depth":369,"text":422},{"id":442,"depth":369,"text":443},{"id":463,"depth":369,"text":464},{"id":476,"depth":41,"text":477,"children":693},[694,695,696,697,698],{"id":480,"depth":369,"text":481},{"id":490,"depth":369,"text":491},{"id":503,"depth":369,"text":504},{"id":524,"depth":369,"text":525},{"id":540,"depth":369,"text":541},{"id":550,"depth":41,"text":551,"children":700},[701,702,703],{"id":554,"depth":369,"text":555},{"id":581,"depth":369,"text":582},{"id":597,"depth":369,"text":598},{"id":616,"depth":41,"text":617,"children":705},[706,707,708],{"id":623,"depth":369,"text":624},{"id":648,"depth":369,"text":649},{"id":655,"depth":369,"text":656},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-02-content",{"title":400,"description":408},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-02-content","topic-02","e5q7ep7RLItoPklx-1toCwluN1I3Namj2hhplhx2Y1c",{"id":717,"title":718,"body":719,"course_slug":43,"description":40,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":948,"navigation":48,"path":949,"section":392,"seo":950,"stem":951,"topic_number":369,"topic_slug":952,"__hash__":953},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-03-content.md","Контейнеризация и архитектура Docker",{"type":7,"value":720,"toc":932},[721,724,728,731,734,737,741,744,747,750,753,757,760,763,766,770,773,781,785,788,791,794,802,805,809,812,815,818,821,824,827,830,833,841,844,848,851,854,857,865,868,872,875,878,886,889,893,896,904,907,911,914,917,921,924,927,929],[10,722,718],{"id":723},"контейнеризация-и-архитектура-docker",[78,725,727],{"id":726},"контейнер-как-модель-изоляции-процессов","Контейнер как модель изоляции процессов",[68,729,730],{},"Контейнеризация решает практическую задачу: запускать приложения изолированно и воспроизводимо, не создавая для каждого приложения отдельную виртуальную машину. В отличие от VM, контейнер не содержит собственного ядра операционной системы. Контейнер — это набор процессов, которые выполняются на ядре хоста, но «видят» ограниченную картину системы: свои процессы, свою файловую систему, свои сетевые интерфейсы и свои квоты ресурсов. Эта изоляция достигается механизмами ядра Linux, а Docker предоставляет удобную модель упаковки и запуска.",[68,732,733],{},"Ключевое отличие контейнера от виртуальной машины проявляется на уровне ядра операционной системы. Виртуальная машина запускает полноценную гостевую ОС поверх гипервизора и требует выделения ресурсов на её обслуживание. Контейнер же разделяет ядро хоста с другими контейнерами, что существенно сокращает время запуска — с минут до секунд — и снижает накладные расходы на оперативную память и дисковое пространство.",[68,735,736],{},"Практический смысл контейнеризации раскрывается на всех этапах жизненного цикла приложения. На этапе разработки контейнер позволяет воспроизвести окружение, идентичное целевому, и устранить расхождения между рабочими станциями участников команды. На этапе тестирования контейнеры обеспечивают быстрое создание и уничтожение изолированных сред, необходимых для проверки различных конфигураций. На этапе эксплуатации контейнеризация упрощает развёртывание и обновление приложений, поскольку образ контейнера является единым переносимым артефактом, содержащим приложение и все его зависимости.",[78,738,740],{"id":739},"сопоставление-с-виртуализацией","Сопоставление с виртуализацией",[68,742,743],{},"Контейнеризация и виртуализация решают общую задачу — изоляцию вычислительных сред — но принципиально различаются уровнем, на котором эта изоляция реализуется. Понимание этих различий необходимо для обоснованного выбора подхода при проектировании инфраструктуры.",[68,745,746],{},"Виртуальная машина представляет собой полноценную вычислительную среду с собственной операционной системой. Гипервизор обеспечивает аппаратную изоляцию: гостевая система не имеет прямого доступа к ресурсам хоста или других виртуальных машин. Границы изоляции проходят на уровне виртуального оборудования, что обеспечивает высокую степень безопасности. Даже если гостевая операционная система подвергнется компрометации, злоумышленнику потребуется преодолеть дополнительный барьер в виде гипервизора, чтобы получить доступ к хостовой системе.",[68,748,749],{},"Контейнер не содержит собственной операционной системы и не требует гипервизора. Изоляция обеспечивается средствами ядра хостовой системы — пространствами имён и группами управления ресурсами, рассмотренными в предыдущей теме. С точки зрения хостовой системы контейнер представляет собой процесс (или группу процессов) в ограниченном окружении. Это даёт ряд практических преимуществ: минимальные накладные расходы (нет необходимости загружать дополнительную операционную систему), быстрый запуск (секунды вместо минут) и компактность артефакта (контейнерный образ содержит только приложение и его зависимости).",[68,751,752],{},"Вместе с тем изоляция средствами ядра менее строга, чем аппаратная изоляция гипервизора. Уязвимость в ядре хостовой системы потенциально затрагивает все контейнеры, работающие на этом хосте. Поэтому в сценариях с повышенными требованиями к безопасности — например, при многоарендном размещении ненадёжного кода — виртуальные машины остаются предпочтительным решением.",[83,754,756],{"id":755},"сценарии-предпочтительного-использования","Сценарии предпочтительного использования",[68,758,759],{},"Виртуальные машины предпочтительны, когда требуется запуск различных операционных систем на одном физическом сервере, когда предъявляются повышенные требования к изоляции и безопасности, а также когда приложение зависит от специфической конфигурации ядра, которая не может быть воспроизведена в контейнерной среде.",[68,761,762],{},"Контейнеры предпочтительны в сценариях, где критичны скорость развёртывания, плотность размещения и эффективность использования ресурсов: микросервисная архитектура, непрерывная интеграция и поставка, масштабирование веб-приложений.",[68,764,765],{},"На практике виртуализация и контейнеризация часто применяются совместно. Типичная архитектура облачной платформы предполагает, что контейнеры исполняются внутри виртуальных машин. Виртуальная машина обеспечивает аппаратную изоляцию между потребителями облачного сервиса, а контейнеры внутри неё обеспечивают эффективное размещение и управление прикладными компонентами.",[83,767,769],{"id":768},"сравнительная-характеристика","Сравнительная характеристика",[68,771,772],{},"По уровню изоляции виртуальные машины обеспечивают аппаратную изоляцию через гипервизор, тогда как контейнеры полагаются на механизмы ядра операционной системы. По объёму потребляемых ресурсов виртуальная машина включает полную операционную систему и потребляет от сотен мегабайт до нескольких гигабайт оперативной памяти; контейнер потребляет только ресурсы, необходимые для исполнения прикладного процесса. По времени запуска виртуальная машина загружается от десятков секунд до нескольких минут, контейнер — за секунды. По размеру артефакта образ виртуальной машины обычно измеряется гигабайтами, контейнерный образ — десятками или сотнями мегабайт. По совместимости виртуальная машина может исполнять произвольную операционную систему, тогда как контейнер ограничен ядром хостовой системы.",[119,774,121,775,121,779],{},[123,776],{"src":777,"alt":778},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Fvm_vs_container_stack.svg","Сравнение программных стеков: виртуальная машина и контейнер",[128,780,778],{},[78,782,784],{"id":783},"контейнер-и-образ-назначение-и-взаимосвязь","Контейнер и образ: назначение и взаимосвязь",[68,786,787],{},"В Docker используются две взаимосвязанные сущности: образ и контейнер. Образ — это подготовленный артефакт, который описывает, что именно будет запущено. Контейнер — это экземпляр выполнения, который определяет, как именно это будет запущено в конкретный момент времени.",[68,789,790],{},"Образ (image) представляет собой файловую систему приложения и набор метаданных запуска. В образ не входит ядро операционной системы и драйверы устройств: контейнеры используют ядро системы-хоста. Назначение образа — обеспечить переносимость и воспроизводимость: если используется один и тот же образ, приложение стартует в одинаковом наборе файлов и зависимостей (при условии совместимости архитектуры и платформы). Образ хранится локально или в реестре и может быть версионирован.",[68,792,793],{},"Контейнер (container) создаётся на основе образа и является объектом времени выполнения. При создании контейнера формируется отдельный слой файловой системы с возможностью записи, а также фиксируются параметры запуска: команда, переменные окружения, ограничения ресурсов, подключаемые хранилища и сетевые настройки. Контейнер “живёт” пока выполняется основной процесс внутри контейнера; при завершении этого процесса контейнер переходит в остановленное состояние. Поэтому контейнер — это не “отдельная операционная система”, а управляемая среда для выполнения процесса(ов) с заданной изоляцией и ресурсными ограничениями.",[119,795,121,796,121,800],{},[123,797],{"src":798,"alt":799},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Fimage_and_container.svg","Образ и контейнер: слои только для чтения, слой записи и параметры времени выполнения",[128,801,799],{},[68,803,804],{},"Из этой модели следует ключевая практика эксплуатации. Изменения, выполненные внутри работающего контейнера, относятся к его слою записи и не являются надёжным способом сопровождения: при пересоздании контейнера они исчезают. Поэтому корректный процесс обновления приложения заключается в создании новой версии образа и запуске нового контейнера, а не во внесении изменений в уже запущенный экземпляр.",[78,806,808],{"id":807},"изоляция-контейнеров-пространства-имён-и-группы-управления-ресурсами","Изоляция контейнеров: пространства имён и группы управления ресурсами",[68,810,811],{},"Контейнеры в Linux не являются отдельными операционными системами. Это процессы, которым ядро предоставляет изолированные представления части ресурсов и одновременно применяет количественные ограничения на потребление этих ресурсов. В Docker данная модель скрыта за простым интерфейсом запуска, однако для понимания поведения контейнеров в эксплуатации важно знать, какими механизмами изоляция и ограничения реализуются.",[68,813,814],{},"Изоляция обеспечивается пространствами имён (namespaces). Пространство имён можно понимать как механизм, который заставляет процесс «видеть» только выделенный ему набор системных объектов. Например, в контейнере может быть собственная нумерация процессов, собственная конфигурация сети и отдельное дерево монтирования файловых систем. Для контейнеризации чаще всего используются несколько типов пространств имён.",[68,816,817],{},"PID namespace определяет, какие процессы видимы и как они нумеруются внутри контейнера. Благодаря этому процессы контейнера не видят процессы хоста и других контейнеров, а внутри контейнера основной процесс получает идентификатор PID 1. Этот факт имеет практические последствия: PID 1 в Linux обладает особыми обязанностями по обработке сигналов и «сборке» завершившихся дочерних процессов; поэтому некоторые приложения в роли PID 1 требуют корректной настройки, а в реальных системах нередко используют специализированные минимальные процессы-инициализаторы.",[68,819,820],{},"Network namespace предоставляет контейнеру отдельный сетевой стек: сетевые интерфейсы, таблицы маршрутизации, правила фильтрации. С точки зрения процессов внутри контейнера сеть выглядит «своей», хотя физически пакеты всё равно проходят через подсистему хоста. Именно на базе network namespaces Docker строит модели виртуальных сетей и изоляции взаимодействия между контейнерами.",[68,822,823],{},"Mount namespace формирует отдельное дерево монтирования. Это позволяет контейнеру иметь свою корневую файловую систему, собранную из слоёв образа, а также подключать дополнительные хранилища (например, тома) в заданные точки. Mount namespace тесно связан с механизмом слоистой файловой системы контейнера: процессы контейнера видят единое дерево каталогов, хотя на уровне реализации оно составлено из нескольких слоёв.",[68,825,826],{},"UTS namespace изолирует такие атрибуты, как имя хоста и доменное имя узла, что полезно для корректного поведения приложений, которые используют эти значения. IPC namespace изолирует механизмы межпроцессного взаимодействия (например, разделяемую память и семафоры), снижая риск нежелательных пересечений между контейнерами.",[68,828,829],{},"User namespace позволяет отображать идентификаторы пользователей и групп контейнера на другие идентификаторы на хосте. Это важно для безопасности: процесс может иметь высокие привилегии внутри контейнера, но соответствовать непривилегированному пользователю на хосте. На этой возможности основаны режимы запуска без прав суперпользователя (rootless), которые уменьшают последствия возможной компрометации приложения.",[68,831,832],{},"Одной из целей контейнеризации является также управляемое распределение ресурсов. Для этого ядро предоставляет механизм групп управления ресурсами (cgroups). Через cgroups можно ограничивать и учитывать потребление процессорного времени и оперативной памяти, а также собирать статистику использования ресурсов. Практическая ценность cgroups проявляется в мультисервисных системах: без ограничений один контейнер способен занять всю память или создать избыточную нагрузку на CPU и тем самым нарушить работу других сервисов. В эксплуатационной практике задавать лимиты — это способ сделать поведение системы предсказуемым и снизить влияние ошибок и пиков нагрузки.",[119,834,121,835,121,839],{},[123,836],{"src":837,"alt":838},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Fnamespaces_cgroups_isolation.svg","Механизмы изоляции контейнера: пространства имён обеспечивают изоляцию видимости, cgroups ограничивают потребление ресурсов",[128,840,838],{},[68,842,843],{},"Важно понимать, что namespaces и cgroups решают разные задачи. Пространства имён отвечают за границы видимости и изоляцию, тогда как cgroups — за количественные ограничения и учёт. Docker объединяет оба механизма в единый объект контейнера, что и создаёт эффект «изолированной среды выполнения».",[78,845,847],{"id":846},"файловая-система-контейнера-слои-образа-слой-записи-и-внешнее-хранение-данных","Файловая система контейнера: слои образа, слой записи и внешнее хранение данных",[68,849,850],{},"Когда контейнер создаётся из образа, Docker должен предоставить процессам контейнера корневую файловую систему, которая выглядит как обычное дерево каталогов. При этом образ состоит из слоёв «только чтение», а контейнер должен иметь возможность изменять файлы в ходе работы. Эта задача решается при помощи объединённой (слоистой) файловой системы, где к слоям образа добавляется верхний слой с возможностью записи.",[68,852,853],{},"Слой записи контейнера является индивидуальным для каждого контейнера. Любые изменения файлов — создание новых, модификация существующих, удаление — фиксируются именно в этом верхнем слое. При чтении данных система формирует итоговую картину: если файл изменён, используется версия из слоя записи; если не изменён, берётся из нижних слоёв образа. Такое устройство обеспечивает быстрое развёртывание и экономию дискового пространства.",[68,855,856],{},"Вместе с тем слой записи связан с жизненным циклом контейнера. При удалении контейнера этот слой исчезает, а значит, исчезают и изменения, которые хранились только в нём. Поэтому слой записи следует рассматривать как место для временных данных и внутренних изменений, которые допускается потерять при пересоздании контейнера. Если приложению требуется постоянное хранение (данные базы, загруженные пользователями файлы, результаты вычислений), такие данные должны быть вынесены во внешнее хранилище — механизмы постоянного хранения данных в контейнерных средах подробно рассматриваются в соответствующей теме курса.",[119,858,121,859,121,863],{},[123,860],{"src":861,"alt":862},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Flayered_filesystem.svg","Слоистая файловая система: контейнеры разделяют общие слои образа, каждый имеет собственный слой записи",[128,864,862],{},[68,866,867],{},"Слоистая файловая система влияет и на производительность. Для большинства приложений накладные расходы невелики, однако при интенсивной записи слой записи и механизмы copy-on-write могут создавать дополнительные издержки. Это обстоятельство учитывается при проектировании политики хранения данных контейнерных приложений.",[78,869,871],{"id":870},"архитектура-docker-клиент-демон-и-компоненты-исполнения","Архитектура Docker: клиент, демон и компоненты исполнения",[68,873,874],{},"Docker включает клиентскую утилиту (CLI) и серверный компонент Docker Engine (демон). Клиент направляет запросы демону, а демон управляет объектами Docker: образами, контейнерами, сетями, томами и процессом сборки. Этот подход отделяет интерфейс управления от реализации операций и позволяет централизованно вести состояние объектов.",[68,876,877],{},"На более низком уровне выполнение контейнеров делегируется специализированным компонентам. Компонент containerd отвечает за управление жизненным циклом контейнеров и взаимодействие с хранилищами образов, а runc выполняет непосредственный запуск контейнера, настраивая изоляцию через пространства имён и ограничения ресурсов через cgroups. Такое разделение упрощает поддержку и развитие системы: высокоуровневые функции управления (образы, сети, тома) отделены от механизма запуска процессов.",[119,879,121,880,121,884],{},[123,881],{"src":882,"alt":883},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Fdocker_architecture.svg","Архитектура Docker: клиент, демон, containerd и runc",[128,885,883],{},[68,887,888],{},"Совместимость контейнерной экосистемы обеспечивается стандартами OCI (Open Container Initiative). OCI определяет формат образов и спецификацию выполнения контейнеров. Благодаря этому образы и средства запуска могут быть взаимозаменяемыми в пределах стандарта, а инфраструктура не оказывается жестко привязанной к одному программному продукту.",[78,890,892],{"id":891},"жизненный-цикл-и-базовые-операции","Жизненный цикл и базовые операции",[68,894,895],{},"Контейнер создаётся из образа, получает параметры запуска и запускает основной процесс. Контейнер считается запущенным, пока выполняется основной процесс. Это объясняет типичную ситуацию, когда контейнер “сразу останавливается”: обычно причиной является корректное завершение основного процесса, а не ошибка платформы.",[119,897,121,898,121,902],{},[123,899],{"src":900,"alt":901},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-03\u002Fcontainer_lifecycle.svg","Жизненный цикл контейнера: создание, запуск, остановка, перезапуск и удаление",[128,903,901],{},[68,905,906],{},"Docker предоставляет механизмы автоматического перезапуска контейнеров при завершении работы, однако эти механизмы являются локальными и не заменяют оркестрацию. Оркестрация решает задачи распределения экземпляров по узлам, управления обновлениями, масштабирования и восстановления при отказах — и будет рассматриваться далее в курсе.",[83,908,910],{"id":909},"журналы-и-инспекция-конфигурации","Журналы и инспекция конфигурации",[68,912,913],{},"При сопровождении контейнеров необходимо иметь доступ к журналам и к точной конфигурации запуска. В контейнерных приложениях распространена практика вывода журналов в стандартные потоки вывода, чтобы среда запуска могла централизованно собирать журнальные записи и передавать их во внешние системы мониторинга.",[68,915,916],{},"Средства инспекции предоставляют доступ к метаданным контейнера и образа: параметрам запуска, используемому образу, подключённым хранилищам, сетевым настройкам и ограничениям ресурсов. Это является основой диагностики, поскольку сбои часто связаны не с логикой приложения, а с ошибками конфигурации запуска.",[83,918,920],{"id":919},"реестры-образов","Реестры образов",[68,922,923],{},"Образы хранятся в реестрах — публичных или частных. Реестр предоставляет интерфейсы для публикации и получения образов и позволяет использовать образы как типовые артефакты поставки. Для сопровождения существенна политика версионирования: в учебных работах часто применяются теги, однако в эксплуатационных сценариях предпочтительнее исключать неоднозначность и фиксировать конкретные версии (включая возможность использования идентификатора содержимого).",[68,925,926],{},"В реальных системах реестр выступает частью цепочки поставки: он позволяет централизованно хранить утверждённые версии образов, управлять доступом и проводить дополнительные проверки (например, сканирование на уязвимости). Эти вопросы будут подробно рассмотрены в главах, посвящённых безопасности и сопровождению.",[78,928,348],{"id":347},[68,930,931],{},"Контейнеризация представляет собой модель изоляции процессов, основанную на механизмах ядра операционной системы — пространствах имён и группах управления ресурсами. В отличие от виртуальных машин, контейнеры разделяют ядро хоста, что обеспечивает существенное сокращение времени запуска и накладных расходов. Docker предоставляет единую модель управления контейнерами, образами, сетями и томами, опираясь на стандарты OCI для обеспечения переносимости. Образ выступает неизменяемым артефактом поставки, а контейнер — его экземпляром времени выполнения с эфемерным слоем записи. Понимание границ контейнерной изоляции, слоистой файловой системы и жизненного цикла контейнера является необходимым основанием для последующего изучения сборки образов, сетевого взаимодействия и оркестрации.",{"title":40,"searchDepth":41,"depth":41,"links":933},[934,935,939,940,941,942,943,947],{"id":726,"depth":41,"text":727},{"id":739,"depth":41,"text":740,"children":936},[937,938],{"id":755,"depth":369,"text":756},{"id":768,"depth":369,"text":769},{"id":783,"depth":41,"text":784},{"id":807,"depth":41,"text":808},{"id":846,"depth":41,"text":847},{"id":870,"depth":41,"text":871},{"id":891,"depth":41,"text":892,"children":944},[945,946],{"id":909,"depth":369,"text":910},{"id":919,"depth":369,"text":920},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-03-content",{"title":718,"description":40},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-03-content","topic-03","ZyJtUa8xGYg0lo1hI-6krFfp4f1mafXJcE-3W8IB-WI",{"id":955,"title":956,"body":957,"course_slug":43,"description":964,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":1490,"navigation":48,"path":1491,"section":392,"seo":1492,"stem":1493,"topic_number":1057,"topic_slug":1494,"__hash__":1495},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-04-content.md","Сборка и сопровождение контейнерных образов",{"type":7,"value":958,"toc":1475},[959,962,965,968,971,975,983,990,1023,1027,1030,1110,1133,1148,1170,1208,1221,1227,1231,1234,1262,1269,1272,1280,1283,1287,1295,1305,1313,1316,1320,1323,1326,1329,1332,1336,1339,1342,1345,1348,1351,1355,1358,1361,1367,1370,1373,1377,1380,1383,1391,1394,1397,1400,1404,1411,1414,1417,1425,1428,1432,1435,1438,1441,1444,1448,1451,1454,1457,1459,1462,1468,1471],[10,960,956],{"id":961},"сборка-и-сопровождение-контейнерных-образов",[68,963,964],{},"Контейнеризация становится практически полезной не в тот момент, когда среда исполнения умеет запускать изолированные процессы, а тогда, когда приложение можно упаковать в воспроизводимый и управляемый артефакт. Таким артефактом в экосистеме Docker является образ (image). Именно образ фиксирует состав файлов, системных библиотек, прикладных зависимостей и параметры запуска, которые затем используются при создании контейнера. Поэтому качество сопровождения контейнерной среды напрямую зависит от того, насколько дисциплинированно организован процесс сборки образов.",[68,966,967],{},"На начальном этапе изучения контейнеров образ иногда воспринимают как «снимок» работающей системы. Такое представление отчасти полезно интуитивно, но методически оно недостаточно. Современная практика исходит не из ручного сохранения состояния, а из декларативного описания сборки. Образ должен получаться не в результате последовательности нефиксируемых действий администратора, а на основе формального сценария, который можно повторить, проверить и встроить в цепочку поставки программного обеспечения. В этой связи сборка контейнерного образа является не только технической процедурой, но и элементом инженерной дисциплины.",[68,969,970],{},"В рамках данной темы важно различать две взаимосвязанные задачи. Первая состоит в конструировании образа как исполнимого артефакта. Вторая связана с его сопровождением: версионированием, повторной сборкой, публикацией, обновлением и контролем состава. Если эти задачи решаются несистемно, контейнеризация теряет одно из своих главных преимуществ — предсказуемость поведения приложения в разных средах. Поэтому дальнейшее изложение сосредоточено не только на синтаксисе Dockerfile, но и на принципах, которые делают образ пригодным для учебной и производственной эксплуатации.",[78,972,974],{"id":973},"dockerfile-как-декларативное-описание-образа","Dockerfile как декларативное описание образа",[68,976,977,978,982],{},"Основным средством описания процесса сборки в Docker является файл ",[979,980,981],"code",{},"Dockerfile",". Он задаёт последовательность инструкций, на основе которых система сборки формирует новый образ. Каждая инструкция изменяет состояние промежуточной файловой системы или задаёт метаданные, необходимые для последующего запуска контейнера. В этом смысле Dockerfile выполняет роль спецификации: он не просто перечисляет команды, а фиксирует, каким должен быть результат сборки.",[68,984,985,986,989],{},"Декларативный характер Dockerfile не означает отсутствия пошаговой логики. Напротив, порядок инструкций имеет принципиальное значение. Сборка обычно начинается с инструкции ",[979,987,988],{},"FROM",", которая определяет базовый образ (base image). Базовый образ задаёт исходную среду: тип дистрибутива, набор системных библиотек, иногда интерпретатор языка программирования или среду выполнения. Выбор базового образа влияет на размер итогового артефакта, совместимость приложения, доступность инструментов диагностики и поверхность потенциальных уязвимостей. Поэтому выбор «самого маленького» образа не всегда является лучшим решением: чрезмерное упрощение может затруднить сопровождение и отладку.",[68,991,992,993,996,997,1000,1001,1004,1005,1008,1009,1008,1012,1008,1015,1018,1019,1022],{},"После выбора базового образа в Dockerfile задаются операции подготовки среды. Инструкции ",[979,994,995],{},"RUN"," используются для выполнения команд в процессе сборки, например для установки пакетов, компиляции исходного кода или формирования каталогов приложения. Инструкция ",[979,998,999],{},"COPY"," переносит файлы из контекста сборки внутрь образа, а ",[979,1002,1003],{},"ADD"," выполняет сходную функцию, но обладает дополнительными возможностями, которые без необходимости часто использовать не рекомендуется, поскольку это усложняет предсказуемость сборки. Инструкции ",[979,1006,1007],{},"ENV",", ",[979,1010,1011],{},"WORKDIR",[979,1013,1014],{},"EXPOSE",[979,1016,1017],{},"CMD"," и ",[979,1020,1021],{},"ENTRYPOINT"," определяют параметры окружения, рабочий каталог, сетевые намерения и поведение контейнера при запуске. Важно понимать, что Dockerfile объединяет как операции подготовки файловой системы, так и описание будущего режима исполнения.",[83,1024,1026],{"id":1025},"пример-базового-dockerfile-для-веб-приложения","Пример базового Dockerfile для веб-приложения",[68,1028,1029],{},"Рассмотрим упрощённый пример Dockerfile для небольшого приложения на Python:",[1031,1032,1036],"pre",{"className":1033,"code":1034,"language":1035,"meta":40,"style":40},"language-dockerfile shiki shiki-themes github-light github-dark","FROM python:3.12-slim\n\nWORKDIR \u002Fapp\n\nCOPY requirements.txt .\nRUN pip install --no-cache-dir -r requirements.txt\n\nCOPY . .\n\nENV PORT=8000\nEXPOSE 8000\n\nCMD [\"python\", \"app.py\"]\n","dockerfile",[979,1037,1038,1045,1050,1055,1060,1066,1072,1077,1083,1088,1094,1100,1104],{"__ignoreMap":40},[1039,1040,1042],"span",{"class":1041,"line":395},"line",[1039,1043,1044],{},"FROM python:3.12-slim\n",[1039,1046,1047],{"class":1041,"line":41},[1039,1048,1049],{"emptyLinePlaceholder":48},"\n",[1039,1051,1052],{"class":1041,"line":369},[1039,1053,1054],{},"WORKDIR \u002Fapp\n",[1039,1056,1058],{"class":1041,"line":1057},4,[1039,1059,1049],{"emptyLinePlaceholder":48},[1039,1061,1063],{"class":1041,"line":1062},5,[1039,1064,1065],{},"COPY requirements.txt .\n",[1039,1067,1069],{"class":1041,"line":1068},6,[1039,1070,1071],{},"RUN pip install --no-cache-dir -r requirements.txt\n",[1039,1073,1075],{"class":1041,"line":1074},7,[1039,1076,1049],{"emptyLinePlaceholder":48},[1039,1078,1080],{"class":1041,"line":1079},8,[1039,1081,1082],{},"COPY . .\n",[1039,1084,1086],{"class":1041,"line":1085},9,[1039,1087,1049],{"emptyLinePlaceholder":48},[1039,1089,1091],{"class":1041,"line":1090},10,[1039,1092,1093],{},"ENV PORT=8000\n",[1039,1095,1097],{"class":1041,"line":1096},11,[1039,1098,1099],{},"EXPOSE 8000\n",[1039,1101,1102],{"class":1041,"line":51},[1039,1103,1049],{"emptyLinePlaceholder":48},[1039,1105,1107],{"class":1041,"line":1106},13,[1039,1108,1109],{},"CMD [\"python\", \"app.py\"]\n",[68,1111,1112,1113,1115,1116,1118,1119,1122,1123,1008,1125,1018,1127,1129,1130,1132],{},"В этом примере инструкция ",[979,1114,988],{}," задаёт базовую среду с интерпретатором Python. Инструкция ",[979,1117,1011],{}," определяет рабочий каталог внутри образа и в дальнейшем делает каталог ",[979,1120,1121],{},"\u002Fapp"," текущей рабочей директорией для последующих инструкций. Это означает, что относительные пути в ",[979,1124,999],{},[979,1126,995],{},[979,1128,1017],{}," далее интерпретируются уже с учётом каталога ",[979,1131,1121],{},", если не указан иной абсолютный путь.",[68,1134,1135,1136,1139,1140,1018,1142,1144,1145,1147],{},"Для правильного понимания примера необходимо пояснить, что такое контекст сборки. Когда пользователь запускает команду вида ",[979,1137,1138],{},"docker build .",", точка в конце означает, что контекстом сборки становится текущий каталог на машине пользователя. Именно из этого каталога Docker может брать файлы для инструкций ",[979,1141,999],{},[979,1143,1003],{},". Следовательно, Docker не копирует файлы произвольно из всей файловой системы хоста, а работает только с тем набором данных, который был передан ему как контекст. Если нужный файл находится вне контекста, инструкция ",[979,1146,999],{}," не сможет его использовать без изменения структуры проекта или явного выбора другого контекста.",[68,1149,1150,1151,1154,1155,1158,1159,1162,1163,1166,1167,1169],{},"Инструкция ",[979,1152,1153],{},"COPY requirements.txt ."," читается слева направо: сначала указывается источник в контексте сборки, затем назначение внутри образа. В данном случае Docker берёт файл ",[979,1156,1157],{},"requirements.txt"," из корня контекста сборки на хостовой машине и копирует его в текущий рабочий каталог внутри образа, то есть в ",[979,1160,1161],{},"\u002Fapp\u002Frequirements.txt",". После этого инструкция ",[979,1164,1165],{},"RUN pip install --no-cache-dir -r requirements.txt"," выполняется уже внутри промежуточного контейнера сборки и использует только что скопированный файл зависимостей. Такое разбиение имеет практический смысл: если исходный код приложения изменится, но файл ",[979,1168,1157],{}," останется прежним, Docker сможет повторно использовать уже собранный слой с установленными пакетами.",[68,1171,1150,1172,1175,1176,1179,1180,1182,1183,1185,1186,1189,1190,1193,1194,1197,1198,1008,1201,1018,1204,1207],{},[979,1173,1174],{},"COPY . ."," требует отдельного пояснения, поскольку запись с двумя точками часто интерпретируется неверно. Первая точка обозначает источник, то есть весь текущий контекст сборки на стороне хоста, за исключением файлов, исключённых через ",[979,1177,1178],{},".dockerignore",". Вторая точка обозначает каталог назначения внутри образа, то есть текущий рабочий каталог ",[979,1181,1121],{},". Иными словами, после выполнения этой инструкции файлы проекта из локального каталога пользователя переносятся внутрь файловой системы образа в каталог ",[979,1184,1121],{},". Если в проекте присутствуют, например, ",[979,1187,1188],{},"app.py",", каталог ",[979,1191,1192],{},"templates\u002F"," и файл ",[979,1195,1196],{},"config.yaml",", то при отсутствии исключений они будут скопированы в ",[979,1199,1200],{},"\u002Fapp\u002Fapp.py",[979,1202,1203],{},"\u002Fapp\u002Ftemplates\u002F",[979,1205,1206],{},"\u002Fapp\u002Fconfig.yaml",".",[68,1209,1210,1211,1213,1214,1217,1218,1220],{},"Наконец, ",[979,1212,1017],{}," задаёт команду, которая будет выполнена при запуске контейнера по умолчанию. В рассматриваемом случае это команда ",[979,1215,1216],{},"python app.py",", выполняемая в каталоге ",[979,1219,1121],{},", где уже находятся скопированные файлы приложения и установленные зависимости.",[68,1222,1223,1224,1226],{},"Этот пример также показывает типичное ограничение Dockerfile. Инструкция ",[979,1225,1014],{}," не публикует порт во внешнюю сеть сама по себе, а лишь документирует намерение приложения использовать соответствующий порт внутри контейнера. Реальный проброс порта на хост задаётся уже при запуске контейнера. Если смешивать эти два уровня, можно ошибочно считать Dockerfile средством полного описания развертывания, хотя он описывает только образ и типовой режим его исполнения.",[83,1228,1230],{"id":1229},"пример-минимального-dockerfile-для-статического-содержимого","Пример минимального Dockerfile для статического содержимого",[68,1232,1233],{},"Если приложение не требует собственной среды выполнения, Dockerfile может быть значительно проще. Например, для публикации статического сайта достаточно использовать готовый веб-сервер:",[1031,1235,1237],{"className":1033,"code":1236,"language":1035,"meta":40,"style":40},"FROM nginx:1.27-alpine\n\nCOPY site\u002F \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\n\nEXPOSE 80\n",[979,1238,1239,1244,1248,1253,1257],{"__ignoreMap":40},[1039,1240,1241],{"class":1041,"line":395},[1039,1242,1243],{},"FROM nginx:1.27-alpine\n",[1039,1245,1246],{"class":1041,"line":41},[1039,1247,1049],{"emptyLinePlaceholder":48},[1039,1249,1250],{"class":1041,"line":369},[1039,1251,1252],{},"COPY site\u002F \u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F\n",[1039,1254,1255],{"class":1041,"line":1057},[1039,1256,1049],{"emptyLinePlaceholder":48},[1039,1258,1259],{"class":1041,"line":1062},[1039,1260,1261],{},"EXPOSE 80\n",[68,1263,1264,1265,1268],{},"Здесь образ не содержит этапов установки прикладных зависимостей, потому что они не нужны. Используется готовый сервер ",[979,1266,1267],{},"nginx",", а задача сборки сводится к копированию статических файлов в каталог, из которого сервер отдаёт содержимое. Этот пример полезен методически, поскольку показывает: Dockerfile должен описывать только те действия, которые действительно необходимы для конкретного артефакта. Попытка сделать любой Dockerfile одинаково сложным приводит не к универсальности, а к избыточности.",[68,1270,1271],{},"С методической точки зрения существенна разница между образом как продуктом сборки и контейнером как объектом времени выполнения. Dockerfile описывает образ, но не фиксирует все аспекты будущего запуска. Например, ограничения ресурсов, сетевые подключения и подключение томов обычно задаются на этапе создания контейнера, а не на этапе сборки. Если смешивать эти уровни, возникает неверная интерпретация назначения Dockerfile. Он должен содержать только те сведения, которые характеризуют сам артефакт и его типовое поведение, а не частную конфигурацию конкретного развертывания.",[119,1273,121,1274,121,1278],{},[123,1275],{"src":1276,"alt":1277},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-04\u002Fdockerfile_build_layers.svg","Формирование слоёв образа из инструкций Dockerfile",[128,1279,1277],{},[68,1281,1282],{},"Практический смысл Dockerfile состоит также в том, что он превращает упаковку приложения в повторяемую процедуру. Вместо того чтобы вручную устанавливать зависимости и копировать файлы на сервер, разработчик или инженер сопровождения описывает эти действия в явном виде. Затем одна и та же инструкция сборки может быть выполнена локально, в системе непрерывной интеграции и в учебной лаборатории. Отсюда вытекает локальный вывод: Dockerfile следует рассматривать как часть исходного кода проекта, а не как вспомогательный побочный файл.",[78,1284,1286],{"id":1285},"контекст-сборки-и-структура-входных-данных","Контекст сборки и структура входных данных",[68,1288,1289,1290,1018,1292,1294],{},"Корректность образа определяется не только содержимым Dockerfile, но и тем набором файлов, который передаётся системе сборки. Этот набор называется контекстом сборки (build context). Когда запускается сборка, Docker получает доступ к каталогу проекта или другой указанной директории и может использовать находящиеся в ней файлы в инструкциях ",[979,1291,999],{},[979,1293,1003],{},". Если в контекст включены лишние данные, например локальные артефакты сборки, временные файлы, каталоги зависимостей или секреты, они могут не только увеличить размер передаваемых данных, но и случайно попасть в образ.",[68,1296,1297,1298,1300,1301,1304],{},"По этой причине важную роль играет файл ",[979,1299,1178],{},". Его назначение сходно с назначением ",[979,1302,1303],{},".gitignore",", но задача иная: исключить из контекста сборки всё, что не должно участвовать в формировании образа. Типичная ошибка начинающих состоит в том, что они копируют в образ весь каталог проекта без анализа его состава. В результате в образ попадают тестовые данные, журналы, кэши пакетных менеджеров, конфиденциальные файлы среды разработки и иные объекты, не относящиеся к приложению. Такая практика противоречит принципу минимально необходимого состава артефакта.",[119,1306,121,1307,121,1311],{},[123,1308],{"src":1309,"alt":1310},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-04\u002Fbuild_context_dockerignore.svg","Контекст сборки и роль .dockerignore",[128,1312,1310],{},[68,1314,1315],{},"Контекст сборки имеет и эксплуатационный аспект. Чем больше его объём, тем медленнее сборка и тем выше вероятность, что несущественные изменения в одном из файлов нарушат кэширование последующих шагов. Следовательно, сопровождение образа начинается ещё до выполнения первой инструкции Dockerfile: инженер должен определить, какие именно исходные материалы являются частью поставки, а какие должны оставаться вне её границ. Этот вопрос связывает процесс контейнеризации с общей культурой управления проектом и конфигурацией репозитория.",[78,1317,1319],{"id":1318},"слои-образа-и-логика-пошаговой-сборки","Слои образа и логика пошаговой сборки",[68,1321,1322],{},"Ключевая особенность контейнерного образа состоит в его слоистой структуре. Каждый существенный шаг сборки формирует новый слой файловой системы или новый набор метаданных. На практике это означает, что образ не является монолитным архивом, собранным одним действием. Он представляет собой последовательность изменений, применяемых к базовому состоянию. Такая организация позволяет повторно использовать общие фрагменты между разными образами и снижает издержки хранения и передачи.",[68,1324,1325],{},"Слоистость имеет не только инфраструктурный, но и методический смысл. Если в Dockerfile сначала устанавливаются системные зависимости, затем копируются описания зависимостей приложения, затем выполняется их установка и лишь после этого копируется остальной исходный код, то повторная сборка после изменения одного исходного файла затронет только поздние шаги. Если же в начале сборки копируется весь проект целиком, а затем выполняется установка зависимостей, любое изменение в рабочем каталоге приведёт к повторному исполнению тяжёлых операций. Следовательно, структура Dockerfile должна отражать не только логику получения результата, но и ожидаемую частоту изменения отдельных составляющих проекта.",[68,1327,1328],{},"Следует учитывать, что слой не является независимым контейнером или самостоятельной файловой системой в полном смысле. Он представляет собой набор различий по отношению к предыдущему состоянию. Поэтому удаление файла на позднем этапе не означает, что соответствующие данные физически не присутствуют в нижележащих слоях. Этот момент особенно важен для безопасности и оптимизации. Если секретный файл был скопирован в образ на одном шаге, а затем удалён на следующем, его след может сохраниться в истории слоёв. Из этого вытекает важное ограничение: нежелательные данные нельзя сначала включать в сборку с расчётом на их последующее удаление.",[68,1330,1331],{},"Понимание слоистой структуры позволяет корректно интерпретировать и размер образа. Итоговый объём определяется не только «видимым» содержимым файловой системы контейнера, но и историей изменений, накопленных в слоях. Поэтому рациональная организация шагов сборки является одновременно вопросом производительности, безопасности и эксплуатационной ясности.",[78,1333,1335],{"id":1334},"кэширование-и-воспроизводимость-сборки","Кэширование и воспроизводимость сборки",[68,1337,1338],{},"Одним из преимуществ Docker является механизм кэширования шагов сборки. Если инструкция Dockerfile и все зависящие от неё входные данные не изменились, система может повторно использовать уже сформированный слой. Это существенно ускоряет повторную сборку, особенно если в процессе участвуют длительные операции: установка пакетов, загрузка зависимостей или компиляция. Однако кэширование не является самоцелью. Быстрая, но непредсказуемая сборка не решает инженерную задачу.",[68,1340,1341],{},"Более фундаментальным требованием является воспроизводимость. Под воспроизводимостью в данном контексте понимается способность получить функционально эквивалентный образ при повторном выполнении одной и той же процедуры сборки. Абсолютная двоичная идентичность в практических условиях достигается не всегда, поскольку на результат могут влиять временные метки, внешние источники пакетов и детали компиляции. Тем не менее инженерная цель состоит в том, чтобы минимизировать число нефиксированных факторов, от которых зависит результат.",[68,1343,1344],{},"Нарушение воспроизводимости часто связано с несколькими типичными причинами. Во-первых, используются плавающие версии зависимостей, когда при каждой сборке пакетный менеджер получает потенциально новый состав библиотек. Во-вторых, в Dockerfile включаются операции, зависящие от текущего времени, внешнего состояния сети или изменяющихся удалённых ресурсов без фиксации конкретных версий. В-третьих, сборка опирается на локальные, неформализованные предпосылки: существование файла в рабочем каталоге, который не включён в репозиторий, особенности конфигурации хоста или неявные секреты среды. Подобные зависимости особенно опасны в учебных и командных проектах, поскольку делают результат неустойчивым к переносу.",[68,1346,1347],{},"Следовательно, при проектировании Dockerfile необходимо стремиться к явной фиксации входных условий. Версии базового образа должны задаваться осмысленно, зависимости приложения должны устанавливаться на основе контролируемых описаний, а контекст сборки должен содержать только то, что действительно требуется для получения результата. Практическое значение этого принципа проявляется при сопровождении: если образ можно собрать только на одном рабочем месте и только у одного участника команды, такой артефакт нельзя считать надёжно сопровождаемым.",[68,1349,1350],{},"Кэширование и воспроизводимость связаны между собой, но не совпадают. Инструкция, максимально оптимизированная для повторного использования кэша, может оставаться плохо воспроизводимой, если она опирается на изменчивый внешний ресурс. И наоборот, воспроизводимая сборка может выполняться медленнее, если её шаги организованы нерационально. Поэтому инженер должен учитывать оба критерия одновременно и принимать компромиссные решения осознанно.",[78,1352,1354],{"id":1353},"практика-написания-dockerfile-и-типичные-ошибки","Практика написания Dockerfile и типичные ошибки",[68,1356,1357],{},"Несмотря на кажущуюся простоту синтаксиса, Dockerfile легко превращается в набор механически перенесённых команд из локальной инструкции развертывания. Такой подход ошибочен, потому что контейнерная сборка требует проектирования. Необходимо различать этапы, которые относятся к подготовке приложения, и этапы, которые должны выполняться уже после запуска контейнера. Например, миграции схемы базы данных, если они зависят от доступности внешнего сервиса, не всегда корректно помещать в стадию сборки образа. Сборка должна давать переносимый артефакт, а не пытаться решать все задачи будущей эксплуатации заранее.",[68,1359,1360],{},"Одна из распространённых ошибок связана с созданием чрезмерно универсального образа, который содержит инструменты разработки, компиляторы, тестовые утилиты и средства диагностики, хотя в реальном запуске требуется только приложение и его минимальные зависимости. Такой образ удобен на коротком отрезке обучения, но в долгосрочной перспективе он увеличивает размер поставки, расширяет поверхность атаки и затрудняет анализ состава. Противоположная крайность также нежелательна: чрезмерно «обрезанный» образ может затруднить диагностику ошибок и сопровождение. Отсюда следует, что выбор состава образа должен учитывать назначение среды: разработка, тестирование, обучение или эксплуатация.",[68,1362,1363,1364,1366],{},"Ещё одна типичная ошибка состоит в объединении несвязанных команд в одну длинную инструкцию ",[979,1365,995],{}," исключительно ради уменьшения числа слоёв. Формально это может уменьшить историю изменений, но ухудшает читаемость, затрудняет сопровождение и делает диагностику сбоев менее прозрачной. Сокращение числа слоёв не должно становиться самоцелью. Более правильный подход заключается в логическом группировании действий: в пределах одного шага объединяются тесно связанные операции, а между шагами сохраняется понятная структура.",[68,1368,1369],{},"Следует также избегать помещения конфиденциальных данных непосредственно в Dockerfile или в контекст сборки. Пароли, токены доступа и приватные ключи не должны становиться частью образа, поскольку это нарушает базовые требования безопасности и создаёт долговременный риск утечки. Если в процессе сборки требуется доступ к закрытому ресурсу, должны использоваться специальные механизмы безопасной передачи секретов, а не их явное включение в текст сценария.",[68,1371,1372],{},"Локальный вывод этого раздела состоит в том, что хороший Dockerfile отличается не только корректностью, но и объяснимостью. Его структура должна позволять понять, какие шаги выполняются, зачем они нужны и какие предпосылки они предполагают. Такая прозрачность особенно важна в учебном курсе, где цель состоит не просто в получении работающего артефакта, а в формировании устойчивого инженерного мышления.",[78,1374,1376],{"id":1375},"оптимизация-образов-и-многоэтапная-сборка","Оптимизация образов и многоэтапная сборка",[68,1378,1379],{},"После того как базовая процедура сборки становится корректной и воспроизводимой, возникает задача оптимизации образа. Чаще всего оптимизация понимается как уменьшение размера. Это действительно важный показатель: более компактный образ быстрее передаётся по сети, быстрее загружается в среду исполнения и обычно содержит меньше лишних компонентов. Однако размер не является единственным критерием. Оптимизация должна сохранять функциональную полноту, ясность состава и сопровождаемость.",[68,1381,1382],{},"Наиболее важным инструментом оптимизации является многоэтапная сборка (multi-stage build). Её идея состоит в том, что разные стадии Dockerfile выполняют разные роли. На одной стадии могут устанавливаться компиляторы и инструменты сборки, на другой — выполняться компиляция или упаковка приложения, а в итоговый образ переносятся только результаты, необходимые для запуска. Такой подход позволяет отделить среду построения артефакта от среды его исполнения. Практическое следствие очевидно: в финальном образе не остаются лишние инструменты, которые увеличивали бы размер и расширяли бы потенциальную поверхность атаки.",[119,1384,121,1385,121,1389],{},[123,1386],{"src":1387,"alt":1388},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-04\u002Fmultistage_build.svg","Многоэтапная сборка: стадия построения и финальный образ",[128,1390,1388],{},[68,1392,1393],{},"Многоэтапная сборка особенно полезна для компилируемых языков, однако её значение не исчерпывается ими. Даже в интерпретируемых средах она помогает отделить подготовительные шаги, тестовые зависимости и служебные файлы от финальной поставки. Вместе с тем следует учитывать ограничения. Слишком агрессивная оптимизация, ориентированная только на уменьшение размера, может привести к тому, что образ станет неудобным для диагностики, а процесс сборки — слишком сложным для сопровождения. Следовательно, инженер должен выбирать такую степень оптимизации, которая соответствует назначению конкретного артефакта.",[68,1395,1396],{},"Оптимизация затрагивает и выбор базового образа. Существуют полноценные дистрибутивные образы, минимальные варианты и специализированные среды исполнения. Полноценный образ часто проще в сопровождении, поскольку содержит стандартные инструменты и ожидаемую структуру системы. Минимальный образ уменьшает размер и снижает число потенциально уязвимых компонентов, но может осложнить разбор сбоев и потребовать более аккуратной настройки зависимостей. Выбор между этими вариантами нельзя делать абстрактно: он зависит от требований к безопасности, наблюдаемости, переносимости и удобству сопровождения.",[68,1398,1399],{},"Таким образом, оптимизация не сводится к механическому «сжатию» образа. Она предполагает инженерный анализ того, какие компоненты действительно необходимы в среде выполнения, какие шаги должны остаться на стадии сборки и какие компромиссы приемлемы для конкретного сценария использования.",[78,1401,1403],{"id":1402},"версионирование-образов-и-управление-изменениями","Версионирование образов и управление изменениями",[68,1405,1406,1407,1410],{},"Образ становится полноценным артефактом поставки только тогда, когда им можно управлять как версией программного продукта. Для этого используются теги (tags), позволяющие различать варианты образа по версии, назначению или каналу поставки. Однако использование тегов нередко сопровождается методической ошибкой: тег воспринимается как абсолютная и неизменная сущность. На практике тег является лишь именованной ссылкой на конкретный образ и может быть переназначен. Поэтому теги вида ",[979,1408,1409],{},"latest"," удобны для демонстрации, но плохо подходят для управляемого сопровождения.",[68,1412,1413],{},"С инженерной точки зрения предпочтительно, чтобы версия образа была связана с версией исходного кода, конфигурации сборки и, при необходимости, с состоянием цепочки поставки. Это не означает, что в учебном курсе требуется вводить сложную схему релизного управления, но важно сформировать правильный принцип: образ должен быть однозначно соотнесён с тем исходным состоянием проекта, из которого он получен. Тогда появляется возможность анализировать изменения, воспроизводить прежние версии, выполнять откат и проверять, какой именно артефакт был развернут.",[68,1415,1416],{},"Управление изменениями включает не только присвоение имени версии, но и пересборку образов при обновлении базовых компонентов. Даже если исходный код приложения не менялся, может измениться базовый образ, исправиться уязвимость в системной библиотеке или обновиться среда исполнения. Следовательно, сопровождение контейнерного образа не заканчивается в момент первой успешной сборки. Образ требует периодического пересмотра, повторной сборки и контроля актуальности своих зависимостей.",[119,1418,121,1419,121,1423],{},[123,1420],{"src":1421,"alt":1422},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-04\u002Fimage_lifecycle.svg","Жизненный цикл контейнерного образа: от сборки до развёртывания",[128,1424,1422],{},[68,1426,1427],{},"Это обстоятельство особенно важно в облачной среде, где образы часто используются многократно и автоматически распространяются по нескольким средам. Если организация или учебная группа не контролирует происхождение и актуальность образов, она фактически теряет прозрачность поставки. Локальный вывод таков: версионирование образов должно поддерживать не только удобство использования, но и прослеживаемость жизненного цикла артефакта.",[78,1429,1431],{"id":1430},"публикация-образов-и-роль-реестров","Публикация образов и роль реестров",[68,1433,1434],{},"Собранный образ приносит практическую пользу лишь тогда, когда его можно передать в среду исполнения или другой участник процесса может его получить. Для этого используются реестры контейнерных образов (registry). Реестр выполняет роль централизованного хранилища, где образам присваиваются имена, версии и правила доступа. С точки зрения архитектуры поставки реестр является связующим звеном между сборкой и развертыванием.",[68,1436,1437],{},"Публикация образа в реестр позволяет перейти от локального, разового использования к повторяемому сценарию. Один и тот же артефакт может быть собран в системе непрерывной интеграции, затем опубликован в утверждённое хранилище и оттуда использован при развертывании на тестовом или эксплуатационном контуре. Этот подход принципиально отличается от практики «собирать на каждом сервере отдельно». Локальная сборка на целевом узле увеличивает вариативность результата и делает сопровождение менее контролируемым.",[68,1439,1440],{},"Реестры могут быть публичными и частными. Публичные реестры удобны как источник типовых базовых образов и как средство распространения открытых приложений. Частные реестры важны тогда, когда требуется ограничить доступ, обеспечить контроль состава поставляемых артефактов или встроить публикацию в корпоративный контур разработки. Независимо от типа реестра, инженер должен учитывать политику именования, правила аутентификации, сроки хранения образов и процедуры удаления устаревших версий.",[68,1442,1443],{},"Публикация связана и с вопросами доверия. Образ, полученный из внешнего источника, не следует автоматически считать безопасным или корректным. Требуется понимать, кто его опубликовал, как он был собран и насколько прозрачен его состав. В учебном процессе этот вопрос особенно полезен методически: он показывает, что контейнеризация не отменяет необходимость критически оценивать происхождение программных компонентов, а, напротив, требует ещё большей дисциплины в управлении артефактами.",[78,1445,1447],{"id":1446},"сопровождение-образов-в-цепочке-поставки","Сопровождение образов в цепочке поставки",[68,1449,1450],{},"На зрелом уровне контейнерный образ рассматривается как объект цепочки поставки программного обеспечения, а не как разовый технический продукт. Это означает, что сборка образа должна быть встроена в процессы проверки исходного кода, автоматического тестирования, анализа состава зависимостей и публикации результатов. Чем меньше ручных действий требуется для получения и размещения образа, тем ниже риск человеческой ошибки и тем выше воспроизводимость.",[68,1452,1453],{},"Сопровождение образов включает несколько повторяющихся операций: пересборку при изменении кода, обновление базовых компонентов, проверку корректности Dockerfile, контроль размера и состава образа, а также удаление устаревших или неподдерживаемых версий из хранилища. Эти задачи не являются внешними по отношению к контейнеризации; они составляют её нормальный эксплуатационный контур. Ошибочно считать, что после упаковки приложения в контейнер проблема сопровождения исчезает. Меняется не наличие этой проблемы, а форма её решения.",[68,1455,1456],{},"Для учебного курса принципиально важно усвоить следующее: контейнерный образ должен быть прослеживаемым, воспроизводимым и управляемым. Прослеживаемость означает возможность понять происхождение артефакта. Воспроизводимость означает возможность получить сопоставимый результат повторно. Управляемость означает возможность осознанно обновлять, публиковать, отзывать и заменять версии. Именно сочетание этих свойств превращает контейнерный образ в полноценный объект инженерной практики и подготавливает переход к дальнейшим темам курса, связанным с запуском, композицией сервисов и облачной эксплуатацией.",[78,1458,348],{"id":347},[68,1460,1461],{},"Сборка контейнерного образа является не вспомогательной технической процедурой, а центральным элементом контейнерной модели поставки приложения. Dockerfile задаёт формальное описание артефакта, а качество этого описания определяет воспроизводимость, переносимость и сопровождаемость результата. По этой причине образ следует проектировать так же внимательно, как и само приложение.",[68,1463,1464,1465,1467],{},"Слоистая структура образов, кэширование и организация контекста сборки требуют осознанной инженерной логики. Нерациональный порядок шагов, включение лишних файлов и использование нефиксированных зависимостей приводят к росту размера образа, ухудшению предсказуемости и снижению безопасности. Напротив, аккуратная структура Dockerfile, применение ",[979,1466,1178],{}," и фиксация входных условий делают сборку устойчивой и объяснимой.",[68,1469,1470],{},"Оптимизация, многоэтапная сборка, версионирование и публикация в реестры показывают, что образ должен сопровождаться на всём протяжении жизненного цикла. Его необходимо не только однажды собрать, но и регулярно пересматривать, обновлять и связывать с контролируемой цепочкой поставки. Это создаёт основу для следующего этапа изучения: управления контейнерами в среде выполнения и организации повторяемого развертывания приложений.",[1472,1473,1474],"style",{},"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);}",{"title":40,"searchDepth":41,"depth":41,"links":1476},[1477,1481,1482,1483,1484,1485,1486,1487,1488,1489],{"id":973,"depth":41,"text":974,"children":1478},[1479,1480],{"id":1025,"depth":369,"text":1026},{"id":1229,"depth":369,"text":1230},{"id":1285,"depth":41,"text":1286},{"id":1318,"depth":41,"text":1319},{"id":1334,"depth":41,"text":1335},{"id":1353,"depth":41,"text":1354},{"id":1375,"depth":41,"text":1376},{"id":1402,"depth":41,"text":1403},{"id":1430,"depth":41,"text":1431},{"id":1446,"depth":41,"text":1447},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-04-content",{"title":956,"description":964},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-04-content","topic-04","M2cqTS9XyY_r1M4xpBtNicxlquQ-rPTv3f74FdKdsSI",{"id":1497,"title":1498,"body":1499,"course_slug":43,"description":1506,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":1888,"navigation":48,"path":1889,"section":392,"seo":1890,"stem":1891,"topic_number":1062,"topic_slug":1892,"__hash__":1893},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content.md","Управление контейнерами в среде выполнения",{"type":7,"value":1500,"toc":1859},[1501,1504,1507,1510,1513,1517,1520,1524,1527,1530,1536,1540,1543,1546,1549,1553,1556,1562,1565,1569,1572,1575,1578,1582,1585,1588,1596,1600,1603,1607,1610,1614,1617,1620,1623,1627,1638,1651,1670,1678,1682,1685,1688,1691,1694,1697,1701,1704,1708,1714,1717,1720,1724,1731,1737,1741,1747,1753,1757,1763,1769,1777,1781,1784,1788,1791,1795,1798,1801,1805,1808,1811,1819,1823,1829,1832,1835,1839,1842,1845,1847,1850,1853,1856],[10,1502,1498],{"id":1503},"управление-контейнерами-в-среде-выполнения",[68,1505,1506],{},"Предыдущие темы курса были посвящены созданию контейнерного образа как воспроизводимого артефакта поставки. Однако образ сам по себе не является работающим приложением. Он представляет собой инертное описание файловой системы и параметров запуска, которое приобретает практический смысл лишь тогда, когда на его основе создаётся и запускается контейнер. Именно на этом этапе начинается управление контейнером в среде выполнения: инженер должен определить, как приложение будет запущено, каким образом оно взаимодействует с внешним окружением, как контролировать его поведение и как корректно обновлять.",[68,1508,1509],{},"Управление контейнерами на этапе выполнения принципиально отличается от управления образами на этапе сборки. Если сборка отвечает на вопрос «что содержит артефакт», то среда выполнения отвечает на вопрос «как именно этот артефакт функционирует в конкретных условиях». Один и тот же образ может быть запущен с различными переменными окружения, ограничениями ресурсов, сетевыми настройками и политиками перезапуска. Поэтому корректная эксплуатация контейнера требует не только умения собрать образ, но и понимания того, какие параметры определяют поведение контейнера после его создания.",[68,1511,1512],{},"В рамках данной темы рассматриваются четыре взаимосвязанных аспекта. Во-первых, параметры запуска контейнера и их влияние на поведение приложения. Во-вторых, роль основного процесса и механизм завершения контейнера. В-третьих, средства инспекции и диагностики. В-четвёртых, принцип неизменяемой инфраструктуры и модель обновления контейнерных приложений. Вместе эти аспекты формируют представление о контейнере не как о «чёрном ящике», а как об управляемом объекте с определённым жизненным циклом.",[78,1514,1516],{"id":1515},"параметры-запуска-контейнера","Параметры запуска контейнера",[68,1518,1519],{},"Запуск контейнера — это не просто создание экземпляра образа. Это конфигурирование среды выполнения, в которой приложение будет работать. Параметры запуска определяют, какие ресурсы доступны контейнеру, как он взаимодействует с сетью, какие данные получает из внешнего окружения и каким образом среда выполнения реагирует на его остановку или аварийное завершение. Понимание этих параметров необходимо для перехода от разового экспериментального запуска к воспроизводимому и контролируемому развёртыванию.",[83,1521,1523],{"id":1522},"переменные-окружения-и-аргументы-команды","Переменные окружения и аргументы команды",[68,1525,1526],{},"Одним из основных способов передачи конфигурации контейнеру являются переменные окружения (environment variables). Они задаются при запуске и доступны процессу внутри контейнера точно так же, как переменные окружения в обычной операционной системе. Переменные окружения удобны тем, что позволяют отделить конфигурацию от образа: один и тот же артефакт может быть запущен с разными параметрами подключения к базе данных, адресами внешних сервисов или режимами работы приложения.",[68,1528,1529],{},"Вместе с тем переменные окружения имеют ограничения. Они не предназначены для передачи сложных структурированных данных и не обеспечивают конфиденциальности значений без дополнительных мер. Если приложение требует передачи секретов, таких как пароли или ключи доступа, использование переменных окружения в явном виде создаёт риск: значения могут быть видны при инспекции контейнера или в журналах среды выполнения. Этот аспект становится особенно важным при переходе к эксплуатационным средам, где управление секретами требует отдельных механизмов.",[68,1531,1532,1533,1535],{},"Помимо переменных окружения, при запуске контейнера можно передавать аргументы командной строки, которые переопределяют команду, заданную в образе инструкцией ",[979,1534,1017],{},". Это позволяет, например, запустить контейнер с тем же образом, но в диагностическом режиме, с альтернативной конфигурацией или с выполнением вспомогательной задачи. Такое разделение между образом и параметрами запуска подчёркивает принципиальную идею: образ описывает «что может быть запущено», а параметры запуска определяют «как именно это запускается в данный момент».",[83,1537,1539],{"id":1538},"точка-входа-и-команда-по-умолчанию","Точка входа и команда по умолчанию",[68,1541,1542],{},"Для правильного понимания параметров запуска необходимо различать два механизма, определяющих поведение контейнера при старте: точку входа (entrypoint) и команду по умолчанию (cmd). Точка входа задаёт основной исполняемый файл или сценарий, который будет вызван при запуске контейнера. Команда по умолчанию определяет аргументы, передаваемые точке входа. Если точка входа не задана явно, среда выполнения использует стандартную оболочку операционной системы.",[68,1544,1545],{},"На практике разделение между entrypoint и cmd позволяет создавать образы, которые ведут себя как «параметризуемые команды». Например, образ утилиты может использовать в качестве точки входа саму утилиту, а аргументы командной строки, переданные при запуске контейнера, будут подставлены как параметры. Это удобно для инструментов, выполняющих конкретные задачи, но нежелательно для сервисных приложений, где точка входа и команда обычно фиксируются на этапе сборки образа.",[68,1547,1548],{},"Типичной ошибкой является смешение ролей entrypoint и cmd. Если обе инструкции используются одновременно, но их взаимодействие не продумано, результат может оказаться неожиданным. Например, если пользователь передаёт аргументы при запуске контейнера, они заменяют значение cmd, но не entrypoint. Непонимание этой логики приводит к трудно диагностируемым сбоям при запуске, особенно если поведение образа не документировано.",[83,1550,1552],{"id":1551},"проброс-портов-и-сетевые-параметры","Проброс портов и сетевые параметры",[68,1554,1555],{},"Контейнер по умолчанию работает в изолированном сетевом пространстве. Приложение внутри контейнера может слушать сетевой порт, но это не означает, что порт доступен извне. Для предоставления доступа к сервису из внешней сети используется проброс портов (port mapping): при запуске контейнера указывается соответствие между портом хостовой машины и портом внутри контейнера.",[68,1557,1558,1559,1561],{},"Важно различать два уровня описания. Инструкция ",[979,1560,1014],{}," в Dockerfile лишь документирует намерение приложения использовать определённый порт. Она не выполняет реального проброса. Реальное сетевое подключение формируется только при запуске контейнера, когда указывается конкретное соответствие портов. Смешение этих двух уровней является распространённой ошибкой, которая приводит к тому, что разработчик ожидает сетевой доступности, не выполнив необходимого действия при запуске.",[68,1563,1564],{},"Сетевые параметры запуска не ограничиваются пробросом портов. Контейнер может быть подключён к пользовательской сети, в которой действуют собственные правила разрешения имён и маршрутизации. Эти вопросы подробно рассматриваются в последующей теме, посвящённой сетевому взаимодействию контейнерных приложений. Однако уже на данном этапе необходимо понимать, что сетевая конфигурация является частью параметров запуска, а не свойством образа.",[83,1566,1568],{"id":1567},"ограничение-ресурсов","Ограничение ресурсов",[68,1570,1571],{},"Среда выполнения контейнеров позволяет задавать ограничения на использование вычислительных ресурсов: процессорного времени и оперативной памяти. Эти ограничения реализуются через механизм групп управления ресурсами (control groups, cgroups), который был рассмотрен в теме, посвящённой механизмам изоляции.",[68,1573,1574],{},"Ограничение ресурсов выполняет несколько функций. Во-первых, оно защищает хостовую систему от ситуации, в которой один контейнер потребляет все доступные ресурсы и вызывает деградацию соседних процессов. Во-вторых, оно позволяет моделировать условия, приближённые к реальной эксплуатации, где приложение работает в рамках выделенного бюджета ресурсов. В-третьих, ограничения служат инструментом раннего обнаружения проблем: если приложение не способно работать в заданных рамках, это сигнализирует о необходимости оптимизации или пересмотра архитектуры.",[68,1576,1577],{},"Следует учитывать, что поведение контейнера при превышении лимита зависит от типа ресурса. Превышение лимита памяти обычно приводит к принудительному завершению контейнера операционной системой (механизм OOM Killer). Превышение лимита процессорного времени приводит к замедлению, но не к завершению. Это различие имеет практическое значение: приложение, не рассчитанное на ограниченный объём памяти, может аварийно завершаться без видимой ошибки в журналах приложения, поскольку завершение инициировано не самим процессом, а ядром операционной системы.",[83,1579,1581],{"id":1580},"политика-перезапуска","Политика перезапуска",[68,1583,1584],{},"При запуске контейнера можно задать политику перезапуска (restart policy), которая определяет поведение среды выполнения при завершении контейнера. Существуют несколько вариантов: контейнер не перезапускается; перезапускается всегда; перезапускается только в случае ошибки; перезапускается всегда, кроме явной остановки пользователем.",[68,1586,1587],{},"Политика перезапуска является простейшим механизмом обеспечения живучести сервиса на уровне одного узла. Она полезна для длительно работающих приложений, которые должны автоматически восстанавливаться после кратковременного сбоя. Однако этот механизм имеет существенные ограничения. Он не способен обнаружить ситуацию, когда приложение формально работает, но функционально неработоспособно. Он не позволяет распределить нагрузку между несколькими экземплярами. Он не обеспечивает управляемого обновления. Все эти задачи требуют более развитых средств, которые предоставляются оркестраторами и рассматриваются в последующих темах курса.",[119,1589,121,1590,121,1594],{},[123,1591],{"src":1592,"alt":1593},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fcontainer_run_parameters.svg","Параметры запуска контейнера и их связь с образом",[128,1595,1593],{},[83,1597,1599],{"id":1598},"связь-параметров-запуска-с-воспроизводимостью","Связь параметров запуска с воспроизводимостью",[68,1601,1602],{},"Совокупность параметров запуска определяет не только текущее поведение контейнера, но и воспроизводимость развёртывания. Если параметры задаются вручную при каждом запуске, высока вероятность расхождения между средами разработки, тестирования и эксплуатации. Этот риск нарастает по мере увеличения числа параметров и числа контейнеров в составе приложения. Поэтому уже на данном этапе следует осознавать необходимость фиксации параметров запуска в формализованном описании. Средства такой фиксации, в первую очередь Docker Compose, рассматриваются в последующих темах. Однако важно понимать, что потребность в декларативном описании развёртывания возникает не из абстрактного стремления к порядку, а из конкретных проблем, связанных с ручным управлением параметрами запуска.",[78,1604,1606],{"id":1605},"основной-процесс-и-завершение-контейнера","Основной процесс и завершение контейнера",[68,1608,1609],{},"Контейнер в среде Docker не является самостоятельной операционной системой и не содержит полноценного менеджера процессов. Он представляет собой оболочку для выполнения одного основного процесса, который определяет жизненный цикл контейнера. Понимание этого принципа является ключевым для корректной эксплуатации контейнерных приложений.",[83,1611,1613],{"id":1612},"процесс-pid-1","Процесс PID 1",[68,1615,1616],{},"При запуске контейнера среда выполнения создаёт в изолированном пространстве имён процесс, который получает идентификатор PID 1. Этот процесс является корневым для всего дерева процессов внутри контейнера. В обычной операционной системе PID 1 выполняет особую роль: он является процессом инициализации (init), который отвечает за порождение и контроль дочерних процессов, а также за корректную обработку сигналов.",[68,1618,1619],{},"В контейнерной среде процесс PID 1 обычно является самим приложением, а не специализированным менеджером инициализации. Это означает, что приложение принимает на себя обязанности, которые в традиционной системе выполнялись бы системным процессом init. В частности, оно должно корректно обрабатывать сигналы завершения и, при необходимости, управлять дочерними процессами.",[68,1621,1622],{},"Связь между PID 1 и жизненным циклом контейнера является прямой. Когда процесс с PID 1 завершается, контейнер прекращает существование, вне зависимости от того, продолжают ли работать какие-либо дочерние процессы. Код завершения процесса PID 1 становится кодом завершения контейнера. Следовательно, проектирование основного процесса контейнера — это не только задача разработки приложения, но и задача корректной интеграции с контейнерной средой.",[83,1624,1626],{"id":1625},"обработка-сигналов-и-корректное-завершение","Обработка сигналов и корректное завершение",[68,1628,1629,1630,1633,1634,1637],{},"Когда пользователь или среда выполнения инициирует остановку контейнера, Docker отправляет процессу PID 1 сигнал ",[979,1631,1632],{},"SIGTERM",". Этот сигнал предлагает процессу завершиться корректно: закрыть сетевые соединения, сбросить буферы данных, освободить ресурсы. Если процесс не завершается в течение установленного времени ожидания (по умолчанию 10 секунд), Docker отправляет сигнал ",[979,1635,1636],{},"SIGKILL",", который принудительно прекращает выполнение процесса без возможности корректного завершения.",[68,1639,1640,1641,1643,1644,1647,1648,1650],{},"Проблема состоит в том, что не все приложения корректно обрабатывают ",[979,1642,1632],{},". Если приложение запущено через оболочку (shell), например через ",[979,1645,1646],{},"CMD [\"sh\", \"-c\", \"python app.py\"]",", то PID 1 получает оболочка, а не приложение. Оболочка, как правило, не передаёт полученный сигнал дочернему процессу. В результате приложение не получает уведомления о необходимости завершения и продолжает работать до истечения тайм-аута, после чего принудительно завершается сигналом ",[979,1649,1636],{},". Такое поведение приводит к потере данных, незавершённым транзакциям и увеличению времени остановки контейнера.",[68,1652,1653,1654,1018,1656,1658,1659,1662,1663,1666,1667,1207],{},"Для избежания этой проблемы рекомендуется использовать exec-форму инструкций ",[979,1655,1017],{},[979,1657,1021],{}," в Dockerfile (например, ",[979,1660,1661],{},"CMD [\"python\", \"app.py\"]"," вместо ",[979,1664,1665],{},"CMD python app.py","). В exec-форме приложение становится процессом PID 1 непосредственно и получает сигналы напрямую. Альтернативный подход — использование легковесного процесса инициализации (init process), который берёт на себя роль PID 1 и корректно передаёт сигналы основному приложению. Docker предоставляет встроенную поддержку такого механизма через параметр запуска ",[979,1668,1669],{},"--init",[119,1671,121,1672,121,1676],{},[123,1673],{"src":1674,"alt":1675},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fpid1_signal_handling.svg","Процесс PID 1 и обработка сигналов в контейнере",[128,1677,1675],{},[83,1679,1681],{"id":1680},"типичные-причины-немедленного-завершения-контейнера","Типичные причины немедленного завершения контейнера",[68,1683,1684],{},"Одна из наиболее частых проблем при начальном знакомстве с контейнерами — контейнер, который завершается сразу после запуска. Это поведение, как правило, объясняется одной из нескольких причин.",[68,1686,1687],{},"Во-первых, основной процесс может завершаться, потому что он предназначен для выполнения одноразовой задачи, а не для длительной работы. Например, если в качестве команды указана утилита, выводящая текст и завершающаяся, контейнер прекратит существование после её выполнения. Контейнер существует ровно столько, сколько работает его основной процесс.",[68,1689,1690],{},"Во-вторых, процесс может завершаться с ошибкой из-за отсутствия необходимых файлов, переменных окружения или зависимостей. В этом случае код завершения контейнера будет ненулевым, а журналы могут содержать диагностическое сообщение.",[68,1692,1693],{},"В-третьих, приложение может быть запущено в фоновом режиме (как демон), после чего основной процесс, породивший демон, завершается. С точки зрения контейнерной модели это означает завершение PID 1 и, следовательно, остановку контейнера. Приложения, предназначенные для работы в контейнере, должны выполняться на переднем плане (foreground), а не уходить в фоновый режим.",[68,1695,1696],{},"Понимание этих причин позволяет избежать типичной ошибки начинающих, которые пытаются решить проблему немедленного завершения добавлением искусственных задержек или бесконечных циклов ожидания. Такие «решения» маскируют реальную проблему и создают контейнеры, которые формально работают, но не выполняют полезной функции.",[78,1698,1700],{"id":1699},"инспекция-и-сопровождение-контейнера","Инспекция и сопровождение контейнера",[68,1702,1703],{},"Работающий контейнер является объектом наблюдения и диагностики. Способность получить информацию о состоянии контейнера, его конфигурации и поведении приложения внутри него — это обязательная часть эксплуатационной дисциплины. Docker предоставляет несколько инструментов для решения этой задачи, каждый из которых отвечает на определённый класс вопросов.",[83,1705,1707],{"id":1706},"журналы-приложения","Журналы приложения",[68,1709,1710,1711,1207],{},"Основным источником информации о поведении приложения в контейнере являются журналы (logs). В контейнерной модели стандартной практикой считается направление вывода приложения в стандартные потоки: стандартный поток вывода (stdout) и стандартный поток ошибок (stderr). Docker перехватывает эти потоки и сохраняет их содержимое, предоставляя доступ через команду ",[979,1712,1713],{},"docker logs",[68,1715,1716],{},"Такой подход имеет несколько достоинств. Он не требует настройки файлового журналирования внутри контейнера. Он позволяет централизованно собирать журналы из множества контейнеров. Он совместим с различными драйверами журналирования, которые могут перенаправлять вывод во внешние системы хранения и анализа.",[68,1718,1719],{},"Вместе с тем следует учитывать ограничения. Не все приложения по умолчанию пишут в стандартные потоки. Некоторые пишут журналы в файлы внутри контейнера, что требует дополнительной настройки для их извлечения. Кроме того, объём журналов, хранимых Docker, по умолчанию не ограничен, что при интенсивном журналировании может привести к исчерпанию дискового пространства на хосте. Поэтому в эксплуатационных средах политика ротации и ограничения размера журналов является обязательной мерой.",[83,1721,1723],{"id":1722},"инспекция-конфигурации-контейнера","Инспекция конфигурации контейнера",[68,1725,1726,1727,1730],{},"Команда ",[979,1728,1729],{},"docker inspect"," предоставляет детальную информацию о конфигурации контейнера: его параметрах запуска, сетевых настройках, подключённых томах, переменных окружения, состоянии и метаданных. Эта информация полезна для диагностики расхождений между ожидаемой и фактической конфигурацией. Например, если приложение не подключается к базе данных, инспекция может показать, что переменная окружения с адресом подключения содержит неверное значение или не задана.",[68,1732,1733,1734,1736],{},"Инспекция также позволяет проверить фактические ограничения ресурсов, назначенные контейнеру, его сетевой адрес в контейнерной сети, политику перезапуска и другие параметры, которые могли быть заданы при запуске. Это делает ",[979,1735,1729],{}," инструментом верификации: инженер может убедиться, что контейнер запущен именно с теми параметрами, которые были запланированы.",[83,1738,1740],{"id":1739},"выполнение-команд-внутри-контейнера","Выполнение команд внутри контейнера",[68,1742,1726,1743,1746],{},[979,1744,1745],{},"docker exec"," позволяет запустить дополнительный процесс внутри работающего контейнера. Чаще всего это интерактивная оболочка, с помощью которой можно исследовать файловую систему, проверить наличие файлов, просмотреть сетевую конфигурацию или выполнить диагностическую команду. Этот инструмент полезен при отладке и локализации проблем.",[68,1748,1749,1750,1752],{},"Однако использование ",[979,1751,1745],{}," требует осознанного подхода. Выполнение команд внутри контейнера допустимо как средство диагностики, но не должно становиться инструментом ручной модификации среды. Если инженер вручную устанавливает пакет, изменяет конфигурационный файл или перезапускает процесс внутри контейнера, эти изменения не отражаются в образе и будут потеряны при пересоздании контейнера. Такой подход противоречит принципу неизменяемой инфраструктуры, который рассматривается далее.",[83,1754,1756],{"id":1755},"мониторинг-потребления-ресурсов","Мониторинг потребления ресурсов",[68,1758,1726,1759,1762],{},[979,1760,1761],{},"docker stats"," предоставляет информацию о текущем потреблении ресурсов контейнерами: использование процессора, оперативной памяти, сетевого ввода-вывода и дискового ввода-вывода. Эти данные полезны для оценки фактической нагрузки и обнаружения аномалий: утечек памяти, чрезмерного потребления процессора или неожиданной сетевой активности.",[68,1764,1765,1766,1768],{},"На уровне единичного контейнера ",[979,1767,1761],{}," является инструментом оперативного наблюдения. Для систематического мониторинга, хранения истории метрик и настройки оповещений требуются внешние системы наблюдаемости, которые рассматриваются в теме, посвящённой безопасности и наблюдаемости контейнерных приложений.",[119,1770,121,1771,121,1775],{},[123,1772],{"src":1773,"alt":1774},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fcontainer_inspection.svg","Средства инспекции и диагностики контейнера",[128,1776,1774],{},[83,1778,1780],{"id":1779},"границы-допустимого-вмешательства","Границы допустимого вмешательства",[68,1782,1783],{},"Инструменты инспекции и диагностики формируют важную дисциплинарную границу. Наблюдение за контейнером, чтение его журналов, проверка конфигурации и анализ потребления ресурсов — это нормальные эксплуатационные действия. Однако ручное изменение содержимого работающего контейнера, установка дополнительного программного обеспечения, модификация конфигурационных файлов внутри контейнера — это действия, нарушающие воспроизводимость среды и создающие расхождение между артефактом (образом) и реальным состоянием системы. Разграничение между допустимой диагностикой и нежелательной модификацией является одним из ключевых навыков инженера, работающего с контейнерами.",[78,1785,1787],{"id":1786},"неизменяемость-среды-и-обновление-приложений","Неизменяемость среды и обновление приложений",[68,1789,1790],{},"Контейнерная модель развёртывания основана на принципе неизменяемой инфраструктуры (immutable infrastructure). Этот принцип утверждает, что компоненты среды выполнения не модифицируются после создания. Вместо внесения изменений в работающий экземпляр создаётся новый экземпляр на основе обновлённого артефакта. Применительно к контейнерам это означает, что обновление приложения производится не путём изменения содержимого работающего контейнера, а путём пересборки образа и пересоздания контейнера.",[83,1792,1794],{"id":1793},"суть-принципа-неизменяемости","Суть принципа неизменяемости",[68,1796,1797],{},"В традиционной модели эксплуатации серверов обновление приложения нередко выполнялось путём ручного копирования файлов, установки пакетов и изменения конфигурации на работающем сервере. Такой подход порождает ряд проблем. Состояние сервера постепенно отклоняется от первоначального описания (явление, известное как «дрейф конфигурации», configuration drift). Воспроизвести точное состояние системы на другом узле становится невозможно без детальной документации каждого выполненного действия. Откат к предыдущей версии затруднён, поскольку изменения накапливаются без формальной фиксации.",[68,1799,1800],{},"Принцип неизменяемости устраняет эти проблемы путём жёсткого разделения двух операций. Первая операция — конструирование артефакта (образа), которая выполняется один раз и фиксируется. Вторая операция — запуск экземпляра (контейнера) на основе этого артефакта. Если требуется изменение, создаётся новый артефакт, а прежний экземпляр заменяется новым. Контейнер не модифицируется; он заменяется.",[83,1802,1804],{"id":1803},"цикл-обновления-приложения","Цикл обновления приложения",[68,1806,1807],{},"Корректный цикл обновления контейнерного приложения состоит из нескольких последовательных этапов. Сначала вносятся изменения в исходный код, конфигурацию сборки или зависимости приложения. Затем выполняется пересборка образа, в результате которой создаётся новый артефакт с обновлённым содержимым. После этого существующий контейнер останавливается, и на его месте создаётся новый контейнер на основе обновлённого образа.",[68,1809,1810],{},"Этот цикл обеспечивает несколько важных свойств. Во-первых, каждая версия приложения представлена конкретным образом с определённым тегом, что позволяет точно знать, какой артефакт развёрнут в данный момент. Во-вторых, откат к предыдущей версии сводится к запуску контейнера на основе прежнего образа. В-третьих, устраняется проблема дрейфа конфигурации, поскольку состояние контейнера определяется исключительно его образом и параметрами запуска.",[119,1812,121,1813,121,1817],{},[123,1814],{"src":1815,"alt":1816},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-05\u002Fimmutable_infrastructure.svg","Принцип неизменяемой инфраструктуры и цикл обновления",[128,1818,1816],{},[83,1820,1822],{"id":1821},"ошибки-ручной-модификации-контейнера","Ошибки ручной модификации контейнера",[68,1824,1825,1826,1828],{},"Несмотря на ясность принципа неизменяемости, на практике возникает соблазн внести «быстрое исправление» непосредственно в работающий контейнер. Типичные примеры: ручная правка конфигурационного файла через ",[979,1827,1745],{},", установка дополнительного пакета для решения текущей проблемы, изменение прав доступа к файлам внутри контейнера.",[68,1830,1831],{},"Такие действия создают несколько проблем. Изменение не зафиксировано в образе и будет потеряно при следующем пересоздании контейнера. Другие экземпляры, запущенные из того же образа, не получат данного изменения, что приведёт к несогласованности между средами. Откат к предыдущему состоянию невозможен, поскольку изменение не является частью управляемого жизненного цикла артефакта. Кроме того, ручные вмешательства затрудняют диагностику: если система ведёт себя иначе, чем ожидается на основании образа, инженер не может исключить, что причиной является незафиксированное ручное изменение.",[68,1833,1834],{},"Корректный подход требует возвращения к этапу сборки. Если в приложении обнаружена ошибка, она исправляется в исходном коде, образ пересобирается, и контейнер пересоздаётся. Если требуется изменение конфигурации, оно вносится в параметры запуска или конфигурационные файлы, передаваемые контейнеру извне. Этот подход может казаться более длительным, но именно он обеспечивает прослеживаемость, воспроизводимость и управляемость, которые составляют основу контейнерной модели эксплуатации.",[83,1836,1838],{"id":1837},"границы-применимости-принципа-неизменяемости","Границы применимости принципа неизменяемости",[68,1840,1841],{},"Принцип неизменяемости распространяется на программную среду контейнера, но не на все аспекты его работы. Данные, создаваемые приложением в процессе работы, по определению изменяются. Если приложение работает с базой данных, принимает загружаемые пользователями файлы или формирует кэш, эти данные не могут быть частью неизменяемого образа. Для их хранения используются тома и другие механизмы внешнего хранения, рассматриваемые в теме, посвящённой хранению данных в контейнерных средах.",[68,1843,1844],{},"Таким образом, неизменяемость образа и контейнера не означает неизменяемости всех данных приложения. Она означает неизменяемость программной среды: исполняемых файлов, библиотек, конфигурации, встроенной в образ. Разделение между изменяемыми данными и неизменяемой средой является фундаментальным архитектурным решением, которое должно быть принято на этапе проектирования контейнерного приложения.",[78,1846,348],{"id":347},[68,1848,1849],{},"Управление контейнерами в среде выполнения является самостоятельной инженерной задачей, не сводимой к знанию команд запуска и остановки. Параметры запуска — переменные окружения, проброс портов, ограничения ресурсов и политика перезапуска — определяют поведение контейнера и должны задаваться осознанно, с учётом требований воспроизводимости и сопровождаемости.",[68,1851,1852],{},"Контейнер существует ровно столько, сколько работает его основной процесс. Понимание роли PID 1, механизма обработки сигналов и причин немедленного завершения контейнера позволяет избежать распространённых ошибок и проектировать приложения, корректно интегрированные с контейнерной средой.",[68,1854,1855],{},"Инструменты инспекции и диагностики — журналы, команды инспекции, выполнение команд внутри контейнера и мониторинг потребления ресурсов — обеспечивают наблюдаемость и контроль. Однако диагностика должна оставаться наблюдением, а не ручной модификацией: изменение содержимого работающего контейнера нарушает принцип неизменяемой инфраструктуры и создаёт неуправляемое расхождение между артефактом и реальным состоянием среды.",[68,1857,1858],{},"Принцип неизменяемости связывает этап сборки образа с этапом эксплуатации контейнера. Обновление приложения выполняется через пересборку образа и пересоздание контейнера, а не через модификацию работающего экземпляра. Этот подход обеспечивает прослеживаемость версий, управляемый откат и согласованность между средами. Вместе с разделением изменяемых данных и неизменяемой программной среды он формирует основу для перехода к более сложным сценариям развёртывания: сетевому взаимодействию, хранению данных и композиции многоконтейнерных приложений.",{"title":40,"searchDepth":41,"depth":41,"links":1860},[1861,1869,1874,1881,1887],{"id":1515,"depth":41,"text":1516,"children":1862},[1863,1864,1865,1866,1867,1868],{"id":1522,"depth":369,"text":1523},{"id":1538,"depth":369,"text":1539},{"id":1551,"depth":369,"text":1552},{"id":1567,"depth":369,"text":1568},{"id":1580,"depth":369,"text":1581},{"id":1598,"depth":369,"text":1599},{"id":1605,"depth":41,"text":1606,"children":1870},[1871,1872,1873],{"id":1612,"depth":369,"text":1613},{"id":1625,"depth":369,"text":1626},{"id":1680,"depth":369,"text":1681},{"id":1699,"depth":41,"text":1700,"children":1875},[1876,1877,1878,1879,1880],{"id":1706,"depth":369,"text":1707},{"id":1722,"depth":369,"text":1723},{"id":1739,"depth":369,"text":1740},{"id":1755,"depth":369,"text":1756},{"id":1779,"depth":369,"text":1780},{"id":1786,"depth":41,"text":1787,"children":1882},[1883,1884,1885,1886],{"id":1793,"depth":369,"text":1794},{"id":1803,"depth":369,"text":1804},{"id":1821,"depth":369,"text":1822},{"id":1837,"depth":369,"text":1838},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content",{"title":1498,"description":1506},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-05-content","topic-05","tKoOX7e-UUjViBNxfVB0zdA68XCptlbJHJO3ET9W8cI",{"id":1895,"title":1896,"body":1897,"course_slug":43,"description":1904,"env_label":44,"env_url":44,"extension":45,"group":44,"is_course_project":47,"is_index":47,"level":44,"meta":2484,"navigation":48,"path":2485,"section":392,"seo":2486,"stem":2487,"topic_number":1068,"topic_slug":2488,"__hash__":2489},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-06-content.md","Сетевое взаимодействие контейнерных приложений",{"type":7,"value":1898,"toc":2461},[1899,1902,1905,1908,1911,1915,1918,1922,1942,1953,1972,1976,1987,2002,2012,2016,2031,2038,2042,2045,2071,2075,2086,2096,2105,2109,2123,2134,2141,2145,2148,2163,2174,2178,2194,2198,2207,2216,2230,2233,2237,2242,2254,2277,2281,2284,2288,2302,2311,2318,2339,2343,2360,2369,2372,2377,2381,2384,2391,2395,2398,2401,2415,2421,2428,2430,2433,2452,2455],[10,1900,1896],{"id":1901},"сетевое-взаимодействие-контейнерных-приложений",[68,1903,1904],{},"Предыдущие темы курса рассматривали контейнер как отдельный объект: сначала как воспроизводимый артефакт сборки, затем как управляемый процесс в среде выполнения. Однако реальные приложения почти никогда не сводятся к одному контейнеру. Веб-сервер обращается к базе данных, фоновая задача публикует сообщения в брокер очередей, шлюз проксирует запросы во внутренние сервисы. Все эти взаимодействия требуют сети — и устроены они в контейнерной среде иначе, чем в классической многопроцессной системе на одном хосте.",[68,1906,1907],{},"Сеть в контейнерной среде имеет двухуровневый характер. С одной стороны, сетевой стек контейнера изолирован от хоста средствами ядра операционной системы; контейнер не видит интерфейсов хоста и не может произвольно слушать его порты. С другой стороны, эта изоляция должна быть прозрачно «прорезана» в нужных местах: контейнеры должны находить друг друга по именам, а внешние клиенты — попадать в нужный контейнер через известный порт. Понимание того, как именно среда выполнения создаёт эти точки прозрачности, и составляет основное содержание темы.",[68,1909,1910],{},"Материал темы строится по схеме «от изоляции к публикации». Открывается тема кратким напоминанием базовых сетевых понятий, нужных дальше по тексту. Затем рассматривается, как устроено изолированное сетевое пространство контейнера и какие виртуальные интерфейсы обеспечивают его связь с внешним миром. Далее — как Docker предоставляет несколько режимов подключения (drivers) с разным компромиссом между изоляцией и удобством. После этого разбираются механизмы публикации сервисов наружу и обнаружения сервисов друг другом внутри одного хоста. Завершает тему обзор типичных эксплуатационных рисков и ограничений локальной контейнерной сети, которые мотивируют переход к оркестрации в последующих темах.",[78,1912,1914],{"id":1913},"минимальные-сведения-о-компьютерных-сетях","Минимальные сведения о компьютерных сетях",[68,1916,1917],{},"Дальнейший материал опирается на ряд понятий из курса компьютерных сетей. Глубокое их изложение выходит за пределы темы, но без короткого напоминания текст превратится в перечень терминов. Поэтому ниже собраны те и только те сведения, которые потребуются по ходу разбора контейнерной сетевой модели. Студент, желающий разобраться с предметом основательнее, найдёт ссылки в дополнительных материалах темы.",[83,1919,1921],{"id":1920},"ip-адреса-подсети-и-сетевые-интерфейсы","IP-адреса, подсети и сетевые интерфейсы",[68,1923,1924,1925,1929,1930,1933,1934,1937,1938,1941],{},"Каждое устройство, участвующее в сетевом обмене, идентифицируется IP-адресом (англ. ",[1926,1927,1928],"em",{},"Internet Protocol address","). В курсе мы практически всегда имеем дело с адресами IPv4 — четыре десятичных числа, разделённые точками: например, ",[979,1931,1932],{},"192.168.1.10",". Адрес сам по себе не даёт полной информации о расположении узла; к нему нужна подсеть (англ. ",[1926,1935,1936],{},"subnet",") — диапазон адресов, считающихся «локальными». Подсеть задаётся длиной префикса в записи ",[979,1939,1940],{},"192.168.1.0\u002F24",": первые 24 бита определяют сеть, оставшиеся 8 — конкретный узел в ней.",[68,1943,1944,1945,1948,1949,1952],{},"Подсеть нужна узлу, чтобы решить, как отправить пакет. Если адрес назначения попадает в локальную подсеть, пакет передаётся в неё напрямую, на канальном уровне. Если нет — пакет уходит через шлюз по умолчанию (англ. ",[1926,1946,1947],{},"default gateway","), специально настроенный узел, отвечающий за передачу пакетов в другие сети. Сопоставление «куда пойдёт пакет с данным адресом назначения» хранится в таблице маршрутизации (англ. ",[1926,1950,1951],{},"routing table",") узла; именно эту таблицу будет иметь свой экземпляр у каждого контейнера.",[68,1954,1955,1956,1959,1960,1963,1964,1967,1968,1971],{},"Связь узла с сетью реализуется через сетевой интерфейс (англ. ",[1926,1957,1958],{},"network interface",") — программную сущность, к которой привязан IP-адрес и через которую идут пакеты. Физическому Ethernet-порту обычно соответствует интерфейс ",[979,1961,1962],{},"eth0",", петлевому интерфейсу — ",[979,1965,1966],{},"lo"," (адрес ",[979,1969,1970],{},"127.0.0.1",", видимый только изнутри узла). На одном узле может быть несколько интерфейсов; в контейнерной среде к этому добавятся виртуальные интерфейсы, создаваемые ядром по запросу.",[83,1973,1975],{"id":1974},"транспортные-порты-и-сокеты","Транспортные порты и сокеты",[68,1977,1978,1979,1982,1983,1986],{},"IP-адрес определяет узел, но не приложение. На одном узле обычно работают десятки сетевых процессов одновременно: веб-сервер, СУБД, агенты мониторинга. Чтобы пакет дошёл до нужного процесса, на транспортном уровне используется понятие порта (англ. ",[1926,1980,1981],{},"port",") — целое число от 1 до 65535. Сочетание «IP-адрес + порт» называется сокетом (англ. ",[1926,1984,1985],{},"socket",") и однозначно указывает конкретную точку обмена в сети.",[68,1988,1989,1990,1993,1994,1997,1998,2001],{},"Большинство сетевых соединений в курсе — это TCP-соединения (англ. ",[1926,1991,1992],{},"Transmission Control Protocol","): надёжный поток байтов с установлением и закрытием соединения. Реже встречается UDP (англ. ",[1926,1995,1996],{},"User Datagram Protocol",") — обмен отдельными пакетами без подтверждений, применяется, например, для DNS-запросов. На уровне работы с контейнерами различия между этими протоколами чаще всего не критичны; критично то, что приложение должно «слушать» (англ. ",[1926,1999,2000],{},"listen",") определённый порт, чтобы клиенты могли к нему подключиться.",[68,2003,2004,2005,2007,2008,2011],{},"Адрес, на котором приложение слушает, имеет значение. Привязка к ",[979,2006,1970],{}," (петлевой интерфейс) делает порт доступным только локальным процессам узла. Привязка к конкретному IP-адресу одного из интерфейсов — только клиентам, идущим через этот интерфейс. Привязка к ",[979,2009,2010],{},"0.0.0.0"," означает «на всех интерфейсах»: порт доступен и локально, и снаружи. Различение этих режимов окажется важным при разборе публикации портов контейнеров.",[83,2013,2015],{"id":2014},"разрешение-имён-и-трансляция-адресов","Разрешение имён и трансляция адресов",[68,2017,2018,2019,2022,2023,2026,2027,2030],{},"Запоминать IP-адреса вручную неудобно, поэтому в реальных сетях работает разрешение имён через систему DNS (англ. ",[1926,2020,2021],{},"Domain Name System","). DNS — это иерархическая распределённая база данных, в которой именам узлов ставятся в соответствие их IP-адреса. Когда приложение обращается к имени ",[979,2024,2025],{},"db.example.com",", операционная система отправляет DNS-запрос настроенному серверу имён и получает в ответ адрес. Параметры DNS на узле задаются файлом ",[979,2028,2029],{},"\u002Fetc\u002Fresolv.conf",". В контейнерной среде среда выполнения подменяет содержимое этого файла, чтобы контейнеры обращались к встроенному DNS-серверу Docker, — но механизм остаётся стандартным.",[68,2032,2033,2034,2037],{},"Второе понятие, неизбежное при разборе контейнерных сетей, — трансляция адресов NAT (англ. ",[1926,2035,2036],{},"Network Address Translation","). NAT — это техника, при которой узел-посредник переписывает заголовки проходящих пакетов: подменяет адрес источника, адрес назначения или оба. Самый частый сценарий — преобразование частных адресов локальной сети в один публичный адрес шлюза при выходе наружу; ответы шлюз возвращает в обратном направлении, восстанавливая исходный адрес. Среда выполнения Docker использует NAT для двух задач: чтобы наружу контейнер был виден под адресом хоста (исходящие соединения) и чтобы пакет с порта хоста попадал в нужный контейнер (опубликованные порты). Подробного знания механики NAT от студента не требуется; достаточно помнить, что трансляция выполняется в ядре хоста, прозрачно для приложения и не оставляет следов в самом контейнере.",[83,2039,2041],{"id":2040},"базовая-модель-клиент-сервер-и-фильтрация-трафика","Базовая модель «клиент — сервер» и фильтрация трафика",[68,2043,2044],{},"Большинство сетевых взаимодействий в курсе укладываются в модель «клиент — сервер»: серверный процесс заранее открыл сокет и ждёт подключений на известном порту, клиент устанавливает соединение по адресу и порту сервера, обменивается с ним сообщениями и закрывает соединение. Серверный процесс может одновременно обслуживать множество клиентов, каждое соединение различается своей четвёркой «адрес-порт клиента — адрес-порт сервера».",[68,2046,2047,2048,2051,2052,2055,2056,2059,2060,2063,2064,1018,2067,2070],{},"Помимо собственно передачи пакетов, в современных операционных системах действует межсетевой фильтр (англ. ",[1926,2049,2050],{},"firewall",") — подсистема ядра, проверяющая каждый пакет по заданным правилам. На Linux это ",[979,2053,2054],{},"netfilter",", управляемый утилитами ",[979,2057,2058],{},"iptables"," или ",[979,2061,2062],{},"nftables",". Фильтр может пропускать, отбрасывать или модифицировать пакеты; именно через эти модификации реализуются и проброс портов, и NAT для исходящего трафика контейнеров. Знать конкретный синтаксис правил студенту не нужно — но важно понимать, что результаты команд ",[979,2065,2066],{},"docker run -p ...",[979,2068,2069],{},"docker network create ..."," наблюдаемы как изменения в межсетевом фильтре хоста, и при диагностике сетевых проблем именно туда стоит смотреть в первую очередь.",[78,2072,2074],{"id":2073},"сетевые-пространства-контейнеров","Сетевые пространства контейнеров",[68,2076,2077,2078,2081,2082,2085],{},"Сетевая изоляция контейнера — частный случай более общего механизма пространств имён (англ. ",[1926,2079,2080],{},"namespaces","), рассмотренного в теме 3 в разделе об изоляции контейнеров. Сетевое пространство имён (англ. ",[1926,2083,2084],{},"network namespace",") — это отдельный экземпляр сетевого стека ядра: собственный набор сетевых интерфейсов, своя таблица маршрутизации, свои правила фильтрации, своя таблица сокетов. Процессы, помещённые в одно сетевое пространство, видят только его интерфейсы; процессы хоста видят интерфейсы хоста; пересечения нет, пока его явно не настроить.",[68,2087,2088,2089,2092,2093,2095],{},"С практической точки зрения это означает, что внутри контейнера команда ",[979,2090,2091],{},"ip addr"," покажет только два интерфейса: локальную петлю ",[979,2094,1966],{}," и одно виртуальное Ethernet-устройство, через которое контейнер подключён к внешней сети. Никаких физических интерфейсов хоста, никаких других контейнеров в этом списке нет — даже если на хосте одновременно работают десятки контейнеров. Изоляция сетевого стека делает контейнер минимально похожим на отдельную виртуальную машину с точки зрения сетевой адресации, но при этом не требует виртуализации оборудования.",[119,2097,121,2098,121,2102],{},[123,2099],{"src":2100,"alt":2101},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-06\u002Fnetwork_namespace_isolation.svg","Сетевое пространство имён контейнера: изолированный стек с собственным интерфейсом, отделённый от стека хоста",[128,2103,2104],{},"Изоляция сетевого стека контейнера: каждое сетевое пространство имён содержит собственный набор интерфейсов, маршрутов и таблицу сокетов",[83,2106,2108],{"id":2107},"виртуальные-интерфейсы-и-базовая-модель-подключения","Виртуальные интерфейсы и базовая модель подключения",[68,2110,2111,2112,2115,2116,2118,2119,2122],{},"Передача пакетов между изолированным пространством контейнера и сетью хоста реализуется через пару виртуальных интерфейсов (англ. ",[1926,2113,2114],{},"veth pair","). Это устройство, которое ведёт себя как двусторонний сетевой кабель: один его конец помещается в сетевое пространство контейнера, другой остаётся в пространстве хоста. Всё, что записывается в один конец, выходит из другого. Внутри контейнера этот конец обычно называется ",[979,2117,1962],{},"; на хосте — имеет техническое имя вида ",[979,2120,2121],{},"vethXXXX"," и сам по себе для пользовательских задач не используется.",[68,2124,2125,2126,2129,2130,2133],{},"Простое существование пары виртуальных интерфейсов ещё не делает контейнер достижимым по сети. Хостовый конец пары должен быть подключён к какой-либо коммутирующей структуре, через которую пакеты пойдут дальше. В стандартной конфигурации Docker эта структура — программный сетевой мост (англ. ",[1926,2127,2128],{},"bridge",") с именем ",[979,2131,2132],{},"docker0",". Все хостовые концы пар veth от контейнеров одной мостовой сети подключены к этому мосту, что позволяет пакетам ходить между контейнерами и наружу через таблицу маршрутизации хоста.",[68,2135,2136,2137,2140],{},"Маршрутизация в самом контейнере при этом устроена просто: задан адрес шлюза по умолчанию, совпадающий с адресом моста на стороне хоста. Любой пакет, не относящийся к локальной подсети контейнера, отправляется через этот шлюз и далее — обычными средствами хоста. Внешние ответы возвращаются по тому же пути, и Docker обеспечивает корректную трансляцию исходного адреса (англ. ",[1926,2138,2139],{},"source NAT","), чтобы внешним сервисам контейнер был виден под адресом хоста.",[83,2142,2144],{"id":2143},"внутренняя-связность-и-внешний-доступ-два-разных-вопроса","Внутренняя связность и внешний доступ — два разных вопроса",[68,2146,2147],{},"При работе с сетью контейнеров полезно с самого начала разделять два вопроса, которые часто смешивают. Первый: «могут ли два контейнера обращаться друг к другу?» Второй: «может ли клиент извне обратиться к контейнеру?» Ответы на них даются разными механизмами и регулируются разными настройками.",[68,2149,2150,2151,2153,2154,2158,2159,2162],{},"Внутренняя связность — это вопрос о том, какие контейнеры подключены к одной и той же сети и какие правила фильтрации между ними действуют. Если оба контейнера находятся в стандартной мостовой сети ",[979,2152,2132],{},", они могут обращаться друг к другу по IP-адресам, но не по именам — стандартный мост не предоставляет встроенного разрешения имён. Если же оба контейнера подключены к ",[2155,2156,2157],"strong",{},"пользовательской"," сети, созданной командой ",[979,2160,2161],{},"docker network create",", между ними автоматически работает встроенный DNS-сервис Docker, и они могут обращаться друг к другу по имени контейнера.",[68,2164,2165,2166,2169,2170,2173],{},"Внешний доступ — это вопрос о том, какие порты хоста проброшены в контейнер. Без явного проброса контейнер недоступен извне, даже если он слушает порт ",[979,2167,2168],{},"0.0.0.0:8080",": с точки зрения хоста контейнер находится в собственном пространстве адресов, и снаружи не виден. Эта на первый взгляд избыточная двухуровневая модель оказывается удобной в эксплуатации: она позволяет запустить десяток контейнеров, каждый из которых внутри слушает порт ",[979,2171,2172],{},"8080",", и не получить конфликта на хосте — наружу публикуется только то, что нужно, через явно выбранные порты.",[78,2175,2177],{"id":2176},"сетевые-драйверы-и-режимы-подключения","Сетевые драйверы и режимы подключения",[68,2179,2180,2181,2184,2185,2188,2189,1018,2191,1207],{},"Конкретный способ организации сети для контейнера в Docker задаётся ",[2155,2182,2183],{},"сетевым драйвером"," (англ. ",[1926,2186,2187],{},"network driver","). Драйвер — это реализация конкретной модели сетевого подключения; на одном хосте могут одновременно существовать сети нескольких типов, и каждый контейнер подключается к одной или нескольким из них. Базовая поставка Docker включает несколько встроенных драйверов; на практике в учебном курсе и в большинстве однохостовых сценариев достаточно двух из них — ",[979,2190,2128],{},[979,2192,2193],{},"host",[83,2195,2197],{"id":2196},"мостовая-сеть-как-основной-режим","Мостовая сеть как основной режим",[68,2199,2200,2201,2203,2204,2206],{},"Драйвер ",[979,2202,2128],{}," — стандартный сетевой режим Docker. Именно он используется по умолчанию, если при запуске контейнера не указано иное. Для контейнеров создаётся изолированное сетевое пространство, в нём — интерфейс ",[979,2205,1962],{},", подключённый через пару veth к программному мосту в пространстве хоста. Этот мост действует как ethernet-коммутатор второго уровня: пакеты между контейнерами одной мостовой сети передаются напрямую, без участия таблицы маршрутизации хоста.",[119,2208,121,2209,121,2213],{},[123,2210],{"src":2211,"alt":2212},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-06\u002Fbridge_network_topology.svg","Мостовая сеть Docker: контейнеры подключены парой veth к программному мосту, мост соединён с хостом и через него — с внешней сетью",[128,2214,2215],{},"Топология мостовой сети: контейнеры подключены к программному мосту через пары виртуальных интерфейсов veth; мост обеспечивает связность контейнеров между собой и с внешней сетью",[68,2217,2218,2219,2221,2222,2225,2226,2229],{},"Стандартный мост ",[979,2220,2132],{}," создаётся автоматически при установке Docker, к нему подключаются все контейнеры, для которых явно не указана сеть. Однако опираться на него для прикладных задач не рекомендуется. Как уже было отмечено, он не предоставляет разрешения имён, что вынуждает разработчиков жёстко прописывать IP-адреса и сильно усложняет воспроизводимость окружения. Вместо этого следует создавать ",[2155,2223,2224],{},"пользовательскую мостовую сеть"," командой ",[979,2227,2228],{},"docker network create my-net",". С точки зрения пакетной коммутации она устроена так же, как стандартный мост, но дополнительно включает встроенный DNS-сервер: каждый контейнер этой сети доступен другим её участникам по имени, заданному при запуске.",[68,2231,2232],{},"Пользовательские сети также удобны для логического разделения групп связанных контейнеров. На одном хосте могут одновременно существовать несколько таких сетей, контейнеры из разных сетей не видят друг друга на канальном уровне. Это даёт минимальную, но рабочую модель сетевой сегментации — она не заменяет полноценные сетевые политики оркестратора, но позволяет разделить, например, фронтальную часть приложения и внутренние сервисы на двух разных мостах.",[83,2234,2236],{"id":2235},"режим-хоста-и-особые-драйверы","Режим хоста и особые драйверы",[68,2238,2200,2239,2241],{},[979,2240,2193],{}," принципиально отличается от мостового. Контейнер, запущенный в этом режиме, не получает собственного сетевого пространства имён: он использует сетевой стек хоста напрямую. С точки зрения сети это означает, что приложение внутри контейнера слушает порты хоста так же, как обычный процесс — без проброса, без NAT, без дополнительных интерфейсов.",[68,2243,2244,2245,2247,2248,2250,2251,2253],{},"Режим ",[979,2246,2193],{}," имеет ограниченную область применения. С одной стороны, он минимизирует сетевую нагрузку: пакеты не проходят через дополнительный мост и не подвергаются трансляции адресов, что бывает заметно для приложений с высокой пропускной способностью. С другой стороны, он полностью устраняет сетевую изоляцию контейнера: контейнер видит все интерфейсы хоста, может слушать любой порт и потенциально конфликтовать с другими процессами хоста. На одном хосте нельзя запустить два контейнера в режиме ",[979,2249,2193],{},", оба слушающих один и тот же порт. По этим причинам режим ",[979,2252,2193],{}," используется в качестве оптимизации в специфических случаях, а не как режим общего назначения.",[68,2255,2256,2257,1018,2259,2261,2262,2265,2266,1018,2269,2272,2273,2276],{},"Помимо ",[979,2258,2128],{},[979,2260,2193],{},", среда Docker предоставляет несколько специализированных драйверов: ",[979,2263,2264],{},"none"," (полное отсутствие сети, для контейнеров вспомогательных задач), ",[979,2267,2268],{},"macvlan",[979,2270,2271],{},"ipvlan"," (для случаев, когда контейнеру нужен собственный MAC- или IP-адрес в физической сети), ",[979,2274,2275],{},"overlay"," (для распределённых развёртываний на нескольких хостах в режиме Docker Swarm). В учебном курсе они подробно не разбираются: задачи, которые они решают, относятся либо к узким сетевым сценариям, либо к оркестрации, рассматриваемой в последующих темах.",[78,2278,2280],{"id":2279},"публикация-сервисов-и-межконтейнерное-взаимодействие","Публикация сервисов и межконтейнерное взаимодействие",[68,2282,2283],{},"Создание сети и подключение к ней контейнеров — только первая часть задачи. Прикладной смысл сети раскрывается через два конкретных механизма: публикацию сервисов наружу и обнаружение сервисов внутри сети.",[83,2285,2287],{"id":2286},"проброс-портов-и-публикация-наружу","Проброс портов и публикация наружу",[68,2289,2290,2291,2059,2294,2297,2298,2301],{},"Проброс портов (англ. ",[1926,2292,2293],{},"port mapping",[1926,2295,2296],{},"port publishing",") — основной способ сделать сервис, работающий в контейнере, доступным внешним клиентам. При запуске контейнера ключом ",[979,2299,2300],{},"-p HOST_PORT:CONTAINER_PORT"," указывается соответствие между портом хостовой машины и портом внутри контейнера. Среда выполнения настраивает правила трансляции в межсетевом фильтре хоста; внешний пакет, пришедший на указанный порт хоста, перенаправляется на указанный порт контейнера. Обратный путь обеспечивается стандартными механизмами трансляции состояний.",[119,2303,121,2304,121,2308],{},[123,2305],{"src":2306,"alt":2307},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-06\u002Fport_publishing.svg","Проброс портов: внешний клиент обращается к порту хоста, среда выполнения транслирует соединение на порт контейнера",[128,2309,2310],{},"Публикация сервиса через проброс портов: внешний клиент видит порт хоста, среда выполнения прозрачно перенаправляет соединение на порт внутри контейнера",[68,2312,2313,2314,2317],{},"При выборе порта на стороне хоста часто возникает соблазн использовать тот же номер, что и внутри контейнера. Это удобно для разработки, но создаёт неявные ограничения в эксплуатации: на одном хосте нельзя запустить два контейнера, публикующих один и тот же хостовый порт. По этой причине в реальных конфигурациях номер хостового порта обычно либо выбирается осознанно из выделенного диапазона, либо поручается среде выполнения через синтаксис ",[979,2315,2316],{},"-p :CONTAINER_PORT",", при котором Docker подбирает свободный порт автоматически.",[68,2319,2320,2321,2323,2324,2326,2327,2330,2331,2334,2335,2338],{},"Полезно ясно различать инструкцию ",[979,2322,1014],{}," в Dockerfile и фактический проброс портов при запуске. ",[979,2325,1014],{}," — это ",[2155,2328,2329],{},"документация намерения",": она сообщает, какие порты приложение собирается слушать, но сама по себе не открывает доступ извне. Реальная публикация формируется только в момент запуска контейнера и определяется параметрами ",[979,2332,2333],{},"docker run"," или эквивалентной конфигурацией оркестратора. Смешение этих двух понятий — частый источник недоразумений: студенту кажется, что приложение должно быть доступно, потому что в Dockerfile есть ",[979,2336,2337],{},"EXPOSE 8080",", а на самом деле никакого проброса не выполнено.",[83,2340,2342],{"id":2341},"имена-сервисов-и-встроенное-разрешение-имён","Имена сервисов и встроенное разрешение имён",[68,2344,2345,2346,2348,2349,2352,2353,2356,2357,2359],{},"Внутри пользовательской сети Docker контейнеры обращаются друг к другу не по IP-адресам, а по именам. Среда выполнения поднимает встроенный DNS-сервер; в каждом контейнере, подключённом к пользовательской сети, файл ",[979,2347,2029],{}," указывает на этот сервер. Когда приложение в контейнере ",[979,2350,2351],{},"web"," обращается к адресу ",[979,2354,2355],{},"db",", DNS-запрос разрешается во внутренний IP-адрес контейнера ",[979,2358,2355],{}," в той же сети.",[119,2361,121,2362,121,2366],{},[123,2363],{"src":2364,"alt":2365},"\u002Fimg\u002Faidt-bac-cloud_tech\u002Ftopic-06\u002Fuser_network_dns.svg","Пользовательская сеть Docker со встроенным DNS: контейнер web обращается к контейнеру db по имени, встроенный DNS возвращает IP контейнера db",[128,2367,2368],{},"Разрешение имён в пользовательской сети: контейнеры обращаются друг к другу по именам, встроенный DNS-сервер Docker возвращает соответствующие IP-адреса",[68,2370,2371],{},"С точки зрения архитектуры приложения это даёт важное свойство: имена сервисов фиксируются в конфигурации приложения, а конкретные адреса — нет. Если контейнер пересоздан и получил другой IP-адрес, имя осталось тем же, и приложение продолжит работать без правок. Это первый шаг от модели «сервер с фиксированным IP» к модели «сервис с устойчивым именем», которая в полной мере раскрывается в оркестраторе.",[68,2373,2218,2374,2376],{},[979,2375,2132],{}," встроенного DNS не имеет; для разрешения имён необходимо явно создать пользовательскую сеть. По этой причине рекомендация «не использовать стандартный мост в прикладных конфигурациях» имеет не стилистический, а практический характер: только пользовательская сеть даёт устойчивое именование, а значит — переносимую конфигурацию приложения.",[83,2378,2380],{"id":2379},"разделение-фронтальной-и-внутренней-связности","Разделение фронтальной и внутренней связности",[68,2382,2383],{},"Типичная многосервисная конфигурация на одном хосте требует разделения сетевых ролей. Часть сервисов обслуживает внешних клиентов (фронтальный веб-сервер, API-шлюз) — они публикуют порты наружу. Другая часть работает только во внутренней сети приложения (база данных, очередь сообщений, кеш) — наружу они не публикуются принципиально и должны быть недоступны для прямых внешних обращений.",[68,2385,2386,2387,2390],{},"Простейший способ обеспечить это разделение — две пользовательские сети: одна, в которой находятся фронтальные сервисы и шлюз, и вторая, к которой подключены шлюз и внутренние сервисы. Шлюз присутствует в обеих сетях; внутренние сервисы видят его, но не видят фронтального трафика напрямую. Такая структура — базовый сетевой шаблон контейнерных приложений: «опубликовать минимум, оставить остальное во внутреннем периметре». Декларативное описание подобной топологии естественным образом ложится в формат ",[979,2388,2389],{},"docker compose",", рассматриваемый в одной из следующих тем.",[78,2392,2394],{"id":2393},"сетевые-риски-и-эксплуатационные-ограничения","Сетевые риски и эксплуатационные ограничения",[68,2396,2397],{},"Сетевая модель Docker удобна и компактна, но именно её простота создаёт несколько типичных эксплуатационных рисков. Они проявляются не в учебных сценариях, а на этапе сопровождения, когда конфигурация переходит из рук разработчика в эксплуатацию.",[68,2399,2400],{},"Первый и наиболее распространённый риск — избыточная сетевая открытость. Привычка публиковать порт «на всякий случай» приводит к тому, что внутренние сервисы — базы данных, административные интерфейсы, отладочные эндпоинты — оказываются доступны извне. На рабочей машине разработчика это незаметно, но при переносе конфигурации на сервер с публичным сетевым адресом ошибка превращается в открытую дверь. Правило здесь простое: публиковаться наружу должны только порты, действительно нужные внешним клиентам; всё остальное должно оставаться во внутренней пользовательской сети.",[68,2402,2403,2404,2407,2408,2410,2411,2414],{},"Второй риск связан с используемым адресом привязки на стороне хоста. По умолчанию ",[979,2405,2406],{},"-p 8080:8080"," биндится на адрес ",[979,2409,2010],{},", то есть на все интерфейсы хоста, включая внешние. Если требуется ограничить публикацию только локальной машиной, привязку нужно делать явно: ",[979,2412,2413],{},"-p 127.0.0.1:8080:8080",". Это типовая ошибка при работе с серверами, имеющими несколько сетевых интерфейсов: разработчик считает, что сервис «слушает локально», а на деле он слушает все интерфейсы.",[68,2416,2417,2418,2420],{},"Третье ограничение — пределы локальной сетевой модели. Стандартные сети Docker (мостовые и пользовательские) работают в пределах одного хоста. Если приложение нужно распределить по нескольким хостам, эти средства уже недостаточны: требуются либо специализированные драйверы вроде ",[979,2419,2275],{}," в режиме Docker Swarm, либо переход к оркестратору с собственной сетевой моделью. Локальная контейнерная сеть удобна для разработки, тестирования и одно-узловой эксплуатации; при выходе за этот сценарий она перестаёт быть достаточной, и попытки обойти ограничение «вручную» обычно приводят к хрупким конфигурациям.",[68,2422,2423,2424,2427],{},"Наконец, в эксплуатации значимым становится ",[2155,2425,2426],{},"документирование сетевых зависимостей"," между сервисами. В контейнерной среде эти зависимости естественным образом скрыты внутри частных сетей, и их легко потерять из виду. Какой сервис обращается к какому, на каких портах, по какой сети — всё это должно быть явно зафиксировано в декларативном описании приложения и в эксплуатационной документации. Без такой фиксации диагностика проблем превращается в реверс-инжиниринг: при сетевом сбое непонятно, какой компонент чего ждал и где именно разорвалось соединение.",[78,2429,348],{"id":347},[68,2431,2432],{},"Сетевое взаимодействие контейнерных приложений строится на двухуровневой модели: с одной стороны — изолированное сетевое пространство имён каждого контейнера, с другой — явные точки прозрачности, через которые контейнер сообщается с внешним миром и с другими контейнерами. Связь между уровнями обеспечивают пары виртуальных интерфейсов и программные мосты в пространстве хоста; пакеты ходят между мостом и контейнером прозрачно, а наружу — через таблицу маршрутизации и трансляцию адресов хоста.",[68,2434,2435,2436,2438,2439,2441,2442,2444,2445,1008,2447,1008,2449,2451],{},"Конкретный режим сети задаётся драйвером. Для подавляющего большинства учебных и однохостовых задач используется драйвер ",[979,2437,2128],{},", причём предпочтительна не стандартная сеть ",[979,2440,2132],{},", а явно созданная пользовательская сеть со встроенным разрешением имён. Режим ",[979,2443,2193],{}," отказывается от изоляции в пользу прямого доступа к стеку хоста и применим только в узких сценариях оптимизации. Специальные драйверы (",[979,2446,2268],{},[979,2448,2271],{},[979,2450,2275],{},") решают задачи, выходящие за рамки одного хоста, и подробно рассматриваются в темах, посвящённых оркестрации.",[68,2453,2454],{},"Прикладная сторона сети — это два механизма: проброс портов наружу и обнаружение сервисов внутри. Первый делает контейнер доступным внешним клиентам через явный выбор хостового порта; второй позволяет контейнерам обращаться друг к другу по устойчивым именам, не завися от конкретных IP. Сочетание этих механизмов и нескольких пользовательских сетей даёт основной сетевой шаблон контейнерных приложений: «публиковать только то, что должно быть видно снаружи; всё внутреннее оставлять в частных сетях».",[68,2456,2457,2458,2460],{},"Эксплуатационные риски сетевой модели предсказуемы и связаны не с её сложностью, а с привычкой разработчика к удобной открытой среде. Избыточно опубликованные порты, привязка к ",[979,2459,2010],{}," без необходимости, недокументированные зависимости между сервисами — типичные источники проблем. Локальные средства Docker рассчитаны на один хост; задача распределённого размещения и сетевого взаимодействия между узлами выходит за пределы этой темы и решается уже на уровне оркестратора.",{"title":40,"searchDepth":41,"depth":41,"links":2462},[2463,2469,2473,2477,2482,2483],{"id":1913,"depth":41,"text":1914,"children":2464},[2465,2466,2467,2468],{"id":1920,"depth":369,"text":1921},{"id":1974,"depth":369,"text":1975},{"id":2014,"depth":369,"text":2015},{"id":2040,"depth":369,"text":2041},{"id":2073,"depth":41,"text":2074,"children":2470},[2471,2472],{"id":2107,"depth":369,"text":2108},{"id":2143,"depth":369,"text":2144},{"id":2176,"depth":41,"text":2177,"children":2474},[2475,2476],{"id":2196,"depth":369,"text":2197},{"id":2235,"depth":369,"text":2236},{"id":2279,"depth":41,"text":2280,"children":2478},[2479,2480,2481],{"id":2286,"depth":369,"text":2287},{"id":2341,"depth":369,"text":2342},{"id":2379,"depth":369,"text":2380},{"id":2393,"depth":41,"text":2394},{"id":347,"depth":41,"text":348},{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Ftopic-06-content",{"title":1896,"description":1904},"courses\u002Faidt-bac-cloud_tech\u002Ftopic-06-content","topic-06","KSnjRMx7e3TMxwkJ7F7tHzNDfaNnlbzxJXFEUmvY-qo",{"id":2491,"title":2492,"body":2493,"course_slug":43,"description":40,"env_label":5903,"env_url":5904,"extension":45,"group":44,"is_course_project":48,"is_index":47,"level":44,"meta":5905,"navigation":48,"path":5906,"section":44,"seo":5907,"stem":5908,"topic_number":44,"topic_slug":44,"__hash__":5909},"courses\u002Fcourses\u002Faidt-bac-cloud_tech\u002Fcourse-project.md","Курсовой проект",{"type":7,"value":2494,"toc":5884},[2495,2498,2502,2505,2509,2512,2519,2539,2542,2546,2554,2557,2561,2565,2568,2573,2576,2601,2604,2621,2625,2631,2637,2643,2650,2654,2664,2667,2751,2766,2784,2787,2810,2814,2820,2822,2865,2880,2884,2887,2933,2940,2943,2965,2968,2983,2986,3009,3012,3015,3019,3026,3030,3042,3049,3053,3060,3063,3104,3111,3115,3117,3177,3181,3183,3229,3233,3243,3246,3252,3258,3262,3276,3282,3286,3289,3321,3327,3331,3460,3463,3488,3491,3493,3497,3500,3504,3507,3523,3526,3545,3548,3571,3575,3578,3606,3609,3663,3673,3679,3683,3690,3696,3708,3712,3718,3721,3741,3747,3753,3756,3782,3785,3855,3861,3867,3869,3917,3923,3929,4027,4036,4042,4046,4052,4134,4144,4150,4155,4177,4236,4241,4293,4305,4312,4317,4329,4333,4337,4351,4363,4367,4374,4378,4381,4399,4402,4533,4536,4548,4551,4557,4561,4564,4589,4594,4597,4729,4736,4738,4742,4745,4749,4760,4790,4793,4826,4829,4849,4852,4895,4898,4901,4935,4941,4945,4948,4963,4966,4982,4985,5005,5011,5015,5018,5021,5037,5043,5050,5213,5216,5241,5244,5285,5288,5314,5317,5323,5327,5330,5333,5399,5402,5405,5407,5411,5414,5418,5425,5428,5446,5458,5462,5469,5509,5512,5543,5546,5571,5578,5584,5588,5591,5594,5609,5612,5634,5637,5640,5667,5670,5683,5685,5701,5704,5708,5711,5715,5718,5724,5728,5731,5776,5780,5783,5787,5860,5864,5878,5881],[10,2496,2492],{"id":2497},"курсовой-проект",[78,2499,2501],{"id":2500},"тема","Тема",[68,2503,2504],{},"Контейнеризация и оркестрация веб-приложения: от исходного кода до развертывания в кластере.",[78,2506,2508],{"id":2507},"описание","Описание",[68,2510,2511],{},"Курсовой проект предполагает выполнение полного цикла контейнеризации и развертывания готового многокомпонентного веб-приложения. Студент последовательно решает задачи упаковки приложения в контейнерные образы, организации локальной среды разработки, развертывания в кластере оркестрации и обеспечения эксплуатационных свойств: отказоустойчивости, масштабируемости, наблюдаемости и безопасности.",[68,2513,2514,2515,2518],{},"Разработка самого приложения не входит в задачу курсового проекта. Исходный код приложения предоставляется в каталоге ",[979,2516,2517],{},"app\u002F"," и состоит из трех компонентов:",[14,2520,2521,2527,2533],{},[17,2522,2523,2526],{},[2155,2524,2525],{},"frontend"," — статическая веб-страница, обслуживаемая сервером nginx, который также выполняет роль обратного прокси для API;",[17,2528,2529,2532],{},[2155,2530,2531],{},"backend"," — REST API на JavaScript (Node.js, Express), предоставляющее операции создания, чтения и удаления заметок;",[17,2534,2535,2538],{},[2155,2536,2537],{},"database"," — СУБД PostgreSQL для хранения данных приложения.",[68,2540,2541],{},"Студент не обязан модифицировать исходный код приложения, однако может вносить изменения, если это обосновано задачами контейнеризации или развертывания.",[78,2543,2545],{"id":2544},"структура-исходного-приложения","Структура исходного приложения",[1031,2547,2552],{"className":2548,"code":2550,"language":2551},[2549],"language-text","app\u002F\n├── backend\u002F\n│   ├── app.js              # API-сервер\n│   └── package.json        # зависимости Node.js\n└── frontend\u002F\n    ├── index.html           # веб-интерфейс\n    └── nginx.conf           # конфигурация nginx\n","text",[979,2553,2550],{"__ignoreMap":40},[68,2555,2556],{},"Для компонента database используется официальный образ PostgreSQL; собственный Dockerfile для него не требуется.",[78,2558,2560],{"id":2559},"этапы-выполнения","Этапы выполнения",[83,2562,2564],{"id":2563},"этап-1-контейнеризация-компонентов","Этап 1. Контейнеризация компонентов",[68,2566,2567],{},"На этом этапе необходимо подготовить Dockerfile для каждого прикладного компонента (backend и frontend), собрать образы и убедиться, что контейнеры запускаются и работают корректно.",[2569,2570,2572],"h4",{"id":2571},"_11-подготовка-рабочего-окружения","1.1. Подготовка рабочего окружения",[68,2574,2575],{},"Убедиться, что Docker установлен и доступен:",[1031,2577,2581],{"className":2578,"code":2579,"language":2580,"meta":40,"style":40},"language-bash shiki shiki-themes github-light github-dark","docker --version\ndocker info\n","bash",[979,2582,2583,2593],{"__ignoreMap":40},[1039,2584,2585,2589],{"class":1041,"line":395},[1039,2586,2588],{"class":2587},"sScJk","docker",[1039,2590,2592],{"class":2591},"sj4cs"," --version\n",[1039,2594,2595,2597],{"class":1041,"line":41},[1039,2596,2588],{"class":2587},[1039,2598,2600],{"class":2599},"sZZnC"," info\n",[68,2602,2603],{},"Если Docker не установлен, воспользоваться официальной инструкцией:",[14,2605,2606,2614],{},[17,2607,2608,2609],{},"Docker Desktop (Windows, macOS): ",[21,2610,2611],{"href":2611,"rel":2612},"https:\u002F\u002Fdocs.docker.com\u002Fdesktop\u002F",[2613],"nofollow",[17,2615,2616,2617],{},"Docker Engine (Linux): ",[21,2618,2619],{"href":2619,"rel":2620},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Finstall\u002F",[2613],[2569,2622,2624],{"id":2623},"_12-создание-файла-dockerignore","1.2. Создание файла .dockerignore",[68,2626,2627,2628,2630],{},"Перед написанием Dockerfile создать файл ",[979,2629,1178],{}," в корне каждого компонента. Этот файл указывает Docker, какие файлы и каталоги не следует включать в контекст сборки. Исключение лишних файлов ускоряет сборку и уменьшает размер образа.",[68,2632,2633,2634,2636],{},"Пример ",[979,2635,1178],{}," для backend:",[1031,2638,2641],{"className":2639,"code":2640,"language":2551},[2549],"node_modules\nnpm-debug.log\n",[979,2642,2640],{"__ignoreMap":40},[68,2644,2645,2646],{},"Документация: ",[21,2647,2648],{"href":2648,"rel":2649},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fcontext\u002F#dockerignore-files",[2613],[2569,2651,2653],{"id":2652},"_13-dockerfile-для-backend","1.3. Dockerfile для backend",[68,2655,2656,2657,2660,2661,2663],{},"Создать файл ",[979,2658,2659],{},"docker\u002Fbackend\u002FDockerfile",". Dockerfile должен использовать многоэтапную сборку (multi-stage build) — прием, при котором один Dockerfile содержит несколько инструкций ",[979,2662,988],{},", а итоговый образ формируется на основе последнего этапа. Это позволяет установить зависимости на первом этапе (включая инструменты сборки), а во второй этап скопировать только файлы, необходимые для запуска приложения.",[68,2665,2666],{},"Требования:",[14,2668,2669,2682,2707,2717,2735,2745],{},[17,2670,2671,2674,2675,2678,2679,2681],{},[2155,2672,2673],{},"Базовый образ",": использовать ",[979,2676,2677],{},"node:22-alpine"," (или другую конкретную версию). Alpine-образы существенно меньше стандартных образов на основе Debian. Тег ",[979,2680,1409],{}," использовать запрещено — фиксация версии обеспечивает воспроизводимость сборки.",[17,2683,2684,2687,2688,2691,2692,2695,2696,2698,2699,2702,2703,2706],{},[2155,2685,2686],{},"Первый этап (установка зависимостей)",": скопировать ",[979,2689,2690],{},"package.json"," в рабочий каталог и выполнить ",[979,2693,2694],{},"npm ci",". Команда ",[979,2697,2694],{}," (clean install) предпочтительнее ",[979,2700,2701],{},"npm install"," для контейнерных сборок, так как она устанавливает зависимости строго по ",[979,2704,2705],{},"package-lock.json"," и не модифицирует его.",[17,2708,2709,2712,2713,2716],{},[2155,2710,2711],{},"Второй этап (итоговый образ)",": скопировать установленные зависимости (",[979,2714,2715],{},"node_modules",") и исходный код приложения из первого этапа. Задать рабочий каталог, определить непривилегированного пользователя для запуска процесса.",[17,2718,2719,2722,2723,2726,2727,2730,2731,2734],{},[2155,2720,2721],{},"Непривилегированный пользователь",": образы на основе ",[979,2724,2725],{},"node:alpine"," содержат встроенного пользователя ",[979,2728,2729],{},"node",". Использовать инструкцию ",[979,2732,2733],{},"USER node",", чтобы приложение не запускалось от имени root. Это уменьшает последствия возможной компрометации контейнера.",[17,2736,2737,2740,2741,2744],{},[2155,2738,2739],{},"Точка входа",": инструкция ",[979,2742,2743],{},"CMD [\"node\", \"app.js\"]",". Форма exec (массив строк) предпочтительнее shell-формы, так как процесс Node.js получает PID 1 и корректно обрабатывает сигналы завершения.",[17,2746,2747,2750],{},[2155,2748,2749],{},"HEALTHCHECK",": добавить инструкцию, которая периодически проверяет доступность API. Например:",[1031,2752,2754],{"className":1033,"code":2753,"language":1035,"meta":40,"style":40},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n  CMD wget --quiet --spider http:\u002F\u002Flocalhost:8000\u002Fapi\u002Fhealth || exit 1\n",[979,2755,2756,2761],{"__ignoreMap":40},[1039,2757,2758],{"class":1041,"line":395},[1039,2759,2760],{},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n",[1039,2762,2763],{"class":1041,"line":41},[1039,2764,2765],{},"  CMD wget --quiet --spider http:\u002F\u002Flocalhost:8000\u002Fapi\u002Fhealth || exit 1\n",[68,2767,2768,2769,2772,2773,2776,2777,2779,2780,2783],{},"Утилита ",[979,2770,2771],{},"wget"," используется вместо ",[979,2774,2775],{},"curl",", так как ",[979,2778,2775],{}," отсутствует в alpine-образах по умолчанию. Параметр ",[979,2781,2782],{},"--start-period"," задает период ожидания после запуска контейнера, в течение которого неудачные проверки не считаются ошибкой.",[68,2785,2786],{},"Документация:",[14,2788,2789,2796,2803],{},[17,2790,2791,2792],{},"Справочник инструкций Dockerfile: ",[21,2793,2794],{"href":2794,"rel":2795},"https:\u002F\u002Fdocs.docker.com\u002Freference\u002Fdockerfile\u002F",[2613],[17,2797,2798,2799],{},"Многоэтапная сборка: ",[21,2800,2801],{"href":2801,"rel":2802},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fmulti-stage\u002F",[2613],[17,2804,2805,2806],{},"HEALTHCHECK: ",[21,2807,2808],{"href":2808,"rel":2809},"https:\u002F\u002Fdocs.docker.com\u002Freference\u002Fdockerfile\u002F#healthcheck",[2613],[2569,2811,2813],{"id":2812},"_14-dockerfile-для-frontend","1.4. Dockerfile для frontend",[68,2815,2656,2816,2819],{},[979,2817,2818],{},"docker\u002Ffrontend\u002FDockerfile",". Этот образ проще, чем backend: необходимо скопировать статические файлы и конфигурацию nginx в стандартные каталоги образа nginx.",[68,2821,2666],{},[14,2823,2824,2832,2844,2860],{},[17,2825,2826,2674,2828,2831],{},[2155,2827,2673],{},[979,2829,2830],{},"nginx:1.27-alpine"," (или другую конкретную версию).",[17,2833,2834,2687,2837,2840,2841,1207],{},[2155,2835,2836],{},"Статические файлы",[979,2838,2839],{},"index.html"," в каталог ",[979,2842,2843],{},"\u002Fusr\u002Fshare\u002Fnginx\u002Fhtml\u002F",[17,2845,2846,2687,2849,2840,2852,2855,2856,2859],{},[2155,2847,2848],{},"Конфигурация nginx",[979,2850,2851],{},"nginx.conf",[979,2853,2854],{},"\u002Fetc\u002Fnginx\u002Fconf.d\u002F",", заменив конфигурацию по умолчанию. Стандартный конфигурационный файл nginx называется ",[979,2857,2858],{},"default.conf","; если его не заменить, он будет конфликтовать с пользовательской конфигурацией.",[17,2861,2862,2864],{},[2155,2863,2749],{},": проверять доступность nginx, например:",[1031,2866,2868],{"className":1033,"code":2867,"language":1035,"meta":40,"style":40},"HEALTHCHECK --interval=30s --timeout=5s --retries=3 \\\n  CMD wget --quiet --spider http:\u002F\u002Flocalhost:80\u002F || exit 1\n",[979,2869,2870,2875],{"__ignoreMap":40},[1039,2871,2872],{"class":1041,"line":395},[1039,2873,2874],{},"HEALTHCHECK --interval=30s --timeout=5s --retries=3 \\\n",[1039,2876,2877],{"class":1041,"line":41},[1039,2878,2879],{},"  CMD wget --quiet --spider http:\u002F\u002Flocalhost:80\u002F || exit 1\n",[2569,2881,2883],{"id":2882},"_15-сборка-и-проверка-образов","1.5. Сборка и проверка образов",[68,2885,2886],{},"Собрать образы, находясь в корневом каталоге проекта:",[1031,2888,2890],{"className":2578,"code":2889,"language":2580,"meta":40,"style":40},"docker build -t notes-backend:1.0 -f docker\u002Fbackend\u002FDockerfile app\u002Fbackend\u002F\ndocker build -t notes-frontend:1.0 -f docker\u002Ffrontend\u002FDockerfile app\u002Ffrontend\u002F\n",[979,2891,2892,2914],{"__ignoreMap":40},[1039,2893,2894,2896,2899,2902,2905,2908,2911],{"class":1041,"line":395},[1039,2895,2588],{"class":2587},[1039,2897,2898],{"class":2599}," build",[1039,2900,2901],{"class":2591}," -t",[1039,2903,2904],{"class":2599}," notes-backend:1.0",[1039,2906,2907],{"class":2591}," -f",[1039,2909,2910],{"class":2599}," docker\u002Fbackend\u002FDockerfile",[1039,2912,2913],{"class":2599}," app\u002Fbackend\u002F\n",[1039,2915,2916,2918,2920,2922,2925,2927,2930],{"class":1041,"line":41},[1039,2917,2588],{"class":2587},[1039,2919,2898],{"class":2599},[1039,2921,2901],{"class":2591},[1039,2923,2924],{"class":2599}," notes-frontend:1.0",[1039,2926,2907],{"class":2591},[1039,2928,2929],{"class":2599}," docker\u002Ffrontend\u002FDockerfile",[1039,2931,2932],{"class":2599}," app\u002Ffrontend\u002F\n",[68,2934,2935,2936,2939],{},"Флаг ",[979,2937,2938],{},"-f"," указывает путь к Dockerfile, последний аргумент — контекст сборки (каталог, из которого копируются файлы).",[68,2941,2942],{},"После сборки проверить размер образов:",[1031,2944,2946],{"className":2578,"code":2945,"language":2580,"meta":40,"style":40},"docker images | grep notes\n",[979,2947,2948],{"__ignoreMap":40},[1039,2949,2950,2952,2955,2959,2962],{"class":1041,"line":395},[1039,2951,2588],{"class":2587},[1039,2953,2954],{"class":2599}," images",[1039,2956,2958],{"class":2957},"szBVR"," |",[1039,2960,2961],{"class":2587}," grep",[1039,2963,2964],{"class":2599}," notes\n",[68,2966,2967],{},"Для анализа слоев образа использовать:",[1031,2969,2971],{"className":2578,"code":2970,"language":2580,"meta":40,"style":40},"docker history notes-backend:1.0\n",[979,2972,2973],{"__ignoreMap":40},[1039,2974,2975,2977,2980],{"class":1041,"line":395},[1039,2976,2588],{"class":2587},[1039,2978,2979],{"class":2599}," history",[1039,2981,2982],{"class":2599}," notes-backend:1.0\n",[68,2984,2985],{},"Запустить контейнер backend вручную для проверки (без базы данных он покажет ошибку в health check, но сам процесс должен стартовать):",[1031,2987,2989],{"className":2578,"code":2988,"language":2580,"meta":40,"style":40},"docker run --rm -p 8000:8000 notes-backend:1.0\n",[979,2990,2991],{"__ignoreMap":40},[1039,2992,2993,2995,2998,3001,3004,3007],{"class":1041,"line":395},[1039,2994,2588],{"class":2587},[1039,2996,2997],{"class":2599}," run",[1039,2999,3000],{"class":2591}," --rm",[1039,3002,3003],{"class":2591}," -p",[1039,3005,3006],{"class":2599}," 8000:8000",[1039,3008,2982],{"class":2599},[68,3010,3011],{},"Результат этапа: оба образа собираются без ошибок, размеры образов минимизированы, контейнеры запускаются от непривилегированного пользователя.",[3013,3014],"hr",{},[83,3016,3018],{"id":3017},"этап-2-локальная-композиция-docker-compose","Этап 2. Локальная композиция (Docker Compose)",[68,3020,3021,3022,3025],{},"На этом этапе необходимо описать все три компонента приложения в файле ",[979,3023,3024],{},"docker-compose.yml"," и обеспечить их совместную работу одной командой.",[2569,3027,3029],{"id":3028},"_21-структура-файла-docker-composeyml","2.1. Структура файла docker-compose.yml",[68,3031,2656,3032,3034,3035,1008,3037,1008,3039,3041],{},[979,3033,3024],{}," в корне проекта. Файл описывает три сервиса (",[979,3036,2525],{},[979,3038,2531],{},[979,3040,2355],{},"), пользовательскую сеть и именованный том.",[68,3043,3044,3045],{},"Документация по формату файла: ",[21,3046,3047],{"href":3047,"rel":3048},"https:\u002F\u002Fdocs.docker.com\u002Freference\u002Fcompose-file\u002F",[2613],[2569,3050,3052],{"id":3051},"_22-сервис-db-postgresql","2.2. Сервис db (PostgreSQL)",[68,3054,3055,3056,3059],{},"Для базы данных используется официальный образ PostgreSQL (например, ",[979,3057,3058],{},"postgres:17-alpine","). Собственный Dockerfile не требуется.",[68,3061,3062],{},"Основные параметры:",[14,3064,3065,3088,3098],{},[17,3066,3067,3070,3071,1008,3074,1008,3077,3080,3081,3084,3085,1207],{},[2155,3068,3069],{},"Переменные окружения",": ",[979,3072,3073],{},"POSTGRES_DB",[979,3075,3076],{},"POSTGRES_USER",[979,3078,3079],{},"POSTGRES_PASSWORD"," — задают имя базы данных, пользователя и пароль при первом запуске контейнера. Значения следует читать из переменных окружения Compose (синтаксис ",[979,3082,3083],{},"${VARIABLE}","), а значения по умолчанию определить в файле ",[979,3086,3087],{},".env",[17,3089,3090,3093,3094,3097],{},[2155,3091,3092],{},"Том",": подключить именованный том к каталогу ",[979,3095,3096],{},"\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata"," внутри контейнера. Это обеспечивает сохранность данных при пересоздании контейнера.",[17,3099,3100,3103],{},[2155,3101,3102],{},"Сеть",": подключить к пользовательской сети.",[68,3105,3106,3107],{},"Документация по образу PostgreSQL: ",[21,3108,3109],{"href":3109,"rel":3110},"https:\u002F\u002Fhub.docker.com\u002F_\u002Fpostgres",[2613],[2569,3112,3114],{"id":3113},"_23-сервис-backend","2.3. Сервис backend",[68,3116,3062],{},[14,3118,3119,3128,3151,3172],{},[17,3120,3121,3124,3125,1207],{},[2155,3122,3123],{},"Сборка",": указать путь к Dockerfile и контекст сборки через секцию ",[979,3126,3127],{},"build",[17,3129,3130,3070,3132,3135,3136,3138,3139,1008,3142,1008,3145,1008,3148,1207],{},[2155,3131,3069],{},[979,3133,3134],{},"DB_HOST"," (имя сервиса ",[979,3137,2355],{}," — Docker Compose автоматически разрешает его в IP-адрес контейнера), ",[979,3140,3141],{},"DB_PORT",[979,3143,3144],{},"DB_NAME",[979,3146,3147],{},"DB_USER",[979,3149,3150],{},"DB_PASSWORD",[17,3152,3153,3156,3157,3160,3161,3164,3165,3167,3168,3171],{},[2155,3154,3155],{},"Зависимости",": секция ",[979,3158,3159],{},"depends_on"," с условием ",[979,3162,3163],{},"service_started"," для сервиса ",[979,3166,2355],{},". Это гарантирует, что контейнер базы данных будет запущен до backend, хотя не гарантирует, что PostgreSQL будет готов принимать соединения (эту задачу решает retry-логика в ",[979,3169,3170],{},"app.js",").",[17,3173,3174,3176],{},[2155,3175,3102],{},": подключить к той же пользовательской сети, что и db.",[2569,3178,3180],{"id":3179},"_24-сервис-frontend","2.4. Сервис frontend",[68,3182,3062],{},[14,3184,3185,3190,3206,3214],{},[17,3186,3187,3189],{},[2155,3188,3123],{},": указать путь к Dockerfile и контекст.",[17,3191,3192,3156,3195,3198,3199,3202,3203,1207],{},[2155,3193,3194],{},"Публикация порта",[979,3196,3197],{},"ports",", например ",[979,3200,3201],{},"\"80:80\""," (порт хоста : порт контейнера). После запуска приложение будет доступно в браузере по адресу ",[979,3204,3205],{},"http:\u002F\u002Flocalhost",[17,3207,3208,3070,3210,3164,3212,1207],{},[2155,3209,3155],{},[979,3211,3159],{},[979,3213,2531],{},[17,3215,3216,3218,3219,3221,3222,3224,3225,3228],{},[2155,3217,3102],{},": подключить к пользовательской сети. nginx обращается к backend по имени ",[979,3220,2531],{}," (см. ",[979,3223,2851],{},", директива ",[979,3226,3227],{},"proxy_pass","), поэтому оба сервиса должны находиться в одной сети.",[2569,3230,3232],{"id":3231},"_25-файл-переменных-окружения-env","2.5. Файл переменных окружения (.env)",[68,3234,2656,3235,3237,3238,3240,3241,1207],{},[979,3236,3087],{}," в каталоге, где расположен ",[979,3239,3024],{},". Docker Compose автоматически читает этот файл и подставляет значения переменных в ",[979,3242,3024],{},[68,3244,3245],{},"Пример содержимого:",[1031,3247,3250],{"className":3248,"code":3249,"language":2551},[2549],"DB_NAME=notes\nDB_USER=notes\nDB_PASSWORD=notes\n",[979,3251,3249],{"__ignoreMap":40},[68,3253,2645,3254],{},[21,3255,3256],{"href":3256,"rel":3257},"https:\u002F\u002Fdocs.docker.com\u002Fcompose\u002Fhow-tos\u002Fenvironment-variables\u002Fvariable-interpolation\u002F",[2613],[2569,3259,3261],{"id":3260},"_26-пользовательская-сеть","2.6. Пользовательская сеть",[68,3263,3264,3265,3268,3269,3271,3272,1008,3274,3171],{},"Определить сеть в секции ",[979,3266,3267],{},"networks"," файла ",[979,3270,3024],{}," и подключить к ней все три сервиса. В пользовательской сети Docker Compose предоставляет встроенное разрешение имен: каждый сервис доступен другим по имени, указанному в конфигурации (например, ",[979,3273,2531],{},[979,3275,2355],{},[68,3277,2645,3278],{},[21,3279,3280],{"href":3280,"rel":3281},"https:\u002F\u002Fdocs.docker.com\u002Fcompose\u002Fhow-tos\u002Fnetworking\u002F",[2613],[2569,3283,3285],{"id":3284},"_27-ограничения-ресурсов-и-политика-перезапуска","2.7. Ограничения ресурсов и политика перезапуска",[68,3287,3288],{},"Для каждого сервиса задать:",[14,3290,3291,3308],{},[17,3292,3293,3296,3297,3300,3301,1008,3304,3307],{},[2155,3294,3295],{},"Ограничения ресурсов"," через секцию ",[979,3298,3299],{},"deploy.resources.limits"," (CPU и память). Например, для backend: ",[979,3302,3303],{},"cpus: \"0.5\"",[979,3305,3306],{},"memory: 256M",". Ограничения предотвращают ситуацию, когда один контейнер потребляет все ресурсы хостовой машины.",[17,3309,3310,3296,3313,3316,3317,3320],{},[2155,3311,3312],{},"Политику перезапуска",[979,3314,3315],{},"restart",". Значение ",[979,3318,3319],{},"unless-stopped"," перезапускает контейнер при любом завершении, кроме явной остановки пользователем.",[68,3322,2645,3323],{},[21,3324,3325],{"href":3325,"rel":3326},"https:\u002F\u002Fdocs.docker.com\u002Freference\u002Fcompose-file\u002Fservices\u002F#deploy",[2613],[2569,3328,3330],{"id":3329},"_28-запуск-и-проверка","2.8. Запуск и проверка",[1031,3332,3334],{"className":2578,"code":3333,"language":2580,"meta":40,"style":40},"# Запуск всех сервисов (сборка образов при первом запуске)\ndocker compose up --build\n\n# Или в фоновом режиме\ndocker compose up --build -d\n\n# Проверка состояния сервисов\ndocker compose ps\n\n# Просмотр журналов\ndocker compose logs backend\n\n# Остановка и удаление контейнеров (том сохраняется)\ndocker compose down\n\n# Остановка с удалением тома (данные будут потеряны)\ndocker compose down -v\n",[979,3335,3336,3342,3355,3359,3364,3378,3382,3387,3396,3400,3405,3417,3421,3426,3436,3441,3447],{"__ignoreMap":40},[1039,3337,3338],{"class":1041,"line":395},[1039,3339,3341],{"class":3340},"sJ8bj","# Запуск всех сервисов (сборка образов при первом запуске)\n",[1039,3343,3344,3346,3349,3352],{"class":1041,"line":41},[1039,3345,2588],{"class":2587},[1039,3347,3348],{"class":2599}," compose",[1039,3350,3351],{"class":2599}," up",[1039,3353,3354],{"class":2591}," --build\n",[1039,3356,3357],{"class":1041,"line":369},[1039,3358,1049],{"emptyLinePlaceholder":48},[1039,3360,3361],{"class":1041,"line":1057},[1039,3362,3363],{"class":3340},"# Или в фоновом режиме\n",[1039,3365,3366,3368,3370,3372,3375],{"class":1041,"line":1062},[1039,3367,2588],{"class":2587},[1039,3369,3348],{"class":2599},[1039,3371,3351],{"class":2599},[1039,3373,3374],{"class":2591}," --build",[1039,3376,3377],{"class":2591}," -d\n",[1039,3379,3380],{"class":1041,"line":1068},[1039,3381,1049],{"emptyLinePlaceholder":48},[1039,3383,3384],{"class":1041,"line":1074},[1039,3385,3386],{"class":3340},"# Проверка состояния сервисов\n",[1039,3388,3389,3391,3393],{"class":1041,"line":1079},[1039,3390,2588],{"class":2587},[1039,3392,3348],{"class":2599},[1039,3394,3395],{"class":2599}," ps\n",[1039,3397,3398],{"class":1041,"line":1085},[1039,3399,1049],{"emptyLinePlaceholder":48},[1039,3401,3402],{"class":1041,"line":1090},[1039,3403,3404],{"class":3340},"# Просмотр журналов\n",[1039,3406,3407,3409,3411,3414],{"class":1041,"line":1096},[1039,3408,2588],{"class":2587},[1039,3410,3348],{"class":2599},[1039,3412,3413],{"class":2599}," logs",[1039,3415,3416],{"class":2599}," backend\n",[1039,3418,3419],{"class":1041,"line":51},[1039,3420,1049],{"emptyLinePlaceholder":48},[1039,3422,3423],{"class":1041,"line":1106},[1039,3424,3425],{"class":3340},"# Остановка и удаление контейнеров (том сохраняется)\n",[1039,3427,3429,3431,3433],{"class":1041,"line":3428},14,[1039,3430,2588],{"class":2587},[1039,3432,3348],{"class":2599},[1039,3434,3435],{"class":2599}," down\n",[1039,3437,3439],{"class":1041,"line":3438},15,[1039,3440,1049],{"emptyLinePlaceholder":48},[1039,3442,3444],{"class":1041,"line":3443},16,[1039,3445,3446],{"class":3340},"# Остановка с удалением тома (данные будут потеряны)\n",[1039,3448,3450,3452,3454,3457],{"class":1041,"line":3449},17,[1039,3451,2588],{"class":2587},[1039,3453,3348],{"class":2599},[1039,3455,3456],{"class":2599}," down",[1039,3458,3459],{"class":2591}," -v\n",[68,3461,3462],{},"Убедиться, что:",[14,3464,3465,3471,3474,3477],{},[17,3466,3467,3468,3470],{},"веб-интерфейс доступен по адресу ",[979,3469,3205],{},";",[17,3472,3473],{},"индикатор состояния API показывает «подключено»;",[17,3475,3476],{},"заметки создаются и отображаются;",[17,3478,3479,3480,3483,3484,3487],{},"после выполнения ",[979,3481,3482],{},"docker compose down"," и повторного ",[979,3485,3486],{},"docker compose up"," ранее созданные заметки сохранились (том не был удален).",[68,3489,3490],{},"Результат этапа: приложение запускается одной командой, все компоненты взаимодействуют корректно, данные сохраняются между перезапусками.",[3013,3492],{},[83,3494,3496],{"id":3495},"этап-3-развертывание-в-локальном-кластере-kubernetes","Этап 3. Развертывание в локальном кластере Kubernetes",[68,3498,3499],{},"На этом этапе необходимо развернуть локальный кластер Kubernetes, подготовить YAML-манифесты для всех компонентов и развернуть приложение в кластере.",[2569,3501,3503],{"id":3502},"_31-установка-и-настройка-локального-кластера","3.1. Установка и настройка локального кластера",[68,3505,3506],{},"Установить minikube и kubectl:",[14,3508,3509,3516],{},[17,3510,3511,3512],{},"minikube: ",[21,3513,3514],{"href":3514,"rel":3515},"https:\u002F\u002Fminikube.sigs.k8s.io\u002Fdocs\u002Fstart\u002F",[2613],[17,3517,3518,3519],{},"kubectl: ",[21,3520,3521],{"href":3521,"rel":3522},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Ftools\u002F",[2613],[68,3524,3525],{},"Запуск кластера:",[1031,3527,3529],{"className":2578,"code":3528,"language":2580,"meta":40,"style":40},"minikube start --cpus=2 --memory=4096\n",[979,3530,3531],{"__ignoreMap":40},[1039,3532,3533,3536,3539,3542],{"class":1041,"line":395},[1039,3534,3535],{"class":2587},"minikube",[1039,3537,3538],{"class":2599}," start",[1039,3540,3541],{"class":2591}," --cpus=2",[1039,3543,3544],{"class":2591}," --memory=4096\n",[68,3546,3547],{},"Проверка работоспособности:",[1031,3549,3551],{"className":2578,"code":3550,"language":2580,"meta":40,"style":40},"kubectl cluster-info\nkubectl get nodes\n",[979,3552,3553,3561],{"__ignoreMap":40},[1039,3554,3555,3558],{"class":1041,"line":395},[1039,3556,3557],{"class":2587},"kubectl",[1039,3559,3560],{"class":2599}," cluster-info\n",[1039,3562,3563,3565,3568],{"class":1041,"line":41},[1039,3564,3557],{"class":2587},[1039,3566,3567],{"class":2599}," get",[1039,3569,3570],{"class":2599}," nodes\n",[2569,3572,3574],{"id":3573},"_32-загрузка-образов-в-кластер","3.2. Загрузка образов в кластер",[68,3576,3577],{},"Локальный кластер minikube не имеет доступа к образам, собранным на хостовой машине. Чтобы использовать локально собранные образы, необходимо загрузить их в minikube:",[1031,3579,3581],{"className":2578,"code":3580,"language":2580,"meta":40,"style":40},"minikube image load notes-backend:1.0\nminikube image load notes-frontend:1.0\n",[979,3582,3583,3595],{"__ignoreMap":40},[1039,3584,3585,3587,3590,3593],{"class":1041,"line":395},[1039,3586,3535],{"class":2587},[1039,3588,3589],{"class":2599}," image",[1039,3591,3592],{"class":2599}," load",[1039,3594,2982],{"class":2599},[1039,3596,3597,3599,3601,3603],{"class":1041,"line":41},[1039,3598,3535],{"class":2587},[1039,3600,3589],{"class":2599},[1039,3602,3592],{"class":2599},[1039,3604,3605],{"class":2599}," notes-frontend:1.0\n",[68,3607,3608],{},"Альтернативный подход — собирать образы непосредственно внутри Docker-среды minikube:",[1031,3610,3612],{"className":2578,"code":3611,"language":2580,"meta":40,"style":40},"eval $(minikube docker-env)\ndocker build -t notes-backend:1.0 -f docker\u002Fbackend\u002FDockerfile app\u002Fbackend\u002F\ndocker build -t notes-frontend:1.0 -f docker\u002Ffrontend\u002FDockerfile app\u002Ffrontend\u002F\n",[979,3613,3614,3631,3647],{"__ignoreMap":40},[1039,3615,3616,3619,3623,3625,3628],{"class":1041,"line":395},[1039,3617,3618],{"class":2591},"eval",[1039,3620,3622],{"class":3621},"sVt8B"," $(",[1039,3624,3535],{"class":2587},[1039,3626,3627],{"class":2599}," docker-env",[1039,3629,3630],{"class":3621},")\n",[1039,3632,3633,3635,3637,3639,3641,3643,3645],{"class":1041,"line":41},[1039,3634,2588],{"class":2587},[1039,3636,2898],{"class":2599},[1039,3638,2901],{"class":2591},[1039,3640,2904],{"class":2599},[1039,3642,2907],{"class":2591},[1039,3644,2910],{"class":2599},[1039,3646,2913],{"class":2599},[1039,3648,3649,3651,3653,3655,3657,3659,3661],{"class":1041,"line":369},[1039,3650,2588],{"class":2587},[1039,3652,2898],{"class":2599},[1039,3654,2901],{"class":2591},[1039,3656,2924],{"class":2599},[1039,3658,2907],{"class":2591},[1039,3660,2929],{"class":2599},[1039,3662,2932],{"class":2599},[68,3664,3665,3666,2059,3669,3672],{},"В манифестах Deployment необходимо указать ",[979,3667,3668],{},"imagePullPolicy: Never",[979,3670,3671],{},"imagePullPolicy: IfNotPresent",", чтобы Kubernetes не пытался скачать образ из внешнего реестра.",[68,3674,2645,3675],{},[21,3676,3677],{"href":3677,"rel":3678},"https:\u002F\u002Fminikube.sigs.k8s.io\u002Fdocs\u002Fhandbook\u002Fpushing\u002F",[2613],[2569,3680,3682],{"id":3681},"_33-организация-манифестов","3.3. Организация манифестов",[68,3684,3685,3686,3689],{},"Создать каталог ",[979,3687,3688],{},"k8s\u002F"," и разместить в нем манифесты, организованные по компонентам:",[1031,3691,3694],{"className":3692,"code":3693,"language":2551},[2549],"k8s\u002F\n├── backend\u002F\n│   ├── deployment.yaml\n│   ├── service.yaml\n│   └── configmap.yaml\n├── frontend\u002F\n│   ├── deployment.yaml\n│   └── service.yaml\n├── db\u002F\n│   ├── deployment.yaml\n│   ├── service.yaml\n│   └── pvc.yaml\n├── secrets.yaml\n└── ingress.yaml\n",[979,3695,3693],{"__ignoreMap":40},[68,3697,3698,3699,1008,3702,1018,3705,1207],{},"Каждый манифест — это YAML-файл, описывающий один ресурс Kubernetes. Все манифесты начинаются с полей ",[979,3700,3701],{},"apiVersion",[979,3703,3704],{},"kind",[979,3706,3707],{},"metadata",[2569,3709,3711],{"id":3710},"_34-манифесты-для-компонента-database","3.4. Манифесты для компонента database",[68,3713,3714,3717],{},[2155,3715,3716],{},"PersistentVolumeClaim (pvc.yaml)"," — запрос на выделение постоянного хранилища. В minikube используется встроенный StorageClass (provisioner), который автоматически создает PersistentVolume.",[68,3719,3720],{},"Основные поля:",[14,3722,3723,3732],{},[17,3724,3725,3070,3728,3731],{},[979,3726,3727],{},"spec.accessModes",[979,3729,3730],{},"[\"ReadWriteOnce\"]"," — том доступен для чтения и записи одному узлу.",[17,3733,3734,3737,3738,1207],{},[979,3735,3736],{},"spec.resources.requests.storage",": например, ",[979,3739,3740],{},"1Gi",[68,3742,2645,3743],{},[21,3744,3745],{"href":3745,"rel":3746},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fstorage\u002Fpersistent-volumes\u002F",[2613],[68,3748,3749,3752],{},[2155,3750,3751],{},"Secret (secrets.yaml)"," — хранилище для чувствительных данных (паролей). Значения кодируются в Base64.",[68,3754,3755],{},"Создать Secret с паролем базы данных:",[1031,3757,3759],{"className":2578,"code":3758,"language":2580,"meta":40,"style":40},"# Закодировать значение в Base64\necho -n \"notes\" | base64\n",[979,3760,3761,3766],{"__ignoreMap":40},[1039,3762,3763],{"class":1041,"line":395},[1039,3764,3765],{"class":3340},"# Закодировать значение в Base64\n",[1039,3767,3768,3771,3774,3777,3779],{"class":1041,"line":41},[1039,3769,3770],{"class":2591},"echo",[1039,3772,3773],{"class":2591}," -n",[1039,3775,3776],{"class":2599}," \"notes\"",[1039,3778,2958],{"class":2957},[1039,3780,3781],{"class":2587}," base64\n",[68,3783,3784],{},"В манифесте:",[1031,3786,3790],{"className":3787,"code":3788,"language":3789,"meta":40,"style":40},"language-yaml shiki shiki-themes github-light github-dark","apiVersion: v1\nkind: Secret\nmetadata:\n  name: db-secret\ntype: Opaque\ndata:\n  password: \u003Cзначение в Base64>\n","yaml",[979,3791,3792,3802,3811,3818,3828,3838,3845],{"__ignoreMap":40},[1039,3793,3794,3797,3799],{"class":1041,"line":395},[1039,3795,3701],{"class":3796},"s9eBZ",[1039,3798,3070],{"class":3621},[1039,3800,3801],{"class":2599},"v1\n",[1039,3803,3804,3806,3808],{"class":1041,"line":41},[1039,3805,3704],{"class":3796},[1039,3807,3070],{"class":3621},[1039,3809,3810],{"class":2599},"Secret\n",[1039,3812,3813,3815],{"class":1041,"line":369},[1039,3814,3707],{"class":3796},[1039,3816,3817],{"class":3621},":\n",[1039,3819,3820,3823,3825],{"class":1041,"line":1057},[1039,3821,3822],{"class":3796},"  name",[1039,3824,3070],{"class":3621},[1039,3826,3827],{"class":2599},"db-secret\n",[1039,3829,3830,3833,3835],{"class":1041,"line":1062},[1039,3831,3832],{"class":3796},"type",[1039,3834,3070],{"class":3621},[1039,3836,3837],{"class":2599},"Opaque\n",[1039,3839,3840,3843],{"class":1041,"line":1068},[1039,3841,3842],{"class":3796},"data",[1039,3844,3817],{"class":3621},[1039,3846,3847,3850,3852],{"class":1041,"line":1074},[1039,3848,3849],{"class":3796},"  password",[1039,3851,3070],{"class":3621},[1039,3853,3854],{"class":2599},"\u003Cзначение в Base64>\n",[68,3856,2645,3857],{},[21,3858,3859],{"href":3859,"rel":3860},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fconfiguration\u002Fsecret\u002F",[2613],[68,3862,3863,3866],{},[2155,3864,3865],{},"Deployment (deployment.yaml)"," — описание развертывания PostgreSQL.",[68,3868,3062],{},[14,3870,3871,3877,3886,3900],{},[17,3872,3873,3876],{},[979,3874,3875],{},"spec.replicas: 1"," — для базы данных достаточно одной реплики.",[17,3878,3879,3880,3883,3884,1207],{},"В секции ",[979,3881,3882],{},"containers"," указать образ ",[979,3885,3058],{},[17,3887,3888,3889,1008,3891,3893,3894,3896,3897,3171],{},"Переменные окружения ",[979,3890,3073],{},[979,3892,3076],{}," задать явно, а ",[979,3895,3079],{}," — через ссылку на Secret (",[979,3898,3899],{},"valueFrom.secretKeyRef",[17,3901,3902,3903,3905,3906,1018,3909,3912,3913,3916],{},"Том PVC подключить к каталогу ",[979,3904,3096],{}," через секции ",[979,3907,3908],{},"volumes",[979,3910,3911],{},"volumeMounts",". Для PostgreSQL рекомендуется монтировать том в подкаталог, указав ",[979,3914,3915],{},"subPath",", так как PostgreSQL требует, чтобы каталог данных был пустым при инициализации.",[68,3918,2645,3919],{},[21,3920,3921],{"href":3921,"rel":3922},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fcontrollers\u002Fdeployment\u002F",[2613],[68,3924,3925,3928],{},[2155,3926,3927],{},"Service (service.yaml)"," — обеспечивает стабильный сетевой доступ к PostgreSQL внутри кластера.",[1031,3930,3932],{"className":3787,"code":3931,"language":3789,"meta":40,"style":40},"apiVersion: v1\nkind: Service\nmetadata:\n  name: db\nspec:\n  selector:\n    app: db\n  ports:\n    - port: 5432\n      targetPort: 5432\n  type: ClusterIP\n",[979,3933,3934,3942,3951,3957,3966,3973,3980,3989,3996,4008,4017],{"__ignoreMap":40},[1039,3935,3936,3938,3940],{"class":1041,"line":395},[1039,3937,3701],{"class":3796},[1039,3939,3070],{"class":3621},[1039,3941,3801],{"class":2599},[1039,3943,3944,3946,3948],{"class":1041,"line":41},[1039,3945,3704],{"class":3796},[1039,3947,3070],{"class":3621},[1039,3949,3950],{"class":2599},"Service\n",[1039,3952,3953,3955],{"class":1041,"line":369},[1039,3954,3707],{"class":3796},[1039,3956,3817],{"class":3621},[1039,3958,3959,3961,3963],{"class":1041,"line":1057},[1039,3960,3822],{"class":3796},[1039,3962,3070],{"class":3621},[1039,3964,3965],{"class":2599},"db\n",[1039,3967,3968,3971],{"class":1041,"line":1062},[1039,3969,3970],{"class":3796},"spec",[1039,3972,3817],{"class":3621},[1039,3974,3975,3978],{"class":1041,"line":1068},[1039,3976,3977],{"class":3796},"  selector",[1039,3979,3817],{"class":3621},[1039,3981,3982,3985,3987],{"class":1041,"line":1074},[1039,3983,3984],{"class":3796},"    app",[1039,3986,3070],{"class":3621},[1039,3988,3965],{"class":2599},[1039,3990,3991,3994],{"class":1041,"line":1079},[1039,3992,3993],{"class":3796},"  ports",[1039,3995,3817],{"class":3621},[1039,3997,3998,4001,4003,4005],{"class":1041,"line":1085},[1039,3999,4000],{"class":3621},"    - ",[1039,4002,1981],{"class":3796},[1039,4004,3070],{"class":3621},[1039,4006,4007],{"class":2591},"5432\n",[1039,4009,4010,4013,4015],{"class":1041,"line":1090},[1039,4011,4012],{"class":3796},"      targetPort",[1039,4014,3070],{"class":3621},[1039,4016,4007],{"class":2591},[1039,4018,4019,4022,4024],{"class":1041,"line":1096},[1039,4020,4021],{"class":3796},"  type",[1039,4023,3070],{"class":3621},[1039,4025,4026],{"class":2599},"ClusterIP\n",[68,4028,4029,4030,4032,4033,1207],{},"Имя сервиса ",[979,4031,2355],{}," используется в качестве DNS-имени: backend обращается к базе данных по адресу ",[979,4034,4035],{},"db:5432",[68,4037,2645,4038],{},[21,4039,4040],{"href":4040,"rel":4041},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fservices-networking\u002Fservice\u002F",[2613],[2569,4043,4045],{"id":4044},"_35-манифесты-для-компонента-backend","3.5. Манифесты для компонента backend",[68,4047,4048,4051],{},[2155,4049,4050],{},"ConfigMap (configmap.yaml)"," — хранилище нечувствительных параметров конфигурации.",[1031,4053,4055],{"className":3787,"code":4054,"language":3789,"meta":40,"style":40},"apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: backend-config\ndata:\n  DB_HOST: \"db\"\n  DB_PORT: \"5432\"\n  DB_NAME: \"notes\"\n  DB_USER: \"notes\"\n",[979,4056,4057,4065,4074,4080,4089,4095,4105,4115,4125],{"__ignoreMap":40},[1039,4058,4059,4061,4063],{"class":1041,"line":395},[1039,4060,3701],{"class":3796},[1039,4062,3070],{"class":3621},[1039,4064,3801],{"class":2599},[1039,4066,4067,4069,4071],{"class":1041,"line":41},[1039,4068,3704],{"class":3796},[1039,4070,3070],{"class":3621},[1039,4072,4073],{"class":2599},"ConfigMap\n",[1039,4075,4076,4078],{"class":1041,"line":369},[1039,4077,3707],{"class":3796},[1039,4079,3817],{"class":3621},[1039,4081,4082,4084,4086],{"class":1041,"line":1057},[1039,4083,3822],{"class":3796},[1039,4085,3070],{"class":3621},[1039,4087,4088],{"class":2599},"backend-config\n",[1039,4090,4091,4093],{"class":1041,"line":1062},[1039,4092,3842],{"class":3796},[1039,4094,3817],{"class":3621},[1039,4096,4097,4100,4102],{"class":1041,"line":1068},[1039,4098,4099],{"class":3796},"  DB_HOST",[1039,4101,3070],{"class":3621},[1039,4103,4104],{"class":2599},"\"db\"\n",[1039,4106,4107,4110,4112],{"class":1041,"line":1074},[1039,4108,4109],{"class":3796},"  DB_PORT",[1039,4111,3070],{"class":3621},[1039,4113,4114],{"class":2599},"\"5432\"\n",[1039,4116,4117,4120,4122],{"class":1041,"line":1079},[1039,4118,4119],{"class":3796},"  DB_NAME",[1039,4121,3070],{"class":3621},[1039,4123,4124],{"class":2599},"\"notes\"\n",[1039,4126,4127,4130,4132],{"class":1041,"line":1085},[1039,4128,4129],{"class":3796},"  DB_USER",[1039,4131,3070],{"class":3621},[1039,4133,4124],{"class":2599},[68,4135,4136,4137,2059,4140,4143],{},"В Deployment переменные окружения читаются из ConfigMap (",[979,4138,4139],{},"envFrom",[979,4141,4142],{},"valueFrom.configMapKeyRef","), а пароль — из Secret.",[68,4145,2645,4146],{},[21,4147,4148],{"href":4148,"rel":4149},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fconfiguration\u002Fconfigmap\u002F",[2613],[68,4151,4152],{},[2155,4153,4154],{},"Deployment (deployment.yaml):",[14,4156,4157,4163,4171,4174],{},[17,4158,4159,4162],{},[979,4160,4161],{},"spec.replicas: 2"," — не менее двух реплик для обеспечения доступности.",[17,4164,4165,4166,1008,4169,1207],{},"Образ ",[979,4167,4168],{},"notes-backend:1.0",[979,4170,3671],{},[17,4172,4173],{},"Переменные окружения из ConfigMap и Secret.",[17,4175,4176],{},"Readiness probe — определяет, готов ли под принимать трафик. Kubernetes не направляет запросы на под, пока readiness probe не вернет успешный результат:",[1031,4178,4180],{"className":3787,"code":4179,"language":3789,"meta":40,"style":40},"readinessProbe:\n  httpGet:\n    path: \u002Fapi\u002Fhealth\n    port: 8000\n  initialDelaySeconds: 5\n  periodSeconds: 10\n",[979,4181,4182,4189,4196,4206,4216,4226],{"__ignoreMap":40},[1039,4183,4184,4187],{"class":1041,"line":395},[1039,4185,4186],{"class":3796},"readinessProbe",[1039,4188,3817],{"class":3621},[1039,4190,4191,4194],{"class":1041,"line":41},[1039,4192,4193],{"class":3796},"  httpGet",[1039,4195,3817],{"class":3621},[1039,4197,4198,4201,4203],{"class":1041,"line":369},[1039,4199,4200],{"class":3796},"    path",[1039,4202,3070],{"class":3621},[1039,4204,4205],{"class":2599},"\u002Fapi\u002Fhealth\n",[1039,4207,4208,4211,4213],{"class":1041,"line":1057},[1039,4209,4210],{"class":3796},"    port",[1039,4212,3070],{"class":3621},[1039,4214,4215],{"class":2591},"8000\n",[1039,4217,4218,4221,4223],{"class":1041,"line":1062},[1039,4219,4220],{"class":3796},"  initialDelaySeconds",[1039,4222,3070],{"class":3621},[1039,4224,4225],{"class":2591},"5\n",[1039,4227,4228,4231,4233],{"class":1041,"line":1068},[1039,4229,4230],{"class":3796},"  periodSeconds",[1039,4232,3070],{"class":3621},[1039,4234,4235],{"class":2591},"10\n",[14,4237,4238],{},[17,4239,4240],{},"Liveness probe — определяет, жив ли процесс. Если liveness probe несколько раз подряд возвращает ошибку, Kubernetes перезапускает контейнер:",[1031,4242,4244],{"className":3787,"code":4243,"language":3789,"meta":40,"style":40},"livenessProbe:\n  httpGet:\n    path: \u002Fapi\u002Fhealth\n    port: 8000\n  initialDelaySeconds: 15\n  periodSeconds: 20\n",[979,4245,4246,4253,4259,4267,4275,4284],{"__ignoreMap":40},[1039,4247,4248,4251],{"class":1041,"line":395},[1039,4249,4250],{"class":3796},"livenessProbe",[1039,4252,3817],{"class":3621},[1039,4254,4255,4257],{"class":1041,"line":41},[1039,4256,4193],{"class":3796},[1039,4258,3817],{"class":3621},[1039,4260,4261,4263,4265],{"class":1041,"line":369},[1039,4262,4200],{"class":3796},[1039,4264,3070],{"class":3621},[1039,4266,4205],{"class":2599},[1039,4268,4269,4271,4273],{"class":1041,"line":1057},[1039,4270,4210],{"class":3796},[1039,4272,3070],{"class":3621},[1039,4274,4215],{"class":2591},[1039,4276,4277,4279,4281],{"class":1041,"line":1062},[1039,4278,4220],{"class":3796},[1039,4280,3070],{"class":3621},[1039,4282,4283],{"class":2591},"15\n",[1039,4285,4286,4288,4290],{"class":1041,"line":1068},[1039,4287,4230],{"class":3796},[1039,4289,3070],{"class":3621},[1039,4291,4292],{"class":2591},"20\n",[14,4294,4295],{},[17,4296,4297,4298,1018,4301,4304],{},"Ограничения ресурсов через ",[979,4299,4300],{},"resources.requests",[979,4302,4303],{},"resources.limits"," (необходимы для корректной работы HPA на этапе 4).",[68,4306,4307,4308],{},"Документация по probes: ",[21,4309,4310],{"href":4310,"rel":4311},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Fconfigure-pod-container\u002Fconfigure-liveness-readiness-startup-probes\u002F",[2613],[68,4313,4314],{},[2155,4315,4316],{},"Service (service.yaml):",[14,4318,4319,4326],{},[17,4320,4321,4322,4325],{},"Тип ",[979,4323,4324],{},"ClusterIP",", порт 8000.",[17,4327,4328],{},"Selector должен совпадать с метками (labels) подов в Deployment.",[2569,4330,4332],{"id":4331},"_36-манифесты-для-компонента-frontend","3.6. Манифесты для компонента frontend",[68,4334,4335],{},[2155,4336,4154],{},[14,4338,4339,4343,4348],{},[17,4340,4341,1207],{},[979,4342,4161],{},[17,4344,4165,4345,1207],{},[979,4346,4347],{},"notes-frontend:1.0",[17,4349,4350],{},"Readiness probe с HTTP-запросом к порту 80.",[68,4352,4353,4354,4357,4358,4360,4361,1207],{},"Особенность: nginx.conf содержит директиву ",[979,4355,4356],{},"proxy_pass http:\u002F\u002Fbackend:8000",". В Kubernetes DNS-имя ",[979,4359,2531],{}," должно совпадать с именем Service компонента backend. Если Service backend назван иначе, необходимо либо скорректировать конфигурацию nginx, либо создать Service с именем ",[979,4362,2531],{},[68,4364,4365],{},[2155,4366,4316],{},[14,4368,4369],{},[17,4370,4321,4371,4373],{},[979,4372,4324],{},", порт 80.",[2569,4375,4377],{"id":4376},"_37-ingress","3.7. Ingress",[68,4379,4380],{},"Ingress — ресурс, обеспечивающий внешний HTTP-доступ к сервисам кластера. В minikube необходимо предварительно включить аддон Ingress:",[1031,4382,4384],{"className":2578,"code":4383,"language":2580,"meta":40,"style":40},"minikube addons enable ingress\n",[979,4385,4386],{"__ignoreMap":40},[1039,4387,4388,4390,4393,4396],{"class":1041,"line":395},[1039,4389,3535],{"class":2587},[1039,4391,4392],{"class":2599}," addons",[1039,4394,4395],{"class":2599}," enable",[1039,4397,4398],{"class":2599}," ingress\n",[68,4400,4401],{},"Манифест Ingress направляет входящий трафик на Service компонента frontend:",[1031,4403,4405],{"className":3787,"code":4404,"language":3789,"meta":40,"style":40},"apiVersion: networking.k8s.io\u002Fv1\nkind: Ingress\nmetadata:\n  name: notes-ingress\nspec:\n  rules:\n    - http:\n        paths:\n          - path: \u002F\n            pathType: Prefix\n            backend:\n              service:\n                name: frontend\n                port:\n                  number: 80\n",[979,4406,4407,4416,4425,4431,4440,4446,4453,4462,4469,4482,4492,4499,4506,4516,4523],{"__ignoreMap":40},[1039,4408,4409,4411,4413],{"class":1041,"line":395},[1039,4410,3701],{"class":3796},[1039,4412,3070],{"class":3621},[1039,4414,4415],{"class":2599},"networking.k8s.io\u002Fv1\n",[1039,4417,4418,4420,4422],{"class":1041,"line":41},[1039,4419,3704],{"class":3796},[1039,4421,3070],{"class":3621},[1039,4423,4424],{"class":2599},"Ingress\n",[1039,4426,4427,4429],{"class":1041,"line":369},[1039,4428,3707],{"class":3796},[1039,4430,3817],{"class":3621},[1039,4432,4433,4435,4437],{"class":1041,"line":1057},[1039,4434,3822],{"class":3796},[1039,4436,3070],{"class":3621},[1039,4438,4439],{"class":2599},"notes-ingress\n",[1039,4441,4442,4444],{"class":1041,"line":1062},[1039,4443,3970],{"class":3796},[1039,4445,3817],{"class":3621},[1039,4447,4448,4451],{"class":1041,"line":1068},[1039,4449,4450],{"class":3796},"  rules",[1039,4452,3817],{"class":3621},[1039,4454,4455,4457,4460],{"class":1041,"line":1074},[1039,4456,4000],{"class":3621},[1039,4458,4459],{"class":3796},"http",[1039,4461,3817],{"class":3621},[1039,4463,4464,4467],{"class":1041,"line":1079},[1039,4465,4466],{"class":3796},"        paths",[1039,4468,3817],{"class":3621},[1039,4470,4471,4474,4477,4479],{"class":1041,"line":1085},[1039,4472,4473],{"class":3621},"          - ",[1039,4475,4476],{"class":3796},"path",[1039,4478,3070],{"class":3621},[1039,4480,4481],{"class":2599},"\u002F\n",[1039,4483,4484,4487,4489],{"class":1041,"line":1090},[1039,4485,4486],{"class":3796},"            pathType",[1039,4488,3070],{"class":3621},[1039,4490,4491],{"class":2599},"Prefix\n",[1039,4493,4494,4497],{"class":1041,"line":1096},[1039,4495,4496],{"class":3796},"            backend",[1039,4498,3817],{"class":3621},[1039,4500,4501,4504],{"class":1041,"line":51},[1039,4502,4503],{"class":3796},"              service",[1039,4505,3817],{"class":3621},[1039,4507,4508,4511,4513],{"class":1041,"line":1106},[1039,4509,4510],{"class":3796},"                name",[1039,4512,3070],{"class":3621},[1039,4514,4515],{"class":2599},"frontend\n",[1039,4517,4518,4521],{"class":1041,"line":3428},[1039,4519,4520],{"class":3796},"                port",[1039,4522,3817],{"class":3621},[1039,4524,4525,4528,4530],{"class":1041,"line":3438},[1039,4526,4527],{"class":3796},"                  number",[1039,4529,3070],{"class":3621},[1039,4531,4532],{"class":2591},"80\n",[68,4534,4535],{},"После применения манифеста узнать IP-адрес для доступа:",[1031,4537,4539],{"className":2578,"code":4538,"language":2580,"meta":40,"style":40},"minikube ip\n",[979,4540,4541],{"__ignoreMap":40},[1039,4542,4543,4545],{"class":1041,"line":395},[1039,4544,3535],{"class":2587},[1039,4546,4547],{"class":2599}," ip\n",[68,4549,4550],{},"Приложение будет доступно по этому IP-адресу в браузере.",[68,4552,2645,4553],{},[21,4554,4555],{"href":4555,"rel":4556},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fservices-networking\u002Fingress\u002F",[2613],[2569,4558,4560],{"id":4559},"_38-применение-манифестов-и-проверка","3.8. Применение манифестов и проверка",[68,4562,4563],{},"Применить все манифесты:",[1031,4565,4567],{"className":2578,"code":4566,"language":2580,"meta":40,"style":40},"# Применить все файлы из каталога k8s\u002F рекурсивно\nkubectl apply -R -f k8s\u002F\n",[979,4568,4569,4574],{"__ignoreMap":40},[1039,4570,4571],{"class":1041,"line":395},[1039,4572,4573],{"class":3340},"# Применить все файлы из каталога k8s\u002F рекурсивно\n",[1039,4575,4576,4578,4581,4584,4586],{"class":1041,"line":41},[1039,4577,3557],{"class":2587},[1039,4579,4580],{"class":2599}," apply",[1039,4582,4583],{"class":2591}," -R",[1039,4585,2907],{"class":2591},[1039,4587,4588],{"class":2599}," k8s\u002F\n",[68,4590,4591,4592,3171],{},"Порядок создания ресурсов не критичен: Kubernetes работает по модели желаемого состояния и периодически пытается привести текущее состояние к заданному. Поды backend будут перезапускаться до тех пор, пока база данных не станет доступна (retry-логика в ",[979,4593,3170],{},[68,4595,4596],{},"Команды для проверки:",[1031,4598,4600],{"className":2578,"code":4599,"language":2580,"meta":40,"style":40},"# Состояние всех ресурсов\nkubectl get all\n\n# Состояние подов (подробно)\nkubectl get pods -o wide\n\n# Журналы конкретного пода\nkubectl logs \u003Cимя-пода>\n\n# Описание пода (события, причины ошибок)\nkubectl describe pod \u003Cимя-пода>\n\n# Состояние Ingress\nkubectl get ingress\n\n# Состояние PVC\nkubectl get pvc\n",[979,4601,4602,4607,4616,4620,4625,4640,4644,4649,4667,4671,4676,4694,4698,4703,4711,4715,4720],{"__ignoreMap":40},[1039,4603,4604],{"class":1041,"line":395},[1039,4605,4606],{"class":3340},"# Состояние всех ресурсов\n",[1039,4608,4609,4611,4613],{"class":1041,"line":41},[1039,4610,3557],{"class":2587},[1039,4612,3567],{"class":2599},[1039,4614,4615],{"class":2599}," all\n",[1039,4617,4618],{"class":1041,"line":369},[1039,4619,1049],{"emptyLinePlaceholder":48},[1039,4621,4622],{"class":1041,"line":1057},[1039,4623,4624],{"class":3340},"# Состояние подов (подробно)\n",[1039,4626,4627,4629,4631,4634,4637],{"class":1041,"line":1062},[1039,4628,3557],{"class":2587},[1039,4630,3567],{"class":2599},[1039,4632,4633],{"class":2599}," pods",[1039,4635,4636],{"class":2591}," -o",[1039,4638,4639],{"class":2599}," wide\n",[1039,4641,4642],{"class":1041,"line":1068},[1039,4643,1049],{"emptyLinePlaceholder":48},[1039,4645,4646],{"class":1041,"line":1074},[1039,4647,4648],{"class":3340},"# Журналы конкретного пода\n",[1039,4650,4651,4653,4655,4658,4661,4664],{"class":1041,"line":1079},[1039,4652,3557],{"class":2587},[1039,4654,3413],{"class":2599},[1039,4656,4657],{"class":2957}," \u003C",[1039,4659,4660],{"class":2599},"имя-под",[1039,4662,4663],{"class":3621},"а",[1039,4665,4666],{"class":2957},">\n",[1039,4668,4669],{"class":1041,"line":1085},[1039,4670,1049],{"emptyLinePlaceholder":48},[1039,4672,4673],{"class":1041,"line":1090},[1039,4674,4675],{"class":3340},"# Описание пода (события, причины ошибок)\n",[1039,4677,4678,4680,4683,4686,4688,4690,4692],{"class":1041,"line":1096},[1039,4679,3557],{"class":2587},[1039,4681,4682],{"class":2599}," describe",[1039,4684,4685],{"class":2599}," pod",[1039,4687,4657],{"class":2957},[1039,4689,4660],{"class":2599},[1039,4691,4663],{"class":3621},[1039,4693,4666],{"class":2957},[1039,4695,4696],{"class":1041,"line":51},[1039,4697,1049],{"emptyLinePlaceholder":48},[1039,4699,4700],{"class":1041,"line":1106},[1039,4701,4702],{"class":3340},"# Состояние Ingress\n",[1039,4704,4705,4707,4709],{"class":1041,"line":3428},[1039,4706,3557],{"class":2587},[1039,4708,3567],{"class":2599},[1039,4710,4398],{"class":2599},[1039,4712,4713],{"class":1041,"line":3438},[1039,4714,1049],{"emptyLinePlaceholder":48},[1039,4716,4717],{"class":1041,"line":3443},[1039,4718,4719],{"class":3340},"# Состояние PVC\n",[1039,4721,4722,4724,4726],{"class":1041,"line":3449},[1039,4723,3557],{"class":2587},[1039,4725,3567],{"class":2599},[1039,4727,4728],{"class":2599}," pvc\n",[68,4730,4731,4732,4735],{},"Результат этапа: все поды в состоянии ",[979,4733,4734],{},"Running",", приложение доступно через Ingress, данные сохраняются в постоянном томе.",[3013,4737],{},[83,4739,4741],{"id":4740},"этап-4-обновление-и-масштабирование","Этап 4. Обновление и масштабирование",[68,4743,4744],{},"На этом этапе необходимо продемонстрировать ключевые операции управления жизненным циклом приложения в кластере: обновление версии, откат, автомасштабирование и автоматическое восстановление после отказа. Все действия и их результаты фиксируются в пояснительной записке.",[2569,4746,4748],{"id":4747},"_41-rolling-update-последовательное-обновление","4.1. Rolling update (последовательное обновление)",[68,4750,4751,4752,4755,4756,4759],{},"Внести видимое изменение в backend — например, добавить поле ",[979,4753,4754],{},"version"," в ответ эндпоинта ",[979,4757,4758],{},"\u002Fapi\u002Fhealth",":",[1031,4761,4765],{"className":4762,"code":4763,"language":4764,"meta":40,"style":40},"language-js shiki shiki-themes github-light github-dark","res.json({ status: \"ok\", version: \"2.0\" });\n","js",[979,4766,4767],{"__ignoreMap":40},[1039,4768,4769,4772,4775,4778,4781,4784,4787],{"class":1041,"line":395},[1039,4770,4771],{"class":3621},"res.",[1039,4773,4774],{"class":2587},"json",[1039,4776,4777],{"class":3621},"({ status: ",[1039,4779,4780],{"class":2599},"\"ok\"",[1039,4782,4783],{"class":3621},", version: ",[1039,4785,4786],{"class":2599},"\"2.0\"",[1039,4788,4789],{"class":3621}," });\n",[68,4791,4792],{},"Пересобрать образ с новым тегом и загрузить в minikube:",[1031,4794,4796],{"className":2578,"code":4795,"language":2580,"meta":40,"style":40},"docker build -t notes-backend:2.0 -f docker\u002Fbackend\u002FDockerfile app\u002Fbackend\u002F\nminikube image load notes-backend:2.0\n",[979,4797,4798,4815],{"__ignoreMap":40},[1039,4799,4800,4802,4804,4806,4809,4811,4813],{"class":1041,"line":395},[1039,4801,2588],{"class":2587},[1039,4803,2898],{"class":2599},[1039,4805,2901],{"class":2591},[1039,4807,4808],{"class":2599}," notes-backend:2.0",[1039,4810,2907],{"class":2591},[1039,4812,2910],{"class":2599},[1039,4814,2913],{"class":2599},[1039,4816,4817,4819,4821,4823],{"class":1041,"line":41},[1039,4818,3535],{"class":2587},[1039,4820,3589],{"class":2599},[1039,4822,3592],{"class":2599},[1039,4824,4825],{"class":2599}," notes-backend:2.0\n",[68,4827,4828],{},"Обновить образ в Deployment:",[1031,4830,4832],{"className":2578,"code":4831,"language":2580,"meta":40,"style":40},"kubectl set image deployment\u002Fbackend backend=notes-backend:2.0\n",[979,4833,4834],{"__ignoreMap":40},[1039,4835,4836,4838,4841,4843,4846],{"class":1041,"line":395},[1039,4837,3557],{"class":2587},[1039,4839,4840],{"class":2599}," set",[1039,4842,3589],{"class":2599},[1039,4844,4845],{"class":2599}," deployment\u002Fbackend",[1039,4847,4848],{"class":2599}," backend=notes-backend:2.0\n",[68,4850,4851],{},"Наблюдать за процессом обновления:",[1031,4853,4855],{"className":2578,"code":4854,"language":2580,"meta":40,"style":40},"# Статус обновления в реальном времени\nkubectl rollout status deployment\u002Fbackend\n\n# Наблюдение за подами: старые завершаются, новые запускаются\nkubectl get pods -w\n",[979,4856,4857,4862,4875,4879,4884],{"__ignoreMap":40},[1039,4858,4859],{"class":1041,"line":395},[1039,4860,4861],{"class":3340},"# Статус обновления в реальном времени\n",[1039,4863,4864,4866,4869,4872],{"class":1041,"line":41},[1039,4865,3557],{"class":2587},[1039,4867,4868],{"class":2599}," rollout",[1039,4870,4871],{"class":2599}," status",[1039,4873,4874],{"class":2599}," deployment\u002Fbackend\n",[1039,4876,4877],{"class":1041,"line":369},[1039,4878,1049],{"emptyLinePlaceholder":48},[1039,4880,4881],{"class":1041,"line":1057},[1039,4882,4883],{"class":3340},"# Наблюдение за подами: старые завершаются, новые запускаются\n",[1039,4885,4886,4888,4890,4892],{"class":1041,"line":1062},[1039,4887,3557],{"class":2587},[1039,4889,3567],{"class":2599},[1039,4891,4633],{"class":2599},[1039,4893,4894],{"class":2591}," -w\n",[68,4896,4897],{},"Kubernetes заменяет поды постепенно: сначала создает новый под с образом 2.0, дожидается прохождения readiness probe, затем завершает один старый под. Процесс повторяется, пока все реплики не будут обновлены. Благодаря этому приложение остается доступным на протяжении всего обновления.",[68,4899,4900],{},"Проверить, что обновление применилось:",[1031,4902,4904],{"className":2578,"code":4903,"language":2580,"meta":40,"style":40},"kubectl exec \u003Cимя-нового-пода> -- wget -qO- http:\u002F\u002Flocalhost:8000\u002Fapi\u002Fhealth\n",[979,4905,4906],{"__ignoreMap":40},[1039,4907,4908,4910,4913,4915,4918,4920,4923,4926,4929,4932],{"class":1041,"line":395},[1039,4909,3557],{"class":2587},[1039,4911,4912],{"class":2599}," exec",[1039,4914,4657],{"class":2957},[1039,4916,4917],{"class":2599},"имя-нового-под",[1039,4919,4663],{"class":3621},[1039,4921,4922],{"class":2957},">",[1039,4924,4925],{"class":2591}," --",[1039,4927,4928],{"class":2599}," wget",[1039,4930,4931],{"class":2591}," -qO-",[1039,4933,4934],{"class":2599}," http:\u002F\u002Flocalhost:8000\u002Fapi\u002Fhealth\n",[68,4936,2645,4937],{},[21,4938,4939],{"href":4939,"rel":4940},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fcontrollers\u002Fdeployment\u002F#updating-a-deployment",[2613],[2569,4942,4944],{"id":4943},"_42-откат-rollback","4.2. Откат (rollback)",[68,4946,4947],{},"Посмотреть историю обновлений:",[1031,4949,4951],{"className":2578,"code":4950,"language":2580,"meta":40,"style":40},"kubectl rollout history deployment\u002Fbackend\n",[979,4952,4953],{"__ignoreMap":40},[1039,4954,4955,4957,4959,4961],{"class":1041,"line":395},[1039,4956,3557],{"class":2587},[1039,4958,4868],{"class":2599},[1039,4960,2979],{"class":2599},[1039,4962,4874],{"class":2599},[68,4964,4965],{},"Откатить к предыдущей версии:",[1031,4967,4969],{"className":2578,"code":4968,"language":2580,"meta":40,"style":40},"kubectl rollout undo deployment\u002Fbackend\n",[979,4970,4971],{"__ignoreMap":40},[1039,4972,4973,4975,4977,4980],{"class":1041,"line":395},[1039,4974,3557],{"class":2587},[1039,4976,4868],{"class":2599},[1039,4978,4979],{"class":2599}," undo",[1039,4981,4874],{"class":2599},[68,4983,4984],{},"Убедиться, что поды вернулись к образу 1.0:",[1031,4986,4988],{"className":2578,"code":4987,"language":2580,"meta":40,"style":40},"kubectl describe deployment\u002Fbackend | grep Image\n",[979,4989,4990],{"__ignoreMap":40},[1039,4991,4992,4994,4996,4998,5000,5002],{"class":1041,"line":395},[1039,4993,3557],{"class":2587},[1039,4995,4682],{"class":2599},[1039,4997,4845],{"class":2599},[1039,4999,2958],{"class":2957},[1039,5001,2961],{"class":2587},[1039,5003,5004],{"class":2599}," Image\n",[68,5006,2645,5007],{},[21,5008,5009],{"href":5009,"rel":5010},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fcontrollers\u002Fdeployment\u002F#rolling-back-a-deployment",[2613],[2569,5012,5014],{"id":5013},"_43-horizontal-pod-autoscaler-hpa","4.3. Horizontal Pod Autoscaler (HPA)",[68,5016,5017],{},"HPA автоматически изменяет количество реплик Deployment в зависимости от загрузки. Для его работы необходим metrics-server — компонент, собирающий метрики потребления ресурсов с узлов и подов.",[68,5019,5020],{},"Установка metrics-server в minikube:",[1031,5022,5024],{"className":2578,"code":5023,"language":2580,"meta":40,"style":40},"minikube addons enable metrics-server\n",[979,5025,5026],{"__ignoreMap":40},[1039,5027,5028,5030,5032,5034],{"class":1041,"line":395},[1039,5029,3535],{"class":2587},[1039,5031,4392],{"class":2599},[1039,5033,4395],{"class":2599},[1039,5035,5036],{"class":2599}," metrics-server\n",[68,5038,5039,5040,5042],{},"Убедиться, что в Deployment для backend заданы ",[979,5041,4300],{}," (как минимум для CPU). HPA сравнивает текущее потребление CPU с запрошенным и принимает решение о масштабировании.",[68,5044,5045,5046,5049],{},"Создать манифест HPA (",[979,5047,5048],{},"k8s\u002Fbackend\u002Fhpa.yaml","):",[1031,5051,5053],{"className":3787,"code":5052,"language":3789,"meta":40,"style":40},"apiVersion: autoscaling\u002Fv2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: backend-hpa\nspec:\n  scaleTargetRef:\n    apiVersion: apps\u002Fv1\n    kind: Deployment\n    name: backend\n  minReplicas: 2\n  maxReplicas: 5\n  metrics:\n    - type: Resource\n      resource:\n        name: cpu\n        target:\n          type: Utilization\n          averageUtilization: 50\n",[979,5054,5055,5064,5073,5079,5088,5094,5101,5111,5121,5131,5141,5150,5157,5168,5175,5185,5192,5202],{"__ignoreMap":40},[1039,5056,5057,5059,5061],{"class":1041,"line":395},[1039,5058,3701],{"class":3796},[1039,5060,3070],{"class":3621},[1039,5062,5063],{"class":2599},"autoscaling\u002Fv2\n",[1039,5065,5066,5068,5070],{"class":1041,"line":41},[1039,5067,3704],{"class":3796},[1039,5069,3070],{"class":3621},[1039,5071,5072],{"class":2599},"HorizontalPodAutoscaler\n",[1039,5074,5075,5077],{"class":1041,"line":369},[1039,5076,3707],{"class":3796},[1039,5078,3817],{"class":3621},[1039,5080,5081,5083,5085],{"class":1041,"line":1057},[1039,5082,3822],{"class":3796},[1039,5084,3070],{"class":3621},[1039,5086,5087],{"class":2599},"backend-hpa\n",[1039,5089,5090,5092],{"class":1041,"line":1062},[1039,5091,3970],{"class":3796},[1039,5093,3817],{"class":3621},[1039,5095,5096,5099],{"class":1041,"line":1068},[1039,5097,5098],{"class":3796},"  scaleTargetRef",[1039,5100,3817],{"class":3621},[1039,5102,5103,5106,5108],{"class":1041,"line":1074},[1039,5104,5105],{"class":3796},"    apiVersion",[1039,5107,3070],{"class":3621},[1039,5109,5110],{"class":2599},"apps\u002Fv1\n",[1039,5112,5113,5116,5118],{"class":1041,"line":1079},[1039,5114,5115],{"class":3796},"    kind",[1039,5117,3070],{"class":3621},[1039,5119,5120],{"class":2599},"Deployment\n",[1039,5122,5123,5126,5128],{"class":1041,"line":1085},[1039,5124,5125],{"class":3796},"    name",[1039,5127,3070],{"class":3621},[1039,5129,5130],{"class":2599},"backend\n",[1039,5132,5133,5136,5138],{"class":1041,"line":1090},[1039,5134,5135],{"class":3796},"  minReplicas",[1039,5137,3070],{"class":3621},[1039,5139,5140],{"class":2591},"2\n",[1039,5142,5143,5146,5148],{"class":1041,"line":1096},[1039,5144,5145],{"class":3796},"  maxReplicas",[1039,5147,3070],{"class":3621},[1039,5149,4225],{"class":2591},[1039,5151,5152,5155],{"class":1041,"line":51},[1039,5153,5154],{"class":3796},"  metrics",[1039,5156,3817],{"class":3621},[1039,5158,5159,5161,5163,5165],{"class":1041,"line":1106},[1039,5160,4000],{"class":3621},[1039,5162,3832],{"class":3796},[1039,5164,3070],{"class":3621},[1039,5166,5167],{"class":2599},"Resource\n",[1039,5169,5170,5173],{"class":1041,"line":3428},[1039,5171,5172],{"class":3796},"      resource",[1039,5174,3817],{"class":3621},[1039,5176,5177,5180,5182],{"class":1041,"line":3438},[1039,5178,5179],{"class":3796},"        name",[1039,5181,3070],{"class":3621},[1039,5183,5184],{"class":2599},"cpu\n",[1039,5186,5187,5190],{"class":1041,"line":3443},[1039,5188,5189],{"class":3796},"        target",[1039,5191,3817],{"class":3621},[1039,5193,5194,5197,5199],{"class":1041,"line":3449},[1039,5195,5196],{"class":3796},"          type",[1039,5198,3070],{"class":3621},[1039,5200,5201],{"class":2599},"Utilization\n",[1039,5203,5205,5208,5210],{"class":1041,"line":5204},18,[1039,5206,5207],{"class":3796},"          averageUtilization",[1039,5209,3070],{"class":3621},[1039,5211,5212],{"class":2591},"50\n",[68,5214,5215],{},"Применить манифест и проверить состояние:",[1031,5217,5219],{"className":2578,"code":5218,"language":2580,"meta":40,"style":40},"kubectl apply -f k8s\u002Fbackend\u002Fhpa.yaml\nkubectl get hpa\n",[979,5220,5221,5232],{"__ignoreMap":40},[1039,5222,5223,5225,5227,5229],{"class":1041,"line":395},[1039,5224,3557],{"class":2587},[1039,5226,4580],{"class":2599},[1039,5228,2907],{"class":2591},[1039,5230,5231],{"class":2599}," k8s\u002Fbackend\u002Fhpa.yaml\n",[1039,5233,5234,5236,5238],{"class":1041,"line":41},[1039,5235,3557],{"class":2587},[1039,5237,3567],{"class":2599},[1039,5239,5240],{"class":2599}," hpa\n",[68,5242,5243],{},"Для демонстрации масштабирования можно создать искусственную нагрузку, например, запустив в отдельном терминале цикл запросов:",[1031,5245,5247],{"className":2578,"code":5246,"language":2580,"meta":40,"style":40},"kubectl run load-test --rm -i --tty --image=busybox -- sh -c \\\n  \"while true; do wget -qO- http:\u002F\u002Fbackend:8000\u002Fapi\u002Fnotes; done\"\n",[979,5248,5249,5280],{"__ignoreMap":40},[1039,5250,5251,5253,5255,5258,5260,5263,5266,5269,5271,5274,5277],{"class":1041,"line":395},[1039,5252,3557],{"class":2587},[1039,5254,2997],{"class":2599},[1039,5256,5257],{"class":2599}," load-test",[1039,5259,3000],{"class":2591},[1039,5261,5262],{"class":2591}," -i",[1039,5264,5265],{"class":2591}," --tty",[1039,5267,5268],{"class":2591}," --image=busybox",[1039,5270,4925],{"class":2591},[1039,5272,5273],{"class":2599}," sh",[1039,5275,5276],{"class":2591}," -c",[1039,5278,5279],{"class":2591}," \\\n",[1039,5281,5282],{"class":1041,"line":41},[1039,5283,5284],{"class":2599},"  \"while true; do wget -qO- http:\u002F\u002Fbackend:8000\u002Fapi\u002Fnotes; done\"\n",[68,5286,5287],{},"Наблюдать за изменением количества реплик:",[1031,5289,5291],{"className":2578,"code":5290,"language":2580,"meta":40,"style":40},"kubectl get hpa -w\nkubectl get pods -w\n",[979,5292,5293,5304],{"__ignoreMap":40},[1039,5294,5295,5297,5299,5302],{"class":1041,"line":395},[1039,5296,3557],{"class":2587},[1039,5298,3567],{"class":2599},[1039,5300,5301],{"class":2599}," hpa",[1039,5303,4894],{"class":2591},[1039,5305,5306,5308,5310,5312],{"class":1041,"line":41},[1039,5307,3557],{"class":2587},[1039,5309,3567],{"class":2599},[1039,5311,4633],{"class":2599},[1039,5313,4894],{"class":2591},[68,5315,5316],{},"Масштабирование происходит не мгновенно: HPA проверяет метрики каждые 15 секунд (по умолчанию), а решение об увеличении или уменьшении реплик принимается с задержкой для предотвращения колебаний.",[68,5318,2645,5319],{},[21,5320,5321],{"href":5321,"rel":5322},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Frun-application\u002Fhorizontal-pod-autoscale\u002F",[2613],[2569,5324,5326],{"id":5325},"_44-автоматическое-восстановление","4.4. Автоматическое восстановление",[68,5328,5329],{},"Kubernetes поддерживает заданное количество реплик: если под завершается (аварийно или принудительно), контроллер Deployment создает новый.",[68,5331,5332],{},"Продемонстрировать:",[1031,5334,5336],{"className":2578,"code":5335,"language":2580,"meta":40,"style":40},"# Список подов до удаления\nkubectl get pods\n\n# Принудительное удаление одного пода\nkubectl delete pod \u003Cимя-пода-backend>\n\n# Наблюдение: Kubernetes немедленно создает замену\nkubectl get pods -w\n",[979,5337,5338,5343,5352,5356,5361,5380,5384,5389],{"__ignoreMap":40},[1039,5339,5340],{"class":1041,"line":395},[1039,5341,5342],{"class":3340},"# Список подов до удаления\n",[1039,5344,5345,5347,5349],{"class":1041,"line":41},[1039,5346,3557],{"class":2587},[1039,5348,3567],{"class":2599},[1039,5350,5351],{"class":2599}," pods\n",[1039,5353,5354],{"class":1041,"line":369},[1039,5355,1049],{"emptyLinePlaceholder":48},[1039,5357,5358],{"class":1041,"line":1057},[1039,5359,5360],{"class":3340},"# Принудительное удаление одного пода\n",[1039,5362,5363,5365,5368,5370,5372,5375,5378],{"class":1041,"line":1062},[1039,5364,3557],{"class":2587},[1039,5366,5367],{"class":2599}," delete",[1039,5369,4685],{"class":2599},[1039,5371,4657],{"class":2957},[1039,5373,5374],{"class":2599},"имя-пода-backen",[1039,5376,5377],{"class":3621},"d",[1039,5379,4666],{"class":2957},[1039,5381,5382],{"class":1041,"line":1068},[1039,5383,1049],{"emptyLinePlaceholder":48},[1039,5385,5386],{"class":1041,"line":1074},[1039,5387,5388],{"class":3340},"# Наблюдение: Kubernetes немедленно создает замену\n",[1039,5390,5391,5393,5395,5397],{"class":1041,"line":1079},[1039,5392,3557],{"class":2587},[1039,5394,3567],{"class":2599},[1039,5396,4633],{"class":2599},[1039,5398,4894],{"class":2591},[68,5400,5401],{},"Зафиксировать в пояснительной записке: имя удаленного пода, имя нового пода, время восстановления.",[68,5403,5404],{},"Результат этапа: в пояснительной записке задокументированы все четыре операции с командами и их выводом (или снимками экрана).",[3013,5406],{},[83,5408,5410],{"id":5409},"этап-5-безопасность","Этап 5. Безопасность",[68,5412,5413],{},"На этом этапе необходимо применить базовые меры усиления безопасности контейнерной среды и выполнить аудит собранных образов.",[2569,5415,5417],{"id":5416},"_51-непривилегированный-пользователь-в-dockerfile","5.1. Непривилегированный пользователь в Dockerfile",[68,5419,5420,5421,5424],{},"Убедиться, что в Dockerfile для backend и frontend контейнеры запускаются от непривилегированного пользователя (инструкция ",[979,5422,5423],{},"USER","). Если это не было сделано на этапе 1, внести исправления.",[68,5426,5427],{},"Проверить, от какого пользователя запущен процесс внутри контейнера:",[1031,5429,5431],{"className":2578,"code":5430,"language":2580,"meta":40,"style":40},"docker run --rm notes-backend:1.0 whoami\n",[979,5432,5433],{"__ignoreMap":40},[1039,5434,5435,5437,5439,5441,5443],{"class":1041,"line":395},[1039,5436,2588],{"class":2587},[1039,5438,2997],{"class":2599},[1039,5440,3000],{"class":2591},[1039,5442,2904],{"class":2599},[1039,5444,5445],{"class":2599}," whoami\n",[68,5447,5448,5449,5451,5452,5454,5455,1207],{},"Ожидаемый результат: ",[979,5450,2729],{}," (для backend) или ",[979,5453,1267],{}," (для frontend), но не ",[979,5456,5457],{},"root",[2569,5459,5461],{"id":5460},"_52-securitycontext-в-манифестах-kubernetes","5.2. SecurityContext в манифестах Kubernetes",[68,5463,5464,5465,5468],{},"Добавить в манифесты Deployment для backend и frontend секцию ",[979,5466,5467],{},"securityContext"," на уровне контейнера:",[1031,5470,5472],{"className":3787,"code":5471,"language":3789,"meta":40,"style":40},"securityContext:\n  runAsNonRoot: true\n  allowPrivilegeEscalation: false\n  readOnlyRootFilesystem: true\n",[979,5473,5474,5480,5490,5500],{"__ignoreMap":40},[1039,5475,5476,5478],{"class":1041,"line":395},[1039,5477,5467],{"class":3796},[1039,5479,3817],{"class":3621},[1039,5481,5482,5485,5487],{"class":1041,"line":41},[1039,5483,5484],{"class":3796},"  runAsNonRoot",[1039,5486,3070],{"class":3621},[1039,5488,5489],{"class":2591},"true\n",[1039,5491,5492,5495,5497],{"class":1041,"line":369},[1039,5493,5494],{"class":3796},"  allowPrivilegeEscalation",[1039,5496,3070],{"class":3621},[1039,5498,5499],{"class":2591},"false\n",[1039,5501,5502,5505,5507],{"class":1041,"line":1057},[1039,5503,5504],{"class":3796},"  readOnlyRootFilesystem",[1039,5506,3070],{"class":3621},[1039,5508,5489],{"class":2591},[68,5510,5511],{},"Пояснение:",[14,5513,5514,5520,5526],{},[17,5515,5516,5519],{},[979,5517,5518],{},"runAsNonRoot: true"," — Kubernetes откажется запускать контейнер, если его образ настроен на запуск от root. Это страховка на случай ошибки в Dockerfile.",[17,5521,5522,5525],{},[979,5523,5524],{},"allowPrivilegeEscalation: false"," — запрещает процессу внутри контейнера получать привилегии выше тех, с которыми он был запущен.",[17,5527,5528,5531,5532,1018,5535,5538,5539,5542],{},[979,5529,5530],{},"readOnlyRootFilesystem: true"," — файловая система контейнера монтируется в режиме только для чтения. Это затрудняет модификацию файлов при компрометации контейнера. Если приложению необходимо писать во временные каталоги (например, nginx пишет в ",[979,5533,5534],{},"\u002Fvar\u002Fcache\u002Fnginx",[979,5536,5537],{},"\u002Fvar\u002Frun","), следует подключить к ним ",[979,5540,5541],{},"emptyDir","-тома.",[68,5544,5545],{},"После применения обновленных манифестов убедиться, что поды запускаются без ошибок:",[1031,5547,5549],{"className":2578,"code":5548,"language":2580,"meta":40,"style":40},"kubectl apply -R -f k8s\u002F\nkubectl get pods\n",[979,5550,5551,5563],{"__ignoreMap":40},[1039,5552,5553,5555,5557,5559,5561],{"class":1041,"line":395},[1039,5554,3557],{"class":2587},[1039,5556,4580],{"class":2599},[1039,5558,4583],{"class":2591},[1039,5560,2907],{"class":2591},[1039,5562,4588],{"class":2599},[1039,5564,5565,5567,5569],{"class":1041,"line":41},[1039,5566,3557],{"class":2587},[1039,5568,3567],{"class":2599},[1039,5570,5351],{"class":2599},[68,5572,5573,5574,5577],{},"Если под не запускается, изучить причину через ",[979,5575,5576],{},"kubectl describe pod \u003Cимя-пода>"," и скорректировать конфигурацию.",[68,5579,2645,5580],{},[21,5581,5582],{"href":5582,"rel":5583},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Fconfigure-pod-container\u002Fsecurity-context\u002F",[2613],[2569,5585,5587],{"id":5586},"_53-сканирование-образов-на-уязвимости","5.3. Сканирование образов на уязвимости",[68,5589,5590],{},"Выполнить сканирование собранных образов с помощью Trivy (рекомендуется) или Docker Scout.",[68,5592,5593],{},"Установка Trivy:",[14,5595,5596,5602],{},[17,5597,5598,5599],{},"macOS: ",[979,5600,5601],{},"brew install trivy",[17,5603,5604,5605],{},"Linux: ",[21,5606,5607],{"href":5607,"rel":5608},"https:\u002F\u002Faquasecurity.github.io\u002Ftrivy\u002Flatest\u002Fgetting-started\u002Finstallation\u002F",[2613],[68,5610,5611],{},"Сканирование:",[1031,5613,5615],{"className":2578,"code":5614,"language":2580,"meta":40,"style":40},"trivy image notes-backend:1.0\ntrivy image notes-frontend:1.0\n",[979,5616,5617,5626],{"__ignoreMap":40},[1039,5618,5619,5622,5624],{"class":1041,"line":395},[1039,5620,5621],{"class":2587},"trivy",[1039,5623,3589],{"class":2599},[1039,5625,2982],{"class":2599},[1039,5627,5628,5630,5632],{"class":1041,"line":41},[1039,5629,5621],{"class":2587},[1039,5631,3589],{"class":2599},[1039,5633,3605],{"class":2599},[68,5635,5636],{},"Trivy проверяет все пакеты в образе (системные пакеты ОС и зависимости приложения) по базам данных известных уязвимостей (CVE). Результат содержит список найденных уязвимостей с указанием критичности (CRITICAL, HIGH, MEDIUM, LOW).",[68,5638,5639],{},"Альтернатива — Docker Scout (встроен в Docker Desktop):",[1031,5641,5643],{"className":2578,"code":5642,"language":2580,"meta":40,"style":40},"docker scout cves notes-backend:1.0\ndocker scout cves notes-frontend:1.0\n",[979,5644,5645,5657],{"__ignoreMap":40},[1039,5646,5647,5649,5652,5655],{"class":1041,"line":395},[1039,5648,2588],{"class":2587},[1039,5650,5651],{"class":2599}," scout",[1039,5653,5654],{"class":2599}," cves",[1039,5656,2982],{"class":2599},[1039,5658,5659,5661,5663,5665],{"class":1041,"line":41},[1039,5660,2588],{"class":2587},[1039,5662,5651],{"class":2599},[1039,5664,5654],{"class":2599},[1039,5666,3605],{"class":2599},[68,5668,5669],{},"В пояснительную записку включить:",[14,5671,5672,5675,5678],{},[17,5673,5674],{},"вывод сканирования (полный или сводку);",[17,5676,5677],{},"краткий анализ: какие уязвимости обнаружены, какой критичности, возможно ли их устранить обновлением базового образа или зависимостей;",[17,5679,5680,5681,3171],{},"если уязвимости устранимы — описать, какие изменения в Dockerfile потребуются (например, обновление версии базового образа или зависимостей в ",[979,5682,2690],{},[68,5684,2786],{},[14,5686,5687,5694],{},[17,5688,5689,5690],{},"Trivy: ",[21,5691,5692],{"href":5692,"rel":5693},"https:\u002F\u002Faquasecurity.github.io\u002Ftrivy\u002Flatest\u002F",[2613],[17,5695,5696,5697],{},"Docker Scout: ",[21,5698,5699],{"href":5699,"rel":5700},"https:\u002F\u002Fdocs.docker.com\u002Fscout\u002F",[2613],[68,5702,5703],{},"Результат этапа: манифесты содержат настройки безопасности, поды запускаются без ошибок, отчет о сканировании включен в пояснительную записку.",[78,5705,5707],{"id":5706},"состав-отчетных-материалов","Состав отчетных материалов",[68,5709,5710],{},"Курсовой проект оформляется в виде репозитория и пояснительной записки.",[83,5712,5714],{"id":5713},"репозиторий","Репозиторий",[68,5716,5717],{},"Рекомендуемая структура каталогов:",[1031,5719,5722],{"className":5720,"code":5721,"language":2551},[2549],"project\u002F\n├── app\u002F                        # исходный код приложения (предоставлен)\n│   ├── backend\u002F\n│   └── frontend\u002F\n├── docker\u002F                     # Dockerfile для каждого компонента\n│   ├── backend\u002F\n│   │   └── Dockerfile\n│   └── frontend\u002F\n│       └── Dockerfile\n├── docker-compose.yml          # композиция для локального запуска\n├── .env                        # переменные окружения (значения по умолчанию)\n├── k8s\u002F                        # манифесты Kubernetes\n│   ├── backend\u002F\n│   │   ├── deployment.yaml\n│   │   ├── service.yaml\n│   │   ├── configmap.yaml\n│   │   └── hpa.yaml\n│   ├── frontend\u002F\n│   │   ├── deployment.yaml\n│   │   └── service.yaml\n│   ├── db\u002F\n│   │   ├── deployment.yaml\n│   │   ├── service.yaml\n│   │   └── pvc.yaml\n│   ├── secrets.yaml\n│   └── ingress.yaml\n└── README.md                   # инструкция по сборке и запуску\n",[979,5723,5721],{"__ignoreMap":40},[83,5725,5727],{"id":5726},"пояснительная-записка","Пояснительная записка",[68,5729,5730],{},"Пояснительная записка должна содержать следующие разделы:",[5732,5733,5734,5740,5746,5752,5758,5764,5770],"ol",{},[17,5735,5736,5739],{},[2155,5737,5738],{},"Введение"," — краткое описание цели работы и состава приложения.",[17,5741,5742,5745],{},[2155,5743,5744],{},"Контейнеризация"," — описание структуры Dockerfile для каждого компонента, обоснование выбора базовых образов и приемов оптимизации, анализ слоев и размера итоговых образов.",[17,5747,5748,5751],{},[2155,5749,5750],{},"Локальная композиция"," — описание файла docker-compose.yml, параметров конфигурации, сетевой топологии и политики хранения данных.",[17,5753,5754,5757],{},[2155,5755,5756],{},"Развертывание в кластере"," — описание манифестов Kubernetes, схема архитектуры развертывания, настройки проверок состояния и внешнего доступа.",[17,5759,5760,5763],{},[2155,5761,5762],{},"Обновление и масштабирование"," — описание выполненных операций (rolling update, откат, HPA, восстановление после отказа) с подтверждением в виде команд и их вывода.",[17,5765,5766,5769],{},[2155,5767,5768],{},"Безопасность"," — описание примененных мер, результаты сканирования образов.",[17,5771,5772,5775],{},[2155,5773,5774],{},"Заключение"," — выводы о проделанной работе, сравнение подходов (ручной запуск, Compose, Kubernetes).",[83,5777,5779],{"id":5778},"схема-архитектуры","Схема архитектуры",[68,5781,5782],{},"Пояснительная записка должна включать схему архитектуры развертывания приложения в кластере. Схема должна отображать: компоненты приложения (Deployment, Pod), сервисы (Service), внешний доступ (Ingress), хранилище (PVC) и направления сетевого взаимодействия.",[78,5784,5786],{"id":5785},"критерии-оценки","Критерии оценки",[5788,5789,5790,5803],"table",{},[5791,5792,5793],"thead",{},[5794,5795,5796,5800],"tr",{},[5797,5798,5799],"th",{},"Критерий",[5797,5801,5802],{},"Доля",[5804,5805,5806,5815,5822,5830,5837,5845,5852],"tbody",{},[5794,5807,5808,5812],{},[5809,5810,5811],"td",{},"Корректность Dockerfile: многоэтапная сборка, непривилегированный пользователь, health check, оптимизация размера",[5809,5813,5814],{},"15%",[5794,5816,5817,5820],{},[5809,5818,5819],{},"Корректность docker-compose.yml: сеть, том, переменные окружения, ограничения ресурсов, воспроизводимость запуска",[5809,5821,5814],{},[5794,5823,5824,5827],{},[5809,5825,5826],{},"Манифесты Kubernetes: Deployment, Service, ConfigMap, Secret, PVC, Ingress, probes",[5809,5828,5829],{},"25%",[5794,5831,5832,5835],{},[5809,5833,5834],{},"Обновление и масштабирование: rolling update, откат, HPA, восстановление",[5809,5836,5814],{},[5794,5838,5839,5842],{},[5809,5840,5841],{},"Безопасность: securityContext, сканирование образов",[5809,5843,5844],{},"10%",[5794,5846,5847,5850],{},[5809,5848,5849],{},"Пояснительная записка: полнота, обоснованность решений, схема архитектуры",[5809,5851,5814],{},[5794,5853,5854,5857],{},[5809,5855,5856],{},"Структура репозитория и инструкция по запуску (README)",[5809,5858,5859],{},"5%",[78,5861,5863],{"id":5862},"минимальные-системные-требования","Минимальные системные требования",[14,5865,5866,5869,5872,5875],{},[17,5867,5868],{},"Docker Desktop или Docker Engine;",[17,5870,5871],{},"minikube (рекомендуется) или kind;",[17,5873,5874],{},"kubectl;",[17,5876,5877],{},"4 ядра CPU, 8 ГБ оперативной памяти (рекомендуется).",[68,5879,5880],{},"Все этапы курсового проекта выполняются локально на компьютере студента. Использование внешних облачных платформ не требуется.",[1472,5882,5883],{},"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 .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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":40,"searchDepth":41,"depth":41,"links":5885},[5886,5887,5888,5889,5896,5901,5902],{"id":2500,"depth":41,"text":2501},{"id":2507,"depth":41,"text":2508},{"id":2544,"depth":41,"text":2545},{"id":2559,"depth":41,"text":2560,"children":5890},[5891,5892,5893,5894,5895],{"id":2563,"depth":369,"text":2564},{"id":3017,"depth":369,"text":3018},{"id":3495,"depth":369,"text":3496},{"id":4740,"depth":369,"text":4741},{"id":5409,"depth":369,"text":5410},{"id":5706,"depth":41,"text":5707,"children":5897},[5898,5899,5900],{"id":5713,"depth":369,"text":5714},{"id":5726,"depth":369,"text":5727},{"id":5778,"depth":369,"text":5779},{"id":5785,"depth":41,"text":5786},{"id":5862,"depth":41,"text":5863},"Окружение курсового проекта","\u002Ffiles\u002Faidt-bac-cloud_tech\u002Fcourse-project\u002Fcp-env.zip",{},"\u002Fcourses\u002Faidt-bac-cloud_tech\u002Fcourse-project",{"title":2492,"description":40},"courses\u002Faidt-bac-cloud_tech\u002Fcourse-project","WHpI2p07h1OUqTEuCI6yE2sPFuavqCEN3_qpteJsZOY",1779455410464]