Журнал изменений
Формат основан на Keep a Changelog, проект следует семантическому версионированию.
[2.35.0] — 2026-07-15
Добавлено — модуль cf: отказоустойчивый app-ingress через Cloudflare
Новый командный модуль 24-cloudflare
(mitdev cf …): ввод app-узла в отказоустойчивый ingress через Cloudflare —
всё только через API Cloudflare (без дашборда, без локальных tunnel-конфигов).
Отвечает на вопрос «как пользователи попадут на живой узел приложения», в т.ч. при
«сером отказе» (жив app, мёртв путь к БД), не открывая на узле ни одного входящего
порта.
cf tunnel create/config/run/remove— Cloudflare Tunnel в remotely-managed режиме (config_src=cloudflare): ingress-правила хранит Cloudflare, на узле локальногоconfig.ymlнет. cloudflared держит исходящее соединение к edge (QUIC udp/7844 + http2-фолбэк tcp/7844);cf tunnel runставит коннектор и, убедившись что он активен, закрывает вход 80/443 (исходящие 443/7844 разрешаются явно, SSH не трогается, откат правил регистрируется до удаления).cf dns set/rm— идемпотентное управление записями зоны (A/AAAA/CNAME/TXT): поиск по имени И типу, create-vs-update, FQDN-валидация против дублей.cf lb monitor/pool/create— Cloudflare Load Balancing поверх узлов (origin = туннель или публичный IP поCF_INGRESS_MODE).- health-эндпоинт узла (
cf health install) — HTTP200, только если живы app И БД И Redis, иначе503. Слушает loopback/WG-адрес, никогда 0.0.0.0 (сигнал failover не светит наружу). Ловит серый отказ, который простой пинг не видит. - tunnel-watchdog (
cf watchdog install) — при устойчивом503снимает узел из ротации (stop cloudflared), при возврате200возвращает. Дебаунс против флаппинга; снимает только по node-local отказу, не по общей БД/Redis (иначе из ротации вышли бы все узлы разом). - health-монитор Cloudflare уводится в health-эндпоинт через скрытый
health-хост: правило ingress
health.<домен>+ заголовокHostмонитора, при этом публичной DNS-записи на health-хост нет (карта здоровья узлов наружу не видна). cf status— сводка (токен, туннели/коннекторы, пулы/health origin'ов, локальные демоны). Никогда не выдаёт «здорово» по неопрошенной секции;rc 0только если всё опрошено и здорово — годится как гейт мониторинга. Различает «сеть», «Cloudflare деградировал (5xx/429)» и «токен невалиден».
Безопасность: API-токен и connector-token только в кредах (0600), никогда в
ps/argv (curl-конфиг на stdin; connector-token через EnvironmentFile, не в
ExecStart), редакция в логах, минимальные scope. Три режима ingress
(tunnel-only/lb-only/lb-over-tunnel) согласованы между командами; в lb-only
health-failover не обеспечить — команды LB честно отказывают fail-loud.
Проверено офлайн (848 юнит-ассертов: API-вызовы через подменяемую обёртку
cf_api, локальные проверки через стабы). Схема требует чекпойнта на живом
Cloudflare перед продом — вопросы вынесены в
docs/modules/24-cloudflare.md («Чекпойнт живого
Cloudflare»): Host-override для origin'ов cfargotunnel.com, TUNNEL_TOKEN из
окружения cloudflared, форма ответа /pools/{id}/health, socat + IPv6-bind.
[2.34.0] — 2026-07-13
Исправлено — окно двух пишущих primary не было закрыто (self-fence считался в «проверках», а не в секундах)
Предыдущий фикс инварианта C2 сравнивал SELF_FENCE_THRESHOLD=3 с
FAIL_THRESHOLD=6 («3 < 6 → безопасно») — но итерация цикла на пути
self-fence (primary зондирует ВСЕХ пиров дважды: цикл поиска winner'а +
quorum_ok) стоит дороже итерации на пути promote (реплика зондирует только
PRIMARY_HOST). При сетевом blackhole (WireGuard молча роняет пакеты — каждый
зонд уходит в полный таймаут) это давало ~92с до реального огораживания
против ~80с до promote на majority-стороне — окно ~12с, в течение которого
оба узла принимают запись, а pg_rewind затем молча выбрасывает транзакции
одной из сторон.
- Бюджет self-fence переведён из «проверок» в wall-clock СЕКУНДЫ:
SELF_FENCE_SECONDS(дефолт 20с, былоSELF_FENCE_THRESHOLD=3проверки). Отсчитывается от epoch-таймстампа первой потери кворума (should_self_fence_at), а не от количества итераций цикла. - Устранено двойное зондирование пиров за итерацию: раньше primary-ветка
зондировала одних и тех же пиров дважды (поиск winner'а +
quorum_ok→peer_visible) — теперь один проход на итерацию кэширует и роль, и видимость (probe_peer_once/peer_role_cached/peer_visible_cached),quorum_okчитает из кэша (use_cache=yes). Живой пир (роль ≠ down) больше не платит за отдельныйhost_alive-зонд вовсе. - На новых дефолтах худшее время огораживания (53с: дедлайн 20с + худшая
итерация 23с + haproxy
fall 5 × inter 2s= 10с) СТРОГО меньше худшего времени promote (80с) — запас 27с, посчитан тестом (tests/pg-watchdog-unit.sh) из явных констант таймаутов, а не из идеализированной формулы. PG_WATCHDOG_SELF_FENCE_THRESHOLD→PG_WATCHDOG_SELF_FENCE_SECONDS(env дляmodules/18-postgresql-cluster.sh).- Комментарий у снятия self-fence больше не утверждает ложную симметрию:
ветка «кворум есть» дебаунсится тем же дедлайном
SELF_FENCE_SECONDS, что и установка, но проверяется реже (спитCHECK_INTERVAL*4=20с противCHECK_INTERVAL=5с на пути fence) — направление безопасное (медленнее снимаем), но комментарий теперь говорит правду.
Исправлено — редакция пароля в логах не работала (SUPERUSER-пароль PostgreSQL уходил в install.log открытым текстом)
redact_log_text (lib/core.sh) имеет единственного вызывающего —
format_command, который прогонял каждый аргумент через printf '%q' ДО
редакции. После %q строка PASSWORD 'S3cret' превращается в
PASSWORD\ \'S3cret\' — пробел и кавычка становятся экранированными
последовательностями, и правило s/(PASSWORD[[:space:]]+)'[^']*'/…/Ig
никогда не совпадало. run_step писал команды вида
CREATE ROLE "crm_plugin_admin" WITH LOGIN SUPERUSER PASSWORD 'xxx' в
install.log открытым текстом.
format_commandтеперь редактирует каждый argv-элемент ДО квотирования%q, а не после — пробел и кавычки видны как есть.- Добавлен
tests/redact-unit.sh: проверяет именно путьrun_step→format_command(с%q) и падает, если секрет просачивается; покрывает SQL-литералPASSWORD '...'иpassword=/passwd=/token=/secret=/MINIO_ROOT_PASSWORD=.
[2.33.0] — 2026-07-13
Исправлено — self-fence огораживал primary, но не рвал его сессии (тихая потеря транзакций)
Watchdog огораживает вставший в меньшинство mesh primary тем, что переводит
role-endpoint (:8008) в 503 — HAProxy должен на это среагировать разрывом уже
открытых соединений, иначе приложение продолжает писать в огороженный узел, а
при восстановлении сети pg_rewind молча отбрасывает эти транзакции. Ранее в
этой же ветке on-marked-down shutdown-sessions был снят из pg-proxy.cfg,
чтобы блип :8008 не ронял живой пул — но тем самым self-fence лишился
исполнительного механизма.
Первопричина блипов устранена ниже (персистентный listener вместо
socket-activation), поэтому shutdown-sessions возвращён — DOWN теперь
означает настоящую смену роли, а не блип.
templates/haproxy/pg-proxy.cfg.template:default-serverснова рвёт сессии на DOWN (on-marked-down shutdown-sessions), терпимость к джиттеру (fall 5 inter 2s) и таймауты пуловых соединений (timeout client/server/ tunnel 1h) сохранены.- Добавлен TCP keepalive (
option clitcpka,option srvtcpka) — без него полу-мёртвое соединение (узел исчез из mesh без FIN/RST) висело доtimeout tunnel(1ч), занимая слот пула.
Исправлено — role-endpoint терял ~50% коннектов на socket-activation
[email protected] поднимался per-connection через systemd
socket-activation и ронял примерно каждое второе TCP-соединение RST-ом до
ответа — это и было первопричиной блипов :8008 выше. Заменён на персистентный
socat-листенер (mitdev-pg-role.service, User=postgres). На db-узлах
появилась новая зависимость — пакет socat.
modules/12-postgresql.shпри определении, снимать ли оверлей кластера, теперь смотрит на оба варианта юнита (mitdev-pg-role.socketиmitdev-pg-role.service) — иначе на апгрейженных узлах оверлей молча не снимался бы.
Добавлено — отдельная SUPERUSER-роль для провижена плагинов
Провижен плагинов CRM теперь выполняется от отдельной роли
crm_plugin_admin (SUPERUSER), а не от обычной app-роли — привилегия не
размазывается на рантайм-подключения приложения. Повторный прогон не
перевыпускает пароль молча (иначе рвался PLUGIN_DB_ADMIN_URL).
Исправлено — пароль SUPERUSER-роли утекал в лог открытым текстом
redact_log_text редактировал только пары вида password=значение, но не
SQL-литерал PASSWORD '...' — команды вида
CREATE ROLE ... SUPERUSER PASSWORD 'xxx' уходили в install.log открытым
текстом. Каталог логов создавался простым mkdir -p, что при root umask 022
даёт 0755, а лог — 0644: локальный пользователь мог прочитать пароль
суперпользователя PostgreSQL (заодно и пароли app-роли, роли репликации).
redact_log_textтеперь маскируетPASSWORD '...'независимо от регистра.- Каталог логов создаётся с правами
0750, файлы лога —0640.
Исправлено — осиротевший слот репликации мог забить диск на primary
heal_slot в watchdog глотал ошибку создания слота (|| true) и безусловно
шёл дальше на ALTER SYSTEM + рестарт postgres — если слот в итоге не
создавался (например, на новом primary после failover ещё нет нужной
pg_hba-записи), реплика получала ту же ошибку, которую heal_slot должен был
вылечить, плюс лишние рестарты. Теперь результат создания слота проверяется
фактом на целевом узле, а не по проглоченному коду возврата.
max_slot_wal_keep_size ограничивает, сколько WAL слот без потребителя может
удержать — раньше предела не было, и осиротевший слот мог довести pg_wal до
отказа диска на primary. Тот же параметр добавлен и во второе место, где
рендерится drop-in репликации (mitdev pg add-replica), — раньше там был
только wal_keep_size без предела.
Исправлено — self-fence мог сработать позже промоушна на majority-стороне, а снимался несимметрично быстро
SELF_FENCE_THRESHOLDбольше не выводится изFAIL_THRESHOLD(раньше при дефолтах это давало эффективное время фенса больше времени promote — окно, где оба узла одновременно принимают запись, аpg_rewindпотом молча теряет транзакции одной из сторон). Дефолт зафиксирован на значении, гарантированно меньшем времени promote.- Снятие self-fence теперь дебаунсится симметрично огораживанию (тот же
порог подряд чистых проверок кворума) — раньше огораживание требовало
SELF_FENCE_THRESHOLDподряд промахов, а разогораживание снималось на первой же чистой проверке, что могло мгновенно вернуть запись на устаревший primary. - Watchdog больше не принимает решений об огораживании/promote в итерации, где локальный postgres не отвечает (раньше «упавшая СУБД» неотличимо читалась как «я primary и не в recovery» и запускала цепочку demote/pg_rewind/pg_basebackup, рассчитанную на настоящий failover).
[2.32.3] — 2026-07-10
Исправлено — self-update ставил устаревшую сборку из кеша
Установщик /get скачивает архив с ?nocache=<timestamp> — иначе CDN и
обратные прокси отдают вчерашний mitdev.tar.gz. mitdev self-update этой
защиты не имел: он получал закешированный архив, сообщал его версию как «новую»
и рапортовал об успешном обновлении. На узле оставался старый код, хотя
version.txt на сайте показывал свежую версию.
self-updateзапрашивает архив с cache-busting и заголовкамиno-cache.- Версия скачанного архива сверяется с
version.txtна сайте. При расхождении обновление отменяется с явным сообщением о кеше — вместо тихой установки устаревшей сборки.
[2.32.2] — 2026-07-10
Исправлено — «Отставание» реплики показывало тишину на primary
mitdev pg status считал отставание как now() - pg_last_xact_replay_timestamp().
Это не отставание, а время с момента, когда реплика применила последнюю
транзакцию: пока primary ничего не пишет, величина растёт секунда за секундой на
идеально синхронной реплике. «Отставание 233 с» означало «на primary четыре
минуты не было записи». Обратная ошибка опаснее: реально отставшая реплика на
нагруженном primary показывала бы близкие к нулю секунды.
- Отставание определяется сравнением полученного и применённого WAL
(
pg_last_wal_receive_lsnvspg_last_wal_replay_lsn). Секунды показываются, только когда реплика действительно не догнала, вместе с объёмом неприменённого WAL. - Когда всё применено,
mitdevтак и пишет: «нет отставания», указывая, сколько на primary нет новых транзакций. - Новый набор
tests/pg-unit.sh(13 проверок) — включён вtests/smoke.sh.
[2.32.1] — 2026-07-10
Исправлено — правила UFW молча не создавались (авто-failover PostgreSQL не работал)
Проверка «активен ли файрвол» была написана так:
ufw status 2>/dev/null | head -1 | grep -q active
head -1 закрывает поток после первой строки, ufw получает SIGPIPE и
завершается с кодом 141, а pipefail протаскивает его через весь конвейер.
Условие становится ложным, и mitdev решает, что UFW выключен — правила молча
не создаются. Это гонка: на свежем сервере вывод ufw status короткий и всё
работает, на живом кластере с десятками правил — нет.
Последствие для PostgreSQL-кластера: узлы не открывали друг другу 5432 и
8008 (эндпоинт роли), поэтому watchdog видел 1/3 узлов вместо 3/3. Кворум
(N/2+1) не набирался НИКОГДА, авто-failover был невозможен, а primary вдобавок
сам себя огораживал (SELF-FENCE, роль отдаёт 503). Кластер выглядел здоровым и
не переживал первую же аварию.
- Добавлена
ufw_active— проверка без конвейера, устойчивая к длине вывода. Все 17 мест (PostgreSQL, Redis, Kubernetes, VIP, приватная сеть,doctor) переведены на неё. _pg_setup_replicaбольше не молчит, когдаPG_CLUSTER_PEERSне задан: фолбэк на один лишь primary оставляет соседние реплики за файрволом и делает кворум недостижимым. Теперь об этом предупреждают прямо при установке.
[2.32.0] — 2026-07-10
Добавлено — mitdev redis proxy: приложение переживает failover
Failover переключает сервер, а не клиента. Приложение, настроенное на прямой
адрес мастера, после переключения продолжает стучаться в узел, ставший репликой,
и получает -READONLY на каждую запись: кластер жив, лежит приложение.
mitdev redis proxy <ip...>поднимает локальный HAProxy на127.0.0.1:6379(порт настраивается черезREDIS_PROXY_PORT). Приложение подключается обычным Redis-клиентом, без поддержки Sentinel. Полный аналогmitdev pg proxy.- Роль узла выясняется по протоколу Redis (
AUTH→INFO replication→role:master): отдельного role-эндпоинта, как у PostgreSQL, у Redis нет. balance first+backupна всех узлах, кроме первого: запись уходит ровно в один узел даже в момент, когда при split-brain два узла объявляют себя мастером.on-marked-down shutdown-sessionsрвёт соединения при смене мастера, чтобы драйвер переподключился к новому.- Пароль Redis попадает в
haproxy.cfg(без него health-check не пройдёт AUTH), поэтому файл переводится в режим640 root:haproxy. mitdev redis proxy-statusпоказывает, кем отвечает узел за прокси:master— запись работает,slave— будет-READONLY.
[2.31.9] — 2026-07-10
Исправлено — remove redis --purge оставлял dpkg в состоянии «rc»
Удаление вызывало apt-get remove (не purge) и отдельно делало
rm -rf /etc/redis. Для dpkg это означает, что conffiles сняты администратором:
пакет остаётся в состоянии «rc», а при переустановке ТОЙ ЖЕ версии файлы из
пакета не разворачиваются. Redis поднимался с неполным конфигом — в частности
без директивы dir, из-за чего репликация ломалась необъяснимым образом
(см. 2.31.8).
- Добавлены
os_pkg_purge/pkg_purge(apt purge; для остальных пакетных менеджеров — обычное удаление, где conffiles и так не отслеживаются). mitdev remove redis --purgeудаляет пакеты вместе с конфигурацией, поэтому следующая установка разворачивает конфиг из коробки.
[2.31.8] — 2026-07-10
Исправлено — исчезнувшая директива dir ломала репликацию
Реплика бесконечно повторяла «Opening the temp file needed for MASTER <-> REPLICA
synchronization: Read-only file system», хотя /var/lib/redis существовал и был
доступен на запись. Redis туда и не обращался: в redis.conf не было
директивы dir, а без неё каталогом данных считается «.» — рабочий каталог
процесса. Под systemd это /, и внутри ProtectSystem=true оно смонтировано
только для чтения.
Директива исчезала сама. remove redis --purge удаляет /var/lib/redis у
работающего Redis; его cwd повисает на удалённом inode. Дальше Sentinel при
каждой смене роли дёргает CONFIG REWRITE, а тот берёт путь из getcwd() —
не получив его, Redis просто выбрасывает dir из файла.
redis_ensure_data_dirвосстанавливаетdir(и сам каталог) перед перезапуском вjoinиcluster-init; существующий путь не перетирается.module_installдописываетdir, если его нет.redis checkбольше не молчит: отсутствиеdir— отдельная поломка со своим сообщением, а не «нечего проверять».
[2.31.7] — 2026-07-10
Исправлено — ложное «РАСХОЖДЕНИЕ» (split-brain) на исправном мастере
mitdev redis status сравнивал адрес мастера, полученный от Sentinel, с
redis_priv_ip. Без root net_meta_get не читает состояние приватной сети, и
redis_priv_ip откатывается на публичный адрес (eth0). Мастер, слушающий wg0,
«не узнавал сам себя» — и на полностью исправном кластере печаталось пугающее
предупреждение о split-brain.
Теперь проверяется, принадлежит ли адрес узлу по ЛЮБОМУ интерфейсу
(redis_ip_is_local): список локальных адресов виден и без привилегий.
Предупреждение остаётся только когда Sentinel указывает на действительно чужой
адрес — то есть когда split-brain настоящий.
[2.31.6] — 2026-07-10
Исправлено — проверка каталога данных не срабатывала, когда была нужнее всего
redis_data_dir спрашивал путь у живого Redis через config get dir. Но Redis
лежит ровно в тех случаях, когда с каталогом беда: ответа нет, путь пустой,
проверка каталога молча пропускается — и redis check рапортует сплошные PONG,
ни словом не обмолвившись, что каталога данных не существует.
- При недоступном Redis путь читается из
redis.conf(dir). - Ответ
config get dirбез значения больше не принимается за путь. module_installпредупреждает, если пользователяredisнет и каталоги не созданы, вместо того чтобы молча пропустить шаг.
[2.31.5] — 2026-07-10
Исправлено — повреждённый при копировании токен винил файрвол
Токен кластера копируют через терминал, где он переносится на несколько строк.
Вставка добавляет пробелы и переводы строк, а иногда токен склеивается сам с
собой — base64 такую конкатенацию декодирует молча, и в расшифровке
оказывается ПО ДВЕ строки MASTER_IP= и PORT=. grep -oE возвращал оба
совпадения, redis-cli получал многострочный хост и падал с «Name or service
not known», а mitdev выводил «Мастер недоступен — откройте порты» с адресом,
разъехавшимся на три строки.
- Токен очищается от пробелов и переводов строк перед декодированием.
- Из расшифровки берётся ПЕРВОЕ совпадение каждого поля (
redis_token_field). MASTER_IPиPORTпроверяются ДО первого сетевого вызова: при мусореmitdevпрямо говорит, что токен повреждён при копировании, и предлагает взять его заново, вместо того чтобы отправлять открывать порты.
[2.31.4] — 2026-07-10
Исправлено — redis cluster-init ронял живой кластер в failover
Повторный mitdev redis cluster-init перезапускал redis-server на мастере
безусловно — даже когда в конфиге нечего было менять. Мастер пропадал на
несколько секунд, соседние Sentinel'ы объявляли +sdown, набирали кворум и
промоутили реплику. Штатная команда сопровождения роняла кластер.
- Перезапуск теперь выполняется, только если конфигурация действительно
изменилась или служба не запущена (
redis_needs_restart). - Если перезапуск всё же нужен, а Sentinel уже мониторит узел,
mitdevпредупреждает о failover и спрашивает подтверждение. Конфигурация при отказе остаётся записанной — её можно применить вручную в удобное окно. cluster-initпринимает-y(в любой позиции) для неинтерактивного запуска.
[2.31.3] — 2026-07-10
Исправлено — переустановка Redis не возвращала каталог данных
mitdev remove redis --purge удаляет /var/lib/redis. Каталог создаёт postinst
пакета — но только при ПЕРВОЙ установке: если пакет остался в системе, повторный
apt install postinst не запускает, и каталог не возвращается. Redis при этом
спокойно стартует, отвечает PONG и обслуживает клиентов — ломается только
репликация: временный файл RDB писать некуда.
Диагноз при этом обманчив. Юнит запускается с ProtectSystem=true, где
/var/lib/redis пробрасывается на запись через ReadWriteDirectories=-/var/lib/redis
(дефис = «не падать, если пути нет»). Каталога нет → проброса нет → путь остаётся
в read-only части namespace, и Redis сообщает Read-only file system, хотя ФС
смонтирована rw, а настоящая причина — отсутствующий каталог.
module_installтеперь создаёт/var/lib/redisи/var/log/redis(redis:redis,750) — установка идемпотентна и после--purge.redis checkи диагностика послеjoinотличают «каталога нет» от «каталог не пишется» и от сетевых проблем, подсказывая точную команду восстановления. Раньше отсутствующий каталог молча пропускался.
[2.31.2] — 2026-07-10
Исправлено — диагностика реплики врала в двух случаях
- Каталог данных в read-only. Реплика соединяется с мастером, получает RDB
и не может записать
temp-<pid>.rdb— в логе Redis это «Opening the temp file needed for MASTER <-> REPLICA synchronization: Read-only file system». Поinfo replicationсостояние неотличимо от оборванной сети (master_link_status:down,master_last_io_seconds_ago:-1), иmitdevотправлял проверять порты иwg0. Теперьredis checkи диагностика послеjoinпроверяют каталогdirна запись и называют причину прямо. Сеть при этом полностью исправна — обычная причина в том, что ядро защитно перемонтировало ФС после ошибки диска. master_link_down_since_seconds— не длительность обрыва. Redis считает его какnow - repl_down_since, аrepl_down_sinceравен нулю, пока линк не поднимался ни разу: в поле оказывается текущий unix-timestamp (~1.7e9). Диагностика печатала «связь обрывалась 1783682299 секунд назад», выдавая «не соединились ни разу» за обрыв. Теперь эти случаи различаются поmaster_last_io_seconds_ago:-1.
[2.31.1] — 2026-07-10
Исправлено — redis join объявлял неудачу на исправной реплике
- Окно ожидания
master_link_status:upувеличено с 20 до 60 секунд. Первичная синхронизация (BGSAVE на мастере + передача RDB) на непустой базе за двадцать секунд не укладывается, и реплика, которая всё делала правильно, получала «связь с master не подтверждена». - Вместо этого сообщения теперь печатается фактическая причина
(
redis_link_diag): идёт первичная синхронизация / локальный redis не ответил / связь обрывалась N секунд назад / соединения не было ни разу. Раньше во всех случаях советовалиmitdev redis check— а он проверяет достижимость мастера по сети и показываетPONGдаже при неподнявшейся репликации, уводя от причины. REDIS_PORTбольше не игнорируется:mitdev redis status,mitdev doctorи ожидание вjoinходили в жёстко зашитый 6379 и на нестандартном порту молча показывали пустой отчёт.
[2.31.0] — 2026-07-10
Исправлено — mitdev redis join не работал на Ubuntu 22.04
redis-cli -t <таймаут> поддерживается не всеми версиями: Ubuntu 22.04 LTS —
основная целевая система — везёт redis-tools 6.0.16, где эта опция отвергается
(Unrecognized option or bad number of args for: '-t'), и соединение не
устанавливается вовсе. Проба мастера падала до попытки связи, а пользователь
получал вердикт «Мастер недоступен — узел НЕ присоединён» с советом открыть
порты: настоящая причина была на своём же узле, а не в файрволе.
- Ограничение по времени теперь ставится через coreutils
timeout(redis_cli_t), который есть на всех поддерживаемых системах. Опция-tне используется. - Затронуты были все три пробы:
redis join/redis check(проба мастера), проба Sentinel вredis check(молча считала Sentinel мёртвым) и проверка Redis вmitdev web status(ложное «redis не отвечает, вход не сработает»). redis_probeбольше не выдаёт несовместимостьredis-cliза проблему сети: для неё отдельное сообщение. Таймаут (rc 124) тоже назван прямо.
Исправлено — mitdev молча завершался посреди работы (pipefail + errexit)
mitdev redis cluster-remove на боевом узле не делал ничего и не печатал ни
строчки, а узел навсегда оставался role:slave при мёртвом мастере
(master_link_status:down, slave_read_only:1 — то есть база в read-only).
Причина: под set -Eeuo pipefail конструкция x=$(… | grep … | …) завершает
весь скрипт, если grep ничего не нашёл — «нет совпадений» для grep это
код возврата 1, а pipefail протаскивает его через весь конвейер. В
ufw_delete_by_comment это срабатывало на любом кластере, собранном до
появления меток mitdev: в правилах UFW: Sentinel уже остановлен, а до снятия
replicaof из redis.conf дело не доходило.
ufw_delete_by_commentбольше не считает «нечего удалять» ошибкой.redis_ha_teardownотцепляет узел от мастера СРАЗУ после остановки Sentinel, до правил файрвола: сбой на любом последующем шаге теперь оставляет узел автономным мастером, а не read-only репликой несуществующего сервера.- Тот же дефект устранён ещё в шести местах, где он давал тихий обрыв:
mitdev redis statusбез root (обрывался сразу после заголовка, потому чтоredis-cliотвечаетNOAUTHс ненулевым кодом),mitdev doctorпри Redis без пароля,mitdev redis tokenбезsentinel.conf,mitdev minio token,mitdev mongo token(понятное сообщение об ошибке было недостижимо),mitdev removeпоследнего установленного компонента и watchdog приватной сети (lib/net-watchdog.sh). mitdev redis statusбез root больше не притворяется: раньше печатал пустой отчёт, теперь предупреждает, что безsudoпароль Redis не читается.
Добавлено — удаление модуля изнутри службы
mitdev redis delete [--purge], а такжеremove/uninstall— то же самое дляminio,rabbitmq;mitdev mongo deleteиmitdev pg delete(там без алиасаremove, чтобы не путать сremove-member/remove-replica). Все они делегируют в существующийmitdev remove <модуль>, включая--purgeи-y. Раньшеmitdev redis removeотвечал только строкой usage, а команда полного удаления жила на верхнем уровне и не находилась.- Удаление работает поверх сломанной или частичной установки: проверка «модуль установлен» больше не блокирует уборку остатков.
mitdev redis cluster-removeполучил алиасыremove-cluster/uncluster(как уpg).
Исправлено — проверка CRLF в tests/smoke.sh не работала
Проверка «все скрипты — LF» была сломана в обе стороны сразу, и ошибки
маскировали друг друга. Под Git Bash (MSYS) grep читает файл в текстовом
режиме и вырезает \r, поэтому настоящий CRLF не находился никогда; а литерал
$'\r' разворачивался там в пустую строку, из-за чего grep совпадал с каждым
файлом подряд и тест «краснел всегда». Теперь CR-байты считаются через
tr/wc: проверка ловит реальный CRLF и молчит на чистом дереве.
[2.30.0] — 2026-07-10
Исправлено — узел не поднимался после перезагрузки, файрвол и остальные кластеры
- Службы, привязанные к приватному IP, стартовали раньше туннеля.
mitdev net bind <служба> privateзаставляет postgresql/redis/mongod/rabbitmq/ minio слушать адрес WireGuard, но ни у одной из них не былоAfter=wg-quick@<if>. После перезагрузки они поднимались до туннеля и падали сCannot assign requested address; postgresql на Debian не имеетRestart=, поэтому узел возвращался с мёртвой БД. Теперьnet bind … privateставит drop-inAfter=/Wants=wg-quick@<if>, аnet_enable_forwardвключаетip_nonlocal_bind— bind на ещё не поднятый адрес перестал быть ошибкой.mitdev net removeснимает drop-in'ы и называет службы, которые надо вернуть на публичный адрес. mitdev remove firewall --purgeстирал правила всех кластеров.ufw --force resetсносит всё, а повторная установка модуля восстанавливает только SSH и веб-порты — кластеры молча теряли связность. Теперь правила с меткойmitdev:снимаются в/var/lib/mitdev/ufw-mitdev-rules.savedперед сбросом и восстанавливаются при следующей установке файрвола.- MinIO, RabbitMQ, MongoDB: порты открывались только при приватной сети.
Тот же дефект, что был в Redis: код явно допускает публичные IP, но
ufw allowстоял под[[ -n "$sub_cidr" ]], а веткиjoinне открывали портов вообще. Кластер на публичных адресах не собирался и не переживал отказ узла. Порты теперь открываются для каждого узла набора, в обе стороны:minio_allow_peers,rabbit_allow_peers,mongo_allow_peers. joinменял конфигурацию до проверки связи. RabbitMQ успевал сделатьreset(стереть Mnesia) и лишь потом обнаруживал недоступный primary, оставляя узел вне любого кластера. Теперь и RabbitMQ, и MongoDB, и MinIO проверяют доступность до первой мутации и отказываются собирать нерабочий кластер.- RabbitMQ:
/etc/hostsзаполнялся односторонне. Присоединяемый узел прописывал primary, но primary не узнавал имени новичка, а реплики — имён друг друга. Кластеризация идёт поrabbit@<hostname>, поэтому кластер разваливался при первом рестарте. Записи теперь накапливаются (rabbit_hosts_set), добавлена командаmitdev rabbitmq allow <ip> <имя>. - RabbitMQ: не задавался
cluster_partition_handling(дефолтignore— обе половины продолжают работать и расходятся). Теперь выставляетсяautoheal, настраивается черезRABBIT_PARTITION_HANDLING; для 3+ узлов рекомендуетсяpause_minority. Если версия не поддерживает quorum-очереди, предупреждение стало явным: classic-очередь на упавшем узле недоступна. - MongoDB: набор из 2 узлов без арбитра терял primary при отказе одного узла
(нет большинства), и код об этом молчал. Добавлена
mitdev mongo add-arbiter <ip>;add-memberпредупреждает при чётном числе членов. - MinIO: набор из <4 узлов не переживает отказа узла (write-quorum = N/2+1
при одном диске на узел). Раньше — только
log_warn; теперь требуется явное подтверждение.joinждёт кворум и не рапортует ложный успех. removeне снимал кластерный оверлей (переустановка наследовала мусор): у MinIO оставался drop-in10-mitdev-cluster.confсExecStart, ссылающимся на пустойMINIO_VOLUMES; у MongoDB —keyFile,replSetNameи приватныйbindIp(после удаления сети mongod падал на старте); у RabbitMQ узел не покидал кластер и оставался «фантомом» в Mnesia соседей. Всё это теперь снимается, правила UFW удаляются по метке комментария.redis_ha_teardownне закрывал правило для приватной подсети (его закрывала только CLI-обёртка, которую путь удаления модуля не вызывает).
Добавлено
ufw_delete_by_commentиufw_export_mitdev_rules/ufw_restore_mitdev_rulesвlib/os.sh— работа с правилами по меткеmitdev:, вместо неперсистентных списков IP.mitdev rabbitmq allow <ip> <имя>,mitdev mongo allow <ip>,mitdev mongo add-arbiter <ip>.mitdev minio cluster-initиmitdev mongo cluster-initпринимают список узлов, чтобы открыть файрвол заранее.- Переменная
RABBIT_PARTITION_HANDLING.
Исправлено — отказоустойчивость PostgreSQL (потеря данных и split-brain)
Аудит после инцидента с Redis показал, что кластер PostgreSQL не переживал отказ узла: у watchdog не было кворума, а взаимная видимость узлов была сломана. Каждый дефект по отдельности приводил к двум primary одновременно.
- Порт эндпоинта роли (8008) не открывался между узлами кластера — его
открывал только
mitdev pg allowи только для серверов приложений. Именно по нему watchdog узнаёт, кто стал новым primary. При активном UFW реплики не видели друг друга, каждая повышала себя, а вернувшийся primary не мог себя зафенсить. Теперь 5432 иPG_ROLE_PORTоткрываются для всех узлов кластера — и на primary, и на репликах (_pg_allow_cluster_peers), и вmitdev pg add-replica. - Нет кворума: изолированная реплика повышалась, пока живой primary принимал
запись. При схождении сети
pg_rewindмолча откатывал всё, что primary записал во время раздела. Теперь реплика повышается, только видя большинство узлов; primary, оказавшийся в меньшинстве, переводит эндпоинт роли в 503 (мягкий self-fence) и перестаёт получать запись через HAProxy/VIP. Для кластера из 2 узлов введёнPG_WITNESS_HOSTS: достижимость witness отличает «primary умер» от «меня отключили от сети». Без witness двухузловой кластер сохраняет доступность primary, но не повышает реплику автоматически. - Два primary с одинаковым timeline оставались навсегда — код лишь писал в
журнал «разрешите вручную». Добавлен детерминированный tie-break: узел с
бóльшим IP самопонижается через
pg_rewind. Сравнение числовое, а не строковое (иначе23.134.42.10<23.134.42.9). - Вернувшийся primary принимал запись до того, как fencing его понижал.
Записи, принятые в этом окне, затем молча уничтожались
pg_rewind. Теперь watchdog при старте в роли primary ставит маркер.mitdev-pg-unverified; пока он есть, эндпоинт роли отвечает503, аpg-vip-checkне отдаёт узлу VIP. Маркер снимается после первой проверки пиров и удаляется при остановке watchdog (ExecStopPost), чтобы не залипнуть. - HAProxy при двух primary балансировал записи round-robin по обоим.
В шаблоне не было
balance. Теперьbalance first+backupна всех узлах, кроме первого: запись всегда идёт ровно в один узел. mitdev remove pgclusterне удалял.mitdev-pg-primary— watchdog читает этот файл раньше конфига и переопределяет имPRIMARY_HOST, поэтому заново собранный кластер следил за адресом покойного мастера. Теперь удаляются все маркеры состояния.- Правила UFW удалялись по
PG_REPLICA_IPS— переменной времени установки, пустой на моментremove. Теперь удаление по метке комментария (ufw_delete_by_comment), включая правила для 8008. pg_rewindписал лог в/tmp, тогда как юнит работает сPrivateTmp=true— файл, на который ссылалось сообщение об ошибке, был недоступен админу. Лог переехал в/var/log/postgresql/mitdev-pg-rewind.log.tests/pg-watchdog-unit.sh— 19 проверок кворума, tie-break и маркера роли. Проверены мутацией: без кворума падают 5 проверок, при строковом сравнении IP самопонижаются оба узла.
Исправлено
- Redis Sentinel уходил в бесконечный failover при отказе одного узла.
На боевом кластере из трёх узлов это дало более сотни
+switch-masterза 12 минут и двух master одновременно. Три причины, устранены вместе:failover-timeoutбыл10000мс (дефолт Redis —180000). Этим же таймером ограничена переконфигурация реплик: Sentinel обрывал синхронизацию реплики (+failover-end-for-timeout,+slave-reconf-sent-be) и тут же разрешал новый failover. Теперь180000, настраивается черезREDIS_SENTINEL_FAILOVER_TIMEOUT.down-after-millisecondsбыл5000мс — на публичной сети это даёт ложные срабатывания. Теперь10000(REDIS_SENTINEL_DOWN_AFTER).- Правила файрвола для 6379/26379 не создавались вообще, если не была
настроена приватная сеть, — а публичные IP код явно допускает. Sentinel
видел соседние Sentinel'ы (26379), но не сами redis-серверы (6379),
объявлял только что повышенного мастера
+sdownи повышал своего. Теперьcluster-initиjoinоткрывают порты для всех узлов кластера, список узлов хранится в/etc/default/mitdev-redis.
mitdev remove redisне снимал HA-оверлей: оставшийсяredis-sentinelписал+sdownв журнал, а уцелевшиеsentinel.confи строкаreplicaofпри переустановке молча возвращали узел в несуществующий кластер. Теперь оверлей снимается всегда (redis_ha_teardownвlib/os.sh).mitdev redis cluster-init <имя>(без кворума) подставлял имя кластера в список IP-адресов узлов из-заshift 2при одном аргументе.
Добавлено
mitdev redis check— проверка связности: узел должен видеть каждый другой на 6379. Различает «порт закрыт», «неверный пароль» и «redis слушает не тот адрес» — три причины, которые в журнале Sentinel выглядят одинаково (+sdown).mitdev redis cluster-remove— снять HA-оверлей (Sentinel, конфиг, правила UFW,replicaof), не трогая Redis и данные, чтобы собрать кластер заново.mitdev redis allow <ip>— открыть порты узлу и добавить его в список узлов кластера (прежнееadd-replicaосталось алиасом).mitdev redis joinтеперь проверяет доступность master до внесения любых изменений и отказывается собирать заведомо нерабочий кластер; предупреждает, что полная синхронизация затрёт локальные данные (-y— пропустить вопрос).mitdev redis statusпоказывает реплики по данным Sentinel и предупреждает о split-brain (локальный Redis — master, а Sentinel считает мастером другой узел).sentinel announce-ip/announce-portв конфигурации Sentinel — без них узел за NAT анонсирует адрес, по которому до него не достучаться.tests/redis-unit.sh— 29 юнит-проверок: таймауты Sentinel,announce-ip, реестр узлов, классификация ошибок подключения. Подключён кtests/smoke.sh.
Изменено
- Новая переменная
PG_WITNESS_HOSTS— witness-узлы для кворума failover (обязательны для авто-failover на кластере из 2 узлов). - В конфиг watchdog (
/etc/default/mitdev-pg-watchdog) добавленыSELF_IPиWITNESS_HOSTS. Существующие кластеры получат их при повторном запуске установки; до этого tie-break использует адрес изhostname -I. - Новые переменные:
REDIS_PORT,REDIS_SENTINEL_PORT,REDIS_SENTINEL_DOWN_AFTER,REDIS_SENTINEL_FAILOVER_TIMEOUT,REDIS_SENTINEL_PARALLEL_SYNCS,REDIS_PEERS_FILE(см.docs/CONFIGURATION.md). - Формат токена Redis-кластера —
MITDEV-REDIS-2(добавлен список узловPEERS). Токены старого форматаMITDEV-REDIS-1по-прежнему принимаются.
[2.29.0] — 2026-07-09
Добавлено
- Веб-панель управления (
mitdevd) — единый статический Go-бинарник: режимыserve(control-plane, HTTPS UI),agent(узловой mTLS-сервер),bootstrap(первый админ + QR 2FA),ca(per-project CA). Установка черезmitdev web install/agent-install/status, модульpanel. Вход — пароль (argon2id) + TOTP (Google Authenticator) + одноразовые backup-коды; сессии/rate-limit в Redis, инвентарь в PostgreSQL; дашборд читаетmitdev report --json. Панель слушает только loopback (доступ через SSH-туннель). mitdev report --json— машиночитаемый снимок сервера (система, службы, приватная сеть, здоровье кластеров) для панели и автоматизации.mitdev add <модуль>— установка одного компонента без мастера первичной настройки (по образцуmitdev remove).mitdev net set-ip <ip>— сменить приватный IP узла на месте, без полного переприсоединения; выдаёт peer-токен для рассылки.mitdev net join --ssh— теперь можно вставить приватный ключ вручную (не только указать путь): пишется во временный файл 0600, удаляется после.
Изменено
- Кросс-дистрибутивная переносимость (Debian/Ubuntu · RHEL/Fedora · SUSE ·
Arch) — устранены Debian-измы, обходившие абстракцию
lib/os.sh:- Имя службы Redis (
redis-serverв Debian,redisв RHEL/SUSE/Arch) и путь лога — черезredis_service()/redis_log_file()во всех модулях,mitdevиlib/net.sh. - Группа sudo (
sudoв Debian,wheelв RHEL/SUSE/Arch) — черезadmin_group()с созданием группы; иначе deploy не получал sudo на не-Debian. - Все откаты/удаления переведены с прямых
apt-get remove/purgeна абстракциюpkg_remove(Node, Docker, nginx, certbot, fail2ban, PostgreSQL, MongoDB, RabbitMQ, keepalived, PHP);cmd_clean— наos_pkg_autoremove; подсчёт обновлений — наos_pkg_updates_count(dnf/zypper/pacman). - firewalld-шим определяется на любом не-Debian (а не только при уже
установленном
firewall-cmd); модуль firewall на не-Debian ставит firewalld вместо ufw; добавлена формаdelete allow from <cidr>и путь VRRP (proto- для firewalld (VIP-кластер за firewalld).
- nginx PHP-шаблон: убран Debian-only
snippets/fastcgi-php.conf(ломалnginx -tна RHEL/SUSE/Arch) — директивы инлайнены переносимо. os_map_pkg: добавлена ветка Arch (pacman) и маппинг имён; Docker — явные ветки Debian/RHEL/(SUSE·Arch из штатных репо); MongoDB — понятная ошибка на SUSE/Arch (MongoDB Inc. публикует пакеты только для Debian/RHEL),$basearchвместо жёсткогоx86_64(поддержка ARM).- Матрица поддержки (preflight): Debian — полная, RHEL/Fedora — рабочая, SUSE/Arch — экспериментально, non-systemd (Alpine/*BSD) — не поддерживается.
- Имя службы Redis (
Исправлено
mitdevработает безsudoдля любой роли — команды, требующие root, теперь прозрачно перезапускают себя черезsudo -n(уdeploy— NOPASSWD sudo), а при обычных sudo-правах спрашивают пароль один раз; только при полном отсутствии sudo выводится понятное сообщение. Раньшеneed_rootсразу падал с «Команда требует root».mitdev net statusбез sudo больше не пишет «не настроена» (не мог прочитать/etc/wireguardиwg show) — теперь эскалируется автоматически.- NOPASSWD sudo для
deployнастраивается сразу при первом запуске — всегда (раньше только если не задан пароль). На key-only серверах (root/пароль-SSH отключены) вход по ключу уже равносилен root, а без NOPASSWD ломались всеsudo -n(авто-эскалация,net join --ssh). Дополнительно mitdev самовосстанавливает sudoers при запуске от root, если файл отсутствует — так чинятся серверы, развёрнутые до этого изменения (актуально послеself-update, который перезапускает только бинарник). mitdev redis token/ любойmitdev redis …без sudo больше не врёт «Redis не установлен»: детект установки теперь по бинарям (redis-server/redis-cli, world-readable), а не по[[ -f /etc/redis/redis.conf ]]— каталог/etc/redisимеет права750 redis:redis, и не-root не мог его прочитать, из-за чего проверка ложно проваливалась.- Redis 6.0 (Ubuntu 22.04): убран синтаксис 6.2+/7 —
-::1в bind (в т.ч. изconfig/defaults.conf) иsentinel resolve-hostnames, из-за которых служба/sentinel не стартовали.::1добавляется только при реальном IPv6-loopback. Модуль самолечит битыйdpkg-statoverride(блокировавший apt). ЗадаютсяTimeoutStartSec/TimeoutStopSec(systemd drop-in для redis-server и redis-sentinel) — уходит предупреждение «supervised by systemd - you MUST set appropriate values for TimeoutStartSec and TimeoutStopSec». Применяетсяvm.overcommit_memory = 1(sysctl.d) — уходит предупреждение об overcommit и надёжнее фоновое сохранение. - Sudo-блокировка: при пустом пароле deploy на key-only сервере настраивается
passwordless sudo — иначе
sudoне мог аутентифицироваться и root-доступ терялся. - Приватная сеть:
net add-peerотклоняет дубли IP и коллизии hostname (два сервера с одинаковым именем), при коллизии подсказывает свободный адрес. install.sh:prefix: unbound variableпри HA-настройке.- Пресеты
mitdev app(fastapi/django/go/laravel) больше не приводят к 502 — исправлен мапп; приложение запускается под PM2/PHP-FPM, а не «в никуда». - Экранирование одинарных кавычек в паролях PostgreSQL (SQL); пароль
репликации передаётся через
PGPASSWORD, а не вps; MongoDBbindIpдобавляется, если строки нет; MinIO обновляет порт при переустановке; HAProxyoption httpchk— современный синтаксис;DEPLOY_HOMEдля root.
[2.28.1] — 2026-07-08
Добавлено
mitdev app/mitdev deploy: источником деплоя может быть локальный путь на сервере, а не только git-репозиторий. Путь запоминается;deployвыполняет ре-синхронизацию из источника через rsync (с сохранением серверных.env/node_modules).
Изменено — GUIDE разбит на короткие страницы
docs/GUIDE.md(было 322 строки) разделён по стадиям на три читаемых части: Начало (что такое / сервер установки / первый сервер / развернуть проект), Дальше (nginx / Docker / Kubernetes / кластер PostgreSQL), Эксплуатация (бекапы / уход / сбои).- Оглавление и перекрёстные ссылки (prev/next) между частями; ссылка на Рецепты. Новые страницы добавлены в навигацию сайта.
[2.28.0] — 2026-07-08
Добавлено — упаковка/подача (маркетинговая читаемость)
docs/WHY.md— «Почему mitdev»: честная таблица сравнения с bash-скриптом, Docker Compose, Ansible/Puppet/Chef, Dokku/Coolify/CapRover; когда выбирать mitdev и когда — другой инструмент.docs/RECIPES.md— готовые рецепты стеков нашими командами: Next.js+PG+Redis, NestJS+Mongo, Laravel+PG (PHP-FPM), HA-кластер на 3 VPS (приватная сеть + HA PostgreSQL + Redis), приложение в Kubernetes.ARCHITECTURE.md— ASCII-схемы: поток установки, жизненный цикл модуля, топология «приватная сеть + HA».- Сайт: новые страницы
why/recipes(группа «Обзор»); переработан hero главной — новый посыл «Голый VPS → production за минуты», акценты на HA и приватной сети, ссылки на «Почему» и «Рецепты». - Внутренний
docs/BUSINESS.md— стратегия монетизации (Open Core / SaaS / лицензии); на сайте не публикуется.
[2.27.2] — 2026-07-08
Исправлено — защищённый откат при переустановке
- Раньше, если при переустановке уже установленного модуля он падал и
оператор выбирал «откатить», механизм отката выполнял безусловные
деструктивные действия (
pkg_remove,systemctl disable --now,userdel,DROP ROLE,pg_dropcluster,k3s-uninstall…) и мог снести работавшую до запуска службу. - Теперь
run_moduleпомечает переустановку (was_installed) и запускает откат в защищённом режимеROLLBACK_PROTECT=1:rollback_toпропускает деструктивные undo-действия (удаление пакетов/служб/пользователей/ролей), выполняя только безопасные (удаление конфигов/репозиториев, снятие UFW-правил). Для переустановки откат по умолчанию — «нет», с явным предупреждением. - На первой установке поведение прежнее (полный откат частичной установки).
Документация
- TROUBLESHOOTING: отмечено, что откат при переустановке не удаляет работавшие службы.
[2.27.1] — 2026-07-08
Исправлено — корректная переустановка (повторный запуск не ломает службы)
- Причина сбоя: установщик подставлял статические дефолты (SSH-порт 22, deploy-пользователь, политику входа) вместо текущих значений — приняв их на повторном запуске, оператор менял порт SSH и правила файрвола, из-за чего службы «отваливались».
install.shтеперь читает предыдущие ответы изinstall.envи подставляет их как значения по умолчанию (load_previous_answers: SSH-порт, deploy-пользователь, MITDEV_URL); политика SSH (root-вход/пароли) берётся из текущегоsshd_config.d/99-mitdev.conf. Приём дефолтов ⇒ никаких изменений.- Уже установленные компоненты предотмечены в списке выбора; «переустановить?» по-прежнему по умолчанию «нет». SSH-ключи не регенерируются (идемпотентно).
Документация
- TROUBLESHOOTING: раздел про повторный запуск дополнен поведением сохранения настроек.
[2.27.0] — 2026-07-08
Добавлено — единый HA-обзор в doctor и настройка кластеров в установщике
mitdev doctor: новый блок «Кластеры / HA» сводит состояние всех отказоустойчивых сервисов — PostgreSQL (роль/стриминг реплики / число реплик), Redis (master по Sentinel), MongoDB (replica set: primary/узлов), RabbitMQ (узлов в кластере), MinIO (кворум distributed), WireGuard (узлов mesh); «HA-кластеры не настроены», если ничего нет.- Установщик: новые фазы
collect_ha_config/apply_ha_config— для выбранных redis/mongodb/rabbitmq/minio предлагают «настроить кластер?» (single / создать / присоединиться по токену; MinIO спрашивает список узлов) и применяют через установленныйmitdev <svc> cluster-init|joinпосле установки. В unattended-режиме — single (безопасно).
Документация
- COMMANDS: раздел
doctorдополнен блоком «Кластеры / HA».
[2.26.0] — 2026-07-08
Добавлено — распределённый MinIO (erasure coding), команда mitdev minio
- Distributed MinIO:
minio cluster-init <addr...>(все узлы задаются сразу — набор фиксируется при старте и не расширяется динамически) генерирует общие root-креды, пишетMINIO_VOLUMESи systemd drop-in (ExecStart → набор volume'ов), открывает 9000/9001 в UFW, перезапускает;minio token;minio join <токен>(идентичная конфигурация на каждом узле);minio status(single/distributed + здоровье кластера/minio/health/cluster). - Четвёртый и последний HA-сервис по запросу — теперь отказоустойчивую взаимозаменяемую сеть/кластер умеют все ключевые сервисы: PostgreSQL, WireGuard, Redis, MongoDB, RabbitMQ, MinIO.
Документация
- COMMANDS: раздел «Распределённый MinIO».
[2.25.0] — 2026-07-08
Добавлено — кластер RabbitMQ, команда mitdev rabbitmq
- Кластеризация узлов через общий erlang-cookie (в токене) и
join_cluster:rabbitmq cluster-init(management-плагин,default-queue-type quorumна vhost «/», UFW-порты 4369/25672/5672/15672 для подсети);rabbitmq token;rabbitmq join <токен>(резолв имени primary через /etc/hosts, замена cookie,stop_app→reset→join_cluster→start_app, проверкаcluster_status);rabbitmq forget <имя>;rabbitmq status. - Имена узлов
rabbit@<hostname>резолвятся через/etc/hostsприватной сети; quorum-очереди дают отказоустойчивость сообщений. Требуется приватная сеть. - Третий из четырёх HA-сервисов. Остался MinIO (distributed).
Документация
- COMMANDS: раздел «Кластер RabbitMQ».
[2.24.0] — 2026-07-08
Добавлено — отказоустойчивый MongoDB (replica set), команда mitdev mongo
- Нативный replica set с встроенным авто-failover:
mongo cluster-init [имя](генерация keyfile, bind на приватный IP, replSet,rs.initiate, создание пользователяadmin/root, UFW для подсети);mongo token;mongo join <токен>(пишет keyfile, включает replSet, сам добавляет себя в набор через primaryrs.addс admin-кредами из токена, проверяет состояние);mongo add-member/remove-member <ip>;mongo promote(rs.stepDown— перевыборы);mongo status(rs.status). - Internal auth между узлами по общему keyfile (base64 в токене); клиентская авторизация включается автоматически (keyFile ⇒ authorization). Узлы связываются по приватной сети, если она есть.
- Второй из четырёх HA-сервисов. Осталось: RabbitMQ (кластер), MinIO (distributed).
Документация
- COMMANDS: раздел «Отказоустойчивый MongoDB (replica set)».
[2.23.0] — 2026-07-08
Добавлено — отказоустойчивый Redis (Sentinel), команда mitdev redis
- HA-кластер Redis по паттерну pgcluster: master + N реплик, на каждом узле
redis-sentinelс авто-failover и взаимозаменяемостью (бывший master возвращается репликой). Узлы связываются по приватной сети, если она есть. - Команды:
redis cluster-init [имя] [кворум](сделать master: requirepass + masterauth, bind на приватный IP, Sentinel :26379, UFW для подсети);redis token;redis join <токен>(replicaof + Sentinel + проверкаmaster_link_status:up);redis add-replica <ip>(UFW);redis promote(ручнойSENTINEL FAILOVER);redis status(роль, репликация, master по Sentinel, кворум). Общий пароль хранится в/root/.mitdev-credentials. - Первый из четырёх HA-сервисов по запросу «остальные сервисы должны уметь отказоустойчивую взаимозаменяемую сеть как wg и pg». Дальше по очереди: MongoDB (replica set), RabbitMQ (кластер), MinIO (distributed).
Документация
- COMMANDS: раздел «Отказоустойчивый Redis (Sentinel)».
[2.22.0] — 2026-07-08
Добавлено — IPv6 (dual-stack) в приватной сети
mitdev net createпредлагает включить IPv6: автоматически генерируется ULA-подсетьfdXX:XXXX:XXXX::/64; каждому узлу назначается парный адрес с тем же номером хоста (10.100.0.7↔fdXX:…::7).wg0иAllowedIPsстановятся dual-stack (v4/32 + v6/128), включается IPv6-forwarding. IPv6-подсеть переносится в токене (IPV6=), реестр пиров расширен 5-м полем (адрес IPv6);net statusпоказывает IPv6.- Полная обратная совместимость: сети без IPv6 (пустое поле) рендерятся как раньше, только IPv4.
Документация
- COMMANDS: раздел
net createдополнен опцией IPv6 и парной адресацией.
[2.21.0] — 2026-07-08
Добавлено — «Listen on: private only» прямо в установщике
- Если приватная сеть уже создана, установщик при выборе модулей БД/кэшей
(PostgreSQL/Redis/MongoDB/RabbitMQ/MinIO) спрашивает режим прослушивания
(private/public/both, по умолчанию private) и автоматически применяет
net bindк установленным службам после установки — zero-touch, как в ТЗ. install.shподключаетlib/net.sh; новые фазыcollect_listen_mode(до плана) иapply_listen_mode(после модулей); в unattended-режиме берётся значение по умолчанию (private). Каждый bind изолирован в подоболочке, чтобы ошибка одной службы не прерывала установку.
Документация
- COMMANDS: в раздел
net bindдобавлено про автоматический вопрос установщика.
[2.20.0] — 2026-07-08
Добавлено — mitdev net bind: службы слушают только приватную сеть
mitdev net bind <служба> [private|public|both]для PostgreSQL, Redis, MongoDB, RabbitMQ, MinIO — идемпотентно и обратимо переключает интерфейс прослушивания.private— приватный IP сети + localhost;public/both— все интерфейсы. Перед изменением конфиг резервируется, служба перезапускается, в конце показывается фактический список сокетов (ss).- PostgreSQL: drop-in
conf.d/zz-mitdev-listen.conf(сортируется после99-mitdev-replication.confкластера и всегда перекрывает егоlisten_addresses); Redis:bind; MongoDB:net.bindIp; RabbitMQ:listeners.tcp.default+management.tcp.ip; MinIO:MINIO_OPTS --address. - Возврат к публичному прослушиванию —
mitdev net bind <служба> public.
Документация
- COMMANDS: раздел «Использование другими модулями» переписан под
net bind(таблица служб и правок).
[2.19.0] — 2026-07-08
Добавлено — отказоустойчивость приватной сети (WireGuard mesh watchdog)
- Новый самовосстанавливающийся watchdog (
lib/net-watchdog.sh+mitdev-wg-watchdog.service), ставится автоматически приnet create/join. На каждом узле независимо (без выбора лидера): перезапускаетwg0при падении; «оживляет» пиры с устаревшим handshake (генерирует трафик); переразрешает DNS-endpoint (DDNS / меняющийся IP); переключает endpoint на резервный адрес, если у пира их несколько (в реестре —host1:port,host2:port) — переход на другой адрес при недоступности основного. mitdev net watchdog [status|enable|disable]; статус watchdog добавлен вnet statusиnet repair;net removeснимает watchdog;self-updateобновляет и перезапускает его.- Подчёркнута архитектурная отказоустойчивость: mesh без центрального хаба (нет единой точки отказа для трафика) и без «главного» узла-координатора — токен на подключение выдаёт любой сервер (полный реестр пиров и PSK есть у всех).
Документация
- COMMANDS: раздел «Отказоустойчивость: mitdev net watchdog»; smoke дополнен
lib/net-watchdog.shи плейсхолдером нового unit-шаблона.
[2.18.0] — 2026-07-08
Добавлено — zero-touch подключение к сети по SSH
mitdev net join --ssh <host> [user]: MITDEV подключается к существующему узлу по SSH (пароль черезsshpassили ключ), забирает токен сети (mitdev net token --raw), присоединяется локально и сам разносит этот узел как пир на целевой сервер и все остальные известные узлы, затем проверяет связьping. Частичный успех честно перечисляется с командой для ручногоadd-peer.mitdev net token --raw— машиночитаемый вывод токена (для автоматизации).- Локальный join вынесен в переиспользуемую функцию
_net_local_join(общая для токен- и SSH-путей). net statusтеперь показывает MTU интерфейсаwg0.- Требование к удалённой стороне: SSH-пользователь
rootили с NOPASSWD sudo (иначе удалённыйsudoнедоступен) — задокументировано.
Документация
- COMMANDS: добавлен раздел
net join --ssh; CHANGELOG/README актуализированы.
[2.17.0] — 2026-07-08
Добавлено — приватная сеть WireGuard (full-mesh), команда mitdev net
- Новый движок
lib/net.sh+ группа командmitdev net: full-mesh между серверами без центрального хаба, публичные IP сохраняются, у каждого узла появляется приватный адрес и имя<узел>.internal(через/etc/hosts). net create— создать сеть: проверка пересечения подсети с Docker/Kubernetes/ локальными маршрутами, установка WireGuard, генерация ключей + preshared key,wg0, IP-forward, открытие UDP-порта в UFW, автозапускwg-quick@wg0, назначение первого адреса,.internal-DNS.- Токен-подключение без удалённого выполнения команд:
net token(полный дескриптор сети) →net join <токен>(импорт пиров, автовыделение свободного IP, поднятиеwg0, печать peer-токена) →net add-peer <токen>на остальных узлах (двусторонний mesh). net status— пиры, задержки (ping), время handshake, трафик (wg show); раздел добавлен и вmitdev doctor.net remove-peer,net remove,net export/import(миграция/восстановление всей сети одним архивом),net repair(иmitdev repair) — самопроверка и починка (пакет, ключи, конфиг, служба, файрвол, IP-forward).- Идемпотентность (реестр пиров, dedupe по имени), бэкап
wg0.confперед изменениями, hot-reload черезwg syncconfбез обрыва туннеля, IPv4.
Отложено (следующая фаза, честно)
- Полностью автоматическое подключение по SSH (MITDEV сам заходит на другой
сервер и настраивает обе стороны), transfer/MTU-тесты и автоподстановка
«private-only» в PostgreSQL/Redis/MinIO/… — приватные адреса уже доступны
(
<узел>.internal) для настройкиlistenвручную/скриптом.
Документация
- COMMANDS: раздел «Приватная сеть (WireGuard mesh)»; README: краткий обзор;
smoke дополнен
lib/net.sh.
[2.16.0] — 2026-07-08
Добавлено — нативные рантаймы PHP / Python / Go (пресеты заработали без Docker)
- Новые модули:
20-php(PHP-FPM + типовые расширения + Composer),21-python(Python 3 + venv + pip + pipx),22-go(официальный Go в/usr/local/go+ PATH). Опциональные, ставятся черезinstall.sh. - Пресеты
mitdev appтеперь запускают приложения нативно:laravel/wordpress→composer install+ nginx с FastCGI на PHP-FPM (новый шаблонtemplates/nginx/php.conf.template; docrootpublic/у Laravel);fastapi/django→ venv +pip install -r requirements.txt+ запуск под PM2 (uvicorn/gunicorn; имя wsgi-модуля Django ищется автоматически);go→go build+ запуск бинарника под PM2. Команду запуска можно переопределить файлом.mitdev/startв репозитории.
- Все три модуля поддерживают
mitdev remove(Python-интерпретатор как системная зависимость не удаляется даже с--purge). - Реестры обновлены:
MODULE_REGISTRY(install.sh),REMOVABLE_MODULES(CLI);GO_VERSIONдобавлен вconfig/defaults.conf. smoke дополнен проверками новых модулей и плейсхолдеров php-шаблона.
Документация
- COMMANDS: таблица пресетов переписана под нативные рантаймы; MODULES: разделы 20/21/22; README: PHP/Python/Go в списке компонентов.
[2.15.0] — 2026-07-08
Добавлено — пресеты приложений mitdev app <пресет> (роадмап 5/5)
mitdev appпринимает необязательный первый аргумент — пресет фреймворка, задающий порт и стратегию запуска:next/nest/node(сборка + запуск под PM2),static(nginx отдаёт каталог репозитория),docker/fastapi/django/go/laravel/wordpress(запуск через docker-compose из репозитория). Аргументы можно передать позиционно:mitdev app next myapp <repo> app.example.com.- Закрыт пробел: node-приложение без docker-compose теперь реально
запускается под PM2 (
pm2 start npm -- start+pm2 save), а не только проксируется nginx. - Для пресетов с внешним рантаймом (Python/Go/PHP) при отсутствии compose mitdev выдаёт понятное сообщение (эти рантаймы отдельными модулями пока не ставятся) — честно, без «широко, но хрупко».
Документация
- COMMANDS: раздел
appдополнен таблицей пресетов и правилами запуска.
[2.14.0] — 2026-07-08
Добавлено — mitdev update с сохранением конфигураций (роадмап 4/5)
- Перед обновлением пакетов
updateделает снимок конфигураций (nginx, sshd drop-in, redis.conf,/etc/default/minio, fail2ban,/etc/postgresql,install.env) в/var/lib/mitdev/pre-update/config-<дата>.tar.gz(хранятся последние 5) — обновление остаётся обратимым. - Флаг
--backup— сделать полныйmitdev backupперед обновлением.
Документация
- COMMANDS: раздел
updateдополнен снимком конфигов и флагом--backup.
[2.13.0] — 2026-07-08
Добавлено — mitdev doctor --deep: реальные функциональные пробы (роадмап 3/5)
- Помимо статусных проверок,
--deepфактически проверяет работу сервисов: nginx отвечает по HTTP; PostgreSQL выполняетSELECT 1; Redis отвечаетPONG(с учётомrequirepass); MongoDB отвечает наping; SSL-сертификаты/etc/letsencryptдействительны (проверка на истечение <7 дней); RabbitMQ (rabbitmqctl status); MinIO (/minio/health/live); SSH-порт разрешён в UFW; свежесть последней резервной копии. - Проваленная проба увеличивает код выхода —
mitdev doctor --deepгодится как «умный» health-check для cron/мониторинга (mitdev doctor --deep || alert).
Документация
- COMMANDS: раздел
doctorдополнен описанием--deep.
[2.12.0] — 2026-07-08
Добавлено — полноценный backup framework (роадмап 2/5)
mitdev backupтеперь сохраняет одной командой не только код/.env/nginx/БД, но и: SSL (/etc/letsencrypt), SSH (drop-insshd_config.d,authorized_keysdeploy/root, ключи/root/.mitdev-keys), cron (system + crontab root/deploy), RabbitMQ (export_definitions), MinIO (/etc/default/minio). ДобавленBACKUP-MANIFEST.txtс составом архива.- Флаг
--volumes— резервное копирование всех именованных docker-томов (каждый отдельным.tar.gzчерезalpine tar). mitdev restoreсимметрично восстанавливает новые данные — каждое разрушительное действие (SSL, SSH-ключи и sshd, cron, RabbitMQ, MinIO, docker-тома) под отдельным подтверждением; настройки sshd применяются только послеsshd -t.
Документация
- COMMANDS: раздел backup/restore переписан под полный состав архива, отмечена
чувствительность архива и разделение с
export --secrets(пароли служб).
[2.11.0] — 2026-07-08
Добавлено — версионирование и перенос конфигурации (роадмап «Server Operating Platform», 1/5)
mitdev export [--output f] [--secrets]— снимок установленной конфигурации в переносимый манифест (KEY="value"): список компонентов, deploy-пользователь, порт SSH, политика входа, версия Node, роль PostgreSQL, URL. Без--secretsманифест можно хранить в git. С--secretsвкладывает/root/.mitdev-credentials(base64) для переноса паролей.mitdev import <манифест> [--yes] [--components …]— воспроизводит конфигурацию через установщик.--yes— неинтерактивно (значения из манифеста), идемпотентно. Без--yes— установщик с подставленными значениями по умолчанию. При наличии секретов восстанавливает их до запуска.mitdev diff <манифест>— сравнение манифеста с текущим сервером (только чтение): версия, deploy-пользователь, порт SSH и расхождение по компонентам.- Установщик получил unattended-режим:
tui_confirm/input/password/checklistподMITDEV_UNATTENDEDотвечают дефолтами/MITDEV_COMPONENTSбез запросов — основа дляimportи будущей автоматизации.
Документация
- COMMANDS: новый раздел «Миграция и версионирование конфигурации» с примером переноса сервера между VPS (export/import/diff + backup/restore).
[2.10.2] — 2026-07-07
Исправлено — mitdev pg add-replica теперь самодостаточен
- После снятия оверлея кластера (
mitdev remove pgcluster/cluster-remove) на primary пропадалlisten_addresses='*'(возврат кlocalhost), иadd-replicaего не восстанавливал — primary становился недоступен снаружи, реплика падала на проверке доступности (pg_isready … no response). - Теперь
add-replicaпри необходимости восстанавливает параметры репликации primary (listen_addresses='*',wal_level,wal_keep_size, слоты и т.д.), добавляет запись# mitdev-rewindвpg_hba, перезапускает PostgreSQL, если менялсяlisten_addresses(не reload), и в финале показывает, слушает ли primary 5432 на всех интерфейсах. Если роль репликации была удалена (--purge) — подсказывает переустановить роль primary через установщик.
Документация
- TROUBLESHOOTING: добавлен пункт «Реплика:
no response(pg_isready exit 2)» с разбором (listen_addresses после teardown, файрвол провайдера) и починкой на primary/реплике. Отмечено, что предупреждениеcould not change directory to "/root"приsudo -u postgres— безобидное.
[2.10.1] — 2026-07-07
Исправлено
self-update: cleanup-trap усилён (RETURN+${tmp:-}), чтобы очистка временного каталога не могла сработать в глобальной области с неопределённойtmpи уронить команду подset -u(tmp: unbound variable). Старые сборки с этим багом не могут обновиться сами — обновите сервер один раз вручную (инструкция ниже), дальшеself-updateработает штатно.
Документация
- TROUBLESHOOTING: добавлен разбор «
self-updateпадаетtmp: unbound variable/ версия не поменялась» с пошаговым ручным обновлением в обход self-update и починкой симлинка (ln -sf … ; hash -r).
[2.10.0] — 2026-07-07
Добавлено — удаление реплик и их данных (PostgreSQL-кластер)
mitdev pg remove-replica <ip>(на primary) — убрать ненужную реплику из кластера: удаляет её слот репликации (освобождает удерживаемый WAL), снимает доступpg_hbaи правило файрвола для этого IP. Если реплика уже отключена — предлагает удалить неактивные слотыmitdev_*. Счётчик-парный кadd-replica.- Ролевое
--purgeдляmitdev remove pgcluster/mitdev pg cluster-remove:- на replica — удаляет реплицированную базу данных и пересоздаёт пустой
кластер (
pg_dropcluster/pg_createclusterна Debian,initdbна RHEL), приводя узел к чистому состоянию для переустановки или удаления. Также подчищает отложенные каталоги*.mitdev-old.*от прежнихpg_basebackup; - на primary — как раньше, удаляет роль репликации и слоты
mitdev_*. Ранее удалялся только оверлей, данные реплики оставались.
- на replica — удаляет реплицированную базу данных и пересоздаёт пустой
кластер (
mitdev remove postgresqlтеперь сначала автоматически снимает оверлей кластера (watchdog, эндпоинт роли, репликация, правила), если он был, — удаление/переустановка СУБД всегда оставляет чистый узел.
Изменено
- В модуле 18 выделены переиспользуемые функции
_pg_remove_cluster_overlay(переиспользуется модулем 12) и_pg_reinit_empty_cluster. - COMMANDS/MODULES/TROUBLESHOOTING: описаны
remove-replica, ролевое--purge, сценарии «убрать ненужную реплику» и «переустановить/пересобрать реплику»; раздел про неактивные слоты дополнен командойmitdev pg remove-replica.
[2.9.1] — 2026-07-07
Документация
- TROUBLESHOOTING: добавлен разбор «
sudo: пароль не подходит после установки» — причина (пустой пароль на шаге установки → аккаунт с заблокированным паролем, вход по ключу работает, аsudoтребует несуществующий пароль) и восстановление (passwd -S/-u, задание пароля от root, проверка группыsudo/wheel). Уточнено, что пароль deploy-пользователя не хранится в/root/.mitdev-credentials. - Поведение установщика не изменено (правка кода не требовалась): описан штатный ручной способ восстановления на уже развёрнутом сервере.
[2.9.0] — 2026-07-07
Добавлено — удаление компонентов (у каждого модуля своя команда)
- Новая команда
mitdev remove <модуль> [--purge] [-y]— удаляет один установленный компонент, обращая вспять его установку (службы, пакеты, конфигурация, systemd-юниты, репозитории, служебные пользователи). Единой команды «снести всё» намеренно нет — для каждого модуля своё удаление. mitdev remove --list— список компонентов и их статус.- По умолчанию данные сохраняются; флаг
--purgeудаляет и данные (базы, тома, каталоги, сертификаты, домашний каталог deploy-пользователя). Всегда с подтверждением (отключается флагом-y). - Реализован контракт
module_remove()во всех 20 модулях (modules/*.sh). Вmitdevдобавлен реестрREMOVABLE_MODULESи диспетчерcmd_remove(best-effort: сбой одного шага не прерывает уборку). - Удобные алиасы под частые сценарии:
mitdev pg cluster-remove [--purge]— снять оверлей кластера PostgreSQL (watchdog, эндпоинт роли, настройки репликации, правила файрвола); СУБД и данные остаются, узел работает автономно. С--purgeудаляются роль репликации и слотыmitdev_*— штатный способ вычистить незавершённую настройку кластера (в т.ч. зависший слот на primary) перед повтором.mitdev k8s delete-cluster— полностью удалить k3s (кластер и все поды);mitdev k8s delete-all— удалить все приложения mitdev и их поды.
INSTALLED_COMPONENTSвinstall.envи маркер состояния модуля очищаются после успешного удаления.
Изменено
- Контракт модуля пополнен
module_remove()(DEVELOPER.md, MODULES.md); smoke-тест теперь проверяет наличиеmodule_remove()во всех модулях и синхронность реестраREMOVABLE_MODULESс файлами модулей. - COMMANDS.md: новый раздел «Удаление компонентов» с таблицей
«что сохраняется / удаляется» по каждому модулю; README дополнен блоком
про удаление. Уточнено, что
uninstallудаляет только сам mitdev.
[2.8.2] — 2026-07-07
Изменено (синхронизация документации с кодом)
- COMMANDS: раздел failover переписан под watchdog v2 (авто-перецепление,
fencing по timeline,
follow— плановая операция); добавлены разделыmitdev firewallиk8s token+ описание HA-кластера k3s; «после failover» в сценарии трёх узлов — «всё происходит само». - GUIDE: k8s-раздел дополнен HA-кластером; «учебная тревога» переписана («руками не делаете ничего — только смотрите»); self-update в таблице ухода.
- MODULES: модули 12 (вопросы БД, RHEL-initdb), 17 (режимы single/init/join, порты HA, анти-аффинити), 18 (watchdog v2 целиком) актуализированы.
- CONFIGURATION: PG_CLUSTER_PEERS/PG_AUTO_FAILOVER, секция Kubernetes (K8S_*), таблица файлов состояния дополнена (watchdog, repl-pass, sudoers, mitdev.prev, MITDEV_URL).
- SECURITY: пункт о split-brain переписан под fencing v2 (честное окно риска — полное сетевое разделение); INSTALL: вопросы пиров/K8s-режима, зависимость pgvip, строка модуля VIP в таблице; README: списки команд pg (import/allow/proxy) и HA-описания; ARCHITECTURE/DEVELOPER: lib/os.sh в схемах, актуальный диапазон модулей.
[2.8.1] — 2026-07-07
Исправлено / добавлено (свежесть дистрибутива)
/getобходит кэш CDN/прокси (query-параметр при скачивании tarball) и печатает версию скачанного дистрибутива — устаревшая копия на сайте видна сразу.- Сборка сайта создаёт
version.txt—curl домен/version.txtпоказывает, что реально отдаёт сайт; nginx-пример в SITE.md дополнен блокомCache-Control: no-storeдля установочных артефактов + заметка про очистку кэша Cloudflare. - Повторная установка синхронизирует
/opt/mitdevначисто (через staging-каталог) — файлы, удалённые в новых версиях, больше не остаются от старых.
[2.8.0] — 2026-07-07
Добавлено
docs/BASICS.md«Азбука» — слой документации для человека с нулевыми знаниями: что такое VPS и как арендовать (таблица выбора при заказе), SSH с Windows/macOS с двумя главными страхами новичка (fingerprint yes, невидимый ввод пароля), как вводить команды (вставка правой кнопкой, Ctrl+C, история), root/sudo, nano с горячими клавишами, домен и A-запись по полям панели регистратора, git-URL, .env («№1 причина „не работает“»), как читать ошибки, словарик из 24 терминов простыми словами.- Сквозной «путь новичка» ① Азбука → ② Большое руководство → ③ Развёртывание: нумерация карточек сайта, блок на главной, перекрёстные указатели-вступления в GUIDE, DEPLOY, INSTALL, SITE, TROUBLESHOOTING («как пользоваться страницей»), нумерация в README.
[2.7.0] — 2026-07-07
Добавлено
docs/DEPLOY.md«Развёртывание любого проекта» — исчерпывающий рецептурник: дерево выбора способа запуска; общая подготовка (доступ к приватным репозиториям тремя способами, DNS, .env); 9 рецептов по типу проекта (Node API, Next.js SSR/export, статика с SPA-роутингом, Python двумя путями, Go/Rust через systemd, PHP, универсальный Docker Compose, Kubernetes, воркеры/боты с расписанием); таблица строк подключения ко всем службам из credentials (включая кластерные варианты и доступ контейнеров к БД хоста); несколько проектов на одном сервере; обновление/логи/откат на коммит; диагностический чек-лист из 6 шагов с типовыми причинами. Страница добавлена на сайт второй карточкой.
[2.6.0] — 2026-07-07
Добавлено
mitdev self-update [url]— обновление самого mitdev с сервера установки: загрузка mitdev.tar.gz, проверка целостности и сравнение версий, атомарная подмена /opt/mitdev с сохранением прежней версии в /opt/mitdev.prev (ручной откат), обновление рантайм-копий systemd-служб (watchdog перезапускается, эндпоинт роли и VIP-check подменяются), состояние и учётные данные не затрагиваются. URL сервера установки запоминается в install.env при установке через/get.
Исправлено
- Команды
mitdev pgбольше не печатают «could not change directory to /root» (рабочая директория /tmp; относительный путь вpg importразрешается от каталога вызова).
[2.5.1] — 2026-07-07
Исправлено
- Модуль pgcluster (replica): проваленная проверка доступности primary не
останавливала установку (вызов
_pg_setup_replica || return 1отключает errexit внутри функции) — модуль продолжал, останавливал локальный PostgreSQL и откладывал данные при заведомо недоступном primary. Теперь все критические шаги несут явный|| return 1; проверка доступности — жёсткие ворота ДО любых изменений, с подсказками (add-replica, listen_addresses, ufw, файрвол провайдера); при неудаче pg_basebackup прежние данные автоматически восстанавливаются и PostgreSQL запускается в исходном виде — узел не остаётся полусломанным. - Убрано пугающее «could not change directory to /root» (cd /tmp перед запуском команд от postgres).
[2.5.0] — 2026-07-07
Добавлено — полное самовосстановление («настроил и забыл»)
- PG watchdog v2 (ставится на все узлы кластера):
- реплика при отказе primary сначала ищет уже повышенного пира по эндпоинтам ролей и сама перецепляется на него (смена primary_conninfo без копирования данных); повышается только если нового primary ещё нет;
- fencing по timeline: эндпоинт роли отдаёт
primary tl=N; смещённый старый primary видит пира со старшим timeline и сам понижается — pg_rewind (fallback: pg_basebackup) и возврат в кластер репликой. Split-brain исключён,pg followбольше не нужен; - для этого:
wal_log_hints=on, GRANT'ы pg_rewind для replicator, hba-записи dbname=postgres, узкий sudoers (systemctl stop/start/restart postgresql), пароль репликации в ~postgres/.mitdev-repl-pass (0600); - конфиг watchdog: PEERS и ROLE_PORT (и в
pg watchdog set); вопрос «IP остальных узлов» в инсталляторе.
- Kubernetes HA (multi-server k3s, embedded etcd): режимы
single / init / join в инсталляторе,
mitdev k8s token(URL + токен присоединения), кластерные порты в UFW (etcd 2379-2380, kubelet 10250, flannel 8472/udp — точечно по IP пиров), podAntiAffinity в шаблоне деплоя — реплики подов разносятся по серверам, отказ сервера не прерывает сервис. Поды с погибшего узла пересоздаются автоматически. - Плейбуки в TROUBLESHOOTING переписаны: «ваша единственная задача — убедиться, что всё сошлось».
[2.4.1] — 2026-07-07
Добавлено
- Команда
mitdev firewall [status|enable|disable|allow]— безопасное включение UFW: правило SSH (порт из install.env) добавляется первым, затем веб-порты; выключение — с подтверждением. - Подсказки в
mitdev doctor: ошибки Nginx различают «сломан конфиг / служба не запущена / порт 80–443 занят другим процессом (traefik?)» и печатают команду для починки; «UFW выключен» подсказываетmitdev firewall enable. - TROUBLESHOOTING: плейбуки «отказал primary PostgreSQL» (хронология автоматики + три шага возврата узлов) и «недоступен сервер Kubernetes» (честные границы одноузлового k3s и как получить отказоустойчивость).
[2.4.0] — 2026-07-07
Добавлено
- Слой абстракции ОС (
lib/os.sh) — mitdev больше не привязан к Ubuntu:- детекция семейства дистрибутива (debian/rhel/suse/arch) по os-release;
- единые pkg_update/pkg_install/pkg_remove/pkg_installed поверх apt/dnf/yum/zypper/pacman + маппинг имён пакетов (build-essential→gcc-c++, gnupg→gnupg2, redis-server→redis…);
- ufw-совместимый шим поверх firewalld — все существующие вызовы
ufw …работают на RHEL/SUSE без изменений модулей; - семейно-зависимые репозитории NodeSource/Docker/MongoDB (deb и rpm), автоматическое подключение EPEL (certbot, fail2ban, rabbitmq);
- раскладка PostgreSQL: Debian cluster tools или RHEL /var/lib/pgsql
(initdb, include conf.d, promote через pg_ctl) — прозрачно для
модуля 18, watchdog и команд
mitdev pg; - имя службы SSH (ssh/sshd), layout nginx sites-enabled на не-Debian.
- Preflight: честная матрица поддержки вместо «только Ubuntu» — полная (Debian-семейство), экспериментальная с предупреждением (RHEL/SUSE), отказ с объяснением на системах без systemd (Alpine/BSD/macOS); проверки занятости и целостности пакетного менеджера для dnf/rpm.
[2.3.0] — 2026-07-07
Добавлено
- Свой nginx-конфиг в
mitdev app: путь в ответе на вопрос, либо автообнаружение в репозитории проекта (.mitdev/nginx.conf,deploy/nginx.conf,nginx.conf); поддержка плейсхолдеров__APP_NAME__/__DOMAIN__/__PORT__; валидацияnginx -tс восстановлением прежнего состояния при ошибке; автоматический выпуск SSL работает одинаково для своего конфига и шаблона. docs/GUIDE.md— «Большое руководство» для новичка: 11 сценариев от первого сервера до кластера PostgreSQL в трёх ДЦ, с объяснением терминов, таблицами ответов на вопросы инсталлятора и учебной тревогой failover. Страница добавлена на сайт первой карточкой.
[2.2.0] — 2026-07-07
Добавлено
- Вопросы о базе данных проекта в инсталляторе (модуль postgresql): имя базы, пользователь БД, пароль (пусто — сгенерировать, двойной ввод); повторный запуск с новым паролем обновляет его у существующей роли (ALTER ROLE); на реплике кластера вопросы пропускаются — данные приезжают с primary. Переопределение окружением: PG_APP_DB / PG_APP_USER / PG_APP_PASSWORD.
Исправлено
- Скрипт
/getзависал при запуске черезcurl | sudo bash:exec </dev/ttyперенаправлял stdin, из которого bash читал сам скрипт, и молча ждал ввода с клавиатуры. Теперь терминал подаётся только дочернему инсталлятору (bash install.sh </dev/tty), с понятной ошибкой при отсутствии TTY.
[2.1.0] — 2026-07-07
Добавлено
mitdev pg import <файл> [база]— импорт бекапа проекта на primary без ручных команд: автоопределение формата (pg_dump -Fc / .sql / .sql.gz / pg_dumpall), создание базы при отсутствии (владелец — deploy-пользователь), итоговая сводка; на реплике команда отказывается работать с подсказкой.- Вопрос о приоритете реплики в инсталляторе (основная / резервная с задержкой 45 с) — ручная правка PROMOTE_DELAY больше не нужна.
mitdev pg watchdog set <ключ> <значение>— настройка watchdog командой (FAIL_THRESHOLD, PROMOTE_DELAY и др.) с валидацией и перезапуском службы.- Проверка автопродления SSL в
mitdev doctor(ошибка, если certbot.timer не активен). - Гарантия LF при доставке: сборка сайта нормализует переводы строк в
mitdev.tar.gz (стейджинг-копия), скрипт
/getдочищает после распаковки;.editorconfig+ проверка CRLF в smoke-тестах. - Сценарий «кластер из трёх узлов в разных ДЦ» в документации переписан как полностью mitdev-управляемый (без единой ручной правки файлов).
[2.0.0] — 2026-07-07
Добавлено
- Автогенерация Ed25519 SSH-ключей для deploy-пользователя и root. Private keys
хранятся root-only в
/root/.mitdev-keys/, добавлены командыsudo mitdev ssh-key show deployиsudo mitdev ssh-key show rootдля импорта в Termius.
Изменено (несовместимо)
- Проект переименован: bootstrap → mitdev. Затронуто всё:
- команда администрирования:
mitdev(/usr/local/bin/mitdev); - пути:
/opt/mitdev,/var/lib/mitdev,/var/log/mitdev,/usr/local/lib/mitdev/,/opt/mitdev-src,/root/.mitdev-credentials; - systemd-юниты:
mitdev-pg-watchdog.service,mitdev-pg-role.socket; - конфиги и маркеры:
99-mitdev.conf(sshd),99-mitdev-replication.conf, комментарии# mitdev-*в pg_hba/before.rules, блокmitdev-pgв haproxy, слоты репликацииmitdev_<host>, health-endpoint/mitdev-health; - переменные окружения
MITDEV_*, дистрибутивmitdev.tar.gz, бренд и ASCII-логотип MITDEV.
- команда администрирования:
- Записи в этом журнале за версии 1.x отражают историю под новым именем.
[1.6.0] — 2026-07-07
Добавлено
- Кластер PostgreSQL между дата-центрами (узлы в разных сетях, без VIP):
- эндпоинт роли на каждом DB-узле (
mitdev-pg-role.socket, systemd socket-активация, портPG_ROLE_PORT=8008): HTTP200 primary/503 replica; ставится модулем pgcluster, самопроверка при установке; - команда
mitdev pg proxy <ip…>для серверов приложений: локальный HAProxy на127.0.0.1:PG_PROXY_PORTс httpchk по эндпоинту роли — приложения используют одну строкуpostgresql://…@127.0.0.1:5432/…, при failover соединения к старому primary рвутся принудительно (on-marked-down shutdown-sessions); управляемый блок в haproxy.cfg идемпотентен, конфигурация валидируется до перезапуска.
- эндпоинт роли на каждом DB-узле (
- TLS повсюду: записи pg_hba (репликация,
pg allow,add-replica) переведены наhostssl,primary_conninfoреплик —sslmode=require,ssl=onв дроп-ине primary: трафик кластера через интернет всегда зашифрован. mitdev pg allowдополнительно открывает порт эндпоинта роли (8008) для сервера приложений; документация: схема и сценарий для разных ДЦ, диагностика прокси, заметки в INSTALL/MODULES/CONFIGURATION.
[1.5.0] — 2026-07-07
Добавлено
- Команда
mitdev pg allow <ip> [база] [пользователь]— доступ приложений с других серверов к кластеру через VIP: запись pg_hba (scram-sha-256, /32), правило UFW (5432 точечно),pg_reload_conf(); напоминание выполнить на каждом узле кластера. - Руководство
docs/SITE.md«Сервер установки»: развёртывание сайта документации с доменом (DNS, nginx, SSL), проверка, установка новых серверов одной командой, цикл обновления, замечания по безопасности. Страница добавлена на HTML-сайт и в карточки главной. - Документация: раздел о доступе приложений к VIP (pg allow, требования к сети), диагностика «приложение не подключается к VIP» в troubleshooting, рекомендации по ресурсам DB-узлов (1 ГБ/2 ядра — минимум, выделенные узлы).
[1.4.0] — 2026-07-07
Добавлено
- Модуль
pgvip(19-pg-vip.sh) — виртуальный IP кластера PostgreSQL через keepalived: check-скрипт «я primary?» управляет VRRP-приоритетом, VIP всегда на текущем primary и переезжает на повышенную реплику за 2–3 с. Unicast VRRP (работает без multicast), правило proto 112 в UFW before.rules, детерминированный auth-пароль без межузловой координации. Итог: приложения используют одну строку подключенияpostgresql://…@<VIP>:5432/…навсегда. - Опрос VIP в инсталляторе (адрес/маска, пиры с автоподстановкой из данных кластера, интерфейс с автоопределением).
- Команда
mitdev pg vip— держатель VIP, служба, события keepalived. - Команда
mitdev pg follow <ip>— перецепление реплики на нового primary после failover (пересинхронизация pg_basebackup + перенацеливание watchdog); в модуле 18 добавлен флагPG_FORCE_RESYNCдля этого сценария. - Проверка VIP в
mitdev doctor(включая опасный случай «VIP на реплике»), keepalived и watchdog вmitdev services; документация: сценарий трёх узлов, раздел о строках подключения (VIP и multi-host libpq), troubleshooting по VRRP.
[1.3.0] — 2026-07-07
Добавлено
- Автоматический failover PostgreSQL: watchdog
mitdev-pg-watchdog(lib/pg-watchdog.sh + systemd-юнит, работает от postgres) на реплике — двойная проверка primary (pg_isready + TCP) каждые 5 с, повышение после 6 подряд неудач (~30 с) с повторной проверкой после приоритетной задержки; защита от ложных срабатываний и журналирование в journald. Вопрос о включении — при установке реплики; настройкиPG_WATCHDOG_*в defaults.conf. - Команда
mitdev pg watchdog [status|enable|disable]; состояние watchdog вmitdev doctor. - HTML-документация + сервер установки (
site/): генераторbuild.mjs(Node + marked) собирает статический сайт из docs/*.md (самодостаточный CSS, тёмная тема, без CDN), рукописную главную с командой установки, скрипт/get(установка одной командой с вашего домена:curl -fsSL https://домен/get | sudo bash) и дистрибутивmitdev.tar.gz. URL домена запекается при сборке:node build.mjs --url https://ваш-домен.
[1.2.0] — 2026-07-07
Добавлено
- Модуль
pgcluster(18-postgresql-cluster.sh) — потоковая репликация PostgreSQL:- роль primary: роль репликации со сгенерированным паролем, conf.d-дроп-ин (wal_level, max_wal_senders, слоты, wal_keep_size), записи pg_hba и правила UFW по IP реплик (5432/tcp только с них);
- роль replica: проверка доступности primary,
pg_basebackup -R -C -S <слот>(standby.signal + primary_conninfo + физический слот), прежние данные сохраняются вmain.mitdev-old.<дата>(это же — путь отката), верификация streaming-статуса WAL receiver; - идемпотентность: уже работающая реплика не пересоздаётся.
- Опрос конфигурации кластера в инсталляторе (роль, primary, пароль, IP реплик)
с явным предупреждением о замещении данных на реплике; роль сохраняется
в
install.env. - Команда
mitdev pg: status, replication (отставание, слоты),add-replica <ip>,promote(с защитой от split-brain). - Проверка репликации в
mitdev doctor(в т.ч. неактивные слоты, из-за которых копится WAL). - Обобщённый резолвер зависимостей модулей в install.sh (pm2→node, certbot→nginx, pgcluster→postgresql).
- Полный комплект документации: ARCHITECTURE.md (устройство и обоснование решений), MODULES.md (справочник всех модулей), CONFIGURATION.md (каждая переменная defaults.conf), SECURITY.md (модель безопасности), TROUBLESHOOTING.md (диагностика по компонентам); актуализированы INSTALL.md, DEVELOPER.md и README.
[1.1.0] — 2026-07-07
Добавлено
- Модуль
kubernetes(17-kubernetes.sh): k3s (канал stable) + kubectl + Helm; kubeconfig для пользователя деплоя; правила UFW (6443/tcp, pod- и service-сети); автоматическое отключение traefik при установленном nginx (конфликт портов 80/443); откат через официальныйk3s-uninstall.sh. - Команда
mitdev k8s: status, nodes, pods, services, apply, logs,deploy <имя> <образ> [порт] [домен] [реплики](Deployment + Service NodePort, rolling update, probes, публикация через traefik-Ingress или host-nginx + SSL),delete <имя>. - Шаблоны
templates/kubernetes/: app.yaml.template (Deployment + Service), ingress.yaml.template (traefik). - Проверка Kubernetes в
mitdev doctor, секция вmitdev info, k3s вmitdev services; покрытие в smoke- и verify-тестах.
[1.0.0] — 2026-07-07
Добавлено
- Интерактивный TUI-инсталлятор
install.sh(gum с фолбэком на whiptail/dialog/plain): выбор компонентов, настройка пользователя деплоя и SSH, спиннеры, прогресс-бар, экраны успеха и ошибок, итоговая сводка. - Предустановочные проверки: root, Ubuntu 22.04, архитектура, интернет, виртуализация, диск, RAM, блокировка APT.
- 17 изолированных модулей: system, user, security (sshd), node, docker, nginx, pm2, certbot, firewall (ufw), fail2ban, utils, postgresql, redis, mongodb, rabbitmq, minio, cleanup.
- Идемпотентность через маркеры состояния в
/var/lib/mitdev/state/. - Файловый стек отката: сбойный модуль откатывается по подтверждению.
- Инструмент администрирования
mitdev: info, doctor, clean, update, backup, restore, ssl, app, deploy, docker, node, nginx, services, logs, version, uninstall. - Шаблоны: nginx (default + reverse-proxy), sshd drop-in, systemd-юнит MinIO, jail.local для Fail2Ban.
- Генерация и сохранение паролей служб в
/root/.mitdev-credentials(0600). - Логирование каждого действия в
/var/log/mitdev/install.log+errors.log. - Тесты:
tests/smoke.sh(структура, bash -n, shellcheck, контракт модулей),tests/verify.sh(пост-установочная верификация). - Документация: README, руководства по установке, разработке, вкладу, справочник команд.