DIY: создайте собственное облако с помощью Kubernetes (часть 2)
Автор: Andrei Kvapil (Ænix)
Продолжаем нашу серию статей о том, как построить собственное облако, используя только экосистему Kubernetes. В предыдущей статье мы рассказали, как мы готовим базовый дистрибутив Kubernetes на основе Talos Linux и Flux CD. В этой статье мы покажем несколько различных технологий виртуализации в Kubernetes и подготовим всё необходимое для запуска виртуальных машин в Kubernetes — прежде всего хранилище и сеть.
Мы поговорим о таких технологиях, как KubeVirt, LINSTOR и Kube-OVN.
Но сначала давайте объясним, для чего нужны виртуальные машины и почему нельзя просто использовать docker-контейнеры для построения облака? Причина в том, что контейнеры не обеспечивают достаточного уровня изоляции. Хотя ситуация улучшается год от года, мы часто сталкиваемся с уязвимостями, которые позволяют выйти из песочницы контейнера и повысить привилегии в системе.
С другой стороны, Kubernetes изначально не проектировался как мультиарендная система, а значит, базовый шаблон использования предполагает создание отдельного кластера Kubernetes для каждого независимого проекта и команды разработки.
Виртуальные машины — это основное средство изоляции арендаторов друг от друга в облачной среде. В виртуальных машинах пользователи могут выполнять код и программы с административными привилегиями, но это не влияет на других арендаторов или на саму среду. Другими словами, виртуальные машины позволяют достичь жёсткой изоляции при мультиарендности и работают в средах, где арендаторы не доверяют друг другу.
Технологии виртуализации в Kubernetes
Существует несколько различных технологий, которые привносят виртуализацию в мир Kubernetes: наиболее популярные из них — KubeVirt и Kata Containers. Но следует знать, что работают они по-разному.
Kata Containers реализует CRI (Container Runtime Interface) и обеспечивает дополнительный уровень изоляции для стандартных контейнеров, запуская их в виртуальных машинах. Но работают они в рамках одного кластера Kubernetes.
Диаграмма, показывающая, как обеспечивается изоляция контейнеров за счёт их запуска в виртуальных машинах с помощью Kata Containers
KubeVirt позволяет запускать традиционные виртуальные машины через Kubernetes API. Виртуальные машины KubeVirt выполняются как обычные linux-процессы в контейнерах. Другими словами, в KubeVirt контейнер используется как песочница для запуска процессов виртуальной машины (QEMU). Это хорошо видно на рисунке ниже, если посмотреть, как в KubeVirt реализована живая миграция виртуальных машин. Когда требуется миграция, виртуальная машина перемещается из одного контейнера в другой.
Диаграмма, показывающая живую миграцию виртуальной машины из одного контейнера в другой в KubeVirt
Существует также альтернативный проект — Virtink, который реализует лёгкую виртуализацию с помощью Cloud-Hypervisor и изначально ориентирован на запуск виртуальных кластеров Kubernetes с использованием Cluster API.
Учитывая наши цели, мы решили использовать KubeVirt как самый популярный проект в этой области. Кроме того, у нас есть обширная экспертиза, и мы уже внесли большой вклад в KubeVirt.
KubeVirt легко установить, и он позволяет запускать виртуальные машины «из коробки» с помощью возможности containerDisk — это позволяет хранить и распространять образы ВМ напрямую в виде OCI-образов из реестра образов контейнеров. Виртуальные машины с containerDisk хорошо подходят для создания рабочих узлов Kubernetes и других ВМ, которым не требуется сохранение состояния.
Для управления постоянными данными KubeVirt предлагает отдельный инструмент — Containerized Data Importer (CDI). Он позволяет клонировать PVC и наполнять их данными из базовых образов. CDI необходим, если вы хотите автоматически создавать постоянные тома для ваших виртуальных машин, и он также требуется для KubeVirt CSI Driver, который используется для обработки запросов постоянных томов (persistent volume claims) из кластеров Kubernetes арендаторов.
Но сначала вам нужно решить, где и как вы будете хранить эти данные.
Хранилище для ВМ Kubernetes
С появлением CSI (Container Storage Interface) стал доступен широкий спектр технологий, которые интегрируются с Kubernetes. Фактически KubeVirt полностью использует интерфейс CSI, тесно связывая выбор хранилища для виртуализации с выбором хранилища для самого Kubernetes. Однако есть нюансы, которые нужно учитывать. В отличие от контейнеров, которые обычно используют стандартную файловую систему, для виртуальных машин более эффективны блочные устройства.
Хотя интерфейс CSI в Kubernetes позволяет запрашивать оба типа томов — файловые системы и блочные устройства, — важно убедиться, что ваш бэкенд хранилища это поддерживает.
Использование блочных устройств для виртуальных машин избавляет от необходимости в дополнительном слое абстракции, таком как файловая система, что делает их более производительными и в большинстве случаев позволяет использовать режим ReadWriteMany. Этот режим обеспечивает одновременный доступ к тому с нескольких узлов, что является критически важной возможностью для живой миграции виртуальных машин в KubeVirt.
Система хранения может быть внешней или внутренней (в случае гиперконвергентной инфраструктуры). Использование внешнего хранилища во многих случаях делает всю систему более стабильной, так как ваши данные хранятся отдельно от вычислительных узлов.
Диаграмма, показывающая взаимодействие внешнего хранилища данных с вычислительными узлами
Решения с внешним хранилищем часто популярны в корпоративных системах, поскольку такое хранилище нередко предоставляется внешним вендором, который берёт на себя его эксплуатацию. Интеграция с Kubernetes включает лишь небольшой компонент, устанавливаемый в кластере, — драйвер CSI. Этот драйвер отвечает за создание томов в этом хранилище и их подключение к подам, запускаемым Kubernetes. Однако такие решения для хранения также могут быть реализованы с использованием исключительно open-source технологий. Одно из популярных решений — TrueNAS на базе драйвера democratic-csi.
Диаграмма, показывающая локальное хранилище данных, работающее на вычислительных узлах
С другой стороны, гиперконвергентные системы часто реализуются с использованием локального хранилища (когда репликация не нужна) и программно-определяемых хранилищ, нередко устанавливаемых непосредственно в Kubernetes, таких как Rook/Ceph, OpenEBS, Longhorn, LINSTOR и другие.
Диаграмма, показывающая кластерное хранилище данных, работающее на вычислительных узлах
У гиперконвергентной системы есть свои преимущества. Например, локальность данных: когда ваши данные хранятся локально, доступ к ним быстрее. Но есть и недостатки, так как такой системой обычно сложнее управлять и обслуживать.
В Ænix мы хотели предоставить готовое к использованию решение, которое можно было бы применять без необходимости покупать и настраивать дополнительное внешнее хранилище и которое было бы оптимальным с точки зрения скорости и использования ресурсов. Таким решением стал LINSTOR. Проверенные временем и популярные в индустрии технологии, такие как LVM и ZFS в качестве бэкенда, дают уверенность в том, что данные хранятся надёжно. Репликация на основе DRBD невероятно быстра и потребляет небольшое количество вычислительных ресурсов.
Для установки LINSTOR в Kubernetes есть проект Piraeus, который уже предоставляет готовое блочное хранилище для использования с KubeVirt.
Сеть для ВМ Kubernetes
Несмотря на наличие похожего интерфейса — CNI, сетевая архитектура в Kubernetes на самом деле сложнее и обычно состоит из множества независимых компонентов, которые напрямую не связаны друг с другом. Фактически сеть Kubernetes можно разделить на четыре уровня, которые описаны ниже.
Сеть узлов (сеть дата-центра)
Сеть, через которую узлы соединяются друг с другом. Обычно эта сеть не управляется Kubernetes, но она важна, потому что без неё ничего не работало бы. На практике в bare metal-инфраструктуре обычно есть более одной такой сети, например: одна для связи между узлами, вторая для репликации хранилища, третья для внешнего доступа и т. д.
Диаграмма, показывающая роль сети узлов (сети дата-центра) в схеме сети Kubernetes
Настройка физического сетевого взаимодействия между узлами выходит за рамки этой статьи, так как в большинстве ситуаций Kubernetes использует уже существующую сетевую инфраструктуру.
Сеть подов
Это сеть, предоставляемая вашим плагином CNI. Задача плагина CNI — обеспечить прозрачную связность между всеми контейнерами и узлами в кластере. Большинство плагинов CNI реализуют плоскую сеть, из которой на каждом узле выделяются отдельные блоки IP-адресов.
Диаграмма, показывающая роль сети подов (плагина CNI) в схеме сети Kubernetes
На практике в вашем кластере может быть несколько плагинов CNI, управляемых Multus. Этот подход часто применяется в решениях виртуализации на основе KubeVirt — Rancher и OpenShift. Основной плагин CNI используется для интеграции с сервисами Kubernetes, тогда как дополнительные плагины CNI используются для реализации частных сетей (VPC) и интеграции с физическими сетями вашего дата-центра.
Стандартные плагины CNI можно использовать для подключения мостов или физических интерфейсов. Кроме того, есть специализированные плагины, такие как macvtap-cni, которые предназначены для обеспечения большей производительности.
Ещё один аспект, который стоит учитывать при запуске виртуальных машин в Kubernetes, — это необходимость в IPAM (IP Address Management), особенно для вторичных интерфейсов, предоставляемых Multus. Обычно этим управляет DHCP-сервер, работающий в вашей инфраструктуре. Кроме того, выделением MAC-адресов для виртуальных машин может управлять Kubemacpool.
Хотя в нашей платформе мы решили пойти другим путём и полностью положиться на Kube-OVN. Этот плагин CNI основан на OVN (Open Virtual Network), который изначально разрабатывался для OpenStack, и предоставляет полноценное сетевое решение для виртуальных машин в Kubernetes, имеет Custom Resources для управления IP- и MAC-адресами, поддерживает живую миграцию с сохранением IP-адресов между узлами и позволяет создавать VPC для физического разделения сетей между арендаторами.
В Kube-OVN вы можете назначать отдельные подсети целому пространству имён или подключать их как дополнительные сетевые интерфейсы с помощью Multus.
Сеть сервисов
Помимо плагина CNI, в Kubernetes есть также сеть сервисов, которая нужна прежде всего для обнаружения сервисов (service discovery). В отличие от традиционных виртуальных машин, Kubernetes изначально спроектирован для запуска подов со случайным адресом. А сеть сервисов предоставляет удобную абстракцию (стабильные IP-адреса и DNS-имена), которая всегда будет направлять трафик к нужному поду. Тот же подход часто используется и с виртуальными машинами в облаках, несмотря на то что их IP-адреса обычно статичны.
Диаграмма, показывающая роль сети сервисов (плагина сети сервисов) в схеме сети Kubernetes
Реализацией сети сервисов в Kubernetes занимается плагин сети сервисов. Стандартная реализация называется kube-proxy и используется в большинстве кластеров. Но в наши дни эта функциональность может предоставляться как часть плагина CNI. Наиболее продвинутую реализацию предлагает проект Cilium, который можно запускать в режиме замены kube-proxy.
Cilium основан на технологии eBPF, которая позволяет эффективно разгружать сетевой стек Linux, тем самым повышая производительность и безопасность по сравнению с традиционными методами на основе iptables.
На практике Cilium и Kube-OVN можно легко интегрировать, чтобы получить единое решение, обеспечивающее бесшовную мультиарендную сеть для виртуальных машин, а также продвинутые сетевые политики и объединённую функциональность сети сервисов.
Балансировщик внешнего трафика
На этом этапе у вас уже есть всё необходимое для запуска виртуальных машин в Kubernetes. Но на самом деле есть ещё кое-что. Вам всё ещё нужно получать доступ к вашим сервисам извне кластера, и внешний балансировщик нагрузки поможет вам организовать это.
Для bare metal-кластеров Kubernetes доступно несколько балансировщиков нагрузки: MetalLB, kube-vip, LoxiLB, а также Cilium и Kube-OVN предоставляют встроенную реализацию.
Роль внешнего балансировщика нагрузки — предоставить стабильный адрес, доступный извне, и направлять внешний трафик в сеть сервисов. Плагин сети сервисов, как обычно, направит его к вашим подам и виртуальным машинам.
Диаграмма, показывающая роль внешнего балансировщика нагрузки в схеме сети Kubernetes
В большинстве случаев настройка балансировщика нагрузки на bare metal достигается за счёт создания плавающего IP-адреса на узлах внутри кластера и анонсирования его вовне с помощью протоколов ARP/NDP или BGP.
Изучив различные варианты, мы решили, что MetalLB — самое простое и надёжное решение, хотя мы не настаиваем строго на использовании только его.
Ещё одно преимущество в том, что в режиме L2 спикеры MetalLB непрерывно проверяют состояние соседей, выполняя проверки живучести с помощью протокола memberlist. Это обеспечивает отказоустойчивость, которая работает независимо от control-plane Kubernetes.
Заключение
На этом мы завершаем наш обзор виртуализации, хранилища и сети в Kubernetes. Упомянутые здесь технологии доступны и уже преднастроены на платформе Cozystack, где вы можете попробовать их без ограничений.
В следующей статье я подробно расскажу, как поверх всего этого можно реализовать создание полностью функциональных кластеров Kubernetes одним нажатием кнопки.
Впервые опубликовано на https://kubernetes.io 5 апреля 2024 года.