Перенос 1С на Linux и PostgreSQL: восемь ловушек, каждая из которых молчит
Иван Недомолков · Опубликовано: · Обновлено:
Перенос 1С на Linux и PostgreSQL обычно обсуждают в терминах “получится или нет”. Получится: мы перенесли боевую базу 276 ГБ с Windows и MS SQL на Ubuntu 22.04 и PostgreSQL, структура на приёмнике совпала с Windows-прогоном до единицы. Интересно другое - как именно этот переезд ломается по дороге.
Восемь ловушек ниже объединяет одно свойство: ни одна не выдаёт ошибку. Флаг виртуализации не врёт грубо, просто отвечает не на тот вопрос. Скачивание без всякой ошибки замирает. Кластер без жалоб подхватывает системную локаль. Автотюнер считает по памяти и про соединения молчит. Каждая такая штука стоит часа, а часть из них проявляется вообще не в день переезда.
Это развёрнутая версия для тех, кто планирует переход на Linux. Короткий кейс той же истории у нас опубликован на Инфостарте; здесь всё целиком, с командами и настройками ОС, которых в кейс не поместилось. Про главную ловушку самой миграции (она не про Linux вовсе) - отдельная статья: Миграция 1С на PostgreSQL: как проверить базу до переезда.
Стенд: Hyper-V на Windows-хосте, гостевая Ubuntu 22.04.5, Postgres Pro 1С 17.10, платформа 1С 8.3.27, источник MS SQL 2022, база бухгалтерской конфигурации 276 ГБ.
Сначала о том, чего не случилось
Главная проверка перед переездом на PostgreSQL - поиск строк, которые PostgreSQL считает одинаковыми, а MS SQL разными. Из-за них не создаётся уникальный индекс, и перенос встаёт молча. Механизм разобран в первой статье: строки в типах mchar и mvarchar сравниваются через библиотеку ICU, и совместимые формы Unicode схлопываются.
Неразрывный пробел в эти данные чаще всего приезжает копипастом, но бывает, что его ставит сама платформа при переводе числа в строку (как такое найти и вычистить). На Linux-стенде я первым делом повторил те же опыты. Думал, что-то да разойдётся: ОС другая, а с ней сборка ICU и локаль.
SELECT ('a b')::mvarchar = ('a' || chr(160) || 'b')::mvarchar AS nbsp_vs_space;
-- true
SELECT ('No5')::mvarchar = (chr(8470) || '5')::mvarchar AS numero_vs_No;
-- true
SELECT ('ABC')::mvarchar = ('abc')::mvarchar AS case_insensitive;
-- true
SELECT (chr(1077))::mvarchar = (chr(1105))::mvarchar AS e_vs_yo;
-- false
Все четыре ответа слово в слово те же, что на Windows. Вывод неприятный, но честный: к операционной системе главная проверка перед переездом отношения не имеет. Всё упирается в ICU, а он на обеих системах одинаковый. Меняете MS SQL на PostgreSQL, а Windows оставляете? Грабли будут те же самые.
Отдельно про е и ё, раз уж это самая частая догадка: ICU их различает. Работает тут не “наличие разложения по NFKD”, как кажется на первый взгляд, а сила сравнения. mvarchar сравнивается на вторичной силе: регистр и третичные различия игнорируются, диакритика учитывается. Неразрывный пробел и № отличаются от базовых форм третичным весом и потому слипаются, а ё отличается от е диакритикой, то есть вторичным весом, и расходится.
Ловушка 1. Флаг виртуализации отвечает не на тот вопрос
Начал со стенда: получится ли вообще поднять виртуальную машину. Проверяю флаг:
VirtualizationFirmwareEnabled : False
Решил: виртуализация отключена в BIOS, без физического доступа к машине и перезагрузки никак. Записал это как блокер проекта.
Ошибся. False этот флаг выдаёт как раз при уже загруженном гипервизоре: Windows тогда смотрит на прошивку сквозь него и правды не видит. Верный признак другой:
(Get-CimInstance Win32_ComputerSystem).HypervisorPresent # правда
(Get-CimInstance Win32_Processor).VirtualizationFirmwareEnabled # врёт при живом гипервизоре
А HypervisorPresent с самого начала отвечал True. Хватило включить роль Hyper-V с перезагрузкой, через минуту машина уже работала. Цена ошибки в диагностике тут не техническая, а организационная: на её основании я чуть не заказал визит в серверную.
Ловушка 2. Процессы из удалённой сессии умирают вместе с ней
Установочный образ я качал на стенд через Start-Process, завёрнутый в Invoke-Command. На 166,9 МБ загрузка встала, и случилось это ровно тогда, когда закрылась сессия WinRM. Никакой ошибки, просто недокачанный файл.
Выручает планировщик задач: процессом там владеет служба, сессия ни при чём.
$act = New-ScheduledTaskAction -Execute 'curl.exe' -Argument '-L -s -o "файл" "url"'
$pr = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'download' -Action $act -Principal $pr -Settings $st
Start-ScheduledTask -TaskName 'download'
Закономерность я разглядел не сразу, потому что в похожей ситуации перенос ibcmd выживал. Только запускал я его иначе: в живой сессии и с ожиданием завершения, без отсоединения. Отсюда правило: процесс, который обязан пережить закрытие сессии, из этой сессии не запускают.
Для окна переключения это не мелочь. Перенос базы идёт часами, и любой разрыв RDP или VPN не должен его ронять.
Ловушка 3. Автоустановка Ubuntu всё равно ждёт живого “yes”
Подготовил автоустановку по документации: образ с конфигурацией лежит на томе с меткой cidata. Машина стартовала, консоль показала, что шаги применения конфигурации отработали. А потом появилось вот это:
Confirmation is required to continue.
Add "autoinstall" to your kernel command line to avoid this
Continue with autoinstall? (yes|no)
Установщик не доверяет конфигурации, пришедшей с тома: применить применяет, а размечать диск без живого подтверждения отказывается. Вопрос исчезнет, только если autoinstall прописан в командной строке ядра. Попасть туда можно лишь правкой загрузчика в консоли.
Практический вывод для тех, кто разворачивает Linux-стенд автоматизированно: полностью безлюдной установка не будет, если вы не правите параметры ядра. Закладывайте либо доступ к консоли, либо автоматизацию ввода в неё, о которой ниже.
Ловушка 4. Клавиатура Hyper-V возвращает успех и ничего не вводит
Установку спасло то, что консоль виртуальной машины поддаётся автоматизации через WMI. Снимок экрана отдаёт метод GetVirtualSystemThumbnailImage, но сырыми пикселями, картинку из них собираешь сам. Не будь его, гадал бы, отчего процессор стоит без дела, а размер диска не меняется.
С вводом вышло занятнее:
| Метод | Итог |
|---|---|
Msvm_Keyboard.TypeText('yes') | код возврата 0, символы не дошли, приглашение повторилось |
TypeKey посимвольно (0x59 0x45 0x53, затем 0x0D) | сработало, установка пошла |
Ноль, который вернул TypeText, значит только “вызов принят”. Введён ли текст, из него не узнать, это покажет лишь экран. Это тот же принцип, что и с флагом виртуализации: индикатор отвечает на свой вопрос, а не на ваш.
Ловушка 5. Ubuntu отдаёт логическому тому сотню гигабайт из семисот
Захожу после установки, смотрю на диск:
/dev/mapper/ubuntu--vg-ubuntu--lv 98G 7.3G 86G 8% /
Из семисот гигабайт виртуального диска файловой системе досталось девяносто восемь. Установщик Ubuntu Server создаёт логический том примерно на сотню, остальное оставляет нераспределённым в группе томов. Формально всё честно, место никуда не делось. Только выяснилось бы это посреди переноса трёхсотгигабайтной базы.
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
# стало 686 ГБ
В предполётной проверке это теперь отдельный пункт: смотрим место на файловой системе с каталогом данных PostgreSQL, объём диска не показатель. На LVM эти числа по умолчанию расходятся.
Ловушка 6. Кластер молча создаётся с английской локалью
Из восьми эта опаснее всех: сразу она себя не выдаёт.
Кластер создаётся сам, прямо при установке пакета PostgreSQL. Системную локаль он забирает как есть, а свежая Ubuntu ставится с en_US.UTF-8. Русской базе такая не годится, ей нужны правила сортировки ru_RU.UTF-8. В день переезда это не всплывёт. Пройдёт месяц, и кто-нибудь удивится странному порядку строк в отчёте или в списке справочника.
Указываю локаль явно и ловлю ошибку:
initdb: error: too many command-line arguments (first is "--locale=ru_RU.UTF-8")
Дело в оболочке: pg-setup initdb не передаёт аргументы в initdb. Локаль она берёт из окружения, а его sudo зачищает. Сработало так:
sudo locale-gen ru_RU.UTF-8
sudo systemctl stop postgrespro-1c-17
sudo rm -rf /var/lib/pgpro/1c-17/data
sudo env LANG=ru_RU.UTF-8 LC_ALL=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8 LC_CTYPE=ru_RU.UTF-8 \
/opt/pgpro/1c-17/bin/pg-setup initdb --tune=1c
Обратите внимание на третью строку: переинициализация уничтожает данные кластера. Поэтому всё это делается до приезда базы: потом сменить локаль так же дёшево уже не выйдет. После переезда исправление означает повторный перенос.
Проверить, что получилось, стоит сразу:
SELECT datname, datcollate, datctype FROM pg_database;
Уточнение от читателя, которое стоит держать в голове: всё сказанное выше про “аргументы не пробрасываются” относится к оболочке pg-setup из сборки Postgres Pro для 1С. Если вы ставите обычный PostgreSQL или сборку от самой 1С и вызываете initdb напрямую, локаль задаётся его собственным ключом и никаких плясок с env не нужно:
sudo -u postgres /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data \
--allow-group-access --locale=ru_RU.UTF-8 --data-checksums
Цена прямого вызова - теряется --tune=1c, то есть готовый набор параметров под 1С, ради которого оболочку и держат.
И вторая половина той же истории: локаль самой службы 1С задаётся не в PostgreSQL, а в юните systemd. Если её не поставить, сервер 1С поднимется с системной локалью, и часть строковых операций пойдёт по чужим правилам:
# /etc/systemd/system/[email protected]
[Service]
Environment=LANG=ru_RU.UTF-8
Ловушка 7. Расширения, которые не расширения
Пустяк, на который тоже ушло время. Смотрю, подключены ли рекомендованные для 1С online_analyze и plantuner:
ERROR: расширение "online_analyze" отсутствует
DETAIL: Не удалось открыть управляющий файл расширения ".../online_analyze.control"
Хотя параметр plantuner.fix_empty_table в конфигурации есть, и он работает. Разгадка: в сборках PostgreSQL под 1С это библиотеки, их грузит shared_preload_libraries, CREATE EXTENSION тут ни при чём. Поэтому в pg_extension их не найти, ответ лежит в pg_settings:
SELECT name, setting FROM pg_settings
WHERE name IN ('shared_preload_libraries', 'online_analyze.enable', 'plantuner.fix_empty_table');
Ложный вывод тут дороже потерянного времени: увидев “расширения нет”, легко пойти его ставить и переломать рабочую конфигурацию.
Ловушка 8. Автотюнер ставит work_mem 512 МБ при 250 соединениях
Сборка PostgreSQL для 1С умеет настраиваться сама, это режим --tune=1c. На 6 ГБ памяти он выбрал вот что:
| Параметр | Значение |
|---|---|
shared_buffers | 1,4 ГБ |
work_mem | 512 МБ |
max_connections | 250 |
effective_cache_size | 4,3 ГБ |
maintenance_work_mem | 370 МБ |
max_locks_per_transaction | 256 |
Память work_mem отводится под каждую сортировку любого сеанса, а не на сервер целиком. В теории 250 соединений по 512 МБ съедают 128 гигабайт при шести в машине. Объём памяти автотюнер учитывает, а вот умножить свою цифру на max_connections не догадывается.
Скажу прямо: здесь рекомендация вендора вредна. Мы добавили в предполётную проверку пункт, который умножает work_mem на max_connections и сравнивает с физической памятью: больше половины - вердикт “чинить”. На боевом сервере такая настройка не убивает систему сразу - она ждёт дня, когда сойдутся несколько тяжёлых отчётов, и уводит сервер в своп.
Сервер под 1С: ноль шрифтов и настройки “как поставилось”
Кластер заработал, и я запустил на сервере проверку из собственного предполётного чек-листа. Думал, она скажет, что нет Arial и Times. Вышло иначе:
L1_FONTS_TOTAL=0
L1_FONT=Arial;found=0
L1_FONT=Times New Roman;found=0
L1_FONT=Calibri;found=0 ... и так все шесть
Шрифтов нет вообще, ни нужных, ни любых других. Даже fontconfig в минимальную установку Ubuntu Server не входит. Печатным формам там разъезжаться нечему: они просто не отрисуются - а узнаете вы об этом от бухгалтера, которому нужен счёт-фактура.
Та же проверка поймала и английскую системную локаль:
L1_LOCALE=LANG=en_US.UTF-8;LC_COLLATE="en_US.UTF-8";...
А ведь кластер я поднимал с ru_RU.UTF-8. Настройки независимые: поправил одну, вторая осталась английской. Вот и наглядный довод, зачем проверок нужно много и каждая отдельно: “локаль настроена” - это не один факт, а как минимум два.
Учётная запись и лимиты тоже остались такими, “как поставилось”:
| Параметр | Что было | Что рекомендуют для 1С |
|---|---|---|
ulimit -n (открытые файлы) | 1024 | 65536 |
vm.swappiness | 60 | 1 |
| huge pages | madvise | never |
Поставить сервер и переехать всё это не помешает. Проявится оно под нагрузкой, когда пользователи уже в работе, и будет выглядеть как “1С на Linux тормозит”.
Ошибка 802 при переносе: почему цифра в конфиге ничего не значит
Эта история обошлась в два сорванных прогона и хорошо показывает разницу между стендом и продуктивом.
Дважды перенос падал с ошибкой:
Ошибка СУБД: Microsoft OLE DB Driver for SQL Server:
Недостаточно свободной памяти в буферном пуле. native=802
Сначала решил, что SQL Server не хватает памяти, и поднял лимит вдвое, с 3 ГБ до 6. Не помогло. Пошёл разбираться, что там на самом деле:
SELECT
(SELECT CAST(value_in_use AS int) FROM sys.configurations
WHERE name = 'max server memory (MB)') AS max_mem_setting,
(SELECT CAST(cntr_value/1024 AS int) FROM sys.dm_os_performance_counters
WHERE counter_name = 'Target Server Memory (KB)') AS target_mb,
(SELECT process_physical_memory_low FROM sys.dm_os_process_memory) AS low_flag;
max_mem_setting target_mb low_flag
6144 290 1
По настройке SQL Server может взять шесть гигабайт, но сам целится в 290 мегабайт и держит флаг нехватки памяти. Лишней памяти на хосте нет: там крутятся виртуальная машина и рабочий стол, а рабочие наборы в сумме занимают 10,8 ГБ, когда физической памяти всего 15,8.
Поднимать настройку было бесполезно. Число в конфиге лишь разрешение, памяти от него не прибавится. Когда ОС сообщает о нехватке, SQL Server разрешённым честно не пользуется. Сработало другое: я ужал виртуальную машину, снизил shared_buffers на приёмнике, а порцию переноса урезал до 1000 строк вместо 10 000. Ошибка 802 вылетает на конкретном запросе памяти, так что уменьшенная порция действует на саму причину.
Кстати, на ошибке источника ibcmd повёл себя прилично: дописал расшифровку от СУБД в stderr и вернул честный код 1. Молча виснет он на ошибке приёмника. Смотреть надо оба лога, и при успешном коде тоже: при переносе с PostgreSQL на MS SQL всё предупреждение о слипшихся датах уместилось в одну строку stderr при коде 0.
Итог: Linux и Windows дали идентичный результат
| Показатель | Значение (Linux) |
|---|---|
| Время переноса | 3 часа 30 минут (209,9 мин, код возврата 0) |
| Размер на Linux | 300,19 ГБ |
| Таблиц | 4470 |
| Индексов | 10 623, из них уникальных 9111 |
| Невалидных индексов | 0 |
Днём раньше я снимал те же показатели после переноса на PostgreSQL под Windows. Совпало всё до единицы: 300 ГБ и 4470 таблиц, индексов 10 623, уникальных среди них 9111. На двух разных ОС структура получилась одна и та же, и лучшего довода, что для самого переезда выбор операционной системы вторичен, я не знаю.
А вот время сравнивать нельзя, говорю сразу, чтобы никого не запутать: на Windows прогон шёл 2 часа 28 минут, на Linux 3 часа 30. Условия у них разные. После двух падений из-за памяти я свёл параллельность к одному потоку, порцию сократил до 1000 строк против прежних 10 000, а у целевой машины осталось всего 2,5 ГБ памяти из прежних шести. Тормозил тут не Linux, тормозила моя осторожность.
Таблицы растут неравномерно, и это важнее общего процента
В целом база прибавила 8,9 процента. Но по отдельным таблицам от средней цифры ничего не осталось:
| Таблица | MS SQL | PostgreSQL | Отношение |
|---|---|---|---|
_AccRgED14402X1 | 11,17 ГБ | 39,92 ГБ | ×3,6 |
_AccRg14374X1 | 13,84 ГБ | 30,91 ГБ | ×2,2 |
_Document229X1 | 9,20 ГБ | 14,74 ГБ | ×1,6 |
_InfoRg7855X1 | 47,38 ГБ | 44,47 ГБ | ×0,94 |
Бухгалтерские регистры выросли вдвое-втрое, а крупнейшая таблица базы стала даже чуть меньше. Планировать место по общим плюс девяти процентам опасно, это та самая средняя температура по больнице. Разнесёт как раз бухгалтерию, если она у вас нагруженная и с движениями по счетам.
Откуда такая разница, я до конца не разбирался и выдумывать не стану. Похоже на правду вот что: заголовок строки у PostgreSQL занимает 24 байта, у MS SQL 7. В узких регистровых таблицах строк десятки миллионов, полезных данных в каждой мало, и эта разница набегает многократно. К тому же индексы там больше самих данных. Гипотезу можно проверить, но это отдельная работа.
Только на практике это ничего не меняет: ориентируйтесь на свои крупнейшие таблицы, общий размер базы тут плохой советчик. Составить их список может любой скрипт подсчёта размеров, один такой я приводил в статье про подготовку к миграции. И почти всегда в этом списке наверху оказывается не то, что ожидаешь: разбор типичной картины - “Что растёт в базе 1С помимо документов”.
Предполётный чек-лист для переезда на Linux
- Проверьте базу на невидимые символы на копии. Это главное, и ОС тут роли не играет.
- Место на файловой системе, где лежит каталог данных, а не размер диска. На LVM это разные числа по умолчанию.
- Локаль кластера PostgreSQL (
datcollateвpg_database) и отдельно системная локаль сервера. Два разных места. work_mem×max_connectionsпротив физической памяти. Значения автотюнера проверяйте руками.- Шрифты и
fontconfigна сервере приложений, иначе печатные формы не отрисуются. - Лимиты ОС:
ulimit -n65536,vm.swappiness1, huge pagesnever. - Внешние компоненты: у каждой проверьте наличие сборки под Linux в манифесте. Компонента без Linux-сборки просто не загрузится.
- Тома хранения файлов и константы с Windows-путями: после переезда эти пути мертвы.
- Регламентные задания просмотрите глазами на предмет COM-объектов и
ЗапуститьПриложение- на Linux они не работают. - Процессы длиннее сессии запускайте службой или планировщиком, не из RDP.
- Дистрибутив против версии платформы. Список поддерживаемых ОС сокращается от версии к версии: с 8.3.26 из него вышли CentOS 7, Oracle Linux 7, RHEL 7 и Астра Linux CE 2.12. Если переезд совмещается с обновлением платформы, смотрите ещё и “Переход на 1С 8.5: что проверить в конфигурации и на серверах”.
Что в итоге
Общее у восьми ловушек одно: почти каждая вместо честного отказа промолчала. Флаг виртуализации отвечал совсем на другое. Загрузка оборвалась без единой ошибки. Клавиатура отчиталась об успехе, ничего не напечатав. Кластер молча взял ту локаль, какая нашлась в системе. Автотюнер посчитал по памяти, а о соединениях не сказал ни слова.
Поэтому из переезда я унёс один приём, который работает, и он скучный: каждый шаг перепроверять другим, независимым способом. Команда, отработавшая без ошибки, ещё ничего не доказывает. Смотреть надо, какого размера диск на самом деле, русская ли локаль и введён ли текст. На стенде такая проверка отнимает минуты. А в окне переключения, где база лежит и люди ждут, за это платят уже совсем другими деньгами.
Готовые обработки по теме:
- Проверка базы перед миграцией на PostgreSQL - в ней есть сценарий “СУБД и ОС”, который проверяет как раз то, о чём здесь шла речь: внешние компоненты без Linux-сборки, шрифты для макетов, тома хранения, где не заполнен путь для Linux, и Windows-пути в константах.
- Карта объёмов базы 1С - какие таблицы у вас в топе, чтобы планировать место не по среднему проценту.
- Чек-ап СУБД под 1С - уже после переезда покажет, всё ли в СУБД выставлено так, как нужно платформе.
Если планируете переход на Linux и PostgreSQL, это часть нашей работы по обслуживанию баз 1С: подготовка базы, разворачивание и настройка сервера, репетиция переключения на копии. Отдельно про настройку самой СУБД после переезда - в статье PostgreSQL для 1С: настройка.