Мониторинг 1С в PRTG: PowerShell поверх rac.exe
Задача выглядела скромно: показать в PRTG количество активных сеансов кластера 1С. Всё остальное — процессор, память, диски, службы, метрики MSSQL и PostgreSQL — закрывается штатными сенсорами часа за три.
А сеансов 1С в PRTG нет. И не будет: штатного сенсора не существует, единственный путь — скриптом обернуть rac.exe и отдать результат в формате, который PRTG понимает.
В итоге на один этот сенсор ушло больше времени, чем на весь остальной мониторинг сервера. Причём не из-за логики — она тривиальная, — а из-за неправильного порта в регистрации службы и трёх разных проблем с кодировками, дающих один и тот же симптом.
Расклад
Один Windows-сервер, на нём одновременно живут сервер приложений 1С:Предприятие 8.3.27, MSSQL и PostgreSQL 17. Ядро PRTG стоит на отдельной машине.
Первое решение, которое стоит принять до всего остального: ставить Remote Probe на сам сервер 1С. Без него WMI, счётчики производительности, SQL-сенсоры и PowerShell будут ходить по сети с ядра, а это лишние права в домене, лишние правила фаервола и регулярные «Unknown» на WMI-сенсорах, когда на той стороне исчерпается квота. Зонд решает всё разом: проверки выполняются локально, наружу уходит только результат.
Служба зонда называется PRTGProbeService, а кастомные сенсоры лежат здесь:
C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\ — скрипты PowerShell
C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\sql\mssql\ — SQL-запросы к MSSQL
C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\sql\postgresql\
Скрипт, положенный в EXEXML, появляется в выпадающем списке при создании сенсора EXE/Script Advanced — но только после перезапуска зонда или обновления списка. Если файла в списке нет, а он на месте — сначала перезапусти службу зонда, потом ищи ошибку.
RAS, RAC и порт, которого не существует
Чтобы получить данные кластера снаружи, нужен RAS — сервер администрирования. Клиент rac.exe ходит в RAS, RAS ходит в агент кластера.
Порты, которые надо держать в голове:
| Порт | Кто |
|---|---|
| 1540 | ragent — агент сервера |
| 1541 | менеджер кластера |
| 1545 | ras — сервер администрирования |
Служба RAS у меня уже была зарегистрирована и находилась в состоянии «Выполняется». Порт 1545 слушался, телнет проходил. При этом rac.exe упорно не видел кластер.
Разгадка нашлась в bat-файле регистрации, написанном когда-то до меня: там стояло CtrlPort=1570. Откуда взялась эта цифра — вопрос археологический. Факт в том, что RAS должен смотреть на агент, то есть на localhost:1540, а слушать при этом 1545.
Правильная регистрация службы:
"C:\Program Files\1cv8\8.3.27.1859\bin\ras.exe" cluster --service --port=1545 localhost:1540
Проверка, что всё поднялось — она же основной health-check:
"C:\Program Files\1cv8\8.3.27.1859\bin\rac.exe" cluster list localhost:1545
В ответ приходит блок, начинающийся с cluster : 1e71e7b3-.... Этот UUID понадобится всем последующим командам, так что его сразу стоит вынести в переменную скрипта.
Отдельный вывод, который я бы хотел знать заранее: служба «Выполняется» не означает, что служба работает. Порт слушается, TCP-коннект устанавливается, сенсор по портам зелёный — а данных нет. Единственная честная проверка — выполнить ту операцию, ради которой сервис существует. Для RAS это rac cluster list, и её имеет смысл повесить отдельным сенсором с порогом на время ответа.
Кодировки. Все три раза
Здесь была основная потеря времени, поэтому разберу подробно.
Раз. rac.exe — консольная утилита и пишет вывод в CP866. Читаешь как есть — получаешь мусор вместо имён информационных баз и пользователей.
Два. PRTG ожидает от сенсора EXE/Script Advanced документ XML в UTF-8. То есть внутри одного скрипта нужны две разные кодировки: одна на чтение стороннего процесса, другая на собственный вывод.
Рабочая комбинация — переключать [Console]::OutputEncoding дважды:
# читаем rac.exe в CP866
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(866)
$racOutput = & $RacPath session list --cluster=$ClusterId $RasHost 2>&1
# отдаём результат в PRTG в UTF-8 без BOM
$utf8 = New-Object System.Text.UTF8Encoding($false)
$OutputEncoding = $utf8
[Console]::OutputEncoding = $utf8
Три. Сам файл .ps1 должен быть сохранён в UTF-8 с BOM. Без BOM PowerShell читает кириллицу в именах каналов как ANSI, и в PRTG приезжают кракозябры — при том что в консоли тот же скрипт отрабатывает правильно.
И бонусом — грабля, на которой я потерял отдельный час. Скрипт, скачанный браузером, получает альтернативный поток Zone.Identifier, помечающий файл как пришедший из интернета. PowerShell выполняет такой файл с ограничениями, и результат — снова битые имена каналов. Лечится одной командой:
Unblock-File "C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\1c_sessions.ps1"
Ключевое: все четыре причины дают ровно один симптом. Пока не проверены все, лечение выглядит как случайность — что-то поменял, стало правильно, почему именно, непонятно. Поэтому при кракозябрах проверять надо списком: BOM у файла, Unblock-File, кодировка чтения, кодировка вывода.
Разбор вывода rac
rac.exe session list отдаёт плоский текст блоками «ключ : значение», разделёнными пустой строкой. Парсер простой, но есть нюансы конкретной версии платформы, которые надо проверять на своём выводе, а не брать из примеров в интернете.
На 8.3.27.1859:
running : yes— именноyes, а не1, как написано в половине найденных примеров;available-perfomance— в платформе это слово с опечаткой, без первойr. Ищешьperformance— не находишь ничего и делаешь неверный вывод, что поля нет;- строки сеансов и процессов надёжнее матчить регуляркой по UUID, чем по позиции в блоке.
Ещё одно решение, сильно влияющее на осмысленность цифр, — отделить пользовательские сеансы от служебных по полю app-id:
| Пользовательские | Служебные |
|---|---|
1CV8C, 1CV8, WebClient | BackgroundJob, SrvrConsole, Designer |
Иначе фоновые задания ночного регламента дадут всплеск, который на графике выглядит как наплыв пользователей в три часа ночи. Я держу оба числа отдельными каналами: «Сеансы всего» полезен для сверки с консолью кластера, «Сеансы пользователей» — для порогов.
Скрипт
# 1c_sessions.ps1 — активные сеансы кластера 1С для PRTG (EXE/Script Advanced)
# Файл сохранять в UTF-8 с BOM. После копирования: Unblock-File
$ErrorActionPreference = 'Stop'
$RacPath = 'C:\Program Files\1cv8\8.3.27.1859\bin\rac.exe'
$RasHost = 'localhost:1545'
$ClusterId = '1e71e7b3-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
$UserApps = @('1CV8C', '1CV8', 'WebClient')
function Write-PrtgError([string]$Message) {
$utf8 = New-Object System.Text.UTF8Encoding($false)
$OutputEncoding = $utf8
[Console]::OutputEncoding = $utf8
Write-Output "<prtg><error>1</error><text>$Message</text></prtg>"
exit 0
}
try {
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(866)
$raw = & $RacPath session list --cluster=$ClusterId $RasHost 2>&1
if ($LASTEXITCODE -ne 0) { Write-PrtgError "rac.exe exit=$LASTEXITCODE" }
} catch {
Write-PrtgError ("rac.exe: " + $_.Exception.Message)
}
# блоки, разделённые пустой строкой → список хэш-таблиц
$sessions = @()
$cur = @{}
foreach ($line in $raw) {
if ($line -match '^\s*$') {
if ($cur.Count) { $sessions += ,$cur; $cur = @{} }
continue
}
if ($line -match '^\s*([\w\-\.]+)\s*:\s*(.*)$') {
$cur[$matches[1]] = $matches[2].Trim()
}
}
if ($cur.Count) { $sessions += ,$cur }
$total = $sessions.Count
$userCnt = ($sessions | Where-Object { $UserApps -contains $_['app-id'] }).Count
$byDbms = ($sessions | Where-Object { $_['blocked-by-dbms'] -and $_['blocked-by-dbms'] -ne '0' }).Count
$byLs = ($sessions | Where-Object { $_['blocked-by-ls'] -and $_['blocked-by-ls'] -ne '0' }).Count
$utf8 = New-Object System.Text.UTF8Encoding($false)
$OutputEncoding = $utf8
[Console]::OutputEncoding = $utf8
@"
<prtg>
<result>
<channel>Сеансы всего</channel><value>$total</value>
</result>
<result>
<channel>Сеансы пользователей</channel><value>$userCnt</value>
</result>
<result>
<channel>Ждут СУБД</channel><value>$byDbms</value>
<LimitMode>1</LimitMode><LimitMaxWarning>1</LimitMaxWarning>
</result>
<result>
<channel>Ждут блокировку 1С</channel><value>$byLs</value>
<LimitMode>1</LimitMode><LimitMaxWarning>1</LimitMaxWarning>
</result>
</prtg>
"@
Две детали, без которых сенсор будет капризничать.
Скрипт не должен печатать ничего до XML. Любое предупреждение PowerShell, вылезшее раньше, превращает ответ в «Response not well-formed». Отсюда $ErrorActionPreference = 'Stop', перехват в try, и перенаправление 2>&1 в переменную, а не в вывод.
Ошибки надо отдавать в формате PRTG, а не падать. Функция Write-PrtgError возвращает валидный XML с признаком ошибки и завершается с кодом 0. Так в интерфейсе будет красный сенсор с внятным текстом, а не «сенсор вернул мусор».
Настройки сенсора
При создании EXE/Script Advanced стоит поправить три вещи:
- Интервал. Дефолтные 60 секунд для
racизбыточны: утилита не мгновенная, а сеансы столько не живут. Ставлю 300 секунд. - Mutex. Если скриптовых сенсоров к одному кластеру несколько, задать им общий Mutex Name — тогда они не будут дёргать
racодновременно. - Security Context. Скрипт должен выполняться от учётки, имеющей право обращаться к кластеру. Если кластер закрыт администратором, логин и пароль передаются параметрами
--cluster-userи--cluster-pwd; хранить их в теле скрипта — плохая идея, лучше передавать параметром сенсора.
Тем же способом делается второй полезный сенсор — разбивка памяти сервера между rphost, rmngr и СУБД. Логика та же, отличается только источник данных: счётчики производительности вместо rac.
Пороги: на что их ставить, а на что нет
Соблазн — повесить алерт на количество блокировок объектов. Не надо. На этой нагрузке 150–250 активных блокировок — нормальный фон, и порог будет либо орать круглосуточно, либо стоять так высоко, что бесполезен.
Реальное страдание пользователей видно в других каналах: blocked-by-dbms (сеанс ждёт СУБД) и blocked-by-ls (ждёт управляемую блокировку). Вот там ненулевое значение — уже повод посмотреть, что происходит.
Общее правило, которое стараюсь соблюдать: сначала неделя наблюдения за реальными значениями, потом пороги. Выставленные в день настройки «по ощущениям», через месяц они оказываются либо шумом, либо декорацией.
И отдельно — расписание. Ночной регламент 1С (реиндексация, обновление статистики, бэкапы) закономерно даёт всплески по дискам и блокировкам. Проще завести расписание «01:00–05:00» и повесить его паузой на чувствительные сенсоры, оставив работать проверки доступности служб.
Типовые проблемы
| Симптом | Причина | Решение |
|---|---|---|
rac возвращает пустоту | не запущена служба RAS | Start-Service "1C:Enterprise 8.3 Remote Server" |
| RAS «Выполняется», но кластера не видит | в регистрации указан не тот порт агента | перерегистрировать на localhost:1540 |
| Кракозябры в именах каналов | нет BOM / нет Unblock-File / неверный OutputEncoding | проверить все четыре пункта списком |
| «Response not well-formed» | скрипт печатает что-то до XML | $ErrorActionPreference + 2>&1 в переменную |
| Скрипта нет в списке при создании сенсора | зонд не перечитал каталог | перезапустить PRTGProbeService |
| MSSQL-сенсор зелёный, каналы по нулям | нет прав на просмотр состояния | GRANT VIEW SERVER STATE TO [учётка] |
| Deadlocks растут и не падают | канал в режиме Absolute | переключить Value Mode на Difference |
Avg. Disk sec/* всегда 0.00 | значения в секундах, слишком малы | множитель ×1000, единица ms |
Счётчиков PhysicalDisk нет | отключены дисковые счётчики | diskperf -Y и перезагрузка |
| Именованный инстанс MSSQL не находится | другой префикс счётчиков | \MSSQL$ИМЯ:Buffer Manager\... |
Мораль
Первое. Зелёная служба и открытый порт не гарантируют ничего. Единственная честная проверка — выполнить операцию, ради которой сервис существует. У RAS это rac cluster list, и она же должна быть отдельным сенсором, иначе о неработающем администрировании кластера вы узнаете в тот день, когда оно срочно понадобится.
Второе. Кодировки в Windows-мониторинге — это не одна проблема, а стек из четырёх независимых, дающих одинаковый симптом. Пока не проверены все, лечение выглядит как везение.
Третье. Парсер чужого текстового вывода надо писать по выводу своей версии платформы, а не по примеру из интернета. Опечатка available-perfomance, живущая в 1С годами, — прекрасное тому подтверждение.
#1c #prtg #powershell #мониторинг #кодировки