РазделТестовая площадка
Тестовая площадка
Площадка — это Proxmox-хост, на котором по одной виртуальной машине на каждый дистрибутив из матрицы платформ: Debian 12 и 13, Ubuntu 22.04, 24.04 и 26.04, AlmaLinux 9 и 10, Rocky Linux 9 и 10, Oracle Linux 9 и 10. Машины собираются из официальных облачных образов через cloud-init, у каждой сразу после первой загрузки снимается снимок clean, так что прогон всегда начинается с чистой ОС. Всё, что относится к конкретному хосту — адрес, сеть, ключ, — лежит в git-игнорируемом .dev/testbed.env; в репозитории только скрипты.
Как устроено
| Файл | Где выполняется | Что делает |
|---|---|---|
scripts/testbed/pve.sh | на хосте Proxmox, от root | скачивает образы, создаёт машины (q35, OVMF, cpu: host, virtio-scsi, cloud-init на scsi1), ждёт cloud-init и гостевой агент, снимает и откатывает снимок clean |
scripts/testbed/testbed.sh | локально | гонит pve.sh по ssh с настройками из .dev/testbed.env, пишет ssh-алиасы mp-<имя> в ~/.ssh/config.d/monopanel-testbed, собирает пакет, разворачивает панель и запускает e2e |
scripts/testbed/bootstrap.sh | на машине, от root | ставит .deb/.rpm (или голый бинарник), mp setup, затем через панель — nginx, ветку PHP, СУБД, дополнительно apache/fail2ban/firewall/mail; заканчивает mp doctor |
Машина n получает vmid 900+n и адрес TB_PREFIX.(TB_IP_BASE+n), имя хоста mp-<дистрибутив>. cloud-init-диск подключён к virtio-scsi, а не к ide2: у ядра cloud в Debian нет драйвера AHCI, и на q35 диск на ide2 гость не видит. Готовность машины хост видит только по ответу гостевого агента, а закончил ли cloud-init, обёртка спрашивает по ssh — guest-exec в EL заблокирован фильтром агента и SELinux, а образы Oracle поставляются с уже запущенным агентом. Образы Oracle именуются по обновлению и сборке (OL9U8_x86_64-kvm-b293), новые появляются на yum.oracle.com/oracle-linux-templates.html. Прошивка — OVMF без предустановленных ключей Secure Boot, потому что образы EL10 требуют x86-64-v3 и всем удобнее загружаться одинаково.
Настройка
# .dev/testbed.env
PVE_HOST=root@pve.example.com
TB_PREFIX=192.0.2 # первые три октета сети машин
TB_GATEWAY=192.0.2.1
TB_IP_BASE=200 # машина n → 192.0.2.(200+n)
TB_SSH_KEY_FILE=~/.ssh/id_ed25519Команды
make testbed-up # создать недостающие машины, загрузить, снять clean
make testbed-status # таблица: vmid, имя, адрес, состояние, снимок
make testbed-deploy VM=debian13 # собрать пакет, поставить панель и стек
make testbed-e2e VM=debian13 # e2e-тесты против этой машины
make testbed-reset VM=debian13 # откатить к clean
make testbed-matrix # reset + deploy + e2e на всех машинах параллельно, сводная таблица
make testbed-migrate SRC=ubuntu2404 DST=debian13 # перенос аккаунта между двумя машинами с проверкой
make testbed-sources ARGS="migrate bitrixvm debian13" # переезд с BitrixVM / FASTPANEL (машины-источники)
make testbed-full # всё для релиза: матрица, переносы, CMS, чужие источники, doctor — с итоговой сводкой
make testbed-down # удалить машины (образы остаются в кэше)VM= можно опустить там, где команда имеет смысл для всех машин. Логи параллельного прогона — в .dev/testbed/logs/<имя>.log. Стек по умолчанию: nginx, PHP 8.4, Percona; переопределяется переменными TB_PHP, TB_DB (percona, mysql, none) и TB_EXTRA (apache fail2ban firewall mail).
Панель на машине слушает самоподписанный сертификат на адрес машины, поэтому e2e идёт с MONOPANEL_INSECURE=1 — обёртка ставит его сама. Токен для тестов выпускается через root-сокет (mp token create) и отзывается после прогона, пароль администратора нигде не сохраняется: он печатается один раз при первом bootstrap.sh.
Полный прогон
make testbed-full (он же scripts/testbed/full-run.sh, STAGES="matrix cms" — часть этапов) делает всё, что проверяется перед релизом, и печатает сводку: матрица на всех машинах, три переезда между панелями, четыре CMS на каждой машине (упавшая на сетевой ошибке CMS переустанавливается ещё раз), три чужих источника, mp doctor везде с числом отказов и предупреждений. Логи — в .dev/testbed/logs/, пароли панелей и админок CMS — в .dev/panels.txt и .dev/cms.txt. Итог exit 1, если хоть что-то упало. Установщики CMS теперь пишут в лог и строки note — повторы и пропуски шагов мастера Битрикса, которые раньше терялись на успешной установке.
Сертификат панели каждой машины выпускается один раз: у Let's Encrypt лимит пять сертификатов на имя в неделю, а откат к снимку выпускал новый. testbed.sh deploy хранит выпущенный сертификат в .dev/testbed/certs/<машина>/ и ставит его обратно (mp ssl panel import), пока до конца срока больше месяца.
Имена в DNS
Машины сидят в частной сети, поэтому HTTP-01 до них не дотянется, а вот имена и DNS-01 — работают. Если в .dev/testbed.env заданы TB_ZONE (зона в Cloudflare) и TB_CF_TOKEN (API-токен с правом Zone:DNS:Edit на неё), make testbed-dns (он же testbed.sh dns up) создаёт для каждой машины A-записи <имя>.tb.<зона> и *.<имя>.tb.<зона> (метка tb меняется через TB_DNS_LABEL), без проксирования; dns down убирает их, dns list показывает. С заданной зоной deploy называет панель её именем, а не адресом, регистрирует в ней Cloudflare как DNS-провайдера cf — токен уезжает на машину по stdin и нигде не печатается — и сразу выпускает панели боевой сертификат (mp ssl panel issue --dns cf; неудача не роняет деплой, панель остаётся на самоподписанном). Сертификат для сайта выпускается через mp ssl issue <имя> --dns cf; на площадке разумно брать --staging, чтобы не упереться в лимит Let's Encrypt на повторные сертификаты для одних и тех же имён при частых откатах к clean. Проверено 2026-09-09: боевой сертификат панели alma9 через DNS-01 доверен с рабочего места, сайт site1.rocky10.tb.<зона> со staging-сертификатом отдаётся nginx по HTTPS (сайт создаётся с --ssl none, затем mp ssl issue … --dns cf и mp site set … --ssl auto — панель подхватывает уже выпущенный сертификат вместо HTTP-01). Учтите отрицательный кеш резолвера: имя под wildcard, спрошенное до появления записи, публичный резолвер может помнить как несуществующее до получаса.
Перенос между панелями
Две любые машины площадки — готовая пара для переноса. scripts/testbed/migrate.sh (он же make testbed-migrate) создаёт на источнике аккаунт с сайтом, PHP-файлом, базой с данными и заданием cron, выдаёт токен (mp migrate grant), на приёмнике делает mp migrate plan и mp migrate run с --insecure (сертификаты самоподписанные) и проверяет, что всё приехало: unix-аккаунт с тем же хешем пароля, вход в панель старым паролем, сайт отвечает тем же PHP-файлом, строки в базе и пароль её пользователя, задание в crontab, mp doctor без ошибок. Аккаунт остаётся на обеих машинах для осмотра; make testbed-reset убирает его вместе со всем остальным.
Чужие панели как источники
Кроме матрицы на площадке есть три машины-источника для переезда с чужих панелей: bitrixvm (AlmaLinux 9, vmid 913, .213), bitrixvm7 (CentOS 7 с bitrix-env 7 — оба давно EOL, так до сих пор живут старые серверы Битрикса; vmid 914, .214, SeaBIOS, потому что у образа CentOS 7 нет EFI-раздела, репозитории cloud-init переводит на vault.centos.org) и fastpanel (Debian 12, vmid 912, .212). В матрицу они не входят (pve.sh держит их в отдельной таблице SOURCES), но up, reset, status и dns их знают. scripts/testbed/sources.sh (он же make testbed-sources ARGS=…):
install bitrixvmставит bitrix-env 9 официальнымbitrix-env-9.sh(он требует выключить SELinux и перезагрузиться — скрипт делает это сам, ~15 минут) и создаёт management-пул, без которогоbx-sitesне работает;install bitrixvm7— старыйbitrix-env.shна CentOS 7, перед ним EPEL берётся из архива, Remi и Percona — с их ещё живых деревьев EL7 (ссылка наepel-release-latest-7в установщике мертва, а найденные репозитории он пропускает);install fastpanel—install_fastpanel.sh(парольfastuserпопадает в.dev/sources.txt).seed bitrixvm|bitrixvm7 [FROM]кладёт в/home/bitrix/wwwсайт Битрикса, которыйtestbed.sh cms FROM bitrixпоставил на машину матрицы (по умолчаниюalma9), с его базой вsitemanagerи реквизитами bitrix-env в.settings.php; главный сайт остаётся подserver_name _, как у людей.seed fastpanel [FROM]заводит аккаунтshopс WordPress оттуда же, сайтом с docrootpublic/, алиасом и allow-списком, базой, crontab сdata/bin/php, настоящим сертификатом Let's Encrypt (DNS-01 через панель FROM) и ящиком. FASTPANEL без лицензии из её биллинга не пускает ни в API, ни в интерфейс, поэтому строки аккаунта, сайтов, бэкендов, баз, сертификатов и cron пишутся прямо вfastpanel2.db— так, как их пишет живая панель (сверено с рабочим FASTPANEL 1.11); файлы, nginx, php-fpm и MySQL настоящие.- Образ CentOS 7 оставляет в
resolv.confпервым мёртвый nameserver libvirt (192.168.122.1): каждый запрос ждёт 5 секунд, вход по ssh — 40, а разбор переезда — минуты.pve.shвычищает его при каждой загрузке,prepare_centos7— ещё раз перед установкой. migrate bitrixvm|bitrixvm7 DSTиmigrate fastpanel DSTдают root машины DST ключ на источник, ставят там ветку PHP источника, делаютmp migrate planиrunи проверяют: сайт отвечает по новому адресу (у Битрикса —X-Powered-CMS, у WordPress — по HTTPS с перевезённым сертификатом), пути/home/bitrixпереписаны, вход в базу прежним паролем или хешем, unix-хеш совпадает с источником, cron и allow-список на месте.
Пароли, которые придумывает seed, лежат в .dev/sources.txt (git-ignored). Прогон 2026-09-12: BitrixVM → debian13 и FASTPANEL → rocky9 без замечаний.
CMS поверх пресетов
make testbed-cms NAME=<машина> [CMS="wordpress joomla opencart bitrix"] (он же testbed.sh cms) ставит на машину CMS средствами самой панели — mp cms install — в сайты с соответствующими пресетами и проверяет, что сайт работает, а правила пресета действуют. Нужны имена в DNS (dns up): сайты называются <cms>.<машина>.tb.<зона> и живут по HTTP — HTTP-01 до частной сети не дотянется, а проверять пресеты TLS не нужен. Пользователь панели cms; сайт с файлами переустанавливается с --force.
- WordPress: главная и вход,
/wp-admin/уводит на логин,xmlrpc.phpзакрыт (403), PHP вwp-content/uploads/не исполняется, ЧПУ-ссылка отдаёт 200. - Joomla: главная,
/administrator/,configuration.phpиcache/закрыты,/api/отвечает 401, PHP вimages/не исполняется, SEF-маршрут работает,installation/удалён. - OpenCart: витрина,
/admin/,system/иstorage/закрыты,.twigне отдаётся, SEO-URL уходит в_route_,install/удалён. - 1С-Битрикс (пробная «Старт»):
X-Powered-CMS: Bitrix Site Managerна главной, форма входа/bitrix/admin/, закрытыеphp_interface/dbconn.php,modules/,.settings.php,cache/, PHP вupload/не исполняется, несуществующий адрес обрабатываетurlrewrite.php,mp site phpпоказываетshort_open_tag On,max_input_vars 20000,memory_limit 512M.
Доступы к админкам печатаются строками CREDS один раз. Что пришлось учесть, пока сценарий был ещё ручным: на EL Percona идёт с validate_password (пароль базы должен содержать все классы символов, панель их генерирует так); файлы, скопированные cp -a из /root, на EL сохраняют метку SELinux admin_home_t — теперь это чинит mp site fix, а панель после распаковки метит файлы сама; mp db create для существующей базы не ошибается, а переустанавливает пароль.
Что нашёл первый прогон (2026-09-09)
До площадки панель проверялась только на Ubuntu 24.04. Первый же круг по всей матрице дал шесть ошибок, первый переезд — седьмую, и каждая из них воспроизводилась только на настоящей ОС — тест с поддельным агентом их увидеть не мог:
| Где | Что было | Что сделано |
|---|---|---|
| Ubuntu 26.04 | у ppa:ondrej/php нет сборок для resolute, apt update падал на 404 | панель проверяет dists/<codename>/Release перед подключением, ставит PHP из самой Ubuntu (8.5), в списке веток помечает недоступные с причиной, при следующей установке проверяет PPA снова и убирает за собой негодный источник |
| EL9, EL10 | nginx не стартовал: nginx -t, которым агент проверяет конфигурацию, создавал /run/nginx.pid с меткой var_run_t, домен httpd_t его не открывал | у набора конфигов появилось поле restore; агент после записи файлов и создания каталогов делает restorecon; при первой установке nginx/PHP панель настраивает fcontext и булевы SELinux под хостинг (02) |
| EL9 | semanage fcontext отвергает /run/... из-за правила эквивалентности /run ↔ /var/run (на EL10 — наоборот) | панель следует подсказке semanage |
| EL9, EL10 | Percona ставится с временным просроченным паролем root в /var/log/mysqld.log, плагин auth_socket не загружен | панель читает пароль из лога, задаёт свежий (просроченный пускает только ALTER USER), грузит плагин и переводит root на сокет; пароль на время трёх запросов живёт только в памяти и передаётся через окружение |
| EL (Percona) | validate_password MEDIUM отвергал примерно половину сгенерированных паролей баз — случайная строка без гарантии спецсимвола; Rocky прошла случайно | генератор auth.NewPassword гарантирует все классы символов; используется для паролей баз, ящиков и Roundcube |
| Ubuntu 24.04 | repo.percona.com ответил таймаутом TLS, установка упала целиком | загрузки ключей и пакетов репозиториев повторяются при сетевых ошибках |
| перенос | хеш caching_sha2_password с бинарной солью портился в текстовом выводе SHOW CREATE USER (ERROR 1827) | источник просит сервер печатать хеш hex-литералом |
После исправлений полный прогон make testbed-matrix с чистых снимков зелёный на всех машинах (Ubuntu 26.04 — с PHP 8.5, остальные — 8.4; nginx 1.30, Percona 8.4), переносы Ubuntu 24.04 → Debian 13 и Debian 12 → AlmaLinux 10 проходят все проверки.
Oracle Linux 9 и 10 добавлены 2026-09-10 и нашли ещё две особенности: образы Oracle идут с включённым firewalld (только ssh) — теперь mp setup открывает в нём порт панели, установка nginx — 80 и 443, почты — её порты, а mp firewall enable его выключает; и remi-release-10 требует epel-release = 10, которого пакет oracle-epel-release-el10 не предоставляет — на OL10 панель ставит официальный epel-release. После этого e2e на обеих зелёный, перенос Ubuntu 24.04 → Oracle Linux 9 прошёл все проверки.
Сама площадка тоже кое-чему научила: cloud-init-диск на ide2 невидим для ядра cloud Debian; в EL гостевой агент запрещает guest-exec (снимается в /etc/sysconfig/qemu-ga), а SELinux всё равно не даёт ему запускать команды — готовность машины на EL определяется по ответу агента; pkill -f внутри одной ssh-команды убивает и её саму; параллельные deploy не должны собирать пакет каждый сам — matrix собирает один раз.