Инженерный кейс
Парк одноразовых виртуальных окружений
Панель поднимает от одного образа десяток независимых окружений: свой диск, свои сетевые реквизиты и доставка настроек внутрь гостя. Два интерфейса поверх одного ядра и режим работы без гипервизора.
- методов в контракте бэкенда
- 9
- окружений одной кнопкой
- 10
- дедлайн доставки конфига
- 180 с
Оркестратор изолированных виртуальных машин под Windows: от одного «золотого» образа поднимается десяток независимых окружений, у каждого свой диск, свои сетевые реквизиты и автоматическая доставка настроек внутрь гостя. Поверх одного ядра работают два интерфейса — настольное окно и веб-панель — и оба видят одни и те же окружения.
Каждое окружение — разностный диск от неприкосновенного родителя
Создание машины начинается не с копии образа, а с диска, который хранит только отличия от родительского. Родитель после сборки становится доступен лишь для чтения — навсегда.
Копировать образ под каждую машину — очевидно и неподъёмно: полная копия системы это десятки гигабайт, десять окружений превращаются в полтерабайта и ночь ожидания вместо секунд. Разностный диск создаётся мгновенно и растёт только на величину изменений. Цена принята и записана в документацию: родитель становится единой точкой отказа, и обновить образ можно только на копии с пересозданием потомков.
Одноразовое окружение отличается от постоянного моделью диска, а не галочкой
Автоматические снимки состояния глушатся у всех машин сразу при создании. Одноразовое окружение дополнительно получает один чистый снимок, и его «сброс» — это откат к нему. Постоянное снимка не получает вовсе, а кнопка сброса у него не просто скрыта — запрос отвергается на уровне ядра.
Автоматические снимки гипервизора — ловушка именно для долгоживущих окружений: система делает их сама, и состояние может неожиданно откатиться, унеся то, что копилось днями. Запрет же на уровне ядра, а не только в интерфейсе, нужен потому, что интерфейс не один: веб-панель открыта, и один запрос мимо кнопки стёр бы накопленное без возможности восстановления.
Подставной бэкенд — не заглушка, а режим по умолчанию
Под панелью лежит контракт из девяти методов, и реализация выбирается одним полем настроек. Подставная держит состояние в памяти и — главное — повторяет отказы боевой: доставить конфиг в незапущенную машину она отказывается тем же сообщением.
Разработка прямо на гипервизоре означает цикл «правка → запуск от администратора → создание машины минутами → проверка», то есть минуты вместо секунд на каждую мелочь интерфейса, и полную невозможность работать на машине без гипервизора. А повторение именно отказов важнее повторения успеха: иначе на подставном режиме отлаживается счастливый путь, а все сообщения об ошибках впервые видятся в бою.
Реквизиты раздаёт оркестратор, а не бэкенд
Генерация сетевых реквизитов, выбор адреса и раздача из пула вынесены в слой над бэкендом. Бэкенд получает готовое описание окружения и только применяет его.
Положи генерацию внутрь — и два бэкенда начнут выдавать разные окружения: подставной упрощённые, боевой настоящие. Тогда всё, что отлажено на подставном, проверено не на тех данных, и весь смысл режима теряется.
Адреса выдаются как «первый свободный», а не по счётчику
Занятые номера собираются из существующих окружений, дальше берётся первый неиспользованный. Так же раздаются адреса из пула.
Счётчик или «количество плюс один» ломаются на первом же удалении: убрали третье окружение из десяти — его адрес освободился, но счётчик о нём не знает. Хуже того, счётчик, восстановленный из количества записей, после удаления выдаст уже занятый адрес — два окружения с одним адресом, и оба перестанут работать.
Доставка настроек внутрь гостя — цикл с дедлайном, а не одна команда
Файл кладётся внутрь машины по каналу гипервизора: попытка, пауза несколько секунд, следующая попытка — и так до трёх минут, после чего честная ошибка с текстом последней неудачи.
Служба внутри гостя поднимается уже после того, как гипервизор отрапортовал «машина запущена», и разброс большой: зависит от первичной настройки клона, нагрузки хоста, числа одновременно стартующих окон. Одна попытка сразу после старта провалилась бы почти всегда, а фиксированная пауза либо не хватит на загруженном хосте, либо зря задержит быстрый случай.
Основной путь активации не требует пароля от гостя
В образ один раз кладётся задача, которая сама подхватывает файл по известному пути. Панель просто кладёт файл. Второй путь — исполнить команду внутри гостя — включается, только если пароль задан в настройках.
Очевидный вариант «просто выполним команду внутри» требует держать пароль администратора образа открытым текстом — для десятка клонов это один пароль ко всем сразу. Путь через задачу в образе не требует учётных данных вовсе: доставка односторонняя, а решение о применении принимает сам гость. Отдельно ценно, что канал идёт не через сеть: панель управляет гостем как раз тогда, когда его сеть перенастраивается и может быть недоступна.
Два интерфейса — это две проекции одного объекта, а не два процесса
Кнопка «Веб» в настольной панели поднимает веб-сервер в фоновом потоке и передаёт ему тот же экземпляр оркестратора. Оба интерфейса опрашивают его раз в три секунды.
Очевидный способ подружить их — отдельный процесс и общая база. Тогда появляются два независимых состояния поверх одного файла, и гонка становится вопросом времени; а в подставном режиме, где состояние живёт в памяти, процессы разойдутся полностью: в одном окружение запущено, в другом не создано. Передача одного объекта снимает проблему целиком.
Всё, что уходит в оболочку, экранируется одной функцией
Имена и пути приходят из интерфейса и настроек, то есть извне кода, и прогоняются через экранирование. Команды вызываются без пользовательского профиля и без интерактивных подтверждений.
Интерфейс гипервизора доступен только через оболочку, значит команды собираются текстом. Одна кавычка в имени обрывает строку и превращает остаток в исполняемый код — это выполнение команд на машине с правами администратора. Отключение профиля убирает зависимость от того, что пользователь дописал себе в настройки оболочки, а запрет интерактивности не даёт команде повиснуть на подтверждении, которого никто не увидит.
Предполётная проверка — один вызов, а не семь
Отдельный экран показывает состояние окружения: гипервизор, права, виртуальный коммутатор, образ, каталог дисков. Все проверки собираются одним запросом, а список формирует сам бэкенд.
Без такой проверки условия выясняются по одному, в виде невнятной ошибки посреди создания машины, причём самая частая причина — запуск без прав администратора — выглядит как отказ в доступе к чему-то постороннему. Один вызов вместо семи выбран потому, что запуск оболочки стоит сотни миллисекунд: семь запусков превратили бы открытие окна в заметную паузу.
Публикация исходников защищена запретом по умолчанию
В правилах игнорирования сначала обычные исключения, а в конце — запрет на всё в корне с точечными разрешениями. Отдельно исключены образы дисков, локальная база, настоящие настройки и ключи.
Обычные правила работают по принципу «запретим то, о чём вспомнили». В рабочем каталоге лежали образы на десятки гигабайт, настоящие настройки с реквизитами и приватные ключи — одного неосторожного добавления всех файлов достаточно, чтобы это ушло в историю навсегда, откуда просто так уже не вычищается. Инверсия правила меняет цену ошибки: забытый файл по умолчанию не публикуется.
Python · FastAPI · Tkinter · Hyper-V · PowerShell · SQLite