Cloud-native в Kubernetes - Академия Selectel

Cloud-native в Kubernetes

В этой статье мы расскажем про основные концепции работы с системой kubernetes, а также рассмотрим вопросы инфраструктурного характера и подходы к развертыванию приложений.

Введение 

Kubernetes (k8s) стал новым отраслевым стандартом инфраструктуры неожиданно, не все смогли перестроиться под быстро меняющийся рынок. Если раньше администратор всегда знал конкретную ВМ или конкретный хост, где развернуть инстанс того или иного ПО, с приходом Kubernetes правила изменились.

Начать работу с кластером Kubernetes просто: на рынке достаточно готовых managed-решений, например, Managed Kubernetes от Selectel. Кластер разворачивается в течение 5-10 минут и сразу готов принимать нагрузку — это максимально упрощает погружение в новую технологию. О том, как запустить кластер, мы написали подробную инструкцию.

Для неопытного пользователя с первого раза разобраться во всех абстракциях Kubernetes и грамотно развернуть там ПО — задача повышенной сложности. В этой статье мы расскажем про основные концепции работы с Managed Kubernetes, а также рассмотрим вопросы инфраструктурного характера и подходы к развертыванию приложений. 

Статья рассчитана на новичков, которые хотели бы понять отличия работы в Kubernetes и разобраться, какие изменения это за собой повлечет в их продукте. Статья содержит рекомендации по работе с Managed Kubernetes в Selectel, а не системы Kubernetes в целом. Также эта статья не содержит рекомендаций по безопасности.

Планирование инфраструктуры

Кластер Managed Kubernetes позволяет максимально гибко планировать инфраструктуру: кластер можно стартовать всего с одной ноды и постепенно начинать его расширение. Добавление нод упрощено настолько, что этот процесс можно полностью автоматизировать: кластер будет разрастаться по мере роста нагрузки и сужаться, когда нагрузки нет вовсе.

Однако лучше не бронировать ресурсы впритык, важно всегда держать некоторый свободный запас. Большинство развертываний предполагает создание дополнительных реплик приложения при обновлении манифеста, что может привести к вытеснению остальных частей приложения.

Для разных окружений (dev, staging, test, prod) лучше иметь разные кластеры. Гарантировать совместную работу окружений без взаимного влияния крайне сложно. Это требует большого опыта работы с системой Kubernetes. Для тестовых окружений резервацией кластерных компонентов Kubernetes можно пренебречь, сделав кластеры менее отказоустойчивыми, но более дешевыми. Для продуктовых окружений лучшим вариантом будет не экономить на этом аспекте, чтобы не столкнуться с непредвиденным даунтаймом.

Поддержка кластера на актуальных версиях становится сильно проще managed-решениями, где работоспособность версий гарантируется. Что касается серьезных обновлений минорных версий кластера и его совместимости с приложением, то наличие отдельного кластера под dev/stage/prod решают эту проблему: если обновлять сначала тестовую среду, можно сразу определить, где и какие проблемы могут возникнуть в развертываниях вашего ПО. У kubernetes отличная совместимость смежных минорных версий. Но не обновлять манифесты на протяжении 5 и более минорных версий подряд мало у кого получается. 

Интеграция kubernetes с облачными провайдерами позволяет расходовать ресурсы на лету, не прибегая к ручному созданию дисков, балансировщиков и/или других ресурсов с последующим трудоемким распределением по хостам. Это позволяет думать о приложении, а не об инфраструктуре.

Размещение приложений в Kubernetes: ресурсы

Кластер Kubernetes является очень подвижным: ваше приложение может менять рабочие ноды, его реплики могут перезапускаться, расширяться и сужаться в зависимости от нагрузки и т.п. Важно помнить, что приложение или его реплика не развернется, если для этого не хватает ресурсов в кластере. С одной стороны эту проблему поможет решить автоскейлер — сущность в Managed Kubernetes от Selectel, которая следит за тем, какой размер кластера должен быть для вмещения всей нагрузки. 

Однако многие приложения склонны к чрезмерному (greedy) потреблению ресурсов, от того они могут резервировать гораздо больше CPU и RAM, чем вы изначально предполагали. Любые запускаемые приложения в Kubernetes можно ограничивать встроенными механизмами: resource requests (предварительная резервация) и resource limits (максимально допустимое потребление) поможет избежать аварийных ситуаций с вытеснением одного приложения другим.

Являясь по своей сути децентрализованной системой, Kubernetes того же поведения требует и от приложений: лучше создавать больше нод с меньшими ресурсами (желательно еще в разных регионах), чем держать монолитные. Большее количество нод гарантирует большую распределенность приложения и его отказоустойчивость. Не забывайте и про настройки шедулинга: taints, (anti)-affinity, QoS и прочие настройки, которые так или иначе воздействуют на шедулинг, не стоит оставлять их дефолтными. Следует также быть готовыми к тому, что одна или несколько нод могут выйти из строя. На случай этих сбойных нод приложение должно иметь возможность разместиться на оставшихся, не потеснив при этом критически важные компоненты. Для этого можно задействовать механизмы сущности группы нод: установить специальные метки и разрешить только нужным компонентам разворачиваться на этих нодах. А выпавшие ноды безопасно вернет в кластер встроенное решение от Selectel.

Проверить поведение можно командой drain на любой из нод. Если компоненты вашего приложения падают или мешают работе критических компонентов, следует еще раз вернуться к проектированию распределения нагрузки.

Следует сказать про сеть в Kubernetes, которая предполагает простую архитектурную концепцию: каждый развернутый контейнер обязан видеть все другие контейнеры. Разумеется, это вовсе не означает, что доступ от одного к другому нельзя ограничить. Тем не менее тонкие сетевые настройки могут стать причинами очень серьезных проблем, которые крайне сложно диагностировать. Лучше не вмешиваться в работу сети, если нет глубоких знаний по работе с Kubernetes.

Размещение приложений в Kubernetes: развертывания

Подобно известной мудрости «разделяй и властвуй», приложения тоже лучше разделить: распределить по разным неймспейсам кубернетес и разным подам, разнести на разные группы нод и даже разные пулы. Рекомендуется дробить не только на неймспейсы конкретных приложений, но и на функциональные группы: например, выносить из неймспейса my-app кластер СУБД/очереди в отдельный неймспейс my-app-db. Это не только поможет организовать приложение и лучше ограничить доступы, но и грамотно распределить приложение при его дальнейшем росте

Активно потребляющее CPU и RAM ПО следует выносить на отдельную ноду или даже на отдельный сервер. Яркий пример — СУБД (эфимерные хранилища): активная работа с диском и необходимость жесткой привязки стейта делает это ПО крайне сложным в администрировании в системе Kubernetes. К этому же пункту относятся сетевые и файловые настройки: nodePort и hostPath — примеры крайне опасных конфигураций, способных привести к весомым последствиям.

Ввиду подвижности кластера проще размещать в кластере stateless приложения, поскольку этот процесс для системы Kubernetes нативный. Куда сложнее будет разместить в нем stateful, что предполагает не только множество ограничений и допущений в работе приложения, но и большой багаж знаний по работе с системой Kubernetes. Помочь с размещением stateful могут другие managed-продукты от Selectel, например, Managed Databases или реестр хранения образов Container Registry.

Размещение приложений в kubernetes: проектирование ПО

Размещаемое ПО тоже должно соответствовать стилю cloud-native: при его проектировании закладывайте не только параллельную неблокирующую работу реплик, но и операции по graceful shutdown. Приложение в системе kubernetes так или иначе будет менять воркер ноду, а иногда перезапускаться и по иным причинам. Важно заложить операцию грамотного завершения работы приложения, чтобы не потерять клиентские сессии и критичные данные.

Отслеживание работы ПО тоже является неотъемлемой частью работы инфраструктурного инженера. Система Kubernetes не панацея, поэтому иметь представление о том, в каком состоянии находится в конкретный момент приложение будет не лишним, а мониторинг вам с этим очень поможет. Есть множество готовых решений, адаптированных под Kubernetes и работающих в кластере «из коробки».

Микросервисная архитектура предполагает частое и многочисленное общение между отдельно взятыми сервисами. В Kubernetes внутренняя адресация зачастую не является чем-то гарантированным, поэтому общение сервисов между собой лучше выстраивать посредствам внутреннего DNS-плагина. Если приложению крайне необходимо знать какие-то внешние обстоятельства, будь-то адрес пода, неймспейс и т.д., в этом может помочь downward API Kubernetes.

Размещение приложений в kubernetes: деплой

Напоследок осталось немного поговорить о том, как доставлять приложения в кластер Kubernetes. Есть множество вариантов и инструментов, но суть их сводится к одному: взять шаблон манифеста в формате .yaml, применить к нему параметры конкретного инстанса и затем передать этот манифест на исполнение в кластер Kubernetes. Например, самый известный инструмент — helm, своеобразный менеджер пакетов для кластера. Или ArgoCD — более сложный инструмент доставки. Независимо от инструмента рекомендуется придерживаться git-based подхода к доставке приложений, когда состояние кластера описано в Git и находится в состоянии постоянной синхронизации. Это позволит значительно сократить часы простоев и количество откатов неудачных развертываний.

В ходе написания манифеста потребуется указать кластеру, из какого источника он будет брать образ контейнера. Этот реестр контейнеров должен быть доступен 24/7 при определенных настройках развертываний. В противном случае политика пуллинга образов должна быть изменена. Некоторые реестры, например, Docker Hub, имеют строгую политику на скачивание образов на бесплатном аккаунте, что может повлиять на работу приложения и процессы в кластере в целом. Чем легковеснее будет образ, тем лучше. Лишний вес образа пропорционально увеличивает время запуска приложения. Стоит еще отдельно отметить тег образа. Использование тега latest приводит к неожиданным результатам деплоя. На первых порах лучше от этого тега отказаться, пока не освоитесь в кластере достаточно.

Чтобы Kubernetes лучше понимал приложения внутри контейнеров, используйте liveness, readiness и startup пробы: они помогут лучше понимать состояние приложения и позволят системе Kubernetes более точно и оперативно реагировать на изменение этого состояния. Конфигурацию приложения можно положить в configMap, а чувствительную информацию — в Secret. При проектировании регулярных заданий сущности job и cronjob лучше вынести в отдельную группу нод с отдельными настройками автоскейлинга: это убережет от ресурсных проблем еще до момента их возникновения.

Последнее, о чем мы упомянем в этой статье — стороннее ПО. Не всегда все нужно обязательно помещать внутрь кластера, отдельные виды ПО нужны самому кластеру для реализации той или иной функциональности. Лучше такое ПО выносить на отдельные ВМ/хосты (особенно актуально для сетевых файловых систем и блочных систем хранения: nfs, ceph, gluster, longhorn и прочие). В иных случаях произведите соответствующие настройки кластера, чтобы быть уверенным в том, что в каждый конкретный момент времени необходимое количество воркер нод будет запущено, а вместе с ними и нужное количество реплик ПО.

Заключение

В этот статье мы рассмотрели особенности работы с системой Kubernetes в разрезе готового решения от Selectel: коснулись как вопросов инфраструктурного характера, так и особенностей разработки и развертывания ПО cloud-native приложений.