RUEN
Обсудить проект

Инженерный кейс

Парк одноразовых виртуальных окружений

Панель поднимает от одного образа десяток независимых окружений: свой диск, свои сетевые реквизиты и доставка настроек внутрь гостя. Два интерфейса поверх одного ядра и режим работы без гипервизора.

методов в контракте бэкенда
9
окружений одной кнопкой
10
дедлайн доставки конфига
180 с

Оркестратор изолированных виртуальных машин под Windows: от одного «золотого» образа поднимается десяток независимых окружений, у каждого свой диск, свои сетевые реквизиты и автоматическая доставка настроек внутрь гостя. Поверх одного ядра работают два интерфейса — настольное окно и веб-панель — и оба видят одни и те же окружения.

Каждое окружение — разностный диск от неприкосновенного родителя

Создание машины начинается не с копии образа, а с диска, который хранит только отличия от родительского. Родитель после сборки становится доступен лишь для чтения — навсегда.

Копировать образ под каждую машину — очевидно и неподъёмно: полная копия системы это десятки гигабайт, десять окружений превращаются в полтерабайта и ночь ожидания вместо секунд. Разностный диск создаётся мгновенно и растёт только на величину изменений. Цена принята и записана в документацию: родитель становится единой точкой отказа, и обновить образ можно только на копии с пересозданием потомков.

Одноразовое окружение отличается от постоянного моделью диска, а не галочкой

Автоматические снимки состояния глушатся у всех машин сразу при создании. Одноразовое окружение дополнительно получает один чистый снимок, и его «сброс» — это откат к нему. Постоянное снимка не получает вовсе, а кнопка сброса у него не просто скрыта — запрос отвергается на уровне ядра.

Автоматические снимки гипервизора — ловушка именно для долгоживущих окружений: система делает их сама, и состояние может неожиданно откатиться, унеся то, что копилось днями. Запрет же на уровне ядра, а не только в интерфейсе, нужен потому, что интерфейс не один: веб-панель открыта, и один запрос мимо кнопки стёр бы накопленное без возможности восстановления.

Подставной бэкенд — не заглушка, а режим по умолчанию

Под панелью лежит контракт из девяти методов, и реализация выбирается одним полем настроек. Подставная держит состояние в памяти и — главное — повторяет отказы боевой: доставить конфиг в незапущенную машину она отказывается тем же сообщением.

Разработка прямо на гипервизоре означает цикл «правка → запуск от администратора → создание машины минутами → проверка», то есть минуты вместо секунд на каждую мелочь интерфейса, и полную невозможность работать на машине без гипервизора. А повторение именно отказов важнее повторения успеха: иначе на подставном режиме отлаживается счастливый путь, а все сообщения об ошибках впервые видятся в бою.

Реквизиты раздаёт оркестратор, а не бэкенд

Генерация сетевых реквизитов, выбор адреса и раздача из пула вынесены в слой над бэкендом. Бэкенд получает готовое описание окружения и только применяет его.

Положи генерацию внутрь — и два бэкенда начнут выдавать разные окружения: подставной упрощённые, боевой настоящие. Тогда всё, что отлажено на подставном, проверено не на тех данных, и весь смысл режима теряется.

Адреса выдаются как «первый свободный», а не по счётчику

Занятые номера собираются из существующих окружений, дальше берётся первый неиспользованный. Так же раздаются адреса из пула.

Счётчик или «количество плюс один» ломаются на первом же удалении: убрали третье окружение из десяти — его адрес освободился, но счётчик о нём не знает. Хуже того, счётчик, восстановленный из количества записей, после удаления выдаст уже занятый адрес — два окружения с одним адресом, и оба перестанут работать.

Доставка настроек внутрь гостя — цикл с дедлайном, а не одна команда

Файл кладётся внутрь машины по каналу гипервизора: попытка, пауза несколько секунд, следующая попытка — и так до трёх минут, после чего честная ошибка с текстом последней неудачи.

Служба внутри гостя поднимается уже после того, как гипервизор отрапортовал «машина запущена», и разброс большой: зависит от первичной настройки клона, нагрузки хоста, числа одновременно стартующих окон. Одна попытка сразу после старта провалилась бы почти всегда, а фиксированная пауза либо не хватит на загруженном хосте, либо зря задержит быстрый случай.

Основной путь активации не требует пароля от гостя

В образ один раз кладётся задача, которая сама подхватывает файл по известному пути. Панель просто кладёт файл. Второй путь — исполнить команду внутри гостя — включается, только если пароль задан в настройках.

Очевидный вариант «просто выполним команду внутри» требует держать пароль администратора образа открытым текстом — для десятка клонов это один пароль ко всем сразу. Путь через задачу в образе не требует учётных данных вовсе: доставка односторонняя, а решение о применении принимает сам гость. Отдельно ценно, что канал идёт не через сеть: панель управляет гостем как раз тогда, когда его сеть перенастраивается и может быть недоступна.

Два интерфейса — это две проекции одного объекта, а не два процесса

Кнопка «Веб» в настольной панели поднимает веб-сервер в фоновом потоке и передаёт ему тот же экземпляр оркестратора. Оба интерфейса опрашивают его раз в три секунды.

Очевидный способ подружить их — отдельный процесс и общая база. Тогда появляются два независимых состояния поверх одного файла, и гонка становится вопросом времени; а в подставном режиме, где состояние живёт в памяти, процессы разойдутся полностью: в одном окружение запущено, в другом не создано. Передача одного объекта снимает проблему целиком.

Всё, что уходит в оболочку, экранируется одной функцией

Имена и пути приходят из интерфейса и настроек, то есть извне кода, и прогоняются через экранирование. Команды вызываются без пользовательского профиля и без интерактивных подтверждений.

Интерфейс гипервизора доступен только через оболочку, значит команды собираются текстом. Одна кавычка в имени обрывает строку и превращает остаток в исполняемый код — это выполнение команд на машине с правами администратора. Отключение профиля убирает зависимость от того, что пользователь дописал себе в настройки оболочки, а запрет интерактивности не даёт команде повиснуть на подтверждении, которого никто не увидит.

Предполётная проверка — один вызов, а не семь

Отдельный экран показывает состояние окружения: гипервизор, права, виртуальный коммутатор, образ, каталог дисков. Все проверки собираются одним запросом, а список формирует сам бэкенд.

Без такой проверки условия выясняются по одному, в виде невнятной ошибки посреди создания машины, причём самая частая причина — запуск без прав администратора — выглядит как отказ в доступе к чему-то постороннему. Один вызов вместо семи выбран потому, что запуск оболочки стоит сотни миллисекунд: семь запусков превратили бы открытие окна в заметную паузу.

Публикация исходников защищена запретом по умолчанию

В правилах игнорирования сначала обычные исключения, а в конце — запрет на всё в корне с точечными разрешениями. Отдельно исключены образы дисков, локальная база, настоящие настройки и ключи.

Обычные правила работают по принципу «запретим то, о чём вспомнили». В рабочем каталоге лежали образы на десятки гигабайт, настоящие настройки с реквизитами и приватные ключи — одного неосторожного добавления всех файлов достаточно, чтобы это ушло в историю навсегда, откуда просто так уже не вычищается. Инверсия правила меняет цену ошибки: забытый файл по умолчанию не публикуется.

Python · FastAPI · Tkinter · Hyper-V · PowerShell · SQLite