В любом дистрибутиве Linux — даже в самом любимом — почти всегда приходится что-то донастраивать под специфику конкретной задачи. Или, чтобы экономить ресурсы. Это порождает объёмный фронт континуальных работ, особенно в малых и в средних проектах, где до Kubernetes с Ansible дорасти сложно, а вменяемого инструментария всё же хочется.
Попытки что-то унифицировать были и раньше — вспомнить хотя бы kFreeBSD. Но большинство таких проектов либо споткнулись о классицизм, либо просто не выдержали усложнения. С этого начался эксперимент с Hypersphere OS. Я попытался собрать дистрибутив как гибкий тулсет, где меньше жёстких регуляций и можно точно понимать, что встроено, что опционально, что от чего зависит и что может работать по API вместо магических связок.

Hypersphere OS делает ставку на другое: на простую и разнесённую по логическим функциональным слоям структуру, где системные компоненты, библиотеки, окружения и AI-модели работают как части одного набора инструментов и в согласии между собой.
Я — Алексей Веснин, системный архитектор, создатель HyperSphere — децентрализованной экосистемы для безопасного и цензуроустойчивого пространства. В IT с начала 90-х. Занимаюсь системным администрированием с уклоном в сети, безопасность и построение информационных систем, которые управляли собой сами и преподавал собственный курс в ЦКО «Специалист» при МГТУ им. Баумана и в других местах.
В этой статье, по мотивам выступления на DevOpsConf, расскажу, что мне пришлось переизобрести, чтобы сборка нового типа заработала, почему старые подходы не справились, и как выглядит дистрибутив, который не мешает сам себе.
Дистрибутив как инструмент, а не философия
Под «дистрибутивом» одни понимают идеологию (Debian, Ubuntu), другие — рабочий подход. Я рассматриваю дистрибутив как тулсет, то есть набор инструментов для решения конкретной задачи. Начинал с embedded Unix и BSD-дистрибутивов, и пошёл именно по этому пути.
Если посмотреть на современные системы — будь то Linux или BSD — в глаза бросается общая проблема: масштабы задач, инструментария и проектов часто не совпадают. Есть малые задачи, вроде SMB, когда мы что-то разрабатываем и тестируем, например, «Hello, world». И вовсе не обязательно для этого поднимать Kubernetes на локальном хосте.
Но когда у вас КАМАЗ железа и ресурсов — это, IMHO, не повод ими разбрасываться. Да, можно запустить Kubernetes, Ansible и всё остальное, но у малых проектов и задачи другие, и бюджеты скромнее. Их бы сэкономить. И, как показывает практика, уже сейчас можно экономить вплоть до двух раз.
Поэтому я начал собирать инструменты с нуля — и делал это, исходя из трёх масштабов: задач, инструментов и проекта.
Shell, Python, Lua: базовый набор под задачи
Что нужно, чтобы у нас получился дистрибутив операционной системы? Как минимум — это Shell, Package manager, Python, LUA.
Помимо ядра и его окружения, необходимы оболочка и менеджер пакетов. Но этим дело не ограничивается — нужно что-то более мощное, потому что не все задачи удобно решать с помощью Shell. Из практики — мой выбор пал на Python и Lua.
Lua часто критикуют в интернете, но при правильном применении он даёт отличную эффективность — там, где Shell уже не справляется, а Python избыточен, Lua занимает нишу между ними.
Для примера: Lua используется в OpenWRT — дистрибутиве де-факто для опенсорсных роутеров. Если вы работали с OpenWRT, с его embedded-вариантами или с проектом LEDE, вы уже сталкивались с Lua в реальных задачах.
Но ключевой вопрос не в том, что эти инструменты могут и как их правильно использовать, а в том, как правильно распределить роли, чтобы каждый инструмент выполнял только то, что должен.
Proposal: шаблоны, препроцессоры, плагины
Первое, с чего я начал — разнёс всё по шаблонам.
Казалось бы, у нас уже есть Dockerfile, Jenkinsfile, манифесты Ansible, shell-скрипты — но их общая проблема в том, что они всё смешивают. А шаблон должен быть независимым — описывать только сервис и его поведение и потребности для работы.
Препроцессор — мост между шаблоном и средствами запуска
Допустим, разработчик, создавший компонент — веб-сервер или сервер на Python — описывает, что нужно для запуска. Но запускается всё через препроцессоры, которые связывают шаблон с конкретным железом и текущими политиками проекта, в том числе по безопасности. Мы не собираем всё в одном месте, а наоборот — разносим.
Патчи и плагины — максимальное облегчение сервисной нагрузки, включая только необходимый функционал
Ещё может возникнуть вопрос, как добавлять функционал. Я начал с дистрибутива Debian, собрал под него свою PPA-репу и начал добавлять нужные куски. Но со временем пришёл к более чистому решению: использовать патчи или плагины, чтобы облегчить сервисную нагрузку, и включать только необходимый функционал в продукты, которые есть в дистрибутиве.
Спойлер: если взять за 100% нагрузку обычного Debian, который крутит типовое веб-приложение (данные с продакшеном), и сравнить с моим решением, где уже всё в закрытом тесте работает на проде, — нагрузка падает до 60%. При этом нагрузка на сами приложения от клиентов не уменьшается, а иногда и растёт. Такие цифры достижимы, когда мы просто разделяем, что должно быть встроено, а что может подключаться отдельно.
Какие подходы я использовал и что от них взял
Очевидно, сделать что-то с нуля, полностью игнорируя накопленный опыт индустрии, невозможно — и, более того, не нужно. За десятки лет в этой области было опробовано множество решений, и всегда можно (и нужно) опираться на уже существующие наработки. Но не как на классику, а трезво: подходы, уже опробованные индустрией, тоже имеют минусы.
Linux From Scratch (LFS)
LFS родом из embedded-среды — и, более того, продолжает там использоваться до сих пор. Ни один другой подход не позволяет собрать полноценный рабочий образ весом 150–200 МБ (включая приложение), или 400–500 МБ с графическим интерфейсом.
Какие плюсы:
Нет Overhead’а
Вы вручную строите систему, начиная с базовых компонентов, и просто не включаете лишнее.
Максимальная утилизация железа
Сейчас мы покупаем мощное железо — особенно процессоры с новыми наборами инструкций и соответствующими ценниками. Но если посмотреть, что лежит в стандартных дистрибутивах вроде Debian или Ubuntu, то там по-прежнему используется архитектура AMD64, и в лучшем случае — чуть больше 50% её возможностей. Получается, мы платим за ресурсы, а они просто гоняют электричество и генерируют операционные расходы. LFS лишён этой проблемы.
Есть и последствия:
Нулевой Package management
Его там просто нет. Это осознанное решение: сборка идёт по build-листу — по сути, предшественнику Jenkins-файлов, только в эпоху, когда Jenkins ещё назывался Hudson, и этих файлов не существовало вовсе.
Безопасность — на максимуме, достижимом в сборках
Вы точно контролируете, как именно собирается каждая часть системы. Из готовых дистрибутивов это можно сравнить разве что с OpenBSD, где всё сигналит, если приложение ведёт себя «не так».
Вывод: LFS — живёт и будет жить в embedded-сегменте, но не может быть основой для универсального дистрибутива, поскольку не имеет полноценного Package management.
Debian
Как дистрибутив, Debian не был первым в мире Linux, но стал первым массовым. Если бы путь был изначально неправильным, Debian бы давно вымер, вместе с идеологией. Но этого не случилось. Хороший пример того, что какие бы ни были спонсорские и пиар-возможности, дистрибутив умирает, это Mandriva, которая поработала и благополучно упала. Там развалилось всё — и дистрибутив, и комьюнити, и команда разработчиков. Debian — time-proven, и уже этим заслуживает внимания.
APT как комплементарная часть логики процесса эволюции
Debian подарил нам APT (Advanced Packaging Tool/Toolkit). На момент появления это было революционное решение. APT в своё время даже во многом переиграл RPM, который был и остаётся частью стандарта Linux, но только потому, что rpmbuild умел собирать пакеты без CI/CD. Он делал это самостоятельно и сильно экономил ресурсы. Но потом всё стало замедляться, и сейчас, к сожалению, APT застыл.
Patch/Bug management + поколения = ?
Patch management, bug management, поколения системы — всё это у APT не связано между собой. А если заглянуть под капот, то максимум, что там есть — это Bug-Submit-URL, встроенные в пакеты. На уровне архитектуры никакой работы с причинами проблем не ведётся, и, естественно, это блокирует развитие. IMHO — проект не умер, но явно зашёл в тупик.
В итоге от Debian я взял APT и небольшой кусок его структуры, но сам переписал пакетный менеджер заново, как Extended Packaging Toolkit. Об этом расскажу дальше.
FreeBSD
FreeBSD, как и BSD, шёл совершенно своим путём — когда ещё существовал BSD/i386, и ни Free, ни OpenBSD не было вовсе.
Научный подход: стабильность как ограничение
FreeBSD создавался на научной основе. Там не было классического пакетного менеджера — и, по сути, до сих пор нет. Тот же pkg умеет лишь распаковать двоичный архив.
Научный подход заключается в том, что дерево портов FreeBSD — это действительно мини-CI/CD, который собирает софт под конкретное железо, с учётом архитектуры и инструкций, за которые вы заплатили. Всё это кастомизировалось. Но, к сожалению, это решение так и осталось на уровне файлов, поэтому тоже зашло в тупик.
Строгость ядра от архитектуры до API
Среди сильных сторон, но и ограничений тоже — невероятная строгость архитектуры. Во FreeBSD чётко заданы принципы: от ядра до архитектуры и API. Но именно поэтому, например, во FreeBSD не работали софт-модемы. Ещё эта строгость стала причиной, по которой в 90-х и 2000-х FreeBSD почти не ставили на обычные пользовательские машины. Потому что в софт-модемах нет такого, что ядро (либо модуль ядра) делает то, что не должно делать.
Устаревший пакетный менеджер и декаданс в дереве портов
К сожалению, дерево портов пришло в упадок. Даже то, что там есть — обновляется нерегулярно или неактуально.
kFreeBSD
kFreeBSD — это гибрид Debian и FreeBSD, созданный Debian Community. По сути это было историческое событие: я не видел ничего подобного.
Правильный подход к финансированию и спросу
Проект был построен грамотно. Во-первых, у него было устойчивое финансирование от команды Debian. Во-вторых, он опирался на уже существующую базу пользователей — ка�� со стороны Debian, так и со стороны BSD, с их конкретными потребностями.
Это как с Bitcoin или Ethereum: они не просто появились из ниоткуда, а ответили на сформировавшийся запрос сообщества. В случае Ethereum — это были смарт-контракты. В случае kFreeBSD — BSD-ядро с Linux-пакетами. Фактически использовались те же пакеты и пакетный менеджер.
Почему не взлетело
Несмотря на сильную идею и реализацию, проект kFreeBSD погубил научный классицизм. Это та же проблема, что и в медицине: там, где слишком высокая ответственность, новые подходы проходят с огромным скрипом. Вы не сможете сделать что-то работающее на ядре BSD, чтобы оно конфигурировалось через конфиги Linux, потому что это конфиги Linux. То же самое, как и конфиг BSD rc.conf никогда не сконфигурирует Linux, потому что это другое.
kFreeBSD не повторил судьбу Solaris
Операционная система Solaris, разработанная Sun Microsystems и далее купленная Oracle, была закрыта. Но из него вырос OpenSolaris, а от него — жизнеспособные форки. Значит проект жив до сих пор. Но kFreeBSD просто встал. Что конкретно с ним произошло — неизвестно.
Почему я начал свой дистрибутив
В качестве основы я взял LFS и Debian: первый — за контроль и гибкость, второй — за массовость и проверенность временем. А вот классику и жёсткую стандартизацию — сразу отбросил.
По стандарту Linux всё должно быть строго: RPM, фиксированное дерево каталогов и так далее. Но зачем? Мы строим набор инструментов, который не навязывает, как решать задачу, а помогает её решить. В этом суть.
Я также взял ключевые концепции ещё от двух проектов:
OpenWRT — как концепт подборки компонентов, где через пакетный менеджер устанавливаются даже модули ядра, и это работало, причём не один год. Например, это успешно делала Gentoo.
TrueNAS + Scalable — как пример того, что один проект может быть двуядерным.
Двухъядерный не в смысле, что одновременно работают два ядра. Там либо TrueNAS использует BSD-ядро, либо Scalable — Linux-ядро. Но оба проекта используют одну и ту же базу портов и пакетов.
Теперь — важный момент про архитектуру дистрибутивов в целом.
Подавляющее большинство дистрибутивов построено так: есть ядро, модули и системные утилиты. Всё это логически и технически связано между собой — и разрывать эту связку нельзя. Попытка это сделать убила kFreeBSD.

Но вот что странно: в ту же монолитную связку прикручены библиотеки и пакетный менеджер. А все приложения и сервисы, которые мы устанавливаем и запускаем, оказываются где-то в стороне, как будто они не часть общей системы. Если вы работаете с Ubuntu или Debian, вы могли это заметить.
Возьмём, например, pip — пакетный менеджер Python. Предположим, мы что-то хотим поставить в глобале: скажем, есть несколько AI-сервисов, и хотим сделать несколько кусков. Но так как есть общие компоненты, почему бы не поставить их в глобал, чтобы не тащить одно и то же в каждый venv? Звучит логично. А теперь попробуйте это сделать. Сюрприз: не получится. Хотя так не должно быть. Мы же не просим ничего невозможного — просто понятное управление зависимостями и адекватное поведение системных инструментов.
И в какой-то момент я понял, почему об этом никто не догадался: потому что этого просто не видно. Соответственно, Advanced Packaging Toolkit был переписан, и так появился Extended Packaging Toolkit.
От Advanced к Extended Packaging Toolkit
Extended Packaging Toolkit (ранее Advanced) — это логичное продолжение, к которому всё пришло.
Первое, что я сделал — поместил APT внутрь venv. Теперь всё можно ставить глобально, потому что venv есть и в Python, и в Ubuntu, и в Debian. Он нормально держит кусок, который обслуживает пакетный менеджер, и всё отлично работает.
Caps matrix как ключ к упрощению CI/CD/PM
Чтобы понять, почему APT зашёл в эволюционный тупик, нужно посмотреть на саму идею. Сейчас у нас есть платформы — amd64, i686, arm64, armv7. На старте этого достаточно. А как насчёт того, чтобы утилизировать возможности железа? Подобный подход давно применялся в эмбеде в мобильной версии — это Caps matrix. Как пример, Android или iOS, где каждое приложение запрашивает разрешения: доступ к камере, микрофону, GPS и т.д. Фактически — это битовая маска: что именно нужно приложению для работы. Почему бы не сделать так же для инструкций процессора? Я попробовал — и заработало.
Flavour — full-dynamic/dynamic/semistatic/static
Второй важный параметр — Flavour. Он показывает, как собран пакет. Его содержание может быть полностью динамическим — особенно в embedded-сценариях это хорошо экономит ресурсы, в частности, оперативную память. Или наоборот — полностью статическим.
Мы не можем сделать даже такой шаг: допустим, сервис лежит в облаке, и из соображений безопасности хотим заменить OpenSSL. Это стандартная задача. Но если используется пакетный SSH, он упадёт, потому что использует OpenSSL, подключаемый динамически — и вы не зайдёте на сервер ни физически, ни виртуально, кроме как через KVM.
Почему бы просто не собрать SSH статически? Сейчас это приходится делать вручную, и это мешает стандартной работе системы. Но если бы у пакета был Flavour — и мы могли бы пометить, что нужен статик, — всё встало бы на свои места.
IPFS и magnet из коробки
Плюс, та же история с доставкой: по умолчанию всё идёт из центральной точки — HTTP или HTTPS. И работает, как fallback. Но у каждого пакетного менеджера, не только у EPT, есть локальный кэш. Мы уже скачали пакет и развернули его. Почему бы не раздать его из этого локального кэша? Ничего не мешает.
Можно раздавать и через IPFS, и через magnet — это не нарушает подписи, проверка которых остаётся. Зато нагрузка моментально снижается.
Target Hardware вместо просто платформы
Строго говоря, не вместо, а в дополнение.
Платформа — это базис, с которого мы стартуем. А Target Hardware — это капсы, доступные инструкции и не только они. У части пакетов также есть капсы по тому, какие опции должны быть включены в ядре. Всё это можно собрать в единое описание.
Имена APT-пакетов перегружены. По сравнению с ними, самые длинные в мире таксы покажутся типографскими точками, потому что название пакета пытается вместить в себя всё что можно. А на деле остаётся всего несколько сущностей: bin — то есть binary, dev, lib и doc. Ничего сложного. Всё это отлично легло и в GitLab, и в Jenkins.
По такому же принципу был расширен Bash.
Bash → Extended Bash
Появление Bash и его предка sh было серьёзным прогрессом. Но с тех пор в Bash не добавляли слишком многое из того, чем мы пользуемся ежедневно. Каждый раз, когда мы пишем длинные скрипты, то вызываем сторонние приложения. PID'ов сейчас много, но это ресурсозатратно, и, зачастую, лишено смысла. Особенно если мы просто хотим дёрнуть RESTful веб-интерфейс под JSON.
Почему, получив JSON, мы не можем сразу извлечь из него Bash-массив? Или наоборот — взять Bash-массив и получить из него JSON, чтобы отправить в веб-сервис? Технически мы можем сделать это через вызов большого количества сторонних приложений. Но может, проще подключить CURL и JSON к Bash напрямую?
Extended Bash (Ebash) — это расширение Bash, где нужные вещи встроены под капотом:
REST, JSON, XML, INI и их парсинг/форматирование.
Интеграция с LDAP, потому что это стандарт для хранения групповых политик и настроек.
Dbus, не только для systemD, но вообще как универсальная шина для работы с пулом сервисов и коммуникации между ними. Это нужно, например, в Shell-скриптах. Работать с этим тоже можно одной встроенной командой.
Daemon, встроенная поддержка демонизации. Сейчас она есть в любом живом языке, и Bash — теперь не исключение.
Когда я всё это внёс, размер Bash почти не изменился. А вот функциональность — стала совершенно другой.
Hypersphere OS: как должен выглядеть дистрибутив с осмысленной архитектурой
Что получилось в результате? Если коротко — то, чего не смогли сделать разработчики kFreeBSD. А если чуть подробнее — архитектура, где системные компоненты и пользовательское окружение наконец-то взаимодействуют как единое целое.

Систему, ядро и модули (выделены красным) — вы никогда не сможете разделить системными утилитами. Эта ошибка и убила kFreeBSD. Вот он, классицизм: есть /etc/network/interfaces из Linux — отличная штука, но она линуксовая и никогда не сконфигурирует BSD-сетку.
То же самое с UFW (Uncomplicated Firewall). Это действительно отличный инструмент для новичков, чтобы не копаться в iptables или nftables. Но он никогда не сконфигурирует ни IPFW, ни PF полноценно. Поэтому системные утилиты всегда будут путешествовать вместе с ядром и его модулями. А вот всё остальное управляется через пакетный менеджер. И это уже не какой-то конкретный кусок, но и виртуалки, контейнеры — что угодно.
Библиотеки и приложения на схеме я специально поставил рядом — граница между ними условна. Потому что часть библиотек может быть частью самого приложения, в том числе, и по причинам безопасности, а другая часть — может быть расшарена, в зависимости от задачи.
В результате можно выбрать BSD-ядро со своими модулями и системными утилитами — или Linux-ядро, если так удобнее.

Жёлтая часть слева на рисунке — это пакетный менеджер. Через него обновляются как системные куски, так и библиотеки и приложения, которые мы запускаем.
Справа — pip, Python-environment. Та же схема работает с PHP, где вместо pip — Composer. Как в любом языке, здесь также есть и системно-зависимые компоненты. В PHP это extensions, завязанные на какие-то важные куски, в Python — PyTorch, TensorFlow и прочие.
Стандартная среда, например, для TensorFlow и стандартная проблема PyTorch возникает, когда мы поставили CUDA, как говорится, по рекомендациям лучших «Shift 2-ководов». У нас несколько venv, мы поставили несколько кусков с нейросетками. Кто гонял именно такие сервисы на venv одновременно, наверное, понимает, о чём я говорю.
CUDA обновилась — никаких конфликтов, значит, пакетный менеджер APT проверил и всё хорошо. А потом начинают разваливаться venv: в итоге PyTorch «поприветствует» вас тем, что не поддерживает новую версию CUDA.
Проблема в том, что у pip не было обратной связи (красная стрелка) с пакетным менеджером системы и единого целого тулсета. В EPT это исправлено: системно-зависимые компоненты обязаны общаться в обе стороны. Из pip → через API → в EPT, и обратно. Хотя бы для того, чтобы pip мог сказать: «У меня PyTorch, ему нужна CUDA такой-то версии и не выше».
Такой же принцип работает с библиотеками. Библиотеки в Debian, Ubuntu и других системах идут через APT или RPM. Но это не совсем правильно. У нас есть соответствующие языку менеджеры компонентов, в частности pip, Composer и др. Вопрос в том, почему всё это должно ставиться через APT. Потому что в классической архитектуре APT у вас просто нет другого выхода — нет связи между слоями и координации. И в этом — тупик старой модели. Но теперь такая связь появилась. И можно оркестрировать менеджеры через единый EPT и получить единый тулсет, который не только стандартизирует окружение под задачу, но и не даст системе развалиться, когда вы меньше всего этого ждёте.
Итоги: как всё заработало
Если упростить выводы до трёх тезисов:
Дублирование нужных связей между подсистемами ОС убивает дистрибутивы.
Дублирование функционала блокирует внедрение необходимых инструментов в дистрибутив.
Исправив вышеперечисленное, инструментарий упрощается в разы без потери функциональности.
Как только дублирование разбили и сделали связи там, где они должны быть, мы можем спокойно готовить стейки из «священных коров»:
SystemD + Dbus + ?
Когда-то SystemD называли «bloatware», или «раздутое ПО» — и не зря. Изначально он использовал D-Bus, работающий с KDE, GNOME и др. D-Bus позволяет не просто управлять пулом именованных сервисов, а работать с каждым сервисом отдельно. Через этот тулсет сейчас можно работать с ними в единой связке хоть из shell скрипта.
SystemD + venv
Сейчас я делаю дополнение к SystemD для Python: чтобы можно было запускать venv напрямую через SystemD, из без написания километров ненужных, на мой взгляд, shell-скриптов. Так же, как, например, в случае запуска обычного бинарного файла или сервиса в режиме simple или forking.
Чтобы разработчик, пишущий на Python, просто описал шаблоном, какой сервис нужно запустить. А система уже сама решала, куда это положить: на железо, в контейнер, или в виртуалку, в зависимости от контекста. Связка SystemD + venv здесь отлично справляется.
EPT API: +pip, +composer, +cpan
Я прихожу к тому, что в новых EPT API нужно работать с демоном. В рамках EPT API идёт активная работа с pip, composer (PHP), cpan (Perl). И Perl я достал не из нафталина. Я с ним давно экспериментирую, особенно с антиспамом SpamAssassin.
EPT API: +HuggingFace, +DockerRegistry
Но самое интересное началось, когда я подключил HuggingFace. Почему бы не управлять ML-моделями и чекпоинтами как обычными пакетами? И это срослось.
А дальше — Docker Registry. GitLab Registry (и публичный, и приватный) теперь можно обновлять через тот же API. Вам даже не нужно возиться с Watchtower.
В итоге сделать работающий тулсет можно, только разбив те связи, которых быть не должно. И построив новые — осмысленные. Именно это и делается в рамках этого дистрибутива.
Ближе к осени я планирую выпустить полноценный ISO — разумеется, в Open Source. Вишенкой на торте будет первой выложена сборочная база. Вы сможете собрать всё из исходников, установить у себя, держать это как приватную версию, или подключить средства автоматического аудита кода, если понадобится. Все новости и обновления опубликую в группе проекта в telegram.
В итоге получите полноценную внутреннюю верификацию, подстроенную под ваши политики. Это не очередной сборочный стенд Debian и ему подобных, а действительно четыре логически разнесённых куска, которыми можно управлять и контролировать по отдельности.
Скрытый текст
🔧 Инфраструктура, CI/CD, GitOps, безопасность, отказоустойчивость, управление конфигурацией — всё, что болит у инженера, обсуждается открыто и честно на DevOps Conf.
📅 Следите за новостями на devopsconf.io, чтобы не пропустить анонс следующей конференции.
