Cluster · разработчикам
Развёртывание
Cluster запускается на одном Linux-узле. Нужны systemd, cgroup v2, Podman и gVisor/runsc. Для пользовательских томов также нужны fallocate, mkfs.ext4 и mount.
Ядро создаёт тома и среды, поэтому поставленная служба работает от root. HTTP-служба использует отдельную учётку cluster-api. Права ядра не передаются в среды модулей.
Каталоги
| Путь | Содержимое |
|---|---|
/opt/cluster | Исходники и Python-окружение |
/var/lib/cluster | Базы, пакеты, файлы и тома |
/etc/cluster | Ключ и параметры служб |
Можно выбрать другие пути, изменив команды и файлы служб. Ключ хранится вне каталога данных.
Установка
Нужен CPython 3.12-3.14 и пакет Mekansm 3.0.0 под выбранную версию Python и архитектуру. Mekansm устанавливается отдельно. Пример ниже использует CPython 3.12 и пакет для x86-64.
cd /opt/cluster
python3.12 -m venv /opt/cluster/env
/opt/cluster/env/bin/python -m pip install /path/to/mekansm-3.0.0-cp312-cp312-manylinux_2_34_x86_64.whl
/opt/cluster/env/bin/python -m pip install --require-hashes -r requirements-runtime.txt
/opt/cluster/env/bin/python -m pip install --no-deps /opt/cluster
useradd --system --no-create-home --shell /usr/sbin/nologin cluster-api
install -d -m 700 /etc/cluster /var/lib/clusterПуть /path/to/...whl заменяется путём к пакету Mekansm. Пользовательский сайт устанавливается отдельно.
Ключ
Ключ содержит 32 случайных байта и создаётся один раз:
python3 -c 'import os; fd=os.open("/etc/cluster/core.key", os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0o600); os.write(fd, os.urandom(32)); os.close(fd)'Файл должен принадлежать учётке ядра и иметь права 0600. Ключ шифрует центральную и пользовательские базы. Его копия хранится отдельно от резервной копии данных. Повторное создание ключа не открывает прежние базы.
Образы
runsc устанавливается в /usr/local/bin/runsc. Образы заранее загружаются и закрепляются по SHA256. Плавающий тег при запуске среды не принимается.
Базовый образ задан в core.env.example. Отдельный образ с компиляторами собирается из deploy/Buildfile:
podman pull docker.io/library/python@sha256:2b4f19dae3a777dfc3b76730bda1e82e1f66ab2a2686fa93ca78edbfb4f04ffe
podman build -t localhost/cluster-build -f /opt/cluster/deploy/Buildfile /opt/cluster/deploy
podman image inspect localhost/cluster-buildВ CLUSTER_BUILD_IMAGE указывается полученный образ по SHA256. Установка системных зависимостей происходит при сборке этого образа. Среда сборки модуля работает без прямой сети. Python-зависимости берутся из приложенных wheels или получаются из PyPI через канал сборки в ядре. Для этого самому ядру нужен исходящий HTTPS-доступ к pypi.org и files.pythonhosted.org. Прокси из переменных окружения для этих запросов не используется.
Параметры
Скопируй примеры окружения:
install -m 600 /opt/cluster/deploy/core.env.example /etc/cluster/core.env
install -m 600 /opt/cluster/deploy/api.env.example /etc/cluster/api.envВ core.env задаются образы, лимиты и почта. Для SMTP поддержан TLS на порту 465. Нужны адрес сервера, пользователь, пароль и адрес отправителя. Пароль остаётся в файле окружения, а не в аргументах команды.
В api.env задаётся CLUSTER_ORIGIN, полный HTTPS-адрес без пути, общий для сайта и API. Тот же домен указывается в deploy/Caddyfile. Замени REPLACE_WITH_DIGEST, REPLACE_WITH_PASSWORD и адреса example.com своими значениями.
В примере службы установлены следующие пределы на юзербота:
| Ресурс | Предел |
|---|---|
| Хранилище | 1 GiB |
| Память | 768 MiB |
| Процессор | 1 ядро |
| Процессы | 128 |
Это пределы группы юзербота, общие для модулей, сборок и терминала. При --volumes дополнительно резервируется место для уплотнения и восстановления. Размер существующего тома не меняется от нового значения CLUSTER_STORAGE, для этого нужен перенос. Подробнее в STORAGE.md.
Службы
После настройки окружения установи службы:
cp /opt/cluster/deploy/cluster-core.service /etc/systemd/system/
cp /opt/cluster/deploy/cluster-api.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now cluster-core cluster-apiCaddy устанавливается отдельно. Домен направляется на сервер, содержимое deploy/Caddyfile добавляется в конфигурацию Caddy. HTTPS-запросы передаются на 127.0.0.1:8080, наружу этот порт не открывается.
Caddy перезаписывает X-Real-IP. API с параметром --proxy доверяет этому заголовку, поэтому прямой доступ к порту API извне должен быть закрыт.
HTTP-службе доступен только сокет api.sock. Базы, ключ, control.sock и module.sock остаются закрыты. Во время перезапуска ядра операции возвращают недоступность до появления сокета.
Шаблон Caddy направляет запросы в API. Для подключения собственного сайта выдели /api/* под этот прокси, остальные пути направь в свою службу или каталог файлов. Сам API не обслуживает HTML, CSS и JavaScript.
Сайт из каталога web/ сначала собирается командой npm run build. Готовые файлы находятся в web/dist/. deploy/Caddyfile его не включает и по-прежнему отправляет весь хост в API. Отдельный фрагмент для статики:
cluster.example.com {
encode zstd gzip
handle /api/* {
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote_host}
header_up -X-Forwarded-For
}
}
handle {
root * /opt/cluster/web/dist
try_files {path} {path}.html
file_server
}
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options nosniff
Referrer-Policy same-origin
}
}CLUSTER_ORIGIN в этом случае равен https://cluster.example.com. Запуск описан в WEB.md.
Администратор
Сначала создаётся обычный профиль с подтверждённой почтой и TOTP. Его ID возвращает auth/profile. Затем к ExecStart ядра добавляется --admin PROFILE_ID через переопределение systemd. После изменения выполняются systemctl daemon-reload и перезапуск ядра.
По умолчанию административная панель требует назначения ID и включённого TOTP. Параметр --admin можно указать несколько раз. Для явно назначенного администратора требование включённого TOTP можно отдельно отключить параметром --admin-without-totp PROFILE_ID. Этот ID должен также присутствовать в --admin; для остальных администраторов требование сохраняется. Параметр не изменяет второй фактор, который пользователь включил добровольно.
Для ограничения регистрации можно добавить --pilot-email user@example.com. Если этот параметр указан, регистрироваться могут только перечисленные адреса. Без него такого ограничения нет.
Подписка
Поставленная служба запускается без тестовых покупок. Доступ можно выдать подарочным кодом или операторской операцией. Для проверки покупки добавь --test-payments явно: она продлевает доступ без списания денег, API возвращает признак тестового режима.
Для Stripe используется отдельный параметр --payments-file /etc/cluster/stripe-test.json. Ключ API и секрет подписи хранятся в закрытых файлах вне проекта. HTTP-служба принимает подписанные подтверждения по POST /api/payments/stripe. Без поля mode выбирается песочница. Для настоящих платежей нужны отдельные реквизиты и явный mode=live, локальная проверка --test-payments при этом выключается. Порядок перехода приведён в PAYMENTS.md.
Подарочные коды создаются в административной панели и активируются один раз. Периоды и сроки хранения приведены в PRODUCT.md.
Telegram и боты
Подключение Telegram выполняется через HTTP API. Нужны собственные api_id, api_hash, номер, код и при необходимости пароль Telegram. Смена аккаунта подтверждается паролем Cluster и TOTP. Фактическая сессия остаётся в ядре.
Сервисный бот BridgeSentinel настраивается операторской операцией bot/service. Токен передаётся JSON через stdin, он не должен попадать в историю команд. Примеры операторских запросов находятся в API.md.
Для сервисного бота используется HTTPS-адрес доставки обновлений /api/bot/update, для личного /api/bot/update/UNIT_ID. Настройка выполняется через bot/webhook с отдельным случайным секретом. Параметры приведены в ROUTES.md.
Создание личных ботов требует поддержки Managed Bots у сервисного бота. Другой способ подключения личного бота использует готовый токен. После настройки доставки обновлений нужно проверить получение обновления, одной успешной регистрации недостаточно.
Адрес собственного Mini App настраивается у сервисного бота. Telegram-вход через API требует зарегистрированного профиля и подтверждённой привязки Telegram ID.
Проверка
Тесты ядра и документация:
/opt/cluster/env/bin/python -m pip install --require-hashes -r requirements-test.txt
/opt/cluster/env/bin/python -m pytest -q
/opt/cluster/env/bin/python scripts/check_docs.pyСкрипты probe_*.py запускают отдельные проверки окружения. Им нужны операторские права, они создают временные среды и тома. Используй отдельную тестовую машину.
| Скрипт | Проверка |
|---|---|
probe_core.py | gVisor, права, сеть, тома и ограничения |
probe_accounts.py | Профили и доступ к изолированной среде |
probe_modules.py | Загрузка и выполнение модулей |
probe_project.py | Команды, обмен, сборка, копии и терминал |
probe_native.py | C-программа, библиотека, Python-зависимости и обработчики |
probe_network.py | HTTPS и WSS с учебными данными |
probe_service.py | Учётка API, аварийный перезапуск, том и восстановление |
probe_proxy.py | HTTPS и WSS через HTTP/SOCKS5-прокси |
Первые три скрипта принимают закреплённый образ Python. probe_native.py получает образ с компиляторами из CLUSTER_BUILD_IMAGE. probe_proxy.py принимает публичный IPv4 самой тестовой машины и временно открывает на нём порт. Случайные реквизиты и допустимые назначения ограничивают этот проверочный прокси, после завершения он закрывается.
Эти проверки не подключают настоящий Telegram-аккаунт. После настройки служб отдельно проверяются получение письма, TOTP, авторизация Telegram, личный бот и доставка обновлений.
Обновление
Перед обновлением создаётся операторская копия, затем переустанавливается Python-пакет. Службы перезапускаются после завершения установки.
Ядро проверяет версию схемы Mekansm и не открывает неизвестную версию. Старые подключения модулей отзываются, оставшиеся среды этого сервиса удаляются по метке. Исправные включённые экземпляры запускаются заново, отключённые из-за ошибки ждут ручного включения.
Для службы ядра нужен KillMode=mixed: сначала сигнал завершения получает ядро, которое отменяет задачи и закрывает среды. Если systemd одновременно посылает сигнал всем процессам группы, штатная остановка модуля может сохраниться как ошибка. По истечении TimeoutStopSec systemd завершает оставшиеся процессы принудительно.
Восстановление
Пользовательская копия восстанавливается для остановленного юзербота через HTTP API. Операторская копия создаётся при остановленных службах и восстанавливается в новый каталог. Команды и состав копий приведены в STORAGE.md.
Telegram-сессии в копии не включаются. После потери сессии требуется повторное подключение. Внешняя копия данных и отдельная копия ключа нужны для восстановления после потери диска.
Обслуживание
systemctl status cluster-core cluster-api
journalctl -u cluster-core -u cluster-apiЖурналы модулей доступны через API. Ключ, пароль SMTP и токены не выводятся в журнал службы.
Изменение квоты хранилища требует копии и переноса в том подходящего размера. База без --volumes также требует переноса, её нельзя заменить пустым томом.
Место и срок хранения внешних копий выбираются оператором. Восстановление проверяется отдельно от создания копии.