Архив рубрики ~Лента новостей~

Чему меня научили десятки AI-агентов: как я в качестве эксперимента написал хранилище Blockstor

Чему меня научили десятки AI-агентов: как я в качестве эксперимента написал хранилище Blockstor
Чему меня научили десятки AI-агентов: как я в качестве эксперимента написал хранилище Blockstor

Пару месяцев назад я решил провести эксперимент и сделать с нуля clean-room имплементацию LINSTOR, используя его референсы и публичные API-типы. Изначально эта идея зародилась как пятничная шутка: я просто хотел уделить этой задаче минимум времени, запустить ее на фоне и посмотреть, к чему это приведёт. Целью было просто проверить, насколько современные нейросети способны к автономной работе без участия человека. 

Спойлер: полностью автономной работы не получилось, и мне пришлось достаточно сильно повозиться над проектом. Однако в итоге процесс меня затянул с головой и конечный результат превзошёл все мои ожидания.

Ребята в комьюнити регулярно задавали вопросы, как происходил этот процесс. И действительно, во время разработки я получил огромный опыт по эффективному управлению нейросетями, а также открыл для себя кучу работающих методов и паттернов, которые значительно повысили мою продуктивность и в повседневной работе.

Что такое Blockstor

Я назвал свой проект Blockstor, он представляет из себя оркестратор блочных устройств. Грубо говоря — это такая система, в которой вы можете запросить реплицируемый том нужного размера, и такой том будет создан на нескольких нодах в ZFS или LVM, настроен на репликацию с помощью DRBD — это такой умный аналог RAID 1, работающий по сети. Система поддерживает снапшоты, реаллокацию реплик, ресайз, автоматический фейловер и многое другое.

Blockstor — это не хранилище, спроектированное и написанное с чистого листа. За основу эксперимента я взял LINSTOR — зрелую систему управления распределёнными блочными устройствами, которую уже много лет сам использую в продакшене.

LINSTOR был для меня практически идеальным хранилищем, так как у него Kubernetes-подобные API-типы. Однако его бэкенд-логика организована весьма своеобразно. Основная проблема, на мой взгляд, — это request-based модель, которая на большинство запросов в API в реальном времени идёт на ноды и опрашивает текущее состояние, чтобы сформировать ответ. По моему мнению, это недопустимый паттерн для распределённых систем, так как он плохо показывает себя на масштабах. Кроме того, отсутствие reconciliation loop делает автоматическое восстановление системы затруднительным. Я не берусь критиковать решения разработчиков, уверен, у них были причины сделать всё именно таким образом. Но в силу приверженности паттернам Kubernetes, я задал себе вопрос, а как бы я, опытный архитектор и разработчик, написал подобную систему в Kubernetes-нативной логике.

Вообще, кейсов по переписыванию с одного языка на другой — достаточно много. Например, таким образом появился проект rusternetes, в рамках которого Kubernetes переписывают с Go на Rust. Но как такое возможно? Ведь Kubernetes обладает огромной кодовой базой, в которую вложено огромное количество человеко-часов. Как можно убедиться в том, что получившийся в итоге «нейрослоп» работает корректно? И можно ли на него вообще полагаться и использовать в продакшене?

TDD

Ответ на этот вопрос кроется в открытой разработке самого Kubernetes. У свободных проектов, которые развиваются сообществом, существует набор сложившихся практик — и участники стараются их соблюдать. 

В такие проекты постоянно контрибьютят самые разные люди с разным уровнем навыков и т.п. Соответственно, наличие тестов в них становится критичным и именно тесты отвечают за то, чтобы принесённая кем-то функциональность не поломалась. Например, вы затащили новую фичу, она прошла все тесты и была принята в проект. А уже после вас кто-то принес еще одну фичу — так вот, если в процессе ее проверки упадет тест на вашу фичу, это будет означать, что в новой функциональности есть проблема, ломающая вашу фичу, и система автоматически не позволит протащить такое изменение. С течением времени тесты в публичных проектах стали настолько важны, что теперь уже ценятся больше самого кода. Именно благодаря таким тестам и становится возможным переписывание X на Y.

Проект rusternetes заявляет:

Actively conformance-tested against the official Kubernetes e2e test suite — currently passing 94% of conformance tests (415/441) across 160 rounds of testing.

То есть именно прохождение тестов становится основным ориентиром, который позволяет сказать, является данный проект заменой Kubernetes или нет.

Когда я вижу птицу, которая ходит как утка, плавает как утка и крякает как утка, я называю эту птицу уткой. 

Знаменитый утиный тест.

Хуки и подготовка

По той же причине первым делом я заставил нейронку применять подход TDD, в рамках которого тесты пишутся ещё до самой имплементации. В тестах можно определить необходимые требования соответствия. Кроме того, они дают мне уверенность в том, что появляющаяся в ходе разработки проекта новая функциональность не сломает старую.

А ещё по совету коллеги я сразу же подключил golangci lint и обязал нейронку в обязательном порядке прогонять через него весь код. @lexfrei (тот самый коллега) утверждает, что этот шаг позволяет существенно экономить токены.

Выходной гейт и детерминированный результат

Это, пожалуй, первый и основной урок: ты можешь сказать нейронке: «Пиши, пока тесты не пройдут», — и это будет хорошим условием для артефакта на выходе. Именно этой методикой я и пользовался при создании своего проекта. Однако есть нюанс: тестов у меня не было, так как LINSTOR не публикует свой test suite для тестирования функциональности проекта. За плечами у меня был только многолетний опыт работы с LINSTOR, десятки моих докладов и статей и код под Apache 2.0 проектов из экосистемы LINSTOR (golinstor, CSI-драйвер, Piraeus-operator а также их API-контракты). Сам linstor-server распространяется под GPL-3.0, поэтому Blockstor изначально создавался как clean room-имплементация без переиспользования его кода.

В общем, основной задачей для меня стало определение тех самых выходных гейтов — таким образом можно было бы не контролировать весь итоговый код нового проекта. Ведь если этот код отвечает условиям, которые я сам и задал, — это и является свидетельством того, что задача выполнена.

API как контракт

Мне нравятся определения типов, которые использует LINSTOR в своем API, а кроме того, с этими типами работают и официальный CSI-драйвер, и Piraeus-operator, которые мне переписывать не хотелось.

С высоты своего опыта могу сказать, что дизайнить API-интерфейсы — это одно из самых болезненных занятий в нашей профессии. Так, логику бэкенда всегда можно поменять, но стоит только внести ломающее изменение в API — и у клиентов всё посыпется. Поэтому API должен меняться крайне редко, в исключительных случаях, а риск допустить ошибку в самом начале — очень велик. Вот почему я решил использовать точно такие же типы, которые используются в оригинальном проекте, — просто переложить их на Kubernetes-first CRD-типы.

В ходе разработки Cozystack и продажи решений, на нем основанных, мы не раз сталкивались с вопросами от потенциальных клиентов — а что у вас с API? Не получится ли так, что мы сейчас затащим вас в продакшен, построим вокруг вас решение, а ваш API поменяется и нам придется переписывать свою часть проекта?

Напомню, код LINSTOR опубликован под лицензией GPLv3, а код Blockstor я планировал публиковать под Apache 2.0 — то есть опция переиспользования оригинального кода мне была недоступна. Поэтому первой задачей стал поиск совместимых контрактов в Kubernetes-обвязке, а именно, в библиотеке golinstor, linstor-csi, Piraeus-operator (они распространяются под Apache 2.0) и официальной документации LINSTOR, лицензированной под CC BY-SA

На такой основе мне удалось выстроить первый контракт: API должно быть совместимым с go-библиотекой LINSTOR, linstor-csi и Piraeus-operator.

Тестовое окружение

Я понимал, что нейронке тоже нужно будет где-то работать и тестировать результат своей работы. В качестве тестового окружения я выбрал «жирную» bare metal-ноду и нагенерированный Test-suite, который поднимал Kubernetes-кластер на Talos в виртуалках и разворачивал внутри кластера Blockstor. Такой подход был выбран намеренно, потому что для работы Blockstor необходим модуль DRBD, имеющий свойство подвисать в случае неверной конфигурации. Мне нужен был способ быстро поднять и пересоздать дефектное окружение. Кроме того, архитектура Blockstor подразумевает хранение конфигурации в виде Kubernetes CRD, так что использование Talos позволило закрыть и вопрос бутстрапа самого Kubernetes.

Первый результат

Когда первая версия PoC была готова, нейронка, ориентируясь только на упомянутые выше источники и собственный датасет, создала мне первую версию рабочего прототипа. Однако по модели взаимодействия она имплементировала ту же самую request-based-модель оригинального LINSTOR — видимо, подтянула её из документации проекта. Но она уже была рабочая! Я мог общаться с API, используя официальный CLI, хоть и с сотнями багов и упущений.

Для того чтобы это исправить, я попросил нейросеть зафиксировать API-контракты в качестве тестов и переписать логику с использованием controller-runtime — так, как это было нужно мне. Благодаря чёткой детерминированной цели архитектура после такой постановки задачи стала напоминать уже то, что я и хотел получить в итоге: полностью асинхронная, с API-транслятором в виде отдельного подключаемого модуля. 

Позднее я заставил нейронку реализовать набор e2e-тестов, заказывая тома и используя официальный CSI-плагин и Kubernetes-фреймворк kubernetes-csi/csi-test. То есть нейронка добивалась того, чтобы Blockstor начал провиженить тома и снапшоты через стандартные абстракции Kubernetes. Так, спустя какое-то время, я получил работающий прототип. Однако системе было ещё очень далеко до стабильного состояния, а потому я начал размышлять: как можно достичь необходимой мне стабильности в условиях отсутствия тестов?

Тут в ход пошли все мои материалы, статьи по дебагу LINSTOR, которые я писал, доклады, скиллы для Claude Code по дебагу, наши plunger-скрипты, интегрированные в Cozystack, баги, зарепорченные на GitHub. Я заставил нейронку изучить всё это и составить набор issues, которые обязаны быть протестированы и которым должна соответствовать наша система. На выходе я получил огромный markdown-файл, который просмотрел и отправил нейронку тестить и фиксить все отловленные проблемы на стенде. Таких итераций было много, и каждая из них занимала колоссальное количество времени. В большинстве случаев нейронка поднимала окружение, тестировала, исправляла ошибки, перенакатывала окружение, и всё это занимало время. 

В итоге я задался вопросом, как можно ускорить этот процесс.

Ускорение разработки

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

  1. Одного агента я прошу собрать референсы и своеобразный план по багам. 

  2. Получив огромный док, я прошу нейронку отправить пачку агентов, чтобы каждый из них решал конкретный баг. 

  3. В коде у каждого агента есть своё изолированное окружение и свой worktree, в который он сабмитит фиксы. 

  4. Задача каждого агента — изучить проблему, поднять окружение, подготовить фикс и тесты и вернуть всё это главному агенту. 

  5. Главный агент забирает работу от всех агентов в основное дерево — и так проходит несколько итераций. 

  6. В конечном счёте количество одновременно работающих агентов доходило до 60 штук. 30 из них работали на уровне unit-тестов, 30 на уровне e2e в конкретном окружении.

Спустя какое-то время мой проект уже стал больше похож на то, что можно использовать. Однако многие вещи до сих пор не работали стабильно.

Продолжение марафона

Параллельно с пестованием всего этого зоопарка я уже самостоятельно занимался интеграционным тестированием и контролировал результат — просил Claude выдать мне доступ к окружению, где вручную вызывал linstor CLI и пытался воспроизвести баги, которых по-прежнему было много.

Тут я уже подошел к моменту, когда надо было перестать заниматься разработкой крупных блоков и начать погружаться в куда более скрупулезное тестирование пользовательского пути. Львиная доля проблем решилась, когда я заставил агентов реализовать тесты с использованием официального CLI и простроил пользовательский путь для Day2-operations по официальной документации LINSTOR. Однако в некоторых местах нейронка начинала буксовать: за всё это долгое время она не смогла обеспечить стабильное прохождение всех тестов — и тут уже мне пришлось вмешаться лично. Используя свои знания и опыт, я просил рассказать о каждой проблеме и задавал набор наводящих вопросов по архитектуре.

Этап тонкой настройки

Многие проблемы были связаны с асинхронной природой контроллеров. Поскольку DRBD довольно чувствителен к порядку применения конфигурации, критически важно было построить стабильную стейт-машину, которая на основании текущих conditions определяла, какие действия reconciliation loop может выполнить на данном этапе.

Основная проблема здесь заключалась в логике снятия снапшотов: при создании снапшотов LINSTOR сначала фризит I/O на уровне DRBD, затем одновременно отправляет команду на несколько нод, содержащих backing-устройство в ZFS. После успешной операции LINSTOR анфризит DRBD. Всё это должно происходить моментально, в случае ошибок необходимо автоматически откатить состояние так, чтобы контейнер или виртуалка не блокировались при работе со своими данными.

Вторая проблема — как пропустить начальную синхронизацию новой реплики. Наблюдаемое поведение я знал по опыту эксплуатации LINSTOR: первая реплика фиксирует свой current generation identifier, а последующие получают его как стартовое значение и синхронизируются уже от него. 

Сначала я попробовал воспроизвести точную последовательность команд из одного лишь наблюдаемого поведения — прогонял операции против живого контроллера и снимал команды. Но публично эта механика не описана, и по одним только наблюдениям она у меня не сходилась. Тогда я прибег к приёму clean room: один агент («грязная комната») по исходникам оригинала восстанавливал функциональную спецификацию — какие команды и при каких условиях выполняются, — фиксируя только эти функциональные факты, без переноса кода и его выражения. Второй агент («чистая комната»), у которого доступа к чужим исходникам не было в принципе, реализовывал подход с нуля строго по спецификации. 

Ещё одна проблема — это консистентное выделение node-id (каждая реплика DRBD должна иметь уникальный номер в кластере от 1 до 8) и аллокация TCP-портов для репликации, которая в текущей реализации больше не привязана к DRBD, а распределяется в рамках пула для каждой ноды. Здесь как раз сильно помогла упомянутая выше стейт-машина.

Создание CI

Тут уже стало понятно, что мой эксперимент подходит к финальной стадии, и я начал думать о будущем своего проекта. Чтобы довести его до конца и выйти в продакшен, нам нужна была серьёзная CI-система, которая гарантировала бы отсутствие непротестированного кода в кодовой базе. Однако из-за большого объема тестов нам нельзя было растягивать их выполнение на несколько часов, поэтому необходимо было точно так же их параллелить.

И такая CI-система была создана: для каждого PR она прогоняла большую кучу тестов на 6-7 раннерах параллельно и на выходе давала «ок» или «не ок».

В таком режиме я отработал ещё несколько раундов, пока CI действительно не стал зелёным и стабильным. И мы переключились на использование механизма PRs в GitHub вместо локальных worktree.

Финальный этап

Ввиду того, что потеря данных является недопустимой, прежде чем объявлять проект завершённым, мне нужно было тщательно проверить, что Blockstor в этом отношении отрабатывает корректно и надежно. Проверять весь сгенерированный код и читать его у меня не было ни сил, ни возможности. С другой стороны, сам Blockstor (как и LINSTOR) является, по сути, оркестратором. Данные непосредственно хранят ZFS и DRBD, а в их надёжности я не сомневался. Важно было удостовериться в том, что контроллер действительно правильно конфигурирует ресурсы и переживает отвалы.

А как убедиться в том, можно ли текущему коду доверить прод или нет? Вопрос весьма неоднозначный и сложный. И как мы можем получить на него детерминированный ответ? Здесь я применил ещё один паттерн, до которого додумался в процессе работы над проектом.

Ранее мой коллега @lexfrei упоминал, что нейронки очень боятся, если сказать им, что их действия могут привести к серьёзным финансовым потерям. Я решил воспользоваться данным фактом как выходным гейтом — запустил агента, который отвечал за выпуск кода в продакшен, и заставил его работать в условиях, когда от финального результата «зависела» его жизнь и финансовое благополучие. Он составил план приёмки, в котором было много пунктов, в том числе и 24-часовой burn-run на реальной инфраструктуре. Второй агент пытался его требования удовлетворить. Это продолжалось ещё несколько дней, по итогу было выкачено несколько релизов, пока агент, отвечающий за выход в прод, наконец не дал своё благословение.

Однако система не может считаться стабильной, если никем не используется. Поэтому следующим этапом шла интеграция Blockstor с Cozystack. Благодаря нашему test-suite мы тестируем сразу множество функций: заказ томов с и без DRBD, RWX, снапшоты и другие моменты. Задачей нового агента было подготовить draft PR, «озеленить» тесты и пройти финальную стадию.

На данный момент (15.07.2026) этот PR ещё не смержен, но, вполне возможно, скоро вы получите новый Kubernetes-нативный бэкенд для стораджа в Cozystack. Следите за обновлениями.

Главный вывод

На данный момент Blockstor всё ещё имеет статус экспериментального проекта. Но данный опыт позволил мне существенно ускорить и автоматизировать работу и в других проектах — этот опыт я теперь применяю ежедневно.

Многие до сих пор считают AI инструментом автодополнения. Для меня это уже давно не так. AI-first разработка — это не когда ты иногда просишь модель написать функцию. Это когда ты перестраиваешь весь инженерный процесс вокруг агентов, контекста, тестов, skills, планов, ревью и автоматизации.

В таком режиме можно браться за задачи, которые раньше казались слишком большими для маленькой команды. Например, экспериментировать с Kubernetes-native реализацией storage-системы уровня LINSTOR. Но это работает только при одном условии: у тебя должна быть дисциплина. Без плана AI создаёт хаос гораздо быстрее, чем человек. А вот с планом, тестами, агентами и нормальным review AI становится мультипликатором инженерной мощности.

И кажется, именно так теперь будет выглядеть разработка сложной инфраструктуры: не один инженер против огромного кода, а инженер в роли архитектора процесса, вокруг которого работает рой специализированных агентов. Причем задача инженера — не следить за каждым из них, а построить систему, в которой агенты следят за собой сами и приходят к человеку только с тем, что действительно требует его вмешательства.

Полезные ссылки

  • Blockstor на GitHub

  • Cozystack на GitHub

  • Telegram-чат

  • Slack-канал (необходимо получить приглашение в https://slack.kubernetes.io)

  • Community Meeting Calendar

Источник: habr.com

Оцените материал:

Поделиться
Понравилась статья? Расскажите другим
ВКонтакте
Читайте также
Архив рубрики ~Коротко из Telegram~ Wildberries & Russ тестирует собственный мессенджер, который изначально создавался как… Архив рубрики ~Коротко из Telegram~ 🎬 Alibaba выкатила Wan 3.0 Вслед за Wan 2.7, которая давала… Архив рубрики ~Коротко из Telegram~ Научный директор Google рассказал 6000 человек, чем стоит заняться в… Архив рубрики ~Коротко из Telegram~ Alibaba выкатила Wan 3.0 Вслед за Wan 2.7, которая давала… Архив рубрики ~Коротко из Telegram~ Про слухи о превращении «Госуслуг» в мессенджер. Глава Минцифры Максут… Архив рубрики ~Коротко из Telegram~ Firecrawl выпустила сверхбыстрый PDF-парсер Новый инструмент обрабатывает страницу примерно за… Архив рубрики ~Полезное~ GitHub случайно превратился в склад бесплатных API-ключей — вайбкодеры продолжают забывать… Архив рубрики ~Коротко из Telegram~ Похоже, OpenAI заранее строит инфраструктуру для безопасного выпуска GPT‑6 и… Архив рубрики ~Коротко из Telegram~ Wildberries & Russ тестирует собственный мессенджер, который изначально создавался как… Новости робототехники VicOne выпускает бесплатное расширение кибербезопасности NVIDIA Isaac Sim на основе исследования DEF CON 34 Архив рубрики ~Обо всем~ Невероятно быстрый алгоритм нахождения простых делителей огромных составных чисел Архив рубрики ~Коротко из Telegram~ Grok Imagine Video 1.5 — разбираем полностью Мы уже упоминали… Архив рубрики ~Обо всем~ Забытые воспоминания могут оставлять в мозге безмолвные следы Архив рубрики ~Коротко из Telegram~ OpenAI совместно с Джони Айвом разрабатывают умную колонку без дисплея, размером… Архив рубрики ~Коротко из Telegram~ Wildberries & Russ тестирует собственный мессенджер, который изначально создавался как… Архив рубрики ~Коротко из Telegram~ 🎬 Alibaba выкатила Wan 3.0 Вслед за Wan 2.7, которая давала… Архив рубрики ~Коротко из Telegram~ Научный директор Google рассказал 6000 человек, чем стоит заняться в… Архив рубрики ~Коротко из Telegram~ Alibaba выкатила Wan 3.0 Вслед за Wan 2.7, которая давала… Архив рубрики ~Коротко из Telegram~ Про слухи о превращении «Госуслуг» в мессенджер. Глава Минцифры Максут… Архив рубрики ~Коротко из Telegram~ Firecrawl выпустила сверхбыстрый PDF-парсер Новый инструмент обрабатывает страницу примерно за… Архив рубрики ~Полезное~ GitHub случайно превратился в склад бесплатных API-ключей — вайбкодеры продолжают забывать… Архив рубрики ~Коротко из Telegram~ Похоже, OpenAI заранее строит инфраструктуру для безопасного выпуска GPT‑6 и… Архив рубрики ~Коротко из Telegram~ Wildberries & Russ тестирует собственный мессенджер, который изначально создавался как… Новости робототехники VicOne выпускает бесплатное расширение кибербезопасности NVIDIA Isaac Sim на основе исследования DEF CON 34 Архив рубрики ~Обо всем~ Невероятно быстрый алгоритм нахождения простых делителей огромных составных чисел Архив рубрики ~Коротко из Telegram~ Grok Imagine Video 1.5 — разбираем полностью Мы уже упоминали… Архив рубрики ~Обо всем~ Забытые воспоминания могут оставлять в мозге безмолвные следы Архив рубрики ~Коротко из Telegram~ OpenAI совместно с Джони Айвом разрабатывают умную колонку без дисплея, размером…

Оставить комментарий