Kubernetes (куб, k8s) во истину огромный комбайн, который очень много что умеет. И не даром его так любят и так боятся. Когда начинаешь осваивать какую-то технологию, пытаешься начать с чего-то малого. Но где то малое у Kubernetes, с чего нужно начать? Я бы начал с обзора основных его возможностей. О чем и пойдет речь в этой статье.
Прежде хочу обратить внимание на то, что никакая из описанных здесь возможностей, по моему, не является основной. Весь функционал очень сильно пересекается и взаимозависим от остального.
Запуск сервисов и мониторинг #
K8S – это оркестратор контейнеров. Его обязанность – запускать ПО, укутанное в контейнер, и следить за его состоянием.
Куб может:
- Запустить сервис;
- Остановить сервис;
- Переподнять умерший сервис;
- Направлять трафик только на те сервисы, которые уже готовы его принять;
- И наоборот – ограничить трафик на те процессы, которые уже не могут его принять;
- Проверять сервис на его жизнеспособность и убить его, если он превратился в зомби;
Реальные кейсы:
- Сервер с NodeJS требующий на инициализацию около 1.5 минут. Пока сервис не готов, на него нельзя направлять трафик, иначе будет 503 или какой-нибудь timeout.
- Тоже сервер с NodeJS, но где-то подтекает память. Со временем процесс съедает всю доступную ему память и превращается в зомби. То есть процесс живой, но он ничего не делает, обработка http запросов тоже сломана. K8S со своей стороны закрывает трафик на этот сервис, убивает его и поднимает новый. Это не идеальное решение – протечку все еще нужно найти и залатать. Но это решение лучше, чем совсем уйти в оффлайн.
Как это работает #
У куба в БД хранится текущее состояние системы и желаемое. В случае, если какой-то сервис погибает, куб пытается его поднять снова. И снова. И снова. Этим он похож на обычный супервизор.
Также у куба есть 2 вида проверки (их больше, но эти основные):
- Проверка готовности (readiness) отвечает за возможность сервиса принять на себя трафик. Когда сервис отвечает успехом, куб начинает направлять туда трафик. И наоборот – как только проверка начинает проваливаться, трафик на сервис ограничивается.
- Проверка жизнеспособности (liveness) отвечает на вопрос “Ты живой?”. Сервис вполне может быть живым, но не готовым. Но если эта проверка проваливается (один или несколько раз; все зависит от настроек), куб сначала просит сервис умереть самостоятельно (graceful shutdown), а потом, через достаточно короткое время, убивает его. Что в свою очередь инициирует запуск этого сервиса вновь.
Деплой #
Пожалуй, это одна из самых приятных фич, которую предоставляет куб. И вот в чем соль. Представьте, что у вас есть приложение, и вам нужно выкатить новую версию на 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: