
Изд. № 29
.pdfРазвёртывание динамически связанных приложений, для которых требуются различные версии совместно используемых библиотек и/или другие особенности среды, может быстро стать кошмаром для системных администраторов, которые развёртывают их и управляют ими на рабочих серверах. Чем больше количество компонентов необходимо развернуть на одном хосте, тем сложнее будет управлять всеми их зависимостями, чтобы удовлетворить все их требования.
1.2. Обеспечение консистентного окружения для приложений
Независимо от того, сколько отдельных компонентов разрабатывается
иразвертывается, одна из самых больших проблем, с которой всегда приходится сталкиваться разработчикам и системным администраторам, – это различия в окружениях, в которых они выполняют свои приложения. Мало того, что существует огромная разница между окружением разработки
ирабочим окружением, различия существуют даже между отдельными машинами в рабочем окружении. Ещё одним неизбежным фактом является то, что окружение одной рабочей машины будет меняться с течением времени.
Эти различия варьируются от аппаратного обеспечения до операционной системы и библиотек, доступных на каждой машине. Рабочие окружения управляются системными администраторами, в то время как разработчики занимаются своими компьютерами, на которых ведётся разработка, самостоятельно. Разница в том, насколько эти две группы людей знают о системном администрировании, и это, по понятным причинам, приводит к относительно большим различиям между этими двумя системами, не говоря уже о том, что системные администраторы уделяют гораздо больше внимания последним обновлениям в системе безопасности, в то время как многих разработчиков это не интересует в такой же мере.
Кроме того, рабочие системы могут выполнять приложения от нескольких разработчиков или групп разработчиков, что необязательно верно для компьютеров разработчиков. Рабочая система должна обеспечивать надлежащую среду для всех размещаемых на ней приложений, даже если для них могут потребоваться разные, порой конфликтующие версии библиотек.
Для того чтобы уменьшить количество проблем, которые проявляются только в рабочем окружении, было бы идеально, если бы приложения могли работать в одной и той же среде во время разработки и в период эксплуатации, чтобы они имели одну и ту же операционную систему,
10
библиотеки, конфигурацию системы, сетевую среду и прочее. Желательно, чтобы эта среда не слишком сильно менялась с течением времени или вообще не менялась. Кроме того, если это возможно, требуется способность добавлять приложения на тот же сервер, не затрагивая ни одно из существующих приложений на этом сервере.
1.3.Переход к непрерывной доставке: DevOps и NoOps
Впоследние несколько лет наблюдаются изменения во всем процессе разработки приложений и в том, как приложения обслуживаются в рабочем окружении. В прошлом работа команды разработчиков состояла в создании приложения и ее передаче группе системных администраторов, которая затем его развертывала, сопровождала и поддерживала его работу. Однако теперь организации понимают, что лучше, чтобы та же команда, которая разрабатывает приложение, также принимала участие в его развёртывании и сопровождала его на протяжении всего жизненного цикла. Это означает, что команды разработчиков, специалистов по тестированию и системные администраторы теперь должны сотрудничать на протяжении всего процесса. Подобная практика называется DevOps.
Преимущества
Когда разработчики принимают более активное участие в выполнении приложения в производстве, это приводит к тому, что они имеют лучшее понимание потребностей и нужд пользователей и проблем, с которыми сталкиваются системные администраторы при сопровождении приложения. Разработчики приложений теперь также гораздо более склонны передавать пользователям приложение раньше, а затем использовать их отзывы, чтобы определять пути дальнейшего развития приложения.
Для того чтобы чаще выпускать новые версии приложений, необходимо оптимизировать процесс развёртывания. В идеале требуется, чтобы разработчики развёртывали приложения сами, не дожидаясь системных администраторов. Однако развёртывание приложения часто требует понимания базовой инфраструктуры и организации оборудования в дата-центре. Разработчики не всегда знают эти детали и в большинстве случаев даже не хотят знать о них.
Разделение ролей разработчиков и системных администраторов
Несмотря на то, что разработчики и системные администраторы работают для достижения одной и той же цели – запуск успешного программного приложения в качестве службы для своих клиентов, они имеют различные индивидуальные цели и мотивирующие факторы.
11
Разработчики любят создавать новые функциональные средства и улучшать пользовательский опыт. Обычно они не хотят следить за тем, чтобы своевременно вносились исправления в систему безопасности базовой операционной системы и т.п. Они предпочитают оставлять это на усмотрение системных администраторов.
Системные администраторы отвечают за процесс развёртывания программного обеспечения в рабочем окружении и аппаратную инфраструктуру, на которой они работают. Они занимаются обеспечением безопасности системы, задействованием ресурсов и другими аспектами, которые не являются приоритетными для разработчиков. Системные администраторы не хотят иметь дело с неявными взаимозависимостями всех компонентов приложения и не хотят думать о том, как изменения операционной системы или инфраструктуры могут повлиять на работу приложения в целом, но они влияют.
В идеале требуется, чтобы разработчики развёртывали приложения сами, ничего не зная об аппаратной инфраструктуре и не имея дела с системными администраторами. Это называется NoOps. Очевидно, что нужно, чтобы кто-то занимался аппаратной инфраструктурой, при этом, в идеале, не занимаясь особенностями каждого приложения, работающего на нём.
Kubernetes и позволяет достичь всего этого. Абстрагируясь от фактического оборудования и обеспечивая к нему доступ как к единой платформе для развёртывания и выполнения приложений, эта инфраструктура позволяет разработчикам настраивать и развёртывать свои приложения без какой-либо помощи от системных администраторов и даёт возможность системным администраторам сосредотачиваться на поддержании базовой инфраструктуры в рабочем состоянии, не зная ничего о реальных приложениях, работающих поверх него.
Контрольные вопросы к разделу 1
1. Из чего состоят монолитные приложения?
2.Что требуется для запуска монолитного приложения?
3.Какие проблемы заставили начать разбивать сложные монолитные приложения?
4.Что происходит при масштабировании микросервисов?
5.Какими недостатками обладают микросервисы?
6.Что подразумевается под различием в окружениях?
7.Что должна обеспечивать рабочая система?
8.Что такое DevOps?
9.Чего позволяет достичь Kubernetes?
12
2. Основы контейнерных технологий
Как было сказано выше, проблемы, представленные в разделе 1, и другие проблемы, с которыми сталкиваются сегодняшние команды разработчиков и системные администраторы, решаются, в том числе, с
помощью Kubernetes.
Kubernetes использует контейнерные технологии Linux, для того чтобы обеспечить изоляцию выполняющихся приложений, поэтому прежде чем перейти к выполнению практических заданий по работе с системой Kubernetes необходимо ознакомиться с основами контейнеров, чтобы понимать, что система Kubernetes делает сама, а что она сбрасывает на контейнерные технологии, такие как Docker или rkt (произносится как
«рокит», от англ. «rock-it»).
2.1.Что такое контейнеры
Вразделе 1.1 было показано, как различные программные компоненты, выполняющиеся на одной машине, будут требовать разные, возможно конфликтующие, версии библиотек, от которых они зависят, или иметь другие разные требования к среде в целом.
Когда приложение состоит лишь из небольшого количества крупных компонентов, вполне допустимо предоставить каждому компоненту выделенную виртуальную машину (VM) и изолировать их среды, предоставив каждому из них собственный экземпляр операционной системы. Но когда эти компоненты начинают уменьшаться в объёме и их количество начинает расти, становится невозможным предоставлять каждому из них свою собственную виртуальную машину, если вы не хотите тратить аппаратные ресурсы и снизить затраты на оборудование. Но дело не только в растрате аппаратных ресурсов. Поскольку каждая виртуальная машина обычно должна настраиваться и управляться индивидуально, увеличение количества виртуальных машин также приводит к трате человеческих ресурсов, поскольку они значительно увеличивают рабочую нагрузку на системных администраторов.
Изолирование компонентов с помощью контейнерных технологий Linux
Вместо того чтобы использовать виртуальные машины для изоляции сред каждого микросервиса (или программных процессов в целом), разработчики обращаются к контейнерным технологиям Linux. Данные
13
технологии позволяют запускать несколько сервисов на одной хост-машине, не только обеспечивая доступ к разным средам, но и изолируя их друг от друга, подобно виртуальным машинам, но с гораздо меньшими затратами.
Процесс, запущенный в контейнере, выполняется внутри операционной системы хоста, как и все другие процессы (в отличие от виртуальных машин, где процессы выполняются в отдельных операционных системах). Но процесс в контейнере по-прежнему изолирован от других процессов. Для самого процесса это выглядит, как если бы он был единственным работающим на машине и в её операционной системе.
Сравнение виртуальных машин с контейнерами
По сравнению с виртуальными машинами, контейнеры гораздо облегчённее, что позволяет запускать большее количество программных компонентов на одном и том же оборудовании, главным образом потому, что каждая виртуальная машина должна запускать свой собственный набор системных процессов, который требует ещё вычислительных ресурсов в дополнение к тем, которые потребляются собственным процессом компонента. С другой стороны, контейнер – это не что иное, как отдельный изолированный процесс, выполняющийся в центральной ОС, потребляющий только те ресурсы, которые приложение потребляет, без накладных расходов в виде дополнительных процессов.
Из-за накладных расходов виртуальных машин в конечном счете приходится группировать несколько приложений в каждую отдельную виртуальную машину, поскольку может быть недостаточно ресурсов для выделения всей виртуальной машины каждому приложению. При использовании контейнеров, как показано на рисунке 4, можно (и нужно) иметь по одному контейнеру для каждого приложения. Конечным результатом является то, что на одном и том же аппаратном обеспечении можно умещать ещё больше приложений.
14

Рисунок 4 - Использование виртуальных машин для изоляции групп приложений по сравнению с изоляцией отдельных приложений с помощью контейнеров
Когда запускаются три виртуальные машины на одном устройстве, имеются три совершенно отделённые друг от друга операционные системы, работающие на одном и том же аппаратном обеспечении. Под этими виртуальными машинами находятся хостовая ОС и гипервизор, который разделяет физические аппаратные ресурсы на меньшие наборы виртуальных ресурсов, которые могут использоваться операционной системой внутри каждой виртуальной машины. Приложения, запущенные на этих виртуальных машинах, выполняют системные вызовы ядра гостевой ОС в виртуальной машине, а затем ядро выполняет инструкции x86 на физическом центральном процессоре (ЦП) хоста через гипервизор.
ПРИМЕЧАНИЕ. Существуют два типа гипервизоров, то есть программ управления операционными системами. Гипервизоры первого типа не используют хостовую ОС, в то время как гипервизоры второго типа ее используют.
15

Контейнеры же выполняют системные вызовы на одном и том же ядре, работающем в хостовой ОС. Это единственное ядро, выполняющее инструкции x86 на процессоре хоста. ЦП не нужно делать какой-либо виртуализации, как он делает с виртуальными машинами (рисунок 5).
Рисунок 5 - Разница между тем, как приложения в виртуальных машинах используют процессор и как он используется в контейнерах
Главное преимущество виртуальных машин заключается в их полной изоляции, поскольку каждая виртуальная машина работает под управлением собственного ядра Linux, в то время как все контейнеры обращаются к одному ядру, что может явно представлять угрозу безопасности. При ограниченном количестве аппаратных ресурсов виртуальные машины могут использоваться только в том случае, если требуется изолировать небольшое количество процессов. Для выполнения большего количества изолированных процессов на одной машине контейнеры являются гораздо лучшим выбором из-за их низкой нагрузки.
Важно: каждая виртуальная машина выполняет свой собственный набор системных служб, в то время как контейнеры этого не делают, потому что все они выполняются в одной ОС. Это также означает, что для запуска контейнера начальная загрузка компонентов не требуется, как в случае с виртуальными машинами. Процесс, выполняющийся в контейнере, запускается немедленно.
Механизмы, обеспечивающие изоляцию контейнеров
Как именно контейнеры могут изолировать процессы, если они работают в одной операционной системе? Это возможно благодаря двум механизмам. Первый – пространство имен Linux, гарантирует, что каждый процесс видит своё персональное представление о системе (файлы,
16
процессы, сетевые интерфейсы, сетевое имя (hostname) и т.д.). Второй – контрольные группы Linux (cgroups), ограничивающие объём ресурсов, которые может потреблять процесс (ЦП, оперативная память, пропускная способность сети и т.д.).
Изоляция процессов с помощью пространств имен Linux
По умолчанию каждая система Linux изначально имеет одно пространство имен. Все системные ресурсы, такие как файловые системы, идентификаторы процессов, идентификаторы пользователей, сетевые интерфейсы и др., принадлежат одному пространству имен. Но можно создавать дополнительные пространства имён и организовывать ресурсы между ними. При запуске процесса его запускают внутри одного из этих пространств имен. Процесс будет видеть только ресурсы, находящиеся внутри одного пространства имен. Существует несколько типов пространств имен, поэтому процесс принадлежит не одному пространству имен, а одному пространству имен каждого типа.
Существуют следующие виды пространств имен:
файловая система (mnt);
идентификатор процессов (pid);
сеть (net);
межпроцессное взаимодействие (ipc);
UTS;
пользовательские идентификаторы (user).
Каждый вид пространства имен используется для изоляции определённой группы ресурсов. Например, пространство имён UTS определяет, какой хостнейм и доменное имя видит процесс, запущенный внутри этого пространства имен. Назначив двум процессам два разных пространства имен UTS, можно заставить их видеть разные локальные хостнеймы. Другими словами, для двух процессов это будет выглядеть так, как будто они выполняются на двух разных машинах (по крайней мере, в том, что касается хостнеймов).
Точно так же то, к какому сетевому пространству имен принадлежит процесс, определяет, какие сетевые интерфейсы видит приложение, запущенное внутри процесса. Каждый сетевой интерфейс принадлежит только одному пространству имен, но он может быть перемещен из одного пространства имен в другое. Каждый контейнер использует своё собственное сетевое пространство имен, и поэтому каждый контейнер видит свой собственный набор сетевых интерфейсов.
17
Ограничение ресурсов, доступных процессу
Другая половина изоляции контейнеров связана с ограничением объёма системных ресурсов, которые может потреблять контейнер. Это достигается с помощью cgroups, функционального средства ядра Linux, которое ограничивает использование ресурсов процессом (или группой процессов). Процесс не может использовать больше, чем настроенный объём ЦП, оперативной памяти, пропускной способности сети и т.д. Благодаря этому процессы не могут перехватывать ресурсы, зарезервированные для других процессов, что аналогично тому, когда каждый процесс выполняется на отдельной машине.
2.2. Контейнерная платформа Docker
Хотя контейнерные технологии существуют уже давно, они стали более широко известны с появлением контейнерной платформы Docker. Docker была первой контейнерной системой, которая сделала контейнеры легко переносимыми на разные машины. Это упростило процесс упаковки не только приложения, но и всех его библиотек и других зависимостей, даже всей файловой системы ОС, в простой переносимый пакет, который может использоваться для подготовки приложения к работе на любой другой машине, на которой работает Docker.
При выполнении приложения, упакованного с помощью Docker, оно видит точное содержимое файловой системы, поставляемое вместе с ним. Оно видит одни и те же файлы, независимо от того, работает ли оно на машине, предназначенной для разработки, или же на машине из рабочего окружения, даже если на рабочем сервере запущена совершенно другая ОС Linux. Приложение не будет видеть ничего с сервера, на котором оно выполняется, поэтому не имеет значения, имеет или нет сервер совершенно другой набор установленных библиотек по сравнению с вашей машиной для разработки.
Например, если вы упаковали свое приложение с файлами всей операционной системы Red Hat Enterprise Linux (RHEL), приложение будет считать, что оно работает внутри RHEL, как при запуске на компьютере разработчика, на котором работает Fedora, так и при запуске на сервере под управлением Debian или другого дистрибутива Linux. Только ядро может отличаться.
Это похоже на создание образа виртуальной машины путём установки операционной системы на виртуальную машину, установки приложения
18
внутри неё, а затем распространения всего образа виртуальной машины и его выполнения. Платформа Docker достигает того же эффекта, но вместо того чтобы использовать виртуальные машины для изоляции приложений, она использует контейнерные технологии Linux, упомянутые в предыдущем разделе, чтобы обеспечить (почти) тот же уровень изоляции, что и у виртуальных машин. Вместо использования больших монолитных образов виртуальных машин применяются образы контейнеров, которые обычно меньше.
Ключевая разница между образами контейнеров на основе Docker и образами виртуальных машин заключается в том, что образы контейнеров состоят из слоев, которые можно совместно повторно использовать в нескольких разных образах. Это означает, что должны загружаться только определённые слои образа, если другие слои уже были загружены ранее при запуске другого образа контейнера, который тоже содержит те же слои.
Основные понятия платформы Docker
Docker – это платформа для упаковки, распространения и выполнения приложений. Она позволяет упаковывать приложение вместе со всей его средой. Это могут быть либо несколько библиотек, которые требуются приложению, либо даже все файлы, которые обычно доступны в файловой системе установленной операционной системы. Docker позволяет переносить этот пакет в центральный репозиторий, из которого он затем может быть перенесён на любой компьютер, на котором работает Docker, и выполнен там (по большей части, но не всегда).
Этот сценарий состоит из трёх главных понятий в Docker:
образы (Images). Образ контейнера на основе Docker – это то, во что упаковывается приложение и его среда. Он содержит файловую систему, которая будет доступна приложению, и другие метаданные, такие как путь к исполняемому файлу, который должен быть исполнен при запуске образа;
хранилища (Registry). Хранилище Docker – это репозиторий, в котором хранятся образы Docker и который упрощает обмен этими образами между различными людьми и компьютерами. Когда пользователь создаёт образ, он может либо запустить его на компьютере, на котором он его создал, либо отправить (закачать) образ в хранилище, а затем извлечь (скачать) его на другом компьютере и запустить его там. Некоторые хранилища являются общедоступными, позволяя любому извлекать из них образы, в то
19