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

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

В статье мы расскажем об основных объектах и компонентах кластера.

Что такое Kubernetes

Kubernetes — это платформа для управления жизненным циклом контейнеров, которая позволяет построить распределенную отказоустойчивую систему.

Kubernetes автоматически управляет контейнерами: создает, запускает, останавливает и уничтожает их. Он особенно полезен, когда количество контейнеров растет, ими становится сложно управлять вручную. В основе его работы лежит декларативный подход. Это значит, что разработчики или администраторы указывают желаемое состояние системы, а Kubernetes сам стремится поддерживать это состояние.

Например

Можно указать, что у контейнера должно быть 3 реплики. Kubernetes будет стремиться поддерживать это количество, и если один из контейнеров выйдет из строя — перезапустит его, чтобы снова было 3 рабочих реплики.

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

Основные компоненты кластера

Компоненты — это модули, службы и программы, которые обеспечивают работу всего кластера. Они работают внутри нод, то есть физических или виртуальных серверов, на которых работает Kubernetes — некоторые внутри управляющих Master-нод, некоторые внутри рабочих Worker-нод.

Упрощенная архитектура компонентов кластера

Итак, в кластер Kubernetes входят следующие компоненты:

  1. kube-apiserver

    Сервер API обеспечивает работу API кластера, обрабатывает REST-операции и предоставляет интерфейс, через который все остальные компоненты взаимодействуют между собой. Также через него проходят все запросы на чтение или изменение состояния кластера. Работает на Master-нодах.

  2. etcd

    Распределенное хранилище формата «ключ-значение», в котором хранится состояние всего кластера. Его основная задача — обеспечить консистентность данных и отказоустойчивость кластера. Etcd — это самостоятельный проект, который развивается отдельно от Kubernetes и используется в разных продуктах. Хранилище использует алгоритм консенсуса Raft, то есть в нем уже заложены механизмы для достижения отказоустойчивости. Чтобы их использовать, нужно создать несколько реплик etcd на отдельных машинах либо на основных нодах кластера. Работает на Master-нодах.

  3. kube-scheduler

    Компонент, который планирует, на каких узлах будут разворачиваться поды. При этом он учитывает множество факторов, включая требования к ресурсам, ограничения, местонахождение данных и так далее. Работает на Master-нодах.

  4. kube-controller-manager

    Компонент, который запускает процессы контроллеров. Что такое контроллеры, мы рассказываем ниже. Работает на Master-нодах.

  5. kubelet

    Cлужба, которая отвечает за управление состоянием ноды: запуск, остановку и поддержание работы подов и контейнеров. Работает на Worker-нодах.

  6. kube-proxy

    Служба, которая управляет правилами балансировки нагрузки. Она конфигурирует правила iptables или IPVS, через которые осуществляется роутинг и проксирование. Работает на Worker-нодах.

Основные объекты кластера Kubernetes

Объекты — сущности, которые Kubernetes использует для представления состояния кластера. Здесь мы расскажем о самых основных объектах Kubernetes. Есть и другие, но так как цель статьи — познакомиться с основами Kubernetes, этого будет достаточно.

Nodes (Ноды, узлы)

Как мы уже говорили, это виртуальные или физические серверы, на которых работает Kubernetes. Они бывают двух типов:

  1. Master (мастер-нода) — узел, который управляет всем кластером. Он следит за остальными нодами и распределяет между ними нагрузку. Как правило, мастер-нода занимается только управлением и не берет на себя никакие рабочие нагрузки. Для повышения отказоустойчивости мастер-нод должно быть несколько.
  2. Worker (рабочие ноды) — узлы, на которых и работают контейнеры. На одном узле может работать много контейнеров в зависимости от параметров ноды (объем памяти и CPU) и требований контейнера.

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

Вот так выглядит кластер Kubernetes в упрощенном виде

Pods (Поды)

Это абстрактный объект Kubernetes, который представляет собой группу из одного или нескольких контейнеров. Контейнеры внутри пода вместе запускаются и работают, имеют общее хранилище и сетевые ресурсы. Под — минимальная единица, которой оперирует Kubernetes, то есть он не управляет контейнерами напрямую, он «оборачивает» их в поды и управляет ими.

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

Чаще всего под — это обертка лишь над одним контейнером (приложением), потому что у каждого приложения могут быть разные требования к производительности, масштабированию, SLA и так далее.

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

Например

Веб-сервер использует базу данных Redis для кэширования. Если Redis больше нигде не используется и нужна только для этого веб-сервера, тогда есть смысл объединить их в один под. При этом инстансы одного приложения не используют кэш совместно, базы данных Redis в разных подах никак не связаны между собой. Если эта же БД используется не только для одного веб-сервера, но и для других приложений, тогда лучше обернуть ее в отдельный под, который не будет зависеть от жизненного цикла остальных контейнеров.

На одном узле кластера находится несколько подов, внутри которых может быть один или несколько контейнеров

Обычно поды не создаются вручную — Kubernetes делает это сам. Но для этого ему нужно знать, как создавать поды: какие именно образы, какие требования к узлам и так далее. Для этого используются контроллеры, например Deployments.

Controllers (Контроллеры)

Контроллеры — это общее название класса средств управления, которые следят за кластером и стараются поддерживать желаемое состояние. Есть несколько типов контроллеров, которые следят за ресурсами и делают это немного по-разному, например:

  1. Deployment — это описание желаемого состояния для подов; указание Kubernetes, как он должен управлять жизненным циклом подов. Поддерживает набор подов с нужной конфигурацией, управляет обновлениями и откатами. Самый распространенный способ разместить приложение в Kubernetes.
  2. ReplicaSet — создает стабильный набор подов, выполняющих одну и ту же задачу. Гарантирует, что всегда работает указанное число реплик подов. То есть если с каким-то подом что-то случится, Kubernetes запустит новый, чтобы число реплик оставалось заданным. Обычно это служебный объект, который Kubernetes создает сам.
  3. StatefulSet — используется для управления приложениями с отслеживанием состояния (Stateful-приложениями) с помощью постоянного хранилища.
  4. DaemonSet — гарантирует, что все или некоторые узлы запускают копию пода. То есть по мере добавления узлов в кластер добавляются поды.
  5. Job — недолговечные рабочие нагрузки, используемые для выполнения одной задачи. Job создает один или несколько подов и дождется, пока они выполнят свою работу.
  6. CronJob — создает задачи по расписанию.

Мы для примера рассмотрим, как работают Deployments (развертывания).

Например, в развертывании мы указываем, что нужно запустить образ HTTP-сервера, у которого должно быть три реплики, при этом для работы ему нужно 256 Мб памяти. После создания развертывания Kubernetes сам проанализирует, на каких узлах нужно разместить поды, и будет следить за их состоянием. Если по какой-то причине одна из реплик пода или весь узел выйдут из строя, Kubernetes сам развернет новые реплики на других узлах.

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

Вот что происходит, когда мы выпускаем обновление приложения:

Kubernetes стремится достичь заданного состояния

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

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

Services (Сервисы)

Это абстракция, которая объединяет поды в логическую группу и определяет политику доступа к ним. То есть что-то вроде маршрутизатора и балансировщика нагрузки между подами.

Поды — непостоянные ресурсы. Они могут уничтожаться, пересоздаваться, переезжать на другие ноды и менять IP-адреса. Если внешняя система или сервис захотят обратиться к поду, то они должны постоянно следить за его жизненным циклом и знать, куда обратиться в конкретный момент времени. Это неудобно и неправильно.

Поэтому в Kubernetes существуют сервисы, которые знают:

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

Сервис принимает запрос от внешних систем или других сервисов, решает, какому именно поду адресовать запрос, и адресует его. При этом внешней системе не нужно знать о жизненном цикле подов: они обращаются к сервису по DNS-именам, которые всегда постоянны.

Схема работы сервисов в Kubernetes

Persistent Volumes (Постоянные тома)

Это способ хранить данные между перезапусками контейнеров.

Контейнеры по своей природе непостоянные сущности. Они могут быть в любой момент уничтожены или перезапущены. Идея контейнеров в том, что они легко «умирают» и появляются заново при необходимости. Поэтому любые постоянные данные нужно хранить где-то вне контейнера. Но контейнеры изолированы от основной системы — это сделано для безопасности. Приложения не могут напрямую получить доступ к файловой системе. Также часто для отказоустойчивости создается несколько экземпляров контейнеров, и это порождает новую проблему: нужно как-то синхронизировать данные между репликами.

Kubernetes решает эти проблемы с помощью Persistent Volume. Это абстракция над хранилищами данных, которая позволяет хранить данные в зависимости от жизни контейнеров. Это постоянные тома, жизненный цикл которых никак не связан с подами и контейнерами. PV — это самостоятельные ресурсы, которые создаются и управляются отдельно.

Для работы с PV используются два ресурса: PersistentVolume и PersistentVolumeClaim.

  1. PersistentVolume — это место, где хранятся постоянные данные: раздел на жестком диске, облачное хранилище или распределенная файловая система (CephFS, NFS и др.). Жизненный цикл PV не зависит какого-то отдельного контейнера или пода. Если контейнер или под уничтожается, PV остается.
  2. PersistentVolumeClaim — это запрос на использование хранилища. Под отправляет этот запрос в PV с просьбой выделить ему место в постоянном хранилище. Storage Provisioner (специальная программа, которую запускает Kubernetes) оценивает запрос и принимает решение, где лучше выделить место, ведь PV может быть несколько и они могут быть разных типов. Когда решение найдено, PVC выделяет место и возвращает результат поду.

Такой подход позволяет приложениям абстрагироваться от конкретной реализации хранилища. Вместо того чтобы запрашивать конкретное хранилище по конкретному адресу, приложения просто запрашивают PVC, как бы говоря: «Мне нужно 1GB для моих данных». А PersistentVolume уже сам определяет, где находится это хранилище, и резервирует место для пода.

Как работают постоянные тома Kubernetes

При этом сами хранилища не создаются автоматически. Для начала нужно определить, какое это хранилище и где оно будет находиться, создать его. Потом создать соответствующий ресурс в Kubernetes и подключить его к хранилищу. Если запускать Kubernetes On-premise, то внедрение и обслуживание постоянного хранилища — дополнительная головная боль. А управляемый Kubernetes, например KaaS на платформе Mail.ru Cloud Solutions, умеет создавать PV динамически по требованию.

Namespaces (пространства имен)

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

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

В каждом пространстве имен есть свой набор ресурсов: поды, сервисы, развертывания. В одном пространстве имен у ресурсов должны быть уникальные названия, но эти же названия можно использовать в других Namespace. Однако не все ресурсы входят в пространство имен, например, ноды и Persistent Volumes доступны всему кластеру.

Так выглядят отдельные Namespace внутри Kubernetes

Расширяемость кластера сторонними инструментами

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

Мониторинг. Часто для мониторинга используется связка Prometheus + Grafana. Первый инструмент собирает метрики, второй визуализирует их. Можно мониторить как инфраструктуру самого Kubernetes, так и свои приложения.

Инструменты для разработки. Например, Jenkins позволяет настроить процесс непрерывной интеграции, а пакетный менеджер Helm помогает создавать единые шаблоны для описания приложений и запускать их.

Инструменты безопасности. Например, Kubesec. Он может обнаруживать избыточные привилегии и разрешения, предоставленные поду, запуск контейнера с Root-полномочиями или опасные монтирования вроде /proc хоста или сокета Docker.

Service Mesh. Например, Istio — это инструмент конфигурации Service Mesh; полноценный фреймворк для подключения, управления и мониторинга микросервисной архитектуры.

Нужен ли вам Kubernetes?

Чтобы самостоятельно развернуть кластер Kubernetes, нужно подготовить оборудование (физические или виртуальные серверы), установить Kubernetes и затем настроить его. Настройка — самое сложное, потому что по умолчанию в Kubernetes нет всех нужных инструментов и надстроек. Для нормальной работы кластера потребуется настроить сеть и аутентификацию, сконфигурировать поды, организовать балансировку нагрузки, мониторинг, логирование и многое другое.

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

Kubernetes не нужен Kubernetes нужен
  • вы только начинаете знакомиться с микросервисной архитектурой;
  • вы только начинаете переводить свои приложения в контейнеры;
  • у вас мало контейнеров, и вам не трудно управлять ими вручную;
  • у вас небольшой проект, и для оркестрации контейнеров подходят более простые системы вроде Docker Swarm.
  • у вас много контейнеров, и сопровождать и поддерживать их жизненный цикл становится трудно;
  • на вашем проекте постоянно ведется разработка, нужно часто выкатывать новые фичи;
  • ваши системы должны поддерживать максимально быстрые обновления или доработки приложений.

Нужно учесть, что Kubernetes — сложная и многоуровневая система и мало кто сможет разобраться, правильно его настроить и поддерживать без предварительного обучения. Поэтому нужно либо искать соответствующего специалиста, либо использовать Managed-решение от облачного провайдера, например Mail.ru Cloud Solutions.

Это позволит не вдаваться в подробности установки кластера и переложить его поддержку на плечи провайдера. А также получить дополнительные преимущества над «обычным» Kubernetes вроде автомасштабирования кластера по клику, автоматического балансировщика нагрузки и простой интеграции с другими облачными сервисами.