Постигая Kubernetes — мощь и величие
DevOps ~6 min.

Постигая Kubernetes — мощь и величие

Kubernetes (куб, k8s) во истину огромный комбайн, который очень много что умеет. И не даром его так любят и так боятся. Когда начинаешь осваивать какую-то технологию, пытаешься начать с чего-то малого. Но где то малое у Kubernetes, с чего нужно начать? Я бы начал с обзора основных его возможностей. О чем и пойдет речь в этой статье.

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

Запуск сервисов и мониторинг

K8S – это оркестратор контейнеров. Его обязанность – запускать ПО, укутанное в контейнер, и следить за его состоянием.

Куб может:

  • Запустить сервис;
  • Остановить сервис;
  • Переподнять умерший сервис;
  • Направлять трафик только на те сервисы, которые уже готовы его принять;
  • И наоборот – ограничить трафик на те процессы, которые уже не могут его принять;
  • Проверять сервис на его жизнеспособность и убить его, если он превратился в зомби;

Реальные кейсы:

  • Сервер с NodeJS требующий на инициализацию около 1.5 минут. Пока сервис не готов, на него нельзя направлять трафик, иначе будет 503 или какой-нибудь timeout.
  • Тоже сервер с NodeJS, но где-то подтекает память. Со временем процесс съедает всю доступную ему память и превращается в зомби. То есть процесс живой, но он ничего не делает, обработка http запросов тоже сломана. K8S со своей стороны закрывает трафик на этот сервис, убивает его и поднимает новый. Это не идеальное решение – протечку все еще нужно найти и залатать. Но это решение лучше, чем совсем уйти в оффлайн.

Как это работает

У куба в БД хранится текущее состояние системы и желаемое. В случае, если какой-то сервис погибает, куб пытается его поднять снова. И снова. И снова. Этим он похож на обычный супервизор.

Также у куба есть 2 вида проверки (их больше, но эти основные):

  • Проверка готовности (readiness) отвечает за возможность сервиса принять на себя трафик. Когда сервис отвечает успехом, куб начинает направлять туда трафик. И наоборот – как только проверка начинает проваливаться, трафик на сервис ограничивается.
  • Проверка жизнеспособности (liveness) отвечает на вопрос “Ты живой?”. Сервис вполне может быть живым, но не готовым. Но если эта проверка проваливается (один или несколько раз; все зависит от настроек), куб сначала просит сервис умереть самостоятельно (graceful shutdown), а потом, через достаточно короткое время, убивает его. Что в свою очередь инициирует запуск этого сервиса вновь.
Стратегия деплоя - RollingUpdate

Деплой

Пожалуй, это одна из самых приятных фич, которую предоставляет куб. И вот в чем соль. Представьте, что у вас есть приложение, и вам нужно выкатить новую версию на production. Задача в том, чтобы не допустить ни миллисекунды downtime’а. В PHP это сделать достаточно просто:

  • Загрузить код на сервер;
  • Переключить симлинк на папку с новым кодом;
  • Перезапустить сервер на всякий случай.

Но что делать, если у вас нода? А если на разогрев приложения уходит более двух минут? А еще и не один сервер, а несколько? Задача эта типовая, и решения бывает разные. Но вот, что можно сделать с помощью k8s.

У куба есть отдельная сущность, которая так и называется Deployment. Это дает возможность постепенного накатывания новой версии приложения. В зависимости от настроек поведение будет разным, но вот один из сценариев:

  • Куб создает контейнер с новым приложением;
  • Дожидается его готовности;
  • Убивает контейнер со старым кодом.

Заметьте, как гармонично деплой работает с инструментами мониторинга (liveness & readiness probes). Если в прод попадет заведомо сломанное приложение, куб остановит процесс накатывания и оставит код на старой версии. Про существующие стратегии деплоя можно почитать тут.

Балансировка нагрузки

Балансировка, распределение и масштабирование

Каждый отдельный сервис может иметь несколько реплик себя. Например, backend. В этом случае куб, при правильной настройке конечно, распределяет нагрузку между сервисами, что в некоторой степени гарантирует стабильность компонентов. Для kubernetes совершенно не важно, сколько реплик имеет сервис. Он не завязывается на конкретных экземплярах, но может динамически подключаться к новым и отвязываться от удаленных.

Что решает проблему масштабирования: можно увеличивать и уменьшать количество реплик без необходимости делать что-либо вручную – куб сам все подтянет.

Таким образом, благодаря компонентной архитектуре, в k8s есть отдельная сущность Horizontal Pod Autoscaler, которая и отвечает за масштабирование компонентов в обе стороны – и на увеличение, и на уменьшение.

Также, хостинги, предоставляющие куб как сервис, могут автоматически подключать и удалять новые сервера в зависимости от нагрузки. Что, следовательно, может неплохо так сэкономить денег.

Помимо обычной балансировки и масштабирования, которые выполняют исключительно свои задачи, иногда нужно не только увеличить количество экземпляров, но и как можно более равномерно распределить их по серверам в кластере. Или наоборот – группировать некоторые компоненты в пределах одного сервера для уменьшения сетевых задержек. Для этой цели существует т.н. Affinity and anti-affinity (не знаю, как правильно перевести). Тут как раз и задаются правила выбора ноды.

Распределение ресурсов

Менеджмент ресурсов

Куб умеет:

  • Гарантировать (RAM, CPU) доступность ресурсов сервисам.
  • Ограничивать ресурсы сервисам.

Реальные кейсы:

  • Есть тестовый кластер, где крутятся более 10 различных проектов. На этапе разработки всегда что-то может пойти не так, и некоторые процессы начинают подтекать или выедать весь процессор. Куб может ограничивать использование процессора (например, не более 20% одного ядра) и памяти (убивая при этом процесс).
  • В продовой среде может быть несколько серверов (нод в терминологии k8s) и сервисов. Грамотное распределение ресурсов будет гарантировать, что 2 емких по памяти или процессору процесса не будут лежать вместе и бороться за одни и те же ресурсы. Куб будет гарантировать те ресурсы, которые необходимы этому сервису.

Как это работает

У куба есть 2 способа распределения ресурсов:

  • Лимиты (limits) отвечают за ограничения. Если сервис начинает потреблять больше позволенного, то куб начинает ограничивать потребление ЦПУ и убивает в случае оперативной памяти
  • Заявки (requests) – это запрашиваемые сущностью ресурсы. Указывая кубу в заявках количество процессорного времени и оперативной памяти, мы даем ему возможность более обдуманно выбирать сервер, на котором сервис будет чувствовать наиболее комфортно.

И на самом деле это еще не все. Можно указывать квоту на целое пространство имен, ограничивая при этом, суммарное ресурсопотребление. Можно указать максимально возможное значение по лимитам и заявкам для каждого отдельного пода.

В общем, про это можно почитать тут и посмотреть здесь.

Выбор ресурсов также влияет на приоритизацию сервисов в случае проблем – кто из них будет убит первым. Почитать можно тут.

Выводы

Kubernetes – не просто модное словечко или хайповая вещица, которая умрет через несколько лет. Это очень (очень) мощный и удобный инструмент. Я до сей поры восхищаюсь творением Google. Работа с ним доставляет мне массу позитивных эмоций.

Да, он пугает своими размерами и возможностями. Я тоже долго не осмеливался подступить к нему. В этом мне очень помогла книга Kubernetes в действии (Лукша Марко). Качественная подача материала, читается легко и хороший перевод. Очень советую.

Всё описанное в этой статье – только лишь верхушка айсберга. Еще есть задачи, сron-задачи, демоны, сервисы с состоянием, подключаемые хранилища, конфигурации и секреты и т.д. Но все это приходит с опытом и потребностями.


Если есть вопросы, внизу есть блок с комментариями и ссылками на соц. сети. Для меня важна любая обратная связь :blush:

© 2020 Vitaly Zaslavsky. All rights reserved.