Под капотом 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 может развиваться, не привязываясь жёстко к конкретным реализациям контейнерных движков.