19,99 €
С развитием микросервисов и контейнеров изменились подходы к проектированию, созданию и запуску программного обеспечения. Познакомьтесь с новыми паттернами и принципами разработки, которые нужны для реализации облачных приложений в Kubernetes. Эта книга адресована разработчикам, желающим проектировать и разрабатывать облачные приложения для платформы Kubernetes. Наибольшую пользу из нее извлекут читатели, которые хотя бы немного знакомы с контейнерами и хотят подняться на новый уровень. Каждый паттерн проектирования представляет собой описание реальной задачи, а решение поддерживается и иллюстрируется конкретными примерами кода. Вы узнаете о следующих категориях паттернов: • Основные паттерны охватывают базовые принципы и практики создания приложений на основе контейнеров. • Поведенческие паттерны предназначены для управления взаимодействиями контейнеров и платформ. • Структурные паттерны помогают организовать контейнеры в поды. • Конфигурационные паттерны позволяют разобраться в настройке приложений. • Дополнительные паттерны охватывают более сложные темы: операторы и автоматическое масштабирование. «Прочитав эту книгу, вы узнаете не только о паттернах Kubernetes, но и о том, зачем они нужны и как их использовать». Брендан Бернс, Kubernetes «Уникальный подход, который знакомит с ключевыми концепциями Kubernetes, чтобы каждый разработчик смог понять и быстро реализовать их на практике». Эндрю Блок, главный консультант, Red Hat «Отличная книга, в которой объясняется как решать реальные задачи». Майкл Хюттерманн, главный консультант по DevOps, Huettermann.net
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 291
Veröffentlichungsjahr: 2023
Научный редактор М. Малявин
Переводчик А. Макарова
Литературный редактор А. Руденко
Художники В. Мостипан, А. Шляго (Шантурова)
Корректоры С. Беляева, Н. Викторова
Верстка Л. Егорова
Билджин Ибрам, Роланд Хасс
Паттерны Kubernetes: Шаблоны разработки собственных облачных приложений. — СПб.: Питер, 2021.
ISBN 978-5-4461-1443-6
© ООО Издательство "Питер", 2021
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Начиная работу над фреймворком Kubernetes почти пять лет назад, мы — Крейг, Джо и я — понимали, что он способен изменить мир разработки и доставки программного обеспечения. Но мы не думали, что это изменение произойдет так быстро. В настоящее время Kubernetes служит основой для создания переносимых и надежных систем для основных общедоступных и частных облаков, а также невиртуализированных окружений. Несмотря на широкую распространенность фреймворка Kubernetes, благодаря которой кластер в облаке можно развернуть менее чем за пять минут, многие недостаточно четко представляют, что делать дальше, после создания этого кластера. Мы добились значительных успехов в практической реализации самого Kubernetes, но это только часть решения. Это фундамент, на котором создаются приложения. Он предлагает обширную библиотеку инструментов для их создания, но почти не дает советов и рекомендаций архитекторам или разработчикам приложений, как можно объединить различные части этого фундамента, чтобы получить законченную надежную систему, соответствующую целям и потребностям.
Представление о том, что же дальше делать с кластером Kubernetes, можно получить из прошлого опыта работы с аналогичными системами или прибегнуть к методу проб и ошибок, но такой путь обходится слишком дорого с точки зрения времени и качества систем, предлагаемых нашим конечным пользователям. Для тех, кто решит предоставлять критически важные услуги на основе таких систем, как Kubernetes, обретение опыта методом проб и ошибок займет слишком много времени и приведет к очень серьезным проблемам, связанным с простоями и сбоями.
Вот почему книга Билджина и Роланда имеет особую ценность. «Паттерны Kubernetes» знакомят вас с опытом, который мы вложили в API и инструменты, составляющие Kubernetes. Фреймворк Kubernetes является воплощением опыта, накопленного сообществом разработчиков, занимающихся созданием высоконадежных распределенных систем в разных окружениях. Каждый объект и каждая возможность, добавленные в Kubernetes, — это основополагающий инструмент, разработанный и созданный специально для удовлетворения конкретной потребности. В этой книге рассказывается, как использовать идеи, заложенные в Kubernetes, для решения практических задач и построения своей системы.
Работая над Kubernetes, мы всегда говорили, что наша главная цель — максимально упростить разработку распределенных систем, и именно такие книги наглядно показывают, насколько мы преуспели в этом. Билджин и Роланд отобрали основные инструменты разработчика Kubernetes и разбили их на группы, упростив их изучение и применение. К концу этой книги вы будете знать не только о компонентах, доступных вам в Kubernetes, но и о том, «как» и «зачем» строить системы с использованием этих компонентов.
Брендан Бернс (Brendan Burns), разработчик Kubernetes
В последние годы с развитием микросервисов и контейнеров способы проектирования, разработки и эксплуатации ПО значительно изменились. Современные приложения оптимизируются в целях масштабируемости, эластичности, отказоустойчивости и быстрого изменения. Для соответствия новым принципам эти современные архитектуры требуют другого набора паттернов и практик. Цель этой книги — помочь разработчикам создавать облачные приложения с использованием Kubernetes в качестве платформы времени выполнения. Для начала кратко познакомимся с двумя основными составляющими этой книги: фреймворком Kubernetes и паттернами проектирования.
Kubernetes — это платформа для управления контейнерами. Зарождение Kubernetes произошло где-то в центрах обработки данных компании Google, где появилась внутренняя платформа управления контейнерами Borg (https://research.google.com/pubs/pub43438.html). Платформа Borg много лет использовалась в Google для запуска приложений. В 2014 году Google решил передать свой опыт работы с Borg новому проекту с открытым исходным кодом под названием Kubernetes (в переводе с греческого «кормчий», «рулевой»), а в 2015 году он стал первым проектом, переданным в дар недавно основанному фонду Cloud Native Computing Foundation (CNCF).
С самого начала проект Kubernetes приобрел целое сообщество пользователей, и число участников росло невероятно быстрыми темпами. В настоящее время Kubernetes считается одним из самых активных проектов на GitHub. Можно даже утверждать, что на момент написания этой книги Kubernetes был наиболее часто используемой и многофункциональной платформой управления контейнерами. Kubernetes также формирует основу других платформ, построенных поверх него. Наиболее известной из таких систем вида «платформа как услуга» (Platform-as-a-Service) является Red Hat OpenShift, которая добавляет в Kubernetes различные дополнительные возможности, в том числе способы создания приложений на этой платформе. Это только часть причин, по которым мы выбрали Kubernetes в качестве эталонной платформы для описания паттернов использования облачных систем в этой книге.
Эта книга предполагает наличие у читателя некоторых базовых знаний о Kubernetes. В главе 1 мы перечислим основные понятия Kubernetes и заложим основу для обсуждения паттернов в следующих главах.
Понятие паттернов, или шаблонов, проектирования появилось в 1970-х годах в области архитектуры. Кристофер Александер (Christopher Alexander), архитектор и системный теоретик, и его команда опубликовали в 1977 году новаторский труд «A Pattern Language»1 (Oxford University Press), в котором описываются архитектурные шаблоны создания городов, зданий и других строительных проектов. Некоторое время спустя эта идея была принята недавно сформировавшейся индустрией программного обеспечения. Самая известная книга в этой области — «Приемы объектно-ориентированного проектирования. Паттерны проектирования» Эриха Гаммы, Ричарда Хелма, Ральфа Джонсона и Джона Влиссидеса — «Банды четырех» (Addison-Wesley). Когда мы говорим об известных паттернах «Одиночка» (Singleton), «Фабрика» (Factories) или «Делегирование» (Delegation), то используем названия, данные в этой книге. С тех пор было написано много других замечательных книг о паттернах для различных областей с разной степенью подробностей, таких как «Enterprise Integration Patterns»2 Грегора Хопа (Gregor Hohpe) и Бобби Вульфа (Bobby Woolf) или «Patterns of Enterprise Application Architecture»3 Мартина Фаулера (Martin Fowler).
Проще говоря, паттерн описывает повторимое решение задачи4. Паттерн отличается от рецепта тем, что вместо пошаговых инструкций для решения задачи он определяет план решения целого класса подобных задач. Например, паттерн Александера «Пивная» описывает, как следует строить питейные заведения, чтобы они стали местами, где «незнакомцы и друзья становятся собутыльниками», а не «пристанищами для одиночек». Все заведения, построенные по этому шаблону, выглядят по-разному, но имеют общие черты, такие как открытые ниши для групп от четырех до восьми человек и общий зал, где сотни людей могут вместе выпивать, веселиться, слушать музыку или делать что-то еще.
Однако паттерны не просто предоставляют решения. Они также формируют язык. Уникальные названия паттернов образуют компактный язык, в основе которого лежат существительные, и каждый паттерн имеет уникальное название. Когда люди упоминают эти названия в разговорах между собой, они автоматически вызывают у них похожие ментальные представления. Например, когда мы говорим о столе, любой, кто слышит нас, предполагает, что мы говорим о деревянной столешнице на четырех ножках, на которую можно класть разные вещи. То же происходит в программной инженерии, когда мы говорим о «фабрике». В контексте объектно-ориентированного программирования мы немедленно связываем с термином «фабрика» некий объект, который производит другие объекты. Поскольку мы уже знаем решение, лежащее в основе паттерна, то можем перейти к решению еще не решенных проблем.
Есть и другие характеристики языка паттернов. Паттерны взаимосвязаны между собой и могут перекрываться, поэтому вместе охватывают большую часть пространства задач. Кроме того, как отмечается в книге «Язык паттернов», паттерны имеют разные уровни детализации и области действия. Более общие паттерны охватывают более широкий спектр задач и предлагают приблизительные рекомендации, касающиеся их решения. Специализированные паттерны дают очень конкретное решение, но применяются не так широко. Эта книга содержит все виды паттернов, и многие паттерны ссылаются на другие паттерны или даже могут включать другие паттерны как часть решения.
Другая особенность паттернов заключается в том, что они следуют жесткому формату. Однако каждый автор определяет свой формат, и, к сожалению, нет единого стандарта, который определял бы, как должны излагаться паттерны. Мартин Фаулер дает превосходный обзор форматов, используемых для языков паттернов, в своей статье «Writing Software Patterns» (http://bit.ly/2HIuUdJ).
Мы выбрали простой паттерн структуры книги. Мы не придерживаемся какого-либо конкретного языка и для описания каждого паттерна используем следующую структуру:
Каждый паттерн имеет название, которое также является названием главы. Названия образуют основу языка паттернов.
В этом разделе дается широкий контекст и подробное описание пространства паттерна.
Этот раздел рассказывает, как паттерн решает проблему способом, характерным для Kubernetes. Этот раздел также содержит ссылки на другие паттерны, которые либо связаны, либо являются частью данного паттерна.
Обзор достоинств и недостатков решения в данном контексте.
Этот заключительный раздел содержит источники дополнительной информации, касающейся паттерна.
Мы организовали паттерны в этой книге следующим образом:
• Часть I Основные паттерны охватывает основные понятия Kubernetes и перечисляет основополагающие принципы и практики создания облачных приложений на основе контейнеров.
• Часть II Поведенческие паттерны описывает паттерны, основанные на базовых паттернах, и добавляют специализированные идеи управления взаимодействиями между контейнерами и платформой.
• Часть III Структурные паттерны содержит паттерны, имеющие отношение к организации контейнеров в поды (pod) — элементарные единицы платформы Kubernetes.
• Часть IV Конфигурационные паттерны дает представление о различных способах настройки приложений в Kubernetes. Это очень детализированные паттерны, включающие конкретные рецепты для подключения приложений к их конфигурациям.
• Часть V Дополнительные паттерны знакомит с дополнительными понятиями, например, как можно расширить саму платформу или как создавать образы контейнеров непосредственно внутри кластера.
Паттерны не всегда вписываются в какую-то одну категорию. В зависимости от контекста один и тот же паттерн может вписываться в несколько категорий. Каждая глава посвящена одному паттерну и является независимой, поэтому вы можете читать главы по отдельности и в любом порядке.
Эта книга адресована разработчикам, которые хотят проектировать и разрабатывать облачные приложения для платформы Kubernetes. Наибольшую пользу из нее извлекут читатели, которые хотя бы немного знакомы с контейнерами и понятиями Kubernetes и хотят подняться на новый уровень. Однако вам не нужно знать низкоуровневые детали устройства Kubernetes, чтобы понять варианты и паттерны использования. Архитекторы, технические консультанты и разработчики выиграют от знакомства с паттернами, описанными здесь.
Эта книга основана на сценариях использования и уроках, извлеченных из реальных проектов. Мы хотим помочь вам создавать облачные приложения, а не изобретать велосипед.
Вы сделаете массу открытий. Некоторые паттерны могут выглядеть как выдержки из руководства по Kubernetes, но при ближайшем рассмотрении вы увидите, что паттерны представлены с концептуальной точки зрения, чего не хватает в других книгах, посвященных этой теме. Другие описываются иначе, с подробными рекомендациями для каждой конкретной задачи, как, например, в части IV Конфигурационные паттерны.
Независимо от степени подробности описания паттерна, вы узнаете все, что Kubernetes предлагает для каждого конкретного паттерна, со множеством иллюстративных примеров. Все эти примеры были протестированы, и мы расскажем, как получить их исходный код в разделе «Использование примеров кода».
Прежде чем начать погружение, кратко перечислим, чем не является эта книга:
• Эта книга не является руководством по настройке самого кластера Kubernetes. Каждый паттерн и каждый пример предполагает, что вы уже настроили и запустили Kubernetes. Опробовать примеры можно несколькими способами. Желающим узнать, как настроить кластер Kubernetes, рекомендуем книгу «Managing Kubernetes» Брендана Бернса (Brendan Burns) и Крейга Трейси (Craig Tracey), изданную в O’Reilly (https://oreil.ly/2HoadnU). Кроме того, книга «Kubernetes Cookbook» Майкла Хаузенбласа (Michael Hausenblas) и Себастьяна Гоасгена (Sbastien Goasguen), изданная в O’Reilly (http://bit.ly/2FTgJzk), содержит рецепты создания кластера Kubernetes с нуля.
• Эта книга не является ни введением в Kubernetes, ни справочным руководством. Мы затрагиваем многие особенности Kubernetes и объясняем их до определенной степени, но основное внимание уделяется идеям, лежащим в основе этих особенностей. В главе 1 «Введение» предлагается краткий обзор основ Kubernetes. Если вы ищете исчерпывающую книгу об использовании Kubernetes, мы настоятельно рекомендуем книгу «Kubernetes in Action»5 Марко Лукши (Marko Luka), изданную в Manning Publications.
Книга написана в непринужденной манере и больше напоминает серию очерков, которые можно читать независимо.
Как уже упоминалось, паттерны образуют простой, взаимосвязанный язык. Чтобы подчеркнуть эту взаимосвязь, названия паттернов записываются курсивом (например, Sidecar (Прицеп)). Когда паттерн получает название по базовому понятию Kubernetes (например, Init Container (Инициализирующий контейнер) или Controller (Контроллер)), мы используем такой способ оформления только для прямых ссылок на сам паттерн. Там, где это имеет смысл, мы также даем ссылки на главы с описаниями паттернов для упрощения навигации.
Мы также используем следующие соглашения:
• Все, что вводится в командной оболочке или в редакторе, будет оформляться моношириннымшрифтом.
• Имена ресурсов Kubernetes всегда записываются с заглавной буквы (например, Pod). Если ресурс имеет комбинированное имя, такое как ConfigMap, мы используем его вместо более естественного обозначения «config map» (конфигурационная карта), чтобы подчеркнуть, что имеется в виду понятие Kubernetes.
• Имена некоторых ресурсов Kubernetes совпадают с общими понятиями, такими как «служба» или «узел». В этих случаях мы используем оформление, характерное для имен ресурсов, только для ссылок на ресурсы.
Описание каждого паттерна сопровождается примерами, которые вы можете найти на веб-странице книги (https://k8spatterns.io/). Также ссылки на примеры для каждого паттерна приводятся в разделе «Дополнительная информация» в каждой главе.
В разделе «Дополнительная информация» также приводятся ссылки на дополнительную информацию, имеющую отношение к паттерну. Мы постоянно обновляем эти списки в репозитории примеров. Изменения в коллекциях ссылок также будут публиковаться в Twitter (https://twitter.com/k8spatterns).
Исходный код всех примеров в этой книге доступен в GitHub (https://github.com/k8spatterns). Репозиторий и веб-сайт также содержат указания и инструкции о том, как создать кластер Kubernetes для опробования примеров. Просматривая примеры, также загляните в предоставляемые файлы ресурсов. В них вы найдете много полезных комментариев, помогающих понять код примера.
Во многих примерах используется REST-служба random-generator, которая возвращает случайные числа. Она специально создавалась для опробования примеров из этой книги. Исходный код этой службы тоже можно найти в GitHub (https://github.com/k8spatterns/random-generator), а ее образ для развертывания в контейнере k8spatterns/random-generator добавлен в каталог Docker Hub.
Для описания полей ресурсов мы используем обозначение путей в формате JSON. Например, .spec.replicas — это ссылка на поле replicas в разделе spec ресурса.
Если вы найдете ошибку в коде примера или в документации или если у вас появится вопрос, смело создавайте заявку в трекере проблем GitHub (https://github.com/k8spatterns/examples/issues). Мы следим за появлением заявок и с радостью отвечаем на любые вопросы.
Мы приветствуем любые предложения по изменению кода! Если вы считаете, что сможете усовершенствовать примеры, мы будем рады рассмотреть ваши предложения. Просто создайте заявку или запрос на трекере проблем GitHub и начните диалог с нами.
Создание этой книги было долгим путешествием, продолжавшимся два года, и мы хотим поблагодарить всех наших рецензентов, помогавшим нам не сбиться с пути. Особую благодарность хотим выразить Паоло Антинори (Paolo Antinori) и Андреа Тарокки (Andrea Tarocchi), помогавшим нам в этом путешествии. Большое спасибо также Марко Лукше (Marko Luka), Брендону Филипсу (Brandon Philips), Майклу Хуттерманну (Michael Httermann), Брайану Грейсли (Brian Gracely), Эндрю Блоку (Andrew Block), Иржи Кремсеру (Jiri Kremser), Тобиасу Шнеку (Tobias Schneck) и Рику Вагнеру (Rick Wagner), которые поддержали нас своим опытом и советами. Наконец, но не в последнюю очередь, большое спасибо нашим редакторам Вирджинии Уилсон (Virginia Wilson), Джону Девинсу (John Devins), Кэтрин Тозер (Katherine Tozer), Кристине Эдвардс (Christina Edwards) и всем замечательным сотрудникам O’Reilly за то, что помогли довести эту книгу до финала.
Ваши замечания, предложения, вопросы отправляйте по адресу [email protected] (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.
1Кристофер Александер, Сара Исикава, Мюррей Силверстайн. Язык шаблонов. Города. Здания. Строительство. Издательство Студии Артемия Лебедева, 2014. — Примеч. пер.
2Хоп Грегор, Вульф Бобби. Паттерны интеграции корпоративных приложений. М.: Вильямс, 2016. — Примеч. пер.
3Мартин Фаулер, Дейвид Райс, Мэттью Фоммел, Эдвард Хайет, Роберт Ми, Рэнди Стаффорд. Паттерны корпоративных приложений. М.: Вильямс, 2016. — Примеч. пер.
4 Кристофер Александер и его команда определили первоначальное значение слова «паттерн» в контексте архитектуры следующим образом: «Каждый паттерн дает описание той или иной проблемы, регулярно возникающей в окружающем нас пространстве, вслед за которым представлена суть решения данной проблемы, сформулированная таким образом, чтобы вы могли многократно использовать это решение, но никогда не копировать его» (Кристофер Александер, Сара Исикава, Мюррей Силверстайн. Язык шаблонов. Города. Здания. Строительство. С. 20). Мы считаем, что это определение прекрасно подходит также для паттернов, которые описываются в этой книге, с той лишь разницей, что у нас, пожалуй, не так много свободы в реализации решений.
5Лукша Марко. Kubernetes в действии. М.: ДМК Пресс, 2018. — Примеч. пер.
В этой вводной главе мы подготовим основу для остальной части книги и обсудим важные понятия Kubernetes, используемые в проектировании и реализации облачных приложений на основе контейнеров. Понимание этих новых абстракций, а также связанных с ними принципов и паттернов из этой книги является ключом к созданию распределенных приложений, автоматизируемых облачными платформами.
Эта глава не является обязательным условием для понимания паттернов, описываемых далее. Читатели, знакомые с понятиями Kubernetes, могут пропустить ее и сразу перейти к интересующей их категории.
Наибольшей популярностью среди архитектур приложений для облачных платформ, таких как Kubernetes, пользуется архитектура микросервисов. Этот способ организации программного обеспечения помогает снизить сложность его разработки за счет дробления бизнес-функций и замены сложности разработки сложностью эксплуатации.
Существует большое количество теоретических и практических методов создания микросервисов с нуля или деления монолитных приложений на микросервисы. Большинство из этих методов основаны на приемах, описанных в книге Эрика Эванса (Eric Evans) «Domain-Driven Design»6 (Addison-Wesley), и понятиях ограниченного контекста и агрегатов. Ограниченные контексты непосредственно связаны с большими моделями и делят их на разные компоненты, и агрегаты помогают группировать ограниченные контексты в модули с определенными границами транзакций. Однако кроме этих понятий, характерных для каждой предметной области, для каждой распределенной системы, независимо от того, основана она на микросервисах или нет, существует множество технических проблем, связанных с их организацией, структурой и поведением во время выполнения.
Контейнеры и механизмы управления контейнерами, такие как Kubernetes, предлагают много новых примитивов и абстракций для решения проблем распределенных приложений, и здесь мы обсудим разные варианты, которые следует учитывать при переносе распределенной системы в Kubernetes.
В этой книге мы будем исследовать особенности взаимодействий контейнеров и платформ, рассматривая контейнеры как черные ящики. Однако мы включили этот раздел, чтобы подчеркнуть важность внутреннего устройства контейнеров. Контейнеры и облачные платформы дают огромные преимущества распределенным приложениям, но, поместив мусор в контейнеры, вы получите распределенный мусор в большем масштабе. На рис. 1.1 показано, какие навыки необходимы для создания хороших облачных приложений.
Рис. 1.1. Путь в облачное окружение
В общем случае каждое облачное приложение имеет несколько уровней абстракции, которые требуют различных проектных решений:
• В самом низу находится уровень программного кода. На этом уровне каждая переменная, каждый метод и каждый класс, которые вы создаете, оказывают прямое влияние на обслуживание приложения в долгосрочной перспективе. Независимо от технологии контейнеров и платформы управления ими, команда разработчиков и создаваемые ими артефакты будут иметь наибольшее влияние. Важно поддерживать разработчиков, которые стремятся писать чистый код, имеют необходимое количество автоматических тестов, постоянно улучшают качество кода и являются мастерами в сфере разработки программного обеспечения.
• Предметно-ориентированное проектирование (Domain-Driven Design, DDD) — это подход к проектированию программного обеспечения с позиции бизнеса с целью получить архитектуру, как можно более близкую к реальному миру. Он лучше всего соответствует объектно-ориентированным языкам программирования, но есть и другие хорошие подходы к моделированию и проектированию программного обеспечения для решения практических задач. Модель с правильно выбранными границами, простыми в использовании интерфейсами и многофункциональным API является основой для успешной контейнеризации и автоматизации в дальнейшем.
• Архитектурный стиль микросервисов очень быстро стал нормой и определяет ценные принципы и методы проектирования часто изменяющихся распределенных приложений. Применение этих принципов позволяет создавать реализации, оптимизированные для масштабирования, отказоустойчивости и частых изменений, что является общим требованием для любого современного программного обеспечения.
• Контейнеры очень быстро превратились в стандартный способ упаковки и запуска распределенных приложений. Создание модульных, многоразовых контейнеров, которые прекрасно подходят для использования в облачных окружениях, является еще одной фундаментальной предпосылкой. С ростом числа контейнеров в каждой организации возникает необходимость управлять ими, используя более эффективные методы и инструменты. Облачный — это относительно новый термин, используемый для описания принципов, паттернов и инструментов автоматизации масштабирования контейнерных микросервисов. Мы будем использовать как взаимозаменяемые слова облачный и Kubernetes, последнее из которых является названием наиболее популярной в настоящее время облачной платформы с открытым исходным кодом.
В этой книге мы не рассматриваем приемы разработки чистого кода, предметно-ориентированного проектирования и создания микросервисов. Все внимание мы сосредоточим исключительно на шаблонах и методах решения задач управления контейнерами. Но чтобы эти паттерны были максимально эффективными, приложение само должно быть тщательно спроектировано с применением методов разработки чистого кода, предметно-ориентированного проектирования, создания микросервисов и других соответствующих методик.
Чтобы объяснить, что подразумевается под новыми абстракциями и примитивами, мы сравним их с хорошо известным объектно-ориентированным программированием (ООП), например, на языке Java. Во вселенной ООП используются такие понятия, как класс, объект, пакет, наследование, инкапсуляция и полиморфизм. Среда выполнения Java предоставляет конкретные функции и гарантии управления жизненным циклом наших объектов и приложения в целом.
Язык Java и виртуальная машина Java (Java Virtual Machine, JVM) предоставляют локальные, внутрипроцессные строительные блоки для создания приложений. Kubernetes добавляет в эту привычную картину совершенно новое измерение, предлагая новый набор распределенных примитивов и среды выполнения для создания распределенных систем, разбросанных по нескольким узлам и процессам. Используя Kubernetes для реализации всего поведения приложения, мы больше не полагаемся только на локальные примитивы.
Таблица 1.1. Локальные и распределенные примитивы7
Понятие
Локальный примитив
Распределенный примитив
Инкапсуляция поведения
Класс
Образ контейнера
Экземпляр поведения
Объект
Контейнер
Единица повторного использования
.jar
Образ контейнера
Композиция
Класс A содержит класс B
Шаблон Sidecar (Прицеп)
Наследование
Класс A расширяет класс B
Контейнер A создается из родительского образа
Единица развертывания
.jar/.war/.ear
Под (Pod)
Изоляция времени сборки/выполнения
Модуль, Пакет, Класс
Пространство имен, под, контейнер
Начальная инициализация
Конструктор
Инициализирующие контейнеры, или Init-контейнеры
Операции, следующие сразу за начальной инициализацией
Метод Init
postStart
Операции, непосредственно предшествующие уничтожению экземпляра
Метод Destroy
preStop
Процедура освобождения ресурсов
finalize(), обработчик события завершения
Деинициализированный контейнер1
Асинхронное и параллельное выполнение
ThreadPoolExecutor, ForkJoinPool
Задание
Периодическое выполнение
Timer, ScheduledExecutorService
Планировщик заданий
Фоновое выполнение
Фоновые потоки выполнения
Контроллер набора демонов (DaemonSet)
Управление конфигурацией
System.getenv(), Properties
Карта конфигураций (ConfigMap), секрет (Secret)
Мы все еще должны использовать объектно-ориентированные строительные блоки для создания компонентов распределенного приложения, но дополнительно мы можем использовать примитивы Kubernetes для организации некоторых видов поведения приложения. В табл. 1.1 перечислены различные понятия из области разработки приложений и соответствующие им локальные и распределенные примитивы.
Внутрипроцессные и распределенные примитивы имеют общие черты, но их нельзя сравнивать непосредственно и они не являются взаимозаменяемыми. Они работают на разных уровнях абстракции и имеют разные предпосылки и гарантии. Некоторые примитивы должны использоваться вместе. Например, мы должны использовать классы для создания объектов и помещать их в образы контейнеров. Однако некоторые другие примитивы могут служить полноценной заменой поведения в Java, например, CronJob в Kubernetes может полностью заменить ExecutorService в Java.
А теперь рассмотрим несколько распределенных абстракций и примитивов из Kubernetes, которые особенно интересны для разработчиков приложений.
Контейнеры — это строительные блоки для создания облачных приложений на основе Kubernetes. Проводя аналогию с ООП и Java, образы контейнеров можно сравнить с классами, а контейнеры — с объектами. По аналогии с классами, которые можно расширять (наследовать) и таким способом изменять их поведение, мы можем создавать образы контейнеров, которые расширяют (наследуют) другие образы контейнеров, и таким способом изменять поведение. По аналогии с объектами, которые можно объединять и использовать их возможности, мы можем объединять контейнеры, помещая их в поды (Pod), и использовать результаты их взаимодействий.
Продолжая сравнение, можно сказать, что Kubernetes напоминает виртуальную машину Java, но разбросанную по нескольким хостам и отвечающую за запуск контейнеров и управление ими.
Init-контейнеры можно сравнить с конструкторами объектов; контроллеры DaemonSet похожи на потоки выполнения, действующие в фоновом режиме (как, например, сборщик мусора в Java). Поды можно считать аналогами контекста инверсии управления (Inversion of Control, IoC), используемого, например, в Spring Framework, где несколько объектов имеют общий управляемый жизненный цикл и могут напрямую обращаться друг к другу.
Параллели можно было бы проводить и дальше, но не глубоко. Однако следует отметить, что контейнеры играют основополагающую роль в Kubernetes, а создание модульных, многоразовых, специализированных образов контейнеров является основой успеха любого проекта и экосистемы контейнеров в целом. Но что еще можно сказать о контейнерах и их назначении в контексте распределенного приложения, помимо перечисления технических характеристик образов контейнеров, которые обеспечивают упаковку и изоляцию? Вот несколько основных особенностей контейнеров:
• Образ контейнера — это функциональная единица, решающая одну определенную задачу.
• Образ контейнера принадлежит одной команде и имеет свой цикл выпуска новых версий.
• Образ контейнера является самодостаточным — он определяет и несет в себе зависимости времени выполнения.
• Образ контейнера является неизменным: после создания он не изменяется, но может настраиваться.
• Образ контейнера имеет определенные зависимости времени выполнения и требования к ресурсам.
• Образ контейнера экспортирует четко определенные API для доступа к его возможностям.
• Контейнер обычно выполняется как один процесс Unix.
• Контейнер позволяет безопасно масштабировать его вверх и вниз в любой момент.
Кроме всех этих характеристик, правильный образ контейнера должен иметь модульную организацию, поддерживать параметризацию и многократное использование в разных окружениях, где он будет работать. Также он должен предусматривать параметризацию для разных вариантов использования. Наличие небольших, модульных и многократно используемых образов контейнеров помогает создавать более специализированные и надежные образы подобно большой библиотеке в мире языков программирования.
Рассматривая характеристики контейнеров, легко заметить, что они идеально подходят для реализации принципов микросервисов. Образ контейнера предоставляет единую функциональную единицу, принадлежит одной команде, имеет независимый цикл выпуска новых версий и обеспечивает развертывание и изоляцию среды времени выполнения. В большинстве случаев один микросервис соответствует одному образу контейнера.
Однако многие облачные платформы предлагают еще один примитив для управления жизненным циклом группы контейнеров, в Kubernetes он называется подом. Под (Pod8) — это атомарная единица планирования, развертывания и изоляции среды времени выполнения для группы контейнеров. Все контейнеры, входящие в состав одной группы, всегда планируются для выполнения на одном хосте, развертываются вместе и могут совместно использовать пространства имен файловой системы, сети и процесса. Подчинение единому жизненному циклу позволяет контейнерам в поде взаимодействовать друг с другом через файловую систему или через сеть с помощью локальных механизмов межпроцессных взаимодействий, если это необходимо (например, по соображениям производительности).
Как показано на рис. 1.2, на этапах разработки и сборки микросервис соответствует образу контейнера, который разрабатывается и выпускается одной группой. Но во время выполнения аналогом микросервиса является под, представляющий единицу развертывания, размещения и масштабирования. Единственный способ запустить контейнер — в ходе масштабирования или миграции — использовать абстракцию пода. Иногда под содержит несколько контейнеров, например, когда контейнер с микросервисом использует вспомогательный контейнер во время выполнения, как показано в главе 15 «Шаблон Sidecar».
Рис. 1.2. Под как единица развертывания и управления
Уникальные характеристики контейнеров и подов образуют новый набор шаблонов и принципов разработки приложений на основе микросервисов. Мы рассмотрели некоторые характеристики хорошо спроектированных контейнеров; теперь рассмотрим некоторые характеристики пода:
• Под — это атомарная единица планирования. Собираясь запустить под, планировщик пытается найти хост, который удовлетворяет требованиям всех контейнеров, входящих в эту группу (есть некоторые особенности в отношении Init-контейнеров, которые мы рассмотрим в главе 14 «Init-контейнеры»). Если создать под со множеством контейнеров, планировщику придется отыскать хост, обладающий достаточными ресурсами для удовлетворения суммарных требований всех контейнеров. Процесс планирования описан в главе 6 «Автоматическое размещение».
• Под гарантирует совместное размещение контейнеров. Благодаря этому контейнеры в одной группе получают дополнительные возможности для взаимодействия друг с другом, из которых чаще всего используются общая локальная файловая система, сетевой интерфейс localhost и локальные механизмы межпроцессных взаимодействий (IPC).
• Под имеет IP-адрес, имя и диапазон портов, общих для всех контейнеров, входящих в группу. Это означает, что контейнеры в одной группе необходимо тщательно настраивать, чтобы избежать конфликтов портов, точно так, же как параллельно выполняющиеся процессы Unix должны соблюдать осторожность при совместном использовании сетевого пространства хоста.
Под — это атом Kubernetes, в котором находится ваше приложение, но у вас нет прямого доступа к поду. Для этой цели используются службы.
Группы контейнеров, или поды, — это эфемерные образования, они могут появляться и исчезать в любое время по разным причинам: например, в ходе масштабирования вверх или вниз, в случае неудачи при проверке работоспособности контейнеров и при миграции узлов. IP-адрес группы становится известен только после того, как она будет запланирована и запущена на узле. Группу можно повторно запланировать для запуска на другом узле, если узел, на котором она выполнялась, прекратил работу. Все это означает, что сетевой адрес группы контейнеров может меняться в течение жизни приложения, и потому необходим какой-то другой примитив для обнаружения и балансировки нагрузки.
Роль этого примитива играют службы (Services) Kubernetes. Служба — это еще одна простая, но мощная абстракция Kubernetes, которая присваивает имени службы постоянные IP-адрес и номер порта. То есть служба — это именованная точка входа для доступа к приложению. В наиболее распространенном сценарии служба играет роль точки входа для набора групп контейнеров, но это не всегда так. Служба — это универсальный примитив и может также служить точкой входа для доступа к функциональным возможностям за пределами кластера Kubernetes. Соответственно, примитив службы можно использовать для обнаружения служб и распределения нагрузки и для замены реализации и масштабирования без влияния на потребителей службы. Подробнее о службах мы поговорим в главе 12 «Обнаружение служб».
Как было показано выше, на этапе сборки аналогом микросервиса является контейнер, а на этапе выполнения — группа контейнеров. А что можно считать аналогом приложения, состоящего из нескольких микросервисов? Kubernetes предлагает еще два примитива, помогающих провести аналогию с понятием приложения: метки и пространства имен. Мы подробно рассмотрим пространства имен в разделе «Пространства имен» ниже.
До появления микросервисов понятию приложения соответствовала одна единица развертывания с единой схемой управления версиями и циклом выпуска новых версий. Приложение помещалось в один файл .war, .ear или в каком-то другом формате. Но затем приложения были разделены на микросервисы, которые разрабатываются, выпускаются, запускаются, перезапускаются и масштабируются независимо друг от друга. С появлением микросервисов понятие приложения стало более размытым — больше нет ключевых артефактов или действий, которые должны выполняться на уровне приложения. Однако если понадобится указать, что некоторые независимые службы принадлежат одному приложению, можно использовать метки. Давайте представим, что одно монолитное приложение мы разделили на три микросервиса, а другое — на два.
В этом случае мы получаем пять определений групп контейнеров (и, может быть, много экземпляров этих групп), которые не зависят друг от друга с точки зрения разработки и времени выполнения. Тем не менее нам все еще может потребоваться указать, что первые три пода представляют одно приложение, а два других — другое приложение. Поды могут быть независимыми и представлять определенную ценность для бизнеса по отдельности, а могут зависеть друг от друга. Например, один под может содержать контейнеры, отвечающие за интерфейс с пользователем, а два других — за реализацию основной функциональности. Если какой-то из подов прекратит работать, приложение окажется бесполезным с точки зрения бизнеса. Использование меток дает возможность определять набор подов и управлять им как одной логической единицей. На рис. 1.3 показано, как можно использовать метки для группировки частей распределенного приложения в конкретные подсистемы.
Рис. 1.3. Метки используются для идентификации подов как принадлежащих одному приложению
Вот несколько примеров использования меток:
• Метки используются наборами реплик (ReplicaSet) для поддержания в рабочем состоянии некоторых экземпляров подов. Для этого каждое определение пода должно иметь уникальную комбинацию меток, используемых для планирования.
• Метки также широко используются планировщиком для совместного размещения или распределения групп контейнеров по узлам с учетом требований этих групп.
• Метка может задавать логическую группировку набора подов и идентифицировать его как приложение.
• В дополнение к уже перечисленным типичным случаям метки можно использовать для хранения метаданных. Часто трудно заранее предусмотреть все случаи, когда могут пригодиться метки, но лучше иметь достаточно меток, чтобы описать все важные аспекты подов. Например, могут пригодиться метки для описания логических групп внутри приложения, бизнес-характеристик и степени важности, конкретных зависимостей среды времени выполнения, таких как архитектура оборудования или настройки местоположения.
Впоследствии эти метки могут использоваться планировщиком для более точного планирования. Те же метки можно использовать из командной строки для управления соответствующими подами. Однако не следует выходить за рамки разумного и заранее добавлять слишком много меток. При необходимости требуемые метки всегда можно добавить позже. Удаление меток, кажущихся ненужными, намного опаснее, поскольку нет простого способа узнать, для чего они используются и какой эффект может вызвать их удаление.
Другой примитив — аннотации — очень похож на метки. Подобно меткам, аннотации организованы в виде ассоциативного массива, но предназначены для определения метаданных, которые используются компьютером, а не человеком.
Информация в аннотациях не предназначена для использования в запросах и сопоставления объектов. Аннотации предназначены для присоединения дополнительных метаданных к объектам, созданным разными инструментами и библиотеками. Например, аннотации можно использовать для добавления номера версии и сборки, информации об образе, временных меток, имен веток в репозитории Git, номеров запросов на включение, хешей образов изображений, адресов в реестре, имен авторов, сведений об инструментах и многого другого. То есть метки используются главным образом для поиска и выполнения действий с соответствующими ресурсами, а аннотации — для прикрепления метаданных, которые могут использоваться компьютером.
Другой примитив, который также может помочь в управлении группой ресурсов, — пространство имен Kubernetes. Как уже отмечалось выше, пространство имен может показаться похожим на метку, но в действительности это совсем другой примитив со своими характеристиками и назначением.
Пространства имен Kubernetes позволяют разделить кластер Kubernetes (который обычно распределяется по нескольким хостам) на логические пулы ресурсов. Пространства имен определяют области для ресурсов Kubernetes и предоставляют механизм для авторизации и применения других политик к сегменту кластера. Чаще всего пространства имен используются для организации разных программных окружений, таких как окружения для разработки, тестирования, интеграционного тестирования или промышленной эксплуатации. Пространства имен также можно использовать для организации многопользовательских архитектур (multitenancy), а также для изоляции рабочих групп, проектов и даже конкретных приложений. Но, в конечном счете, для полной изоляции некоторых окружений пространств имен недостаточно, поэтому создание отдельных кластеров является обычным явлением. Как правило, создается один непромышленный кластер Kubernetes, используемый, например, для разработки, тестирования и интеграционного тестирования, и другой — промышленный кластер Kubernetes для опытной и промышленной эксплуатации.
Давайте перечислим некоторые особенности пространств имен и посмотрим, как они могут помочь нам в разных сценариях:
• Пространство имен управляется как ресурс Kubernetes.
• Пространство имен создает изолированную область для таких ресурсов, как контейнеры, группы контейнеров (поды), службы или наборы реплик (ReplicaSet). Имена ресурсов должны быть уникальными внутри пространства имен, но могут повторяться в разных пространствах имен.
• По умолчанию пространства имен определяют изолированную область для ресурсов, но ничто не изолирует эти ресурсы и не препятствует доступу одного ресурса к другому. Например, группа контейнеров из пространства имен для разработки может обращаться к другой группе контейнеров из пространства имен для промышленной эксплуатации, если ей известен IP-адрес этой группы. Однако существуют плагины для Kubernetes, которые обеспечивают изоляцию сети для достижения истинной многоарендности.
• Некоторые другие ресурсы, такие как сами пространства имен, узлы и постоянные тома (PersistentVolume), не принадлежат к пространствам имен и должны иметь уникальные имена для всего кластера.
• Каждая служба Kubernetes принадлежит к пространству имен и получает соответствующий DNS-адрес, включающий пространство имен в форме <имя-службы>.<имя-пространства-имен>.svc.cluster.local. То есть имя пространства имен присутствует в URI каждой службы, принадлежащей к данному пространству имен. Это одна из причин, почему так важно выбирать правильные имена для пространств имен.
• Квоты ресурсов (ResourceQuota) определяют ограничения на совокупное потребление ресурсов пространством имен. С помощью квот ResourceQuota администратор кластера может управлять количеством объектов каждого типа в пространстве имен. Например, используя квоты, он может определить, что пространство имен для разработки может иметь только пять карт конфигураций (ConfigMap), пять объектов Secret, пять служб, пять наборов реплик, пять постоянных томов и десять подов.
• Квоты ресурсов (ResourceQuota) также могут ограничивать общую сумму вычислительных ресурсов, доступных для запроса в данном пространстве имен. Например, в кластере с 32 Гбайт ОЗУ и 16 ядрами можно выделить половину ресурсов — 16 Гбайт ОЗУ и 8 ядер — для промышленной эксплуатации, 8 Гбайт ОЗУ и 4 ядра для промежуточного окружения, 4 Гбайт ОЗУ и 2 ядра для разработки и столько же для тестирования. Возможность наложения ограничений на группы объектов с использованием пространств имен и квот ресурсов неоценима.
Мы кратко рассмотрели лишь часть основных понятий Kubernetes, которые используются в этой книге. Примитивов, которыми разработчики пользуются каждый день, намного больше. Например, создавая контейнерную службу, вы можете задействовать множество объектов Kubernetes, которые позволяют воспользоваться всеми преимуществами фреймворка. Имейте в виду, что это только объекты, используемые разработчиками приложений для интеграции контейнерной службы в Kubernetes. Есть и другие понятия, используемые администраторами, чтобы позволить разработчикам эффективно управлять платформой. На рис. 1.4 представлены основные ресурсы Kubernetes, которые могут пригодиться разработчикам.
Рис. 1.4. Понятия Kubernetes для разработчиков
Эти новые примитивы способствуют появлению новых способов решения проблем, наиболее универсальные из которых становятся шаблонами. В этой книге мы не будем подробно описывать все решения, а сосредоточимся только на тех, которые устоялись как шаблоны.
• Принципы проектирования контейнерных приложений (https://red.ht/2HBKqYI).
• Методология «Двенадцать факторов» (https://12factor.net/ru/).