Перейти к содержимому
Tez Base

Перенос 1С на Linux и PostgreSQL: восемь ловушек, каждая из которых молчит

· Опубликовано: · Обновлено:

PostgreSQL SQL Server и 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_buffers1,4 ГБ
work_mem512 МБ
max_connections250
effective_cache_size4,3 ГБ
maintenance_work_mem370 МБ
max_locks_per_transaction256

Память 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 (открытые файлы)102465536
vm.swappiness601
huge pagesmadvisenever

Поставить сервер и переехать всё это не помешает. Проявится оно под нагрузкой, когда пользователи уже в работе, и будет выглядеть как “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)
Размер на Linux300,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 SQLPostgreSQLОтношение
_AccRgED14402X111,17 ГБ39,92 ГБ×3,6
_AccRg14374X113,84 ГБ30,91 ГБ×2,2
_Document229X19,20 ГБ14,74 ГБ×1,6
_InfoRg7855X147,38 ГБ44,47 ГБ×0,94

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

Откуда такая разница, я до конца не разбирался и выдумывать не стану. Похоже на правду вот что: заголовок строки у PostgreSQL занимает 24 байта, у MS SQL 7. В узких регистровых таблицах строк десятки миллионов, полезных данных в каждой мало, и эта разница набегает многократно. К тому же индексы там больше самих данных. Гипотезу можно проверить, но это отдельная работа.

Только на практике это ничего не меняет: ориентируйтесь на свои крупнейшие таблицы, общий размер базы тут плохой советчик. Составить их список может любой скрипт подсчёта размеров, один такой я приводил в статье про подготовку к миграции. И почти всегда в этом списке наверху оказывается не то, что ожидаешь: разбор типичной картины - “Что растёт в базе 1С помимо документов”.

Предполётный чек-лист для переезда на Linux

  1. Проверьте базу на невидимые символы на копии. Это главное, и ОС тут роли не играет.
  2. Место на файловой системе, где лежит каталог данных, а не размер диска. На LVM это разные числа по умолчанию.
  3. Локаль кластера PostgreSQL (datcollate в pg_database) и отдельно системная локаль сервера. Два разных места.
  4. work_mem × max_connections против физической памяти. Значения автотюнера проверяйте руками.
  5. Шрифты и fontconfig на сервере приложений, иначе печатные формы не отрисуются.
  6. Лимиты ОС: ulimit -n 65536, vm.swappiness 1, huge pages never.
  7. Внешние компоненты: у каждой проверьте наличие сборки под Linux в манифесте. Компонента без Linux-сборки просто не загрузится.
  8. Тома хранения файлов и константы с Windows-путями: после переезда эти пути мертвы.
  9. Регламентные задания просмотрите глазами на предмет COM-объектов и ЗапуститьПриложение - на Linux они не работают.
  10. Процессы длиннее сессии запускайте службой или планировщиком, не из RDP.
  11. Дистрибутив против версии платформы. Список поддерживаемых ОС сокращается от версии к версии: с 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С: настройка.

Могу починить это за вас

Настроим регламентное обслуживание так, чтобы база работала стабильно, бэкапы гарантированно восстанавливались, а диски не кончались внезапно.

Стоимость
450 000 - 600 000 ₸ТЗ и разовая наладка, без налогов; сопровождение - от 150 000 ₸/мес отдельно

Рано писать? Измерьте сами

Переезд на PostgreSQL падает беззвучно

Открыть обработку

INFOSTART TECH EVENT 2026: конференция для 1с-специалистов. если собираетесь, регистрируйтесь по нашей ссылке. Регистрация →

Читайте также

Уникальный индекс в PostgreSQL пропускает дубли с NULL: найти, посчитать, починить

Методика для служебных таблиц рядом с базой 1С: один запрос к каталогу PostgreSQL находит все уникальные ключи с необязательными колонками, второй считает, какая доля строк идёт мимо защиты. Дальше ремонт по версиям: NULLS NOT DISTINCT с 15-й, частичные индексы до неё, и проверка повтором в транзакции с откатом. Все запросы прогнаны на PostgreSQL 17.

Персональные данные в базе 1С: карта мест перед тем, как копия уедет наружу

Копию рабочей базы отдают подрядчику, тестировщику или на свой тестовый сервер, и первым делом спрашивают, что в ней надо спрятать. Список обычно заканчивается на справочнике физлиц. На одной рабочей базе карта нашла 164 объекта с персональными данными. Разбираем, где они лежат, почему поиск по имени реквизита промахивается в обе стороны и как проверить копию до отправки.

Регламентное задание 1С не выполняется: порядок проверки и скрипт для консоли

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