TL;DR
Большинство команд просто переносят сайт на новый сервер и чинят ошибки по мере поступления: «модуль X не работает — ставим пакет Y». Это работает ровно до первого миграционного окна, когда выясняется, что незадокументированных пакетов накопилось на полсотни, а CI/CD про них ничего не знает. Единственный способ не гадать на миграции — принудительно описать системные зависимости проекта в формате, который читает и пакетный менеджер, и разработчик.
Возражение звучит логично: «Зачем плодить файлы? Bitrix-ядро само проверяет расширения PHP, а системные пакеты,это забота администратора, не разработчика». Аргумент подкрепляют тем, что список RPM-пакетов меняется от версии ОС к версии ОС, и жёсткая фиксация якобы сломается на Rocky 9 вместо Rocky 8. Плюс, если пакет ставится через DNF с зависимостями, то «лишние» транзитивные пакеты не входят в дескриптор,значит, он неполный.
На деле эти возражения разбиваются о простую практику: дескриптор фиксирует явные зависимости,те, что нужны для работы конкретных модулей проекта, а не всё дерево транзитивных пакетов. Да, на RHEL-семействе могут отличаться имена (libcurl-minimal vs libcurl), но это решается либо conditional-блоками в YAML, либо отдельными файлами под версии ОС (system-requirements.rocky8.yml, system-requirements.rocky9.yml). И главное: без такого файла при миграции вы гарантированно пропустите пакет, который не генерирует ошибку сразу, а ломает фоновый обработчик раз в сутки.
Итог: system-requirements.yml в корне репозитория,это не «ещё один конфиг», а единственный способ превратить хаотичную установку пакетов в воспроизводимый шаг CI/CD. Вы перестаёте полагаться на память админа и начинаете полагаться на файл, который лежит рядом с composer.json и bitrix-deps.list. Если пакет нужен,он в списке. Если не нужен,его там нет. И никаких «ой, а мы забыли поставить ImageMagick» после деплоя.
Симптом
Начните с инвентаризации зависимостей на текущем рабочем сервере. Выполните команду `rpm -qa --queryformat '%{NAME}\n'`, чтобы получить плоский список всех установленных RPM-пакетов. Это даст полную картину того, что установлено в системе сейчас.
$ rpm -qa --queryformat '%{NAME}\n' | sort > /tmp/all-packages.txt
$ wc -l /tmp/all-packages.txt
# Вы увидите число вроде 1200 — общее количество пакетов в системе.
Список из 1200 пакетов бесполезен — 95% из них это стандартное окружение ОС, не относящееся к проекту. Сфокусируйтесь на пакетах, которые использует Bitrix и кастомные модули. Запустите Bitrix на тестовой копии сайта и отследите, какие системные вызовы падают с ошибкой. Самый быстрый способ — выполнить `strace -f -e trace=openat php -f /path/to/bitrix/modules/main/cli/bitrix_server.php 2>&1 | grep -E '(libcurl|libxml|libssl|libmagick|libpng|libjpeg)' | sort -u`.
$ strace -f -e trace=openat php -f /path/to/bitrix/modules/main/cli/bitrix_server.php 2>&1 | grep -E '(libcurl.so|libxml2.so|libssl.so)' | sort -u
# Вывод покажет пути вроде /usr/lib64/libcurl.so.4 — это пакеты, которые реально дёргаются.
Какой пакет отвечает за конкретную .so-библиотеку? Запросите её через `rpm -qf /usr/lib64/libcurl.so.4`. Команда покажет имя RPM-пакета, которому принадлежит файл. Это и есть та самая системная зависимость, которую нужно зафиксировать в дескрипторе.
$ rpm -qf /usr/lib64/libcurl.so.4
# Вывод: libcurl-7.76.1-26.el9_4.x86_64
Соберите все такие пакеты в текстовый файл `bitrix-deps.list` внутри репозитория проекта. Формат,одна строка = одно имя RPM-пакета без версии. Версионирование не нужно: на новом сервере установится актуальная минорная версия из репозитория. Файл должен выглядеть так:
# bitrix-deps.list — системные зависимости проекта
ImageMagick
libcurl
libxml2
openssl
php
php-common
php-gd
php-mbstring
php-mysqli
php-xml
php-curl
php-intl
php-opcache
php-zip
Проверьте этот список на чистой машине одной командой: `dnf install -y $(cat bitrix-deps.list)`. Если пакет из списка не найден,dnf выдаст ошибку на этапе установки, а не в рантайме Bitrix. Если всё прошло успешно,у вас готовый артефакт для CI/CD, который гарантирует, что на новом сервере не отвалятся модули обработки изображений, XML-парсинга или TLS-соединений.
$ dnf install -y $(cat bitrix-deps.list)
# Успешный вывод: Complete! — значит, все зависимости установлены.
# Ошибка: No package php-gd available — значит, список требует корректировки или подключения нужного репозитория.
Причина
Представьте, что ваш Bitrix-проект — это сложный механизм, где каждый модуль — шестерёнка. Одни шестерёнки сделаны из PHP-кода, другие,из системных библиотек, которые лежат в операционной системе. Перенос на новый сервер без фиксации этих библиотек похож на переезд часовой мастерской в другое здание: вы взяли все часы, но забыли ящик с отвёртками и пинцетами. Сами часы (код) на месте, а инструментов для их починки (RPM-пакетов),нет. Разница между хаотичной установкой пакетов «по факту ошибки» и единым манифестом,это разница между тушением пожара спичками и наличием плана эвакуации с огнетушителем на каждом этаже.
Если не собрать чеклист зависимостей заранее, попадёте в цикл «сломалось → нашёл → поставил». Сначала отвалится модуль обработки изображений,полезете ставить php-gd и libjpeg-turbo. Через час парсинг XML упадёт с ошибкой,вспомните про libxml2. К вечеру curl не сможет дозвониться до стороннего API из-за старых TLS-цифр,добавите openssl11. На всё это уйдёт день ручного дайвинга в логи, хотя можно было за 15 минут снять слепок с рабочей машины и одним dnf install закрыть все дыры сразу. Это как собирать IKEA-шкаф: открываешь пакеты по одному и ищешь нужный болт по всему полу, а можно заранее разложить все детали по схеме.
Сделайте rpm -qa --queryformat '%{NAME}\n' | sort > bitrix-deps.list на текущем работающем сервере. Полученный файл,та самая «схема сборки». Положите его в корень репозитория рядом с composer.json. Когда CI/CD готовит новую машину, он читает этот список и выполняет dnf install -y $(cat bitrix-deps.list). Это как повесить в пустой квартире на стену список «купить: хлеб, молоко, лампочки»,не нужно три раза бегать в магазин, потому что каждый раз забываете что-то ещё.
Решение
Общепринятый подход — хранить список системных пакетов вручную, прямо в репозитории — в корне неверен. Вы не можете быть уверены, что ваш разработчик, который сейчас пишет `bitrix-deps.list`, не забыл пакет, который ставился «руками» два года назад на продакшене. Единственный надёжный способ,не выдумывать список, а снять слепок с эталонной машины.
Против этого сразу возникает возражение: «На продакшене сотни пакетов, среди них куча мусора,perl, графические утилиты, которые не нужны для работы Bitrix. Если скопировать весь список, мы затащим лишние зависимости и раздуем образ». Звучит логично. Действительно, на сервере с Bitrix может быть установлено 500–700 пакетов, из которых реально нужны 30–40. Ручная фильтрация этого списка,адский труд, который никто не будет делать.
Это возражение разбивается о простой факт: вам не нужно фильтровать вручную. Установка лишнего пакета,не проблема. Проблема,отсутствие нужного. Наши проекты показывают: разница в размере образа между «чистым» сервером и сервером с «мусорными» пакетами,менее 200 МБ. Это ничто по сравнению с часами даунтайма, когда на новом сервере не работает обработка изображений. Поэтому мы делаем так: на эталонном сервере (где всё работает) выполняем команду:
$ rpm -qa --queryformat '%{NAME}\n' | sort > /tmp/current-packages.txt
Должны увидеть плоский текстовый файл с отсортированными названиями всех пакетов,от `acl` до `zlib`.
Затем на целевой машине (чистый сервер) делаем то же самое и сравниваем diff:
$ rpm -qa --queryformat '%{NAME}\n' | sort > /tmp/target-packages.txt
$ diff /tmp/current-packages.txt /tmp/target-packages.txt | grep '^<'
Вывод покажет только те пакеты, которые есть на эталоне, но отсутствуют на целевой машине.
Этот diff-файл и есть ваш честный `system-requirements.yml`. Он не выдуман, он снят с живой системы. Кладём его в репозиторий, а в CI/CD добавляем шаг:
# dnf install -y $(comm -23 <(sort current-packages.txt) <(sort target-packages.txt))
Команда `comm -23` выдаёт строки, уникальные для первого файла (текущего эталона). После выполнения на новой машине все зависимости будут установлены одним проходом, без ручного перебора.
Никакого ручного составления списка. Никаких споров «а нужен ли этот пакет». Только факты: если пакет стоит на работающем эталоне,он нужен. Исключение,явные артефакты сборки (gcc, make, kernel-devel), которые можно отфильтровать одной регуляркой. Всё остальное,обязательный груз, который гарантирует, что после миграции модули не отвалятся.
Подводные камни
Соберите эталонный список пакетов с рабочего сервера до начала любых миграций. Выполните на работающей машине rpm -qa --queryformat '%{NAME}\n' | sort > /tmp/current_packages.txt — это даст плоский список всех установленных RPM. Затем отфильтруйте его под проект: оставьте только те пакеты, без которых Bitrix и кастомные модули гарантированно не работают. Например, php-{version}-*, ImageMagick, libxml2, curl, openssl, mysql-server (или mariadb-server), nginx/httpd и их зависимости. Всё, что относится к мониторингу, бэкапам и другим сервисам, не связанным с проектом, уберите.
Хранить этот список в репозитории вручную — единственный надёжный способ. Создайте в корне проекта файл system-requirements.yml со структурой:
---
packages:
- php-8.1
- php-8.1-curl
- php-8.1-xml
- php-8.1-mbstring
- php-8.1-mysqlnd
- ImageMagick
- libxml2
- libxslt
- curl
- openssl
- nginx
- mariadb-server
- git
- unzip
Этот файл,единственный источник правды. При развёртывании нового сервера CI/CD читает его и прогоняет dnf install -y $(grep '^ - ' system-requirements.yml | sed 's/^ - //'). После выполнения команды вы должны увидеть Complete! без ошибок.
Как быстро выявить недостающие пакеты на уже проблемном сервере? Запустите скрипт, который сверяет установленные RPM с эталонным списком из репозитория. Если эталона нет,используйте обратный инжиниринг: включите логирование ошибок Bitrix (bitrix/.settings.php,параметр exception_handling), откройте в браузере все страницы и модули, которые вызывают проблемы, и соберите из логов названия отсутствующих библиотек. Ошибка Fatal error: Class 'DOMDocument' not found означает, что не установлен php-xml. Каждую такую находку сразу вносите в system-requirements.yml и фиксируйте в репозитории.
Вопрос: что делать, если на новом сервере версия пакета отличается от эталонной? Не фиксируйте мажорную версию жёстко, если это не критично. В system-requirements.yml укажите минимально необходимую версию,например, php-8.1.*. Но для ключевых пакетов (сама PHP, MySQL, nginx) версию лучше зафиксировать точно, чтобы избежать несовместимости с Bitrix-ядром. Проверьте совместимость вашей версии Bitrix с целевыми версиями пакетов на dev.1c-bitrix.ru перед фиксацией.
FAQ
Мы храним дескриптор системных зависимостей прямо в репозитории проекта — файл bitrix-deps.yml в корневой директории. Этот YAML-файл содержит плоский список RPM-пакетов, необходимых для работы Bitrix-ядра и кастомных модулей. Формат простой: каждая строка — имя пакета без версии, например, php-xml, ImageMagick, curl. При сборке CI/CD читает этот файл и прогоняет dnf install -y $(grep -v '^#' bitrix-deps.yml | tr '\n' ' '),это гарантирует, что на новой машине за один шаг установятся все системные компоненты. Без такого дескриптора мы однажды потеряли сутки на Rocky Linux 9: после миграции перестал работать модуль обработки XML, хотя PHP-код был цел. Причина,отсутствие пакета php-xml в минимальной сборке ОС. Сейчас мы сначала выгружаем эталонный список с рабочего сервера командой rpm -qa --queryformat '%{NAME}\n' | sort > /tmp/current_packages, затем вручную отсеиваем системные пакеты (ядро, драйверы) и оставляем только те, что нужны приложению,это обычно 40–70 пакетов для типового Bitrix-проекта. Файл коммитится в репозиторий, и при каждом деплое CI/CD выполняет diff между текущим состоянием сервера и этим списком, устанавливая недостающее.
Сравните этот подход с типичной практикой «чинить по мере появления ошибок»: без дескриптора вы тратите время на ручной поиск каждого отсутствующего пакета, а CI/CD не может автоматически воспроизвести окружение. Наш bitrix-deps.yml работает как package.json для npm, но для системных библиотек. Разница в том, что npm-зависимости обновляются часто, а системные пакеты меняются редко,раз в полгода-год при обновлении дистрибутива или добавлении нового модуля. Поэтому мы не храним версии пакетов: в RHEL-семействе версии фиксированы релизом ОС, и dnf install php-xml всегда поставит ту версию, что в репозитории дистрибутива. Если проект использует специфические пакеты вроде libreoffice-headless для конвертации документов, это тоже идёт в дескриптор. Для проверки целостности мы добавили в CI/CD шаг: после установки пакетов запускается скрипт, который проверяет наличие ключевых бинарников (which convert, php -m | grep xml),если чего-то не хватает, пайплайн падает с явной ошибкой. Это дешевле, чем ждать, пока модуль Bitrix упадёт в production.
Единый дескриптор системных зависимостей в репозитории превращает миграцию на новый сервер из гадания в воспроизводимый процесс: один dnf install,и окружение готово. Без этого вы каждый раз рискуете пропустить пакет, который тихо ломает кастомный модуль через месяц после переезда.
См. также
Хранить список системных пакетов вручную, прямо в репозитории, — это как собирать чемодан в отпуск по памяти, закрыв глаза. Вроде бы взял самое нужное: зубную щётку, пару футболок, ноутбук. А на месте оказывается, что забыл зарядку для телефона, адаптер для розеток и крем от солнца. Разработчик пишет bitrix-deps.list, вспоминая, что ставил на прошлой неделе для модуля «Парсинг XML», но забывает про php-xml, который нужен для штатного импорта каталога. В итоге CI/CD отрабатывает, а на новой машине половина функционала молчит. Ручной список — это иллюзия контроля, а не реальный артефакт.
Контраст,с подходом, который мы используем на своих проектах. Это похоже на разницу между тем, чтобы перевозить квартиру, записывая каждый предмет в блокнот, и тем, чтобы просто сфотографировать все комнаты с разных ракурсов. Мы не гадаем, какие пакеты нужны. Мы берём эталон,текущий рабочий сервер,и снимаем полный слепок всех RPM-пакетов командой rpm -qa --queryformat '%{NAME}\n' | sort. Этот «снимок»,истина. Дальше мы не вычищаем его вручную до идеала, а просто коммитим в репозиторий как bitrix-deps.yml. Да, в нём будут пакеты, которые не относятся к проекту (драйверы принтера, системные утилиты). Но это не проблема. Гораздо хуже,недополучить критичный пакет, чем получить лишний.
Практический вывод: представьте, что ваш сервер,это сложный рецепт многослойного торта. Вы не переписываете рецепт каждый раз, когда нужно испечь второй такой же торт. Вы просто кладёте исходный листок в файл. Наш bitrix-deps.yml,это и есть такой листок. Он не идеален, он сырой, но он рабочий. Когда на новом сервере после dnf install -y $(cat bitrix-deps.yml | grep -v '^#' | tr '\n' ' ') всё завелось с первого раза,это стоит того, чтобы захламить репозиторий одним лишним файлом. И не тратить потом день на то, чтобы выкуривать из логов, какого именно пакета не хватает модулю imageconvert.
- • Миграция Bitrix на новый сервер ломает модули из-за отсутствия системных библиотек. • Ручное выявление зависимостей — долгий процесс; нужен единый файл-дескриптор. • Файл `bitrix-deps.list` содержит список RPM-пакетов, устанавливаемых одной командой DNF. • Список собирается через анализ логов ошибок, проверку PHP-модулей и rpm -qa на старом сервере. • Интеграция в CI/CD позволяет автоматически проверять актуальность зависимостей при деплое.