Поехали
Mixtile Cluster Box снаружи похож на плотный мини‑сервер. Внутри другая история: четыре независимые Blade 3, PCIe‑коммутатор, отдельная плата управления на OpenWrt и задумка связать ноды IP‑сетью поверх PCIe. Я ставил задачу жёстче обычного «поднять k8s дома»: убрать штатный Debian, собрать отказоустойчивый кластер на Talos, поставить Cozystack и проверить, что из железа реально живёт — NVMe, KVM, GPU и NPU.
Сейчас это работает. Четыре ARM64-узла, из них три control‑plane и один compute, Talos Linux 1.14.2, Kubernetes 1.34.3, Cozystack 1.7.0-alpha.3 в профиле isp-full. Поверх этого LINSTOR с DRBD и ZFS, KubeVirt с аппаратным KVM, а Mali‑G610 и NPU RK3588 видны на всех платах. До этого места путь был длиннее, чем «скачал образ и пошёл пить чай».
Что такое Cluster Box
Cluster Box — компактное шасси на четыре Blade 3. Внутри backplane с коммутатором ASMedia ASM2824, четыре U.2 под платы, четыре M.2 NVMe на PCIe 3.0 x2, четыре SATA 3.0, отдельная плата управления, два вентилятора по 60 мм и один внешний гигабитный Ethernet. Корпус 213 × 190 × 129 мм, питание 19–19,5 В и 4,74 А, то есть по бумаге до примерно 90 Вт на всю коробку.

Каждая Blade 3 садится в U.2, и по этому разъёму сразу идут питание, SATA и PCIe 3.0 x4. Это не гирлянда из плат: у каждой свой канал до ASM2824. Схема и распиновка есть в даташите Cluster Box v1.1. На второй ревизии отладочный SFF-8643 заменили на OCuLink.
На каждой Blade 3 стоит RK3588: четыре Cortex‑A76, четыре Cortex‑A55, Mali‑G610, NPU до 6 TOPS, 32 ГБ LPDDR4, 256 ГБ eMMC, два 2.5GbE, PCIe 3.0 x4 и SATA через U.2, плюс HDMI 2.1 и аппаратный H.264/H.265 до 8K. Подробности по одной плате — в документации Blade 3.
На весь кластер это 32 ядра, 128 ГБ RAM, четыре GPU, теоретически до 24 TOPS NPU и около терабайта eMMC. У меня на каждой плате Samsung 980 PRO на 2 ТБ, то есть 8 ТБ raw NVMe. Через backplane диск сидит на PCIe 3.0 x2, не на полном x4, но для такого ящика этого хватает.

Плата управления на OpenWrt
Формально это не BMC с IPMI и Redfish. По роли ближе именно к нему: плата по очереди включает Blade 3, отдаёт web и SSH, управляет PCIe‑коммутатором, видит ноды через nodectl, раздаёт адреса, выступает Root Complex для MIOP и держит внешний uplink.
Железо контроллера скромное: MediaTek MT7620A, MIPS24KEc на 580 МГц, 256 МБ DDR2 и OpenWrt. В даташите 2023 года ещё фигурировали 16 МБ SPI Flash. На обновлённой плате хранилище уже 16 ГБ, debug ушёл на OCuLink, а заводскую прошивку можно вернуть кнопкой питания. С завода там OpenWrt 23, про замену платы писали в анонсе Mixtile.
Web открывается по IP контроллера. Логин и пароль по умолчанию — mixtile / mixtile. Это первое, что стоит сменить, и наружу этот интерфейс лучше не торчать.
На этой OpenWrt я уже собираю свой management plane. Из браузера можно включить, выключить и перезагрузить каждую Blade 3, посмотреть статус, переключиться на serial‑консоль, прочитать её журнал, крутить вентиляторы и проверить, отвечает ли Talos API на порту 50000. Мягкое выключение идёт коротким нажатием кнопки питания по GPIO: контроллер ждёт, и только если нода не ушла сама, снимает питание.

Это удобно именно потому, что OpenWrt живёт отдельно от Kubernetes. Упал control‑plane — питание, консоль и шанс достать ноду всё равно остаются. Рядом я ещё поднимал экспериментальный агент Teleport, собранный под MIPS: просто проверить, можно ли заходить внутрь коробки без дырки в интернет. Нормальная интеграция — уже из планов, не из текущего результата.

MIOP: сеть поверх PCIe
Самая заманчивая часть коробки — TCP/IP по PCIe. Контроллер на MT7620A работает Root Complex, четыре Blade 3 — endpoints, ASM2824 режет им отдельные x4, а драйвер поднимает обычный сетевой интерфейс. На контроллере он называется pci0. После nodectl list видны четыре устройства Mixtile Blade 3.
Mixtile для новой версии драйвера пишет про 19 Гбит/с в iperf3 и около 15,5 Гбит/с в двустороннем тесте. Это быстрее и гигабитного uplink, и бортовых 2.5GbE. На OpenWrt MIOP у меня уже в прошивке. На самих Blade 3 вендор отдаёт DEB и отдельный boot image под Debian, Ubuntu и свою сборку. Исходников актуального драйвера нет.
Для Talos это стоп. Система неизменяемая, пакеты .deb туда не ставятся, модуль должен совпасть с ABI ядра и уехать system extension. Mixtile обещала сборку под Talos, на момент статьи её ещё нет. Поэтому ноды у меня ходят по обычному 2.5GbE. Когда драйвер появится, туда логично увести DRBD между NVMe, а внешний Ethernet оставить входом в Ingress.
Почему готовый образ Talos не подошёл
Ядро Talos получилось оставить ванильным. Готовый образ на Blade 3 всё равно не встаёт: нужны свой Device Tree, U‑Boot под плату, TF‑A, бинарник тренировки DDR, SBC overlay, правильный offset загрузчика, installer и набор extensions. Сборка лежит в mixtile‑rockchip/mixtile‑talos.
Сейчас это Talos 1.14.2, официальное ядро Linux 6.18.54, U‑Boot 2026.07, TF‑A lts-v2.14.6 и DTB от mainline‑порта Blade 3 из Armbian. Overlay сделан по той же схеме, что siderolabs/sbc-rockchip, но DTB от Turing RK1 подставлять нельзя. Сокет тот же, плата другая.
Сборка идёт через Docker Buildx: сначала overlay уезжает в OCI‑реестр, потом официальный imager собирает installer и raw. На выходе _out/metal-arm64.raw.xz. Перед прошивкой скрипт смотрит сектор 64 и падает, если там пусто. Так уже ловили сборку, которая делала большой файл без загрузчика внутри.
В образ входят официальные extensions: DRBD 9.3.4, ZFS 2.4.4, iSCSI tools, Panfrost/Panthor для Mali‑G610 и Rockchip RKNN с mainline‑модулем rocket. Все они собраны под ABI Talos 1.14.2 и с той же подписью модулей.
Каждую плату я прошивал отдельно через MaskROM. На macOS это rkdeveloptool: DIP 4 в ON, питание, rkdeveloptool ld, временный SPL и запись raw в eMMC. В репозитории тот же путь завёрнут в scripts/flash-blade3-macos.sh. Скрипт проверяет архив и U‑Boot, ждёт ровно одну плату, спрашивает подтверждение и только потом пишет диск. Временный loader нужен лишь для USB. Боевой U‑Boot уже сидит в образе с 64-го сектора.
Команды сборки и прошивки
docker login ghcr.io USERNAME=<registry-namespace> ./build.sh all rkdeveloptool ld rkdeveloptool db rk3588_spl_loader_v1.08.111.bin ./scripts/flash-blade3-macos.sh
Что ломалось по дороге
Первая сборка честно заканчивалась успехом, а U‑Boot в нужном месте не оказывался. Теперь это ловится на секторе 64. Дальше imager видел overlay, но installer не отрабатывал, пока package layout не привели к API Talos 1.14. На macOS отдельно вылез Python 3.14: часть tooling U‑Boot с ним не собралась, пакет пришлось зафиксировать.
Самый злой бытовой баг был с сетью. Два RTL8125B сидят за ASM1182e и не имеют MAC в EEPROM, поэтому r8169 после каждой загрузки рисовал новый адрес. Дома это раздражает. В Kubernetes это ломает DHCP, etcd и адреса LINSTOR. U‑Boot уже умел доставать стабильные адреса из OTP, но в kernel DTB не было ни aliases, ни нормального описания PCIe. Когда добавили root port, свитч, downstream‑порты и aliases, драйвер начал брать постоянный MAC из дерева.
С хранилищем не хватило одного drbd.ko. В machine config пришлось явно поднять drbd_transport_tcp, после этого LINSTOR наконец собрал реплики. GPU и NPU на плате были, в Linux их просто не включили до конца: для Mali добавили Panfrost/Panthor, для NPU — rocket, в DTB открыли три ядра и IOMMU. На всех четырёх платах теперь есть /dev/dri/renderD128 и /dev/accel/accel0.
Кластер и Cozystack
Конфиг Talos собирается через talm. Управление живёт в 192.168.70.201—204, VIP API — 192.168.70.205. Три ноды держат control‑plane, четвёртая — compute, но поды садятся на все четыре. Сеть стандартная для этой установки: pod 10.244.0.0/16, service 10.96.0.0/16, домен cozy.local. Talos и etcd лежат на eMMC, NVMe целиком отдан хранилищу.
Поверх Kubernetes стоит Cozystack 1.7.0-alpha.3, профиль isp-full. Это alpha, и я не называю это промышленной установкой. По факту платформа поднялась: Flux, Kube‑OVN, Cilium, MetalLB, cert‑manager, KubeVirt, CDI, LINSTOR, VictoriaMetrics, Grafana, Velero, Cluster API и набор операторов баз. На контрольной проверке было 4/4 Ready, 111/111 HelmRelease, 240 подов, из них 237 Running и 3 Succeeded, без Pending и Error.
У каждого узла eMMC около 250 ГБ под систему и Samsung 980 PRO на 2 ТБ под данные. Классов два: local с одной копией и WaitForFirstConsumer, replicated на DRBD с autoPlace: 3. Реплика размазывается по трём платам, одну ноду потерять можно без потери тома. Сейчас это едет по Ethernet. Когда появится MIOP под Talos, репликацию логично увести в PCIe.
KVM на RK3588 есть, модуль в ядре Talos встроенный. KubeVirt видит /dev/kvm на всех узлах. Для проверки поднял Ubuntu 24.04.5: два vCPU, 4 ГБ RAM, 20 ГБ на replicated, вход через virtctl. Внутри uname -m дал aarch64, systemd-detect-virt — kvm, а lscpu показал живые A76 и A55, не эмуляцию. После теста машину и том удалил.
GPU и NPU в ядре уже live. Чтобы отдать их подам, нужен device plugin и userspace в контейнере: Mesa/PanVK для Mali, Teflon или другой стек для rocket. RKNN Toolkit2 к этому драйверу не встанет, ему нужен вендорный rknpu.ko.
Схема сети и проверка VM
192.168.70.201 node 1 192.168.70.202 node 2 192.168.70.203 node 3 192.168.70.204 node 4 192.168.70.205 Kubernetes API VIP Pod CIDR: 10.244.0.0/16 Service CIDR: 10.96.0.0/16 Cluster domain: cozy.local
$ uname -m aarch64 $ systemd-detect-virt kvm
Что хочу доделать на BMC
Следующий кусок работы — научить контроллер не только смотреть на ноды, а вести их от питания до входа в кластер. Сейчас Talos ставится через MaskROM и USB. Хочется, чтобы BMC сам заходил в serial, останавливал загрузку в U‑Boot, забирал образ по TFTP, писал eMMC и ждал порт 50000. В интерфейсе это должна быть кнопка и ссылка на образ. TFTP через dnsmasq уже есть, lrzsz лежит как запас на serial, но гонять так гигабайтный образ бессмысленно. Ещё нюанс: Talos отдаёт .raw.xz, а gzwrite в U‑Boot понимает gzip. Значит, контроллер должен сначала распаковать файл на своей flash.
Сейчас BMC умеет только «порт 50 000 отвечает или нет». Дальше в UI нужны версия Talos, hostname, IP, роль, состояние дисков и сети. Hostname уже мелькает в serial, слот можно привязать к ноде без угадывания адресов. Выключать тоже хочу через Talos API, а GPIO оставить запасным вариантом: для etcd и DRBD внезапный обрыв питания — плохая привычка.
Когда появится модуль MIOP под 6.18.54-talos, через backplane можно увести репликацию, bootstrap и recovery. Без исходников или готового модуля под этот ABI сам я его не соберу. Отдельно нужен простой API на питание, статус, консоль и установку образа. Если получится подмножество Redfish, коробку проще будет подцепить к Ironic и Metal3.
Из бытового — журнал кто что выключил, уведомление если нода зависла в U‑Boot, температуры вместо ручных оборотов вентиляторов, HTTPS для web и терминала, своя management‑сеть и подписанные обновления OpenWrt. Конечная картинка простая: коробка сама ставит Talos, включает ноды, сажает их в кластер и поднимает без флешки и SSH.
Что из этого вышло
Ядро Talos форкать не пришлось. Остались официальный Linux 6.18.54, штатные extensions, обычный imager, обычный Kubernetes и почти полный Cozystack. Своими оказались только U‑Boot, Device Tree, TF‑A/RKBin, overlay, installer и способ прошивки.
Главный потолок сейчас — закрытый MIOP. Железо для быстрой внутренней сети есть, а перенести её на новое ядро без исходников нельзя. Даже так четыре Blade 3 уже собираются в маленький ARM64-кластер: control‑plane, хранилище, виртуалки, мониторинг, GPU и NPU. И это всё ещё ящик чуть больше мини‑ПК.
Под нагрузкой у меня выходит примерно 30–40 Вт. Питать можно через USB‑C PD trigger на DC 5.5×2.5 мм, но официальный диапазон 19–19,5 В, а типичный PD даёт 20 В и формально чуть вылезает за спецификацию.
Для кого это
Для тех, кому мало одного мини‑ПК с Docker, а хочется своё маленькое облако на четырёх нодах, которое ещё и уносится в рюкзаке. Подойдёт, если интересно собирать железо и смотреть, как Cozystack встаёт на ARM64. Не подойдёт, если нужен ящик «включил и забыл»: сначала MaskROM, потом VIP, потом уже гордость. Это не замена датацентру. Это домашний ЦОД на столе. Честно работает, иногда честно ломается и ест меньше чайника.


