architect/concept/INSTALL-PROFILE.md

title: "Концепция: Install Profile — создание дистрибутива платформы"
version: 1.0.0
date: 2026-04-02
status: active
type: concept


Install Profile — концепция создания дистрибутива

Принцип: любой дистрибутив создаётся итеративно.
Ставим → тестируем → сносим → исправляем → повторяем.
Только когда результат чистый — фиксируем как финальный профиль.


1. Суть

Install Profile — это воспроизводимый пакет: скрипты + конфиги + тесты,
которые позволяют поднять окружение на чистом сервере и получить
заранее известный, проверенный результат.

Это применимо к любому дистрибутиву: сервер, сервис, приложение, кластер.


2. Жизненный цикл профиля

 DRAFT ──► TEST ──► BROKEN? ──► FIX ──► WIPE ──► TEST (повтор)
                       │
                    PASSED?
                       │
                    RECORD ──► FINAL ──► RELEASE

Фазы

Фаза Действие Результат
DRAFT Написать скрипты, конфиги, тесты Набор файлов в system/install/
TEST Запустить на свежем сервере Журнал установки
BROKEN Выявить что сломалось Список багов
FIX Исправить скрипты (НЕ сервер!) Патч в git
WIPE Уничтожить сервер полностью Чистая машина
REPEAT Ставить заново по исправленному Новый прогон
RECORD Зафиксировать: версия + теги + артефакты releases/v1.0.0/
RELEASE Опубликовать профиль Готов к использованию

3. Инфраструктура тестирования

3.1 Тестовый сервер (отдельный!)

3.2 Что значит «сносим»

Вариант А (рекомендуется): Snapshot rollback

1. Создать snapshot ПЕРЕД установкой
2. Запустить install.sh
3. Прогнать тесты
4. Если провалились  rollback snapshot  исправить  повторить
5. Если прошли  сохранить snapshot как «CLEAN-v1.0.0»

Вариант Б: Пересоздание VPS

1. Создать VPS (Beget/Hetzner/etc)
2. Запустить install.sh
3. Прогнать тесты
4. Если провалились  удалить VPS  создать новый  исправить  повторить
5. Если прошли  зафиксировать профиль

4. Структура тестов

Каждый модуль должен иметь проверку. Тесты пишутся вместе со скриптом.

4.1 Файл тестов

system/install/tests/
├── test-system.sh       пакеты, UFW, fail2ban
├── test-docker.sh       docker, compose, daemon.json
├── test-networks.sh     proxy-net существует
├── test-wireguard.sh    wg0 запущен, IP доступен
├── test-core.sh         postgres healthy, gitea отвечает
├── test-ops.sh          portainer, vaultwarden, uptime-kuma
├── test-proxy.sh        SOCKS5 проксирует запросы
└── run-all.sh           запускает все тесты, выводит сводку

4.2 Формат теста

#!/bin/bash
# test-docker.sh — проверка Docker после install_docker()
PASS=0; FAIL=0

check() {
  local desc="$1"; shift
  if eval "$@" &>/dev/null; then
    echo "  ✓ ${desc}"
    ((PASS++))
  else
    echo "  ✗ ${desc}"
    ((FAIL++))
  fi
}

check "docker запущен"           systemctl is-active docker
check "docker версия ≥ 24"       docker version --format '{{.Server.Version}}' | awk -F. '$1>=24'
check "compose plugin работает"  docker compose version
check "log rotation настроен"    grep -q '"max-size"' /etc/docker/daemon.json

echo ""
echo "Итог: PASS=${PASS} FAIL=${FAIL}"
[[ $FAIL -eq 0 ]]

4.3 Сводный тест

#!/bin/bash
# run-all.sh
TOTAL_FAIL=0

run_test() {
  local file="$1"
  echo "━━ $(basename $file) ━━"
  bash "$file" || ((TOTAL_FAIL++))
  echo ""
}

for t in "$(dirname $0)"/test-*.sh; do
  run_test "$t"
done

if [[ $TOTAL_FAIL -eq 0 ]]; then
  echo "✅ ВСЕ ТЕСТЫ ПРОШЛИ — профиль готов к записи"
else
  echo "❌ ПРОВАЛЕНО: ${TOTAL_FAIL} тест(ов) — не записывать профиль"
  exit 1
fi

5. Сценарий: Мать + Дочка

Шаг 1 — Тестовое окружение

[ Тестовая Мать ]      [ Тестовая Дочка ]
  чистый Ubuntu          чистый Ubuntu
  любой IP               любой IP

Шаг 2 — Установка Матери

# На тестовой Матери:
git clone <arch-infra> /opt/install
bash /opt/install/system/install/install.sh
# → выбрать "Мать"
# → заполнить конфиг (тестовый домен)
# → выбрать стеки: CORE + OPS + CREATOR + PROJECTOR

Шаг 3 — Тест Матери

bash /opt/install/system/install/tests/run-all.sh

Если FAIL → запись в журнал → исправить скрипт → WIPE → повторить

Шаг 4 — Установка Дочки

# На тестовой Дочке:
bash /opt/install/system/install/install.sh
# → выбрать "Дочка"
# → ввести публичный ключ Матери
# → выбрать стеки: PROJECTOR

Шаг 5 — Тест туннеля

# На Дочке:
ping 10.10.0.1           # Мать
wg show                  # VPN активен

# На Матери:
ping DAUGHTER_WG_IP      # Дочка

Шаг 6 — Интеграционный тест

# Создать тестовый репозиторий в Gitea на Матери
# Подключить Проектор на Дочке к репозиторию
# Проверить что Проектор видит свои проекты, НЕ видит чужие

Шаг 7 — Запись профиля

bash /opt/install/system/install/release.sh v1.0.0

6. release.sh — запись финального профиля

#!/bin/bash
# release.sh — фиксация версии профиля
VERSION="${1:?Укажите версию: release.sh v1.0.0}"

RELEASE_DIR="system/install/releases/${VERSION}"
mkdir -p "${RELEASE_DIR}"

# Копировать все скрипты
cp -r system/install/{lib,modules,configs,tests} "${RELEASE_DIR}/"
cp system/install/install.sh "${RELEASE_DIR}/"
cp system/install/MANUAL-STEPS.md "${RELEASE_DIR}/"

# Зафиксировать дату и результат тестов
{
  echo "version=${VERSION}"
  echo "date=$(date '+%Y-%m-%d %H:%M')"
  echo "tested_on=$(hostname)"
  echo "tester=$(git config user.name)"
} > "${RELEASE_DIR}/RELEASE.txt"

# Прогнать тесты последний раз
bash system/install/tests/run-all.sh >> "${RELEASE_DIR}/test-results.txt" 2>&1
if [[ $? -ne 0 ]]; then
  echo "❌ Тесты провалились — релиз не создан"
  rm -rf "${RELEASE_DIR}"
  exit 1
fi

# Коммит
git add "${RELEASE_DIR}"
git commit -m "release: install profile ${VERSION}"
git tag "${VERSION}"

echo "✅ Профиль ${VERSION} зафиксирован"

7. Журнал итераций

При каждом цикле тестирования записывать:

## Итерация 1 — 2026-04-02

**Сервер:** тест-vps-1 (91.x.x.x)
**Прогон:** install.sh → Мать → CORE+OPS

**Результат тестов:**
- ✓ system: все прошли
- ✗ docker: log rotation не применился (daemon.json перезатёрся)
- ✗ core: postgres не стартовал (init-db.sh — синтаксическая ошибка)

**Исправления:**
- docker.sh: systemctl restart после записи daemon.json
- core.sh: исправлен heredoc в init-db.sh

**WIPE:** да (snapshot rollback)

---

## Итерация 2 — 2026-04-02

...все прошли → RECORD v1.0.0

Файл: system/install/ITERATIONS.md


8. Принципы

  1. Никакого хардкода — все значения через переменные и .env
  2. Имена, не IP — контейнеры общаются по именам (core-postgres, не 172.x)
  3. Идемпотентность — скрипт можно запустить дважды без поломки
  4. Тесты — часть поставки — не "потом добавим", а сразу
  5. Фиксировать только проверенное — draft ≠ release
  6. Сносить полностью — никаких "частичных откатов" на тесте