Архитектура
Документ описывает устройство фреймворка и решения, на которых держится его надёжность. Практический API — в DEVELOPER.md, поведение конкретных модулей — в MODULES.md.
Общая схема
┌────────────────────────────────────────────┐
│ install.sh (оркестратор) │
│ preflight → выбор → опрос → план → цикл │
└───────┬──────────────┬──────────────┬──────┘
│ │ │
lib/preflight.sh lib/tui.sh lib/core.sh + lib/os.sh
(проверки среды) (виджеты UI) (логи/состояние/откат +
семейство ОС: пакеты,
файрвол, PG-раскладка)
│ │
└──────────┬──────────────────┘
▼
modules/NN-*.sh (subshell, set -Eeuo pipefail)
│
/var/lib/mitdev/{state/, rollback.stack, install.env}
▲
│
┌──────────────────┴─────────────────────────┐
│ mitdev (администрирование) │
│ info doctor pg k8s app deploy backup … │
└────────────────────────────────────────────┘
Два исполняемых файла делят общий фундамент (config/ + lib/) и общаются
между собой только через файлы состояния — никаких скрытых зависимостей.
Поток установки
Оператор
│ sudo ./install.sh
▼
┌──────────┐ среда ОК? ┌─────────────┐ ответы ┌──────────┐
│ preflight │ ────────────▶ │ выбор + опрос │ ──────────▶ │ план │
└──────────┘ (иначе стоп) └─────────────┘ (дефолты из └────┬─────┘
install.env) │ подтверждение
▼
┌───────────────────────────────┐
│ цикл по модулям (по порядку) │
│ ┌──────────────────────────┐ │
│ │ module_install (subshell)│ │
│ └────────────┬─────────────┘ │
│ успех │ сбой │
│ ┌───┴────┐ │
│ ▼ ▼ │
│ state_mark rollback_to │
│ (защищённый │
│ при переуст.) │
└───────────────┬────────────────┘
▼
install_admin_tool → apply (listen/HA)
▼
mitdev doctor
Жизненный цикл модуля
┌─────────────────┐
│ module_install │◀── run_step + rollback_register
└───────┬─────────┘ (каждое действие идемпотентно,
успех │ сбой мутация → регистрируется откат)
┌──────────┴──────────┐
▼ ▼
state_mark module_rollback / rollback_to
(маркер .done) (обратные действия в обратном порядке;
│ деструктив пропускается при переустановке)
▼
module_verify ── быстрая проверка «компонент работает»
│
▼
module_remove ── удаление (mitdev remove <модуль> [--purge])
Топология: приватная сеть + HA (несколько серверов)
Публичные IP (пользователи) Приватная сеть WireGuard (mesh)
185.x 91.x 46.x 10.100.0.0/24 — каждый с каждым
│ │ │ app-1 ─── app-2
▼ ▼ ▼ │ ╲ ╱ │
┌─────┐┌─────┐┌─────┐ │ ╳ │ (нет центрального
│app-1││app-2││db-1 │ ← nginx/приложения │ ╱ ╲ │ хаба — SPOF нет)
└─────┘└─────┘└─────┘ db-1 ─────┘
локальный IP 10.100.0.1/.2/.3 + <узел>.internal
Поверх приватной сети — HA-кластеры (cluster-init → token → join):
PostgreSQL (репликация + watchdog) Redis (Sentinel) MongoDB (replica set)
RabbitMQ (кластер) MinIO (distributed)
Службы слушают только приватную сеть: mitdev net bind <служба> private
Ключевые решения
1. Оркестратор ничего не знает о содержимом модулей
install.sh оперирует реестром MODULE_REGISTRY
(id|файл|обязательность|дефолт|метка) и единым контрактом модуля.
Добавление компонента = новый файл + одна строка в реестре. Порядок
выполнения задаётся номерами файлов, зависимость между модулями объявляется
декларативно (add_dependency), а не зашивается в код модулей.
2. Модуль выполняется в строгом subshell
( set -Eeuo pipefail
source config/defaults.conf
source lib/core.sh
source modules/NN-x.sh
module_install
) &
Следствия:
- падение модуля (любая непроверенная команда при
set -e) не убивает инсталлятор — оркестратор получает код возврата и решает, что дальше; - модули не могут случайно засорить окружение друг друга;
- фоновое выполнение даёт честный спиннер без хитростей с TTY.
3. Всё состояние — файлы, а не переменные
Subshell не может передать данные наверх через переменные, поэтому:
- маркеры выполнения —
state/<id>.done: основа идемпотентности и повторных запусков; - стек отката —
rollback.stack: модуль дописывает команды отмены по мере мутации системы; при сбое оркестратор откатывает записи, добавленные «выше» зафиксированной до запуска отметки, в обратном порядке; - факты установки —
install.env: пользователь деплоя, порт SSH, компоненты, роль PG-кластера — их читает инструментmitdev.
Бонус: состояние переживает обрыв SSH-сессии посреди установки.
4. Откат — по границе модуля, а не «всё или ничего»
Полный откат сервера после 40 минут установки почти никогда не нужен; нужен откат именно сломавшегося шага. Поэтому стек отката срезается до отметки, взятой перед запуском модуля, и пользователь сам решает: откатить модуль, продолжить остальные или прервать установку.
Отдельно каждый модуль обязан реализовать module_rollback() — полный
детерминированный откат для ручного использования и тестов.
5. Весь пользовательский ввод — до начала установки
Модули никогда не разговаривают с пользователем: имя/пароль/ключ/порт/роль кластера собираются заранее и передаются через окружение. Это делает модули пригодными для неинтерактивного использования и упрощает их тестирование.
6. TUI — деградирующий слой, а не зависимость
lib/tui.sh выбирает лучший доступный бэкенд: gum (ставится автоматически
из репозитория Charm или GitHub-релиза) → whiptail → dialog → plain-подсказки.
Каждый виджет (tui_confirm, tui_input, tui_password, tui_choose,
tui_checklist, tui_run, tui_progress, экраны успеха/ошибки) реализован
для всех бэкендов. Отказ установки gum ничего не ломает.
7. Безопасность важнее удобства в трёх местах
- sshd: новый конфиг проверяется
sshd -tдо перезапуска; mitdev может сгенерировать ключи Termius для deploy/root, аPasswordAuthentication noприменяется глобально только при наличии deploy-ключа; смена порта учитывает socket-активацию Ubuntu; - UFW: правило SSH добавляется до
ufw enable; - PG-кластер: порт 5432 открывается только с конкретных IP реплик; данные реплики не удаляются, а откладываются.
Подробнее — SECURITY.md.
8. Логи — источник истины
Красивый интерфейс показывает статусы; полная правда — в
/var/log/mitdev/install.log (каждый шаг с полным выводом команд)
и errors.log (только сбои). run_step гарантирует, что ни одна команда
не выполнится без записи в журнал и проверки кода возврата.
Поток данных установки
defaults.conf ──► env ──► install.sh ──┬─► TUI-опрос ──► env (DEPLOY_*, SSH_*, PG_*)
│
└─► цикл модулей:
отметка стека отката
subshell(module_install)
├─ ok → state/<id>.done
└─ fail→ rollback_to(отметка)? → продолжить/прервать
┌─► /opt/mitdev + симлинк
└─► install.env, итоговая сводка
Инструмент mitdev
Тонкий слой над теми же библиотеками: каждая команда — функция cmd_*,
диспетчеризация — один case. Команды только читают состояние
(install.env, маркеры) и системные API (systemd, psql, kubectl, docker) —
у инструмента нет собственной базы данных, поэтому он не может
рассинхронизироваться с реальностью. Read-only команды работают без root
(логирование при этом уводится в /dev/null).
Тестовая стратегия
tests/smoke.sh— статический контракт репозитория: структура,bash -n, ShellCheck, контракт модулей, плейсхолдеры шаблонов, запрет заглушек. Выполняется где угодно, в т.ч. в CI без root.tests/verify.sh— рантайм-контракт сервера:module_verify()каждого установленного модуля + сквозные проверки (HTTP-health nginx, права каталогов, членство в группах). Код выхода = число сбоев.- Ручной прогон на одноразовых VM (LXD/Multipass) — рецепты в DEVELOPER.md; обязательно проверяются повторный запуск и сценарий сбоя модуля.