Под капотом Kubernetes: Container Runtime Interface простыми словами

Под капотом Kubernetes: Container Runtime Interface простыми словами


CRI (Container Runtime Interface) — это интерфейс плагинов, который позволяет компоненту kubelet использовать различные среды выполнения контейнеров (container runtimes), не требуя повторной компиляции компонентов кластера.

Перед тем, как перейти к деталям, напомним базовые концепции:

  • kubelet — демон (daemon), который запускается на каждом узле Kubernetes. Он реализует API подов и узлов, которые управляют большей частью активности в Kubernetes. Kubelet обеспечивает бесперебойную координацию и эффективное распределение ресурсов, поддерживая эффективное взаимодействие между управляющей плоскостью (control plane) и узлами, постоянно мониторит состояние контейнеров и осуществляет автоматическое восстановление, повышая отказоустойчивость кластера.
  • Поды (Pods) — наименьшая единица работы в Kubernetes. Каждый под запускает один или несколько контейнеров, которые вместе образуют единую функциональную единицу.
  • Спецификации подов (Pod specs) — kubelet читает спецификации подов, которые обычно описаны в YAML-файлах. Спецификация пода определяет, какие образы (images) контейнеров должен запускать под. При этом спецификация не описывает детали того, как именно должны запускаться контейнеры — для этого Kubernetes требуется среда выполнения контейнеров (container runtime).
  • Среда выполнения контейнеров (Container runtime) — на каждом узле Kubernetes должна быть установлена среда выполнения контейнеров. Когда kubelet обрабатывает спецификации подов, он обращается к этой среде выполнения для создания контейнеров. Затем среда выполнения берёт на себя ответственность за управление жизненным циклом контейнеров и взаимодействует с ядром операционной системы.

Зачем был создан CRI?

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

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

Решение стало очевидным: необходим был стандартизированный интерфейс, через который Kubernetes — точнее, компонент kubelet — смог бы взаимодействовать с любой средой выполнения контейнеров. Это позволило бы пользователям легко переключаться между различными контейнерными движками, комбинировать их, а также способствовало бы развитию новых решений для выполнения контейнеров.

В 2016 году Kubernetes представил интерфейс Container Runtime Interface (CRI). С тех пор kubelet больше не взаимодействует напрямую ни с одной конкретной средой выполнения контейнеров. Вместо этого он общается через промежуточный слой («shim»), аналогичный программному драйверу, реализующему специфику конкретной среды выполнения контейнеров.

OCI и runc

Инициатива OCI (Open Container Initiative) предоставляет спецификации, которые должны быть реализованы средами выполнения контейнеров. Две важные спецификации OCI:

  • runc — базовый движок (seed runtime engine) для выполнения контейнеров. Большинство современных контейнерных сред выполнения используют runc, расширяя его дополнительной функциональностью.
  • OCI Image Specification — OCI взяла за основу оригинальный формат образов Docker. Этот формат теперь является стандартом для спецификации образов OCI. Большинство инструментов сборки с открытым исходным кодом поддерживают именно этот формат, включая такие инструменты, как BuildKit, Podman и Buildah. Среды выполнения контейнеров, реализующие спецификацию OCI, могут распаковывать образы OCI и запускать их содержимое в виде контейнеров.

Kubelet и CRI

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

Интерфейс CRI определён с использованием протокола Protocol Buffers и фреймворка gRPC. Этот протокол регламентирует взаимодействие между kubelet и средой выполнения контейнеров. В этом взаимодействии kubelet является клиентом, а CRI shim — сервером. Протокол CRI включает в себя конечные точки (endpoints) для управления контейнерами, подами и образами контейнеров.

Реализация CRI

Интерфейс CRI реализован с использованием фреймворка gRPC. При запуске kubelet устанавливает gRPC-соединение с реализацией CRI (например, с сокетами containerd.sock или cri-o.sock). Затем kubelet обнаруживает и проверяет совместимость среды выполнения контейнеров, вызывая определённые API-методы, такие как Version().

Ниже перечислены ключевые методы gRPC, которые kubelet вызывает через CRI:

RuntimeService:

  • Version: возвращает информацию о версии среды выполнения.
  • RunPodSandbox: создаёт изолированную среду (sandbox) для пода.
  • StopPodSandbox: останавливает sandbox пода.
  • RemovePodSandbox: удаляет sandbox пода.
  • CreateContainer: создаёт новый контейнер внутри пода.
  • StartContainer: запускает созданный контейнер.
  • StopContainer: останавливает работающий контейнер.
  • RemoveContainer: удаляет контейнер.

ImageService:

  • PullImage: загружает образ контейнера.
  • ListImages: перечисляет доступные образы.
  • ImageStatus: предоставляет детали о конкретном образе.
  • RemoveImage: удаляет образ с узла.

Диаграмма взаимодействия:

+-------------------+        +-----------------------+
|    Kubernetes     |        |      Container        |
|    Kubelet        |        |       Runtime         |
|                   |        |                       |
| +-------------+   |        |  +-----------------+  |
| | Pod Manager |   |  gRPC  |  | CRI Plugin      |  |
| |             |<---------->|  | (e.g., CRI-O)   |  |
| +-------------+   |        |  +-----------------+  |
+-------------------+        +-----------------------+
                                    |
                                    v
                            +----------------+
                            | Low-level Runtime|
                            |   (e.g., runc)   |
                            +----------------+

Дополнительные инструменты

+---------------------------------------------+
|                                             |
|                Kubelet                      |  <<- Communicates with CRI
|                                             |       (kubectl get pods)
+-----------------------------+---------------+
                              |
                              v
+-----------------------------+---------------+
|         CRI (Container Runtime Interface)   |  <<- CRI Plugin Layer
|                                             |       (crictl pods)
+-----------------------------+---------------+
                              |
                              v
+-----------------------------+---------------+
|              containerd                      |  <<- Container runtime service
|                                             |       (ctr containers ls)
+-----------------------------+---------------+
                              |
                              v
+-----------------------------+---------------+
|          containerd-shim                     |  <<- Manages container lifecycles
|                                             |       (Manages between containerd and OCI)
+-----------------------------+---------------+
                              |
                              v
+-----------------------------+---------------+
|                runc                          |  <<- Low-level OCI runtime
|                                             |       (runc ls)
+---------------------------------------------+

Вышеприведённая диаграмма, вероятно, уже ясна, но давайте рассмотрим упомянутые инструменты более подробно. Поток взаимодействий выглядит следующим образом:

  • Команда kubectl get pods запрашивает Kubernetes API-сервер для получения статуса подов, назначенных на узел.
  • crictl — инструмент командной строки (CLI) для взаимодействия с CRI-совместимыми средами выполнения. Он запрашивает непосредственно контейнерный движок (например, containerd), чтобы получить статус подов.
  • ctr — CLI-инструмент для прямого взаимодействия с containerd. Команда ctr containers ls показывает все контейнеры, управляемые containerd.
  • containerd-shim находится между containerd и runc. Он поддерживает работоспособность контейнеров даже при сбоях или перезапуске самого containerd.
  • runc — низкоуровневый runtime, напрямую взаимодействующий с операционной системой и создающий контейнеры на основе спецификации OCI. Команда runc ls показывает контейнеры, управляемые runc.
Обратите внимание: Приведённая выше схема не показывает реальный поток запросов, происходящий при выполнении команды kubectl get pods. На самом деле эта команда использует серию внутренних компонентов Kubernetes и косвенно взаимодействует со средой выполнения контейнеров. Однако эта схема показывает различные инструменты, которые можно использовать непосредственно на узле для проверки состояния контейнеров.

Итог:

  • CRI — это API, который kubelet использует для управления средами выполнения контейнеров. Он абстрагирует детали реализации контейнерного движка, позволяя Kubernetes поддерживать множество контейнерных движков (например, containerd, CRI-O) без необходимости встраивать специфическую логику каждого runtime в сам kubelet.
  • Благодаря этой абстракции Kubernetes может развиваться, не привязываясь жёстко к конкретным реализациям контейнерных движков.


Report Page