Oxide выпустила интеграции с Kubernetes: как их сформировали клиенты

Oxide, инфраструктурная платформа: клиенты через её API создают инстансы на стойках (racks) Oxide, объединяют их в собственные VPC и получают адреса из внешних IP-пулов платформы. С конца 2024 года клиенты и потенциальные клиенты хотели запускать на этой инфраструктуре ещё и Kubernetes, но готовых поддерживаемых интеграций для этого у Oxide не было. Автор поста, первый в истории Oxide инженер по клиентским решениям (в оригинале должность называется Solutions Software Engineer); его задачей стало упростить развёртывание и эксплуатацию Kubernetes на Oxide. В первую рабочую неделю ему дали два материала для старта: pull request с драйвером узлов для Rancher, который прислал один из клиентов, и черновик внутреннего документа Oxide RFD 493 «Initial Kubernetes Integrations». Дальше работа выросла в командную и с самого начала шла не от абстрактного дизайна, а вслед за проблемами, которые клиенты реально встречали на пути от создания кластера до эксплуатации нагрузок в нём.
Ни один способ создания кластеров не подошёл сразу всем клиентам, поэтому в итоге Oxide выпустила три отдельные интеграции. Первая, драйвер узлов для Rancher: исполняемый плагин, который учит Rancher создавать и удалять виртуальные машины на конкретной платформе, переводя операции Rancher в запросы к API Oxide, и позволяет разворачивать Oxide-инстансы как узлы Rancher-кластеров. Автор до этого не работал ни с Rancher, ни с драйверами узлов и разбирался в вопросе прямо по ходу ревью присланного pull request. Итог, первая интеграция Oxide с Kubernetes, которую клиент к тому моменту уже успешно использовал в проде.
Вторая интеграция, провайдер инфраструктуры для Omni, продукта компании Sidero Labs, который создаёт и регистрирует в Omni инстансы с Talos Linux. За семь недель до совместного мероприятия Oxide+Sidero на конференции KubeCon North America 2025 команда договорилась с Sidero Labs о партнёрстве и построила такой провайдер. Работа вскрыла несколько проблем в самих Omni и Talos Linux, автор завёл issue siderolabs/omni#1633, и команда Sidero Labs охотно включилась в работу над ними; в тексте это прямо связывают с ценностью партнёрства, которую Oxide фиксирует в собственном документе RFD 68 «Partnership as Shared Values». Самая заметная из находок, issue siderolabs/talos#11948: Oxide хранит конфигурацию cloud-init (user-data) на диске с файловой системой FAT12, а не ISO 9660, но проверка Talos пытается прочитать с диска NoCloud только суперблок ISO 9660 и, если это не получается, не пробует другие форматы вроде VFAT или MS-DOS, то есть Talos вообще не читал конфигурацию Oxide, нужную для подключения к Omni. Настоящий фикс к KubeCon выйти не успевал, поэтому нашли временный обход: «раздувать» user-data комментариями до размера, при котором диск начинает определяться как ISO 9660. К началу KubeCon провайдер уже показали на мероприятии Oxide+Sidero, теперь клиенты Omni и Talos Linux могут разворачивать Oxide-инстансы как узлы Omni-кластеров.
Третья интеграция, провайдер для Cluster API (CAPI), декларативного API Kubernetes, через который операторы создают, масштабируют, обновляют и удаляют кластеры пользовательскими ресурсами Kubernetes без стороннего слоя управления вроде Rancher или Omni; провайдер инфраструктуры берёт на себя платформенно-специфичную часть, создание и удаление виртуальных машин. Эту интеграцию задумали ещё в черновике RFD 493, но на старте отложили: создание провайдера требует серьёзных вложений, а клиентского спроса и мощностей команды под них тогда не хватало. Позже изменилось и то, и другое, клиенты начали явно просить провайдер Cluster API, а команда выросла. Работу довели до релиза коллеги автора, Джош и Брэндон, выпустив Cluster API Provider Oxide (CAPOx), Kubernetes-нативный способ создавать кластеры на Oxide. Через CAPOx команда заодно обкатала на себе весь путь создания кластера целиком: Kubernetes Image Builder с плагином Oxide для Packer собирает готовые для CAPI образы Oxide, а кластеры, созданные через CAPOx, во время работы используют отдельно устанавливаемый cloud controller manager Oxide.
Интеграции для создания кластеров создают и удаляют Oxide-инстансы, но не сверяют их с объектами Node в Kubernetes: без этой сверки кластер не может надёжно отличить временно недоступный узел от узла, чей Oxide-инстанс на самом деле уже удалён. Для этого нужен компонент, который работает в каждом кластере, обращается к API Oxide и непрерывно сверяет инфраструктуру Oxide с состоянием Kubernetes. В терминологии Kubernetes это cloud controller manager (CCM), стандартная точка расширения, которая подключает инфраструктуру к Kubernetes без специального кода под конкретного провайдера внутри самого Kubernetes. Oxide построила свой CCM: его контроллер узлов держит объекты Node синхронизированными с реальными Oxide-инстансами, записывает их идентификаторы и сетевые адреса и сообщает, работает ли инстанс, выключен он или уже не существует; по этим данным Kubernetes инициализирует узлы и безопасно удаляет их, когда связанный с ними инстанс Oxide исчезает. Сам CCM не создаёт инстансы и не разворачивает кластеры, это по-прежнему задача драйвера для Rancher, провайдера для Omni и CAPOx; CCM, общий для них слой времени выполнения. Заодно CCM дал Oxide постоянную точку расширения внутри каждого кластера: по мере развития платформы туда можно добавлять новые контроллеры, а не переделывать под изменения каждую интеграцию отдельно.
От Kubernetes, интегрированного с облаком, клиенты ждут поддержки сервисов типа LoadBalancer: когда пользователь создаёт такой Service, Kubernetes просит сервис-контроллер облачного провайдера развернуть нужную инфраструктуру и опубликовать её адрес в статусе Service. Проблема в том, что своего балансировщика нагрузки у Oxide на тот момент не было. Зато у платформы были floating IP, адреса из внешних пулов IP стойки, которые можно подключать к инстансам и отключать от них, делая инстанс доступным снаружи его VPC. Один floating IP решает задачу: он доставляет трафик на конкретный узел Kubernetes, а дальше плоскость данных (dataplane) Service внутри кластера сама распределяет этот трафик по нужным подам. Сложность в том, что floating IP у Oxide не виден гостевой системе: адрес назначения входящего трафика Oxide подменяет на внутренний IP инстанса ещё до того, как трафик доходит до инстанса, а на самом инстансе нет сетевого интерфейса с этим floating IP вообще. Из-за этого плоскости данных Service приходится считать фронтом Service именно внутренний IP узла, это и есть тот адрес, который реально приходит на пакетах внутрь гостевой системы. Поэтому сервис-контроллер Oxide публикует в status.loadBalancer.ingress сразу две записи: подключённый floating IP с пометкой режима Proxy и внутренний IP узла с пометкой режима VIP. В посте это показано на примере: запрос клиента приходит на floating IP 45.154.216.233 на порт 80, сеть Oxide подменяет адрес на внутренний 172.30.0.5 на тот же порт, пакет доходит до узла Kubernetes, плоскость данных Service выбирает эндпоинт Service, и трафик в итоге попадает в под, на его целевом порту.
Ключевые факты
- С конца 2024 года Oxide, отвечая на запросы клиентов, довела до релиза три способа развернуть Kubernetes на своей инфраструктуре: драйвер узлов для Rancher, провайдер инфраструктуры для Omni от Sidero Labs (кластеры на Talos Linux) и провайдер Cluster API, CAPOx.
- Провайдер для Omni собрали за семь недель к совместному мероприятию Oxide+Sidero на KubeCon North America 2025, и по ходу нашли в Talos Linux баг (issue siderolabs/talos#11948): диск с конфигурацией Oxide использует файловую систему FAT12, а проверка Talos ищет только ISO 9660 и не пробует другие форматы при неудаче, из-за чего Talos не читал настройки для подключения к Omni; временный обход, «раздувать» файл конфигурации комментариями до размера, при котором он определяется как ISO 9660.
- Отдельный компонент cloud controller manager (CCM) сверяет объекты Kubernetes Node с реальными Oxide-инстансами, без него кластер не может отличить временно недоступный узел от узла, чей инстанс на самом деле удалён.
- Поддержку сервисов типа LoadBalancer сделали поверх floating IP Oxide, потому что своего балансировщика у платформы ещё нет: в статусе такого сервиса Oxide публикует сразу два адреса, floating IP в режиме Proxy и внутренний IP узла в режиме VIP, поскольку именно на внутренний адрес узла Oxide незаметно для гостевой системы транслирует входящий трафик.
- Cluster API-провайдер CAPOx выпустили коллеги автора, Джош и Брэндон, когда вырос клиентский спрос и увеличилась команда; он использует тот же плагин для Packer при сборке образов и тот же CCM во время работы кластера, что и остальные интеграции.
Почему это важно
Пост, подробный отчёт о том, как инфраструктурная компания шаг за шагом построила поддержку Kubernetes на своей платформе, идя не от абстрактного дизайна, а от конкретных проблем клиентов на всём пути, от создания кластера до эксплуатации нагрузок. На практике показано, как реализуются стандартные точки расширения Kubernetes, драйвер узлов, инфраструктурный провайдер, провайдер Cluster API, cloud controller manager, сервис-контроллер, на инфраструктуре с собственными примитивами (стойками, инстансами, VPC, floating IP), а не на типовой инфраструктуре крупного облачного провайдера. Отдельно показательно, что перед плотным сроком к KubeCon два независимых поставщика инфраструктуры, Oxide и Sidero Labs, публично завели баг-репорт и совместно довели интеграцию до показа на конференции.
Кому это важно
В первую очередь, компаниям, которые уже держат или рассматривают инфраструктуру Oxide и хотят запускать на ней Kubernetes: у них есть три готовых пути (через Rancher, через Omni на Talos Linux, через Cluster API) под разные существующие процессы. Во вторую, инженерам, которые сами пишут cloud controller manager или инфраструктурный провайдер для Kubernetes под нестандартную инфраструктуру: пост разбирает конкретные решения (например, две записи в status.loadBalancer.ingress вместо одной для LoadBalancer-сервисов) для проблем, с которыми они, скорее всего, тоже столкнутся. Полезно и командам, которые работают с Talos Linux и Omni независимо от Oxide, из-за описанного бага с определением файловой системы на загрузочном диске cloud-init.
Как это применить
Под три существующих у клиента процесса, три готовых пути: если инфраструктурой уже управляют через Rancher, ставится драйвер узлов Oxide для Rancher, и Oxide-инстансы становятся узлами Rancher-кластера; если используют Omni и Talos Linux от Sidero Labs, подключается провайдер инфраструктуры Oxide для Omni; если нужен Kubernetes-нативный способ без стороннего слоя управления, есть провайдер Cluster API, CAPOx, который дополнительно опирается на плагин Oxide для Packer при сборке образов. Cloud controller manager ставится отдельно и нужен в любом из трёх сценариев, без него объекты Node не синхронизируются с реальными инстансами. Для сервисов типа LoadBalancer отдельная настройка не нужна: Oxide сама публикует floating IP и внутренний IP узла, но важно учитывать, что реальный трафик приходит именно на внутренний адрес узла, это стоит держать в уме при настройке сетевых политик или мониторинга.
Можно ли доверять
Материал, блог самой Oxide от первого лица её сотрудника, Мэтью Санабрии (Matthew Sanabria), Solutions Software Engineer. Это одновременно первоисточник и корпоративный рассказ о себе. В пользу доверия, высокая техническая конкретность: названы настоящие issue в открытых репозиториях (siderolabs/omni#1633, siderolabs/talos#11948), точный механизм бага на уровне файловых систем (FAT12 против ISO 9660), реальный фрагмент YAML-статуса сервиса. Из пробелов, не указаны ни точные даты (кроме «конец 2024», «семь недель», «пара месяцев до KubeCon»), ни версии Kubernetes, Talos Linux, Rancher или Cluster API, ни число клиентов и кластеров, использующих каждую интеграцию, ни то, вышел ли к моменту публикации сам фикс бага в Talos. Имя клиента, приславшего исходный pull request, тоже не раскрыто.
Риски и подводные камни
Обход бага Talos, раздувание файла cloud-init комментариями, чтобы диск читался как ISO 9660, именно обход, а не исправление; в тексте не сказано, вышел ли и когда официальный фикс в Talos, так что зависимость от такого хрупкого приёма могла сохраняться и после публикации поста. Схема LoadBalancer через floating IP самодельная и завязана на особенности сети Oxide: адрес назначения трафика подменяется незаметно для гостевой системы, из-за чего в статусе сервиса появляются два разных IP вместо одного, команды, которые настраивают у себя мониторинг, правила межсетевого экрана или логи по адресу из статуса LoadBalancer, должны явно учитывать оба режима, Proxy и VIP, иначе рискуют потерять часть трафика из виду. Заявленная во введении тема хранения данных для нагрузок с состоянием раскрыта отдельным разделом (How do I use Oxide storage in Kubernetes?), про PersistentVolumeClaim, hot-plug дисков и CSI-плагин Oxide, который на момент публикации ещё в активной разработке.