diff --git a/1c/README.md b/1c/README.md index b89661b..8873102 100644 --- a/1c/README.md +++ b/1c/README.md @@ -2,6 +2,10 @@ Windows-скрипты для администрирования 1С:Предприятие (клиент и рабочие места). +| Раздел | Назначение | +|---|---| +| [`monitoring-ras/`](monitoring-ras/) | Мониторинг кластера 1С 8.3 в Zabbix через RAS/RAC (сбор сессий, соединений, лицензий, процессов `rphost`, блокировок). | + | Файл | Назначение | |---|---| | `1CDefenderExclusion.ps1` | Добавляет исключения 1С в Windows Defender: каталоги платформы и файловых баз (из `ibases.v8i` всех профилей), расширения и процессы. Запуск от администратора. | diff --git a/1c/monitoring-ras/1c_cluster_ras.ps1 b/1c/monitoring-ras/1c_cluster_ras.ps1 new file mode 100644 index 0000000..312a092 --- /dev/null +++ b/1c/monitoring-ras/1c_cluster_ras.ps1 @@ -0,0 +1,373 @@ +<# +.SYNOPSIS + Сбор метрик кластера 1С:Предприятие 8.3 через RAS/RAC для Zabbix. + +.DESCRIPTION + Порт сборщика из статьи Infostart «Мониторинг кластера 1С 8.3 в Zabbix» + (https://infostart.ru/1c/articles/1632627/) с источника COM+ (V83.COMConnector) + на штатный сервер администрирования RAS (ras.exe) и клиент RAC (rac.exe). + Автор статьи в «Планах по развитию» сам указывал переход COM+ -> RAS/RAC. + + Отдаёт единый JSON той же структуры, что и оригинал: + { cluster{...}, workingservers[...], processes[...], bases{ list[...] } } + пригодный как Zabbix master item; из него JSONPath'ом достаются метрики и + строятся LLD (информационные базы, рабочие серверы, рабочие процессы). + + Один вызов делает фиксированное число обращений к RAS независимо от числа + метрик (cluster info/list, server/session/connection/process/license/ + infobase summary list) — дешевле, чем запуск rac на каждую метрику. + + Что осталось на стороне ОС/агента (в API администрирования кластера этого + нет): статус службы, наличие logcfg.xml, загрузка CPU по ядрам и на процесс. + Зато при переходе на RAS больше не нужен хак с реестром ProcessNameFormat и + сопоставление perfmon-счётчиков rphost_ — RAC отдаёт PID напрямую. + +.PARAMETER Mode + json | discovery.ib | discovery.process | discovery.server + +.PARAMETER RacPath + Путь к rac.exe. По умолчанию — самая свежая установка платформы. + +.PARAMETER RasServer + Адрес RAS в виде host:port (порт по умолчанию 1545). + +.PARAMETER ClusterUser / ClusterPwd + Учётка администратора кластера (если задана). + +.PARAMETER CorpLicensePattern + Regex short-presentation КОРП/базовых лицензий, исключаемых из счётчика ПРОФ + (как в статье: КОРП 500 и базовые). Проверьте формат на своей платформе. + +.NOTES + Версия для Windows (Windows PowerShell 5.1+). rac.exe ищется автоматически. + Для Linux — отдельный 1c_cluster_ras.sh (bash/awk, без PowerShell). + +.EXAMPLE + powershell -NoProfile -ExecutionPolicy Bypass -File 1c_cluster_ras.ps1 json + powershell -NoProfile -ExecutionPolicy Bypass -File 1c_cluster_ras.ps1 discovery.ib +#> +[CmdletBinding()] +param( + [Parameter(Position = 0)] + [ValidateSet('json', 'discovery.ib', 'discovery.process', 'discovery.server')] + [string]$Mode = 'json', + + [string]$RacPath, + [string]$RasServer = 'localhost:1545', + [string]$ClusterUser = '', + [string]$ClusterPwd = '', + [string]$CorpLicensePattern = 'ORG8B .{3} 500|ORGL8 .{3} 1' +) + +$ErrorActionPreference = 'Stop' +# На Linux/pwsh в неинтерактивном режиме смена кодировки может кинуть исключение. +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch { } + +# Windows PowerShell 5.1 не определяет $IsWindows — там платформа всегда Windows. +$script:OnWindows = ($null -eq $IsWindows) -or $IsWindows + +# --- Поиск rac (rac.exe / rac) ------------------------------------------- +# Кроссплатформенно: сначала PATH, затем стандартные каталоги установки платформы. +function Resolve-RacPath { + param([string]$Explicit) + + if ($Explicit) { + if (Test-Path -LiteralPath $Explicit) { return $Explicit } + throw "rac не найден по указанному пути: $Explicit" + } + + if ($script:OnWindows) { + $racName = 'rac.exe' + $roots = @("$env:ProgramFiles\1cv8", "${env:ProgramFiles(x86)}\1cv8") + } + else { + $racName = 'rac' + $roots = @('/opt/1cv8', '/opt/1C', '/usr/local/1cv8', '/usr/lib/1cv8') + } + + # 1) rac в PATH. + $onPath = Get-Command $racName -CommandType Application -ErrorAction SilentlyContinue | + Select-Object -First 1 + if ($onPath) { return $onPath.Source } + + # 2) Рекурсивный поиск в каталогах установки, самая свежая версия — первой. + $roots = $roots | Where-Object { $_ -and (Test-Path -LiteralPath $_) } + if (-not $roots) { throw 'Каталог платформы 1С не найден. Укажите путь параметром -RacPath.' } + + $rac = Get-ChildItem -Path $roots -Recurse -Filter $racName -ErrorAction SilentlyContinue | + Sort-Object -Property FullName -Descending | + Select-Object -First 1 + + if (-not $rac) { throw "$racName не найден. Укажите путь параметром -RacPath." } + return $rac.FullName +} + +# --- Запуск rac и разбор ответа ------------------------------------------ +# rac печатает записи блоками "ключ : значение", разделёнными пустой строкой. +function Invoke-Rac { + param([string[]]$Arguments) + + $auth = @() + if ($ClusterUser) { $auth += "--cluster-user=$ClusterUser" } + if ($ClusterPwd) { $auth += "--cluster-pwd=$ClusterPwd" } + + $all = @($RasServer) + $Arguments + $auth + $raw = & $script:Rac @all 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "rac завершился с кодом $LASTEXITCODE: $($raw -join ' ')" + } + return $raw +} + +function ConvertFrom-RacList { + param([string[]]$Lines) + + $records = New-Object System.Collections.Generic.List[hashtable] + $current = @{} + foreach ($line in $Lines) { + if ([string]::IsNullOrWhiteSpace($line)) { + if ($current.Count -gt 0) { $records.Add($current); $current = @{} } + continue + } + $idx = $line.IndexOf(':') + if ($idx -lt 0) { continue } + $key = $line.Substring(0, $idx).Trim() + $val = $line.Substring($idx + 1).Trim() + $current[$key] = $val + } + if ($current.Count -gt 0) { $records.Add($current) } + return $records +} + +function Get-Field { + param([hashtable]$Record, [string]$Name, $Default = '') + if ($Record -and $Record.ContainsKey($Name)) { return $Record[$Name] } + return $Default +} + +function Get-Int { param($Value) try { return [int]$Value } catch { return 0 } } +function Get-Long { param($Value) try { return [long]$Value } catch { return 0 } } +function Get-Dbl { param($Value) try { return [double](($Value -replace ',', '.')) } catch { return 0.0 } } + +function Measure-AppId { + param($Sessions, [string[]]$AppIds) + @($Sessions | Where-Object { $AppIds -contains (Get-Field $_ 'app-id') }).Count +} + +# Краткая карточка сессии для «топовых» сессий рабочего процесса. +function Get-SessionBrief { + param([hashtable]$Session, [hashtable]$IbNameById) + if (-not $Session) { return $null } + [ordered]@{ + infoBase = $IbNameById[(Get-Field $Session 'infobase')] + SessionID = Get-Field $Session 'session-id' + AppID = Get-Field $Session 'app-id' + userName = Get-Field $Session 'user-name' + MemoryCurrent = Get-Long (Get-Field $Session 'memory-current') + cpuTimeCurrent = Get-Long (Get-Field $Session 'cpu-time-current') + dbProcTook = Get-Long (Get-Field $Session 'db-proc-took') + } +} + +# --- Сбор данных ---------------------------------------------------------- +$script:Rac = Resolve-RacPath -Explicit $RacPath + +$clusters = ConvertFrom-RacList (Invoke-Rac @('cluster', 'list')) +if ($clusters.Count -eq 0) { throw 'Кластеры не найдены (проверьте RAS/учётку).' } +$clusterId = Get-Field $clusters[0] 'cluster' +$cl = @("--cluster=$clusterId") + +$clusterInfo = (ConvertFrom-RacList (Invoke-Rac (@('cluster', 'info') + $cl)))[0] +$servers = ConvertFrom-RacList (Invoke-Rac (@('server', 'list') + $cl)) +$infobases = ConvertFrom-RacList (Invoke-Rac (@('infobase', 'summary', 'list') + $cl)) +$sessions = ConvertFrom-RacList (Invoke-Rac (@('session', 'list') + $cl)) +$connections = ConvertFrom-RacList (Invoke-Rac (@('connection', 'list') + $cl)) +$processes = ConvertFrom-RacList (Invoke-Rac (@('process', 'list') + $cl)) +$licenses = ConvertFrom-RacList (Invoke-Rac (@('license', 'list') + $cl)) + +# --- Индексы -------------------------------------------------------------- +# Сессия по uuid — чтобы обогатить записи лицензий пользователем/базой. +$sessById = @{} +foreach ($s in $sessions) { $sessById[(Get-Field $s 'session')] = $s } + +# Имя ИБ по uuid. +$ibNameById = @{} +foreach ($ib in $infobases) { $ibNameById[(Get-Field $ib 'infobase')] = (Get-Field $ib 'name') } + +# Пользовательские (клиентские) лицензии — привязаны к сессии. +$clientLicenses = @($licenses | Where-Object { Get-Field $_ 'session' }) +# Множество uuid сессий, на которые выдана лицензия (для подсчёта «пользователей»). +$licensedSessions = @{} +foreach ($lic in $clientLicenses) { $licensedSessions[(Get-Field $lic 'session')] = $true } +# ПРОФ = клиентские лицензии, не подпадающие под КОРП/базовые. +$proLicenses = @($clientLicenses | Where-Object { + (Get-Field $_ 'short-presentation') -notmatch $CorpLicensePattern + }) + +switch ($Mode) { + + 'discovery.ib' { + $data = foreach ($ib in $infobases) { + [ordered]@{ + '{#IBUUID}' = Get-Field $ib 'infobase' + '{#IBNAME}' = Get-Field $ib 'name' + '{#IBDESCR}' = Get-Field $ib 'descr' + } + } + (@{ data = @($data) } | ConvertTo-Json -Depth 4 -Compress) + } + + 'discovery.process' { + $data = foreach ($p in $processes) { + [ordered]@{ + '{#PROCUUID}' = Get-Field $p 'process' + '{#PROCHOST}' = Get-Field $p 'host' + '{#PROCPORT}' = Get-Field $p 'port' + '{#PROCPID}' = Get-Field $p 'pid' + } + } + (@{ data = @($data) } | ConvertTo-Json -Depth 4 -Compress) + } + + 'discovery.server' { + $data = foreach ($srv in $servers) { + [ordered]@{ + '{#SRVUUID}' = Get-Field $srv 'server' + '{#SRVNAME}' = Get-Field $srv 'name' + '{#SRVHOST}' = Get-Field $srv 'agent-host' + } + } + (@{ data = @($data) } | ConvertTo-Json -Depth 4 -Compress) + } + + 'json' { + # --- cluster (параметры + счётчики сеансов) ------------------------ + $prolicenseUsers = foreach ($lic in $proLicenses) { + $s = $sessById[(Get-Field $lic 'session')] + [ordered]@{ + infoBase = if ($s) { $ibNameById[(Get-Field $s 'infobase')] } else { '' } + userName = if ($s) { Get-Field $s 'user-name' } else { '' } + host = if ($s) { Get-Field $s 'host' } else { '' } + license = Get-Field $lic 'short-presentation' + } + } + + $cluster = [ordered]@{ + uuid = $clusterId + name = Get-Field $clusterInfo 'name' + host = Get-Field $clusterInfo 'host' + MainPort = Get-Int (Get-Field $clusterInfo 'main-port') + MaxMemorySize = Get-Long (Get-Field $clusterInfo 'max-memory-size') + KillByMemoryWithDump = (Get-Field $clusterInfo 'kill-by-memory-with-dump') -eq 'yes' + LoadBalancingMode = Get-Field $clusterInfo 'load-balancing-mode' + SessionFaultToleranceLevel = Get-Int (Get-Field $clusterInfo 'session-fault-tolerance-level') + ExpirationTimeout = Get-Int (Get-Field $clusterInfo 'expiration-timeout') + LifeTimeLimit = Get-Int (Get-Field $clusterInfo 'lifetime-limit') + SecurityLevel = Get-Int (Get-Field $clusterInfo 'security-level') + KillProblemProcesses = (Get-Field $clusterInfo 'kill-problem-processes') -eq 'yes' + workingservers_count = @($servers).Count + sessions_count = @($sessions).Count + connections_count = @($connections).Count + users_count = @($clientLicenses).Count + clients_count = Measure-AppId $sessions @('1CV8', '1CV8C') + ws_count = Measure-AppId $sessions @('WSConnection') + http_count = Measure-AppId $sessions @('HTTPServiceConnection') + jobs_count = Measure-AppId $sessions @('BackgroundJob') + com_count = Measure-AppId $sessions @('COMConnection') + web_count = Measure-AppId $sessions @('WebServerExtension') + prolicense_count = @($proLicenses).Count + prolicense_users = @($prolicenseUsers) + } + + # --- workingservers ------------------------------------------------- + # CPU по ядрам (counter_processor_*) в RAC отсутствует — остаётся на ОС. + $workingservers = foreach ($srv in $servers) { + [ordered]@{ + uuid = Get-Field $srv 'server' + Name = Get-Field $srv 'name' + HostName = Get-Field $srv 'agent-host' + MainPort = Get-Int (Get-Field $srv 'agent-port') + connections_limit = Get-Int (Get-Field $srv 'connections-limit') + infobases_limit = Get-Int (Get-Field $srv 'infobases-limit') + memory_limit = Get-Long (Get-Field $srv 'memory-limit') + } + } + + # --- processes (rphost) -------------------------------------------- + # Сессии, сгруппированные по процессу — для users/sessions/top-сессий. + $sessByProc = @{} + foreach ($s in $sessions) { + $pid1c = Get-Field $s 'process' + if (-not $sessByProc.ContainsKey($pid1c)) { $sessByProc[$pid1c] = New-Object System.Collections.Generic.List[hashtable] } + $sessByProc[$pid1c].Add($s) + } + + $procMetrics = foreach ($p in $processes) { + $procUuid = Get-Field $p 'process' + $ps = if ($sessByProc.ContainsKey($procUuid)) { $sessByProc[$procUuid] } else { @() } + + $topMem = $ps | Sort-Object { Get-Long (Get-Field $_ 'memory-current') } -Descending | Select-Object -First 1 + $topCpu = $ps | Sort-Object { Get-Long (Get-Field $_ 'cpu-time-current') } -Descending | Select-Object -First 1 + $topDb = $ps | Sort-Object { Get-Long (Get-Field $_ 'db-proc-took') } -Descending | Select-Object -First 1 + + $procLicUsers = @($ps | Where-Object { $licensedSessions.ContainsKey((Get-Field $_ 'session')) }).Count + + [ordered]@{ + PID = Get-Field $p 'pid' + MainPort = Get-Int (Get-Field $p 'port') + HostName = Get-Field $p 'host' + StartedAt = Get-Field $p 'started-at' + MemorySize = Get-Long (Get-Field $p 'memory-size') + connections = Get-Int (Get-Field $p 'connections') + Running = (Get-Field $p 'running') -eq 'yes' + Use = Get-Field $p 'use' + IsEnable = (Get-Field $p 'is-enable') -eq 'yes' + AvailablePerf = Get-Int (Get-Field $p 'available-perfomance') + AvgCallTime = Get-Dbl (Get-Field $p 'avg-call-time') + sessions = @($ps).Count + users = $procLicUsers + session_mem = Get-SessionBrief $topMem $ibNameById + session_CPU = Get-SessionBrief $topCpu $ibNameById + session_proc_took = Get-SessionBrief $topDb $ibNameById + } + } + + # --- bases ---------------------------------------------------------- + $sessByIb = @{} + foreach ($s in $sessions) { + $ibId = Get-Field $s 'infobase' + if (-not $sessByIb.ContainsKey($ibId)) { $sessByIb[$ibId] = New-Object System.Collections.Generic.List[hashtable] } + $sessByIb[$ibId].Add($s) + } + + $baseList = foreach ($ib in $infobases) { + $ibId = Get-Field $ib 'infobase' + $bs = if ($sessByIb.ContainsKey($ibId)) { $sessByIb[$ibId] } else { @() } + + $ibUsers = @($bs | Where-Object { $licensedSessions.ContainsKey((Get-Field $_ 'session')) }).Count + + # Регламентные задания под пользователями вида reg000. + $regList = $bs | + Where-Object { (Get-Field $_ 'user-name') -match '^reg\d{3}' } | + Group-Object { Get-Field $_ 'user-name' } | + ForEach-Object { [ordered]@{ username = $_.Name; count = $_.Count } } + + [ordered]@{ + BaseName = Get-Field $ib 'name' + Description = Get-Field $ib 'descr' + sessions = @($bs).Count + users = $ibUsers + reglaments = @($regList) + } + } + + # --- Итог ----------------------------------------------------------- + $result = [ordered]@{ + cluster = $cluster + workingservers = @($workingservers) + processes = @($procMetrics) + bases = [ordered]@{ list = @($baseList) } + } + ($result | ConvertTo-Json -Depth 8 -Compress) + } +} diff --git a/1c/monitoring-ras/1c_cluster_ras.sh b/1c/monitoring-ras/1c_cluster_ras.sh new file mode 100755 index 0000000..84f22d2 --- /dev/null +++ b/1c/monitoring-ras/1c_cluster_ras.sh @@ -0,0 +1,269 @@ +#!/usr/bin/env bash +# +# Сбор метрик кластера 1С:Предприятие 8.3 через RAS/RAC для Zabbix (Linux). +# +# Порт сборщика из статьи Infostart «Мониторинг кластера 1С 8.3 в Zabbix» +# (https://infostart.ru/1c/articles/1632627/) на штатный клиент администрирования +# rac. Linux-версия сознательно без внешних зависимостей: нужен только сам rac +# (из состава платформы 1С) и awk + coreutils, которые есть в любом дистрибутиве. +# PowerShell НЕ требуется (для Windows — отдельный 1c_cluster_ras.ps1). +# +# Режимы: +# json - единый JSON { cluster, workingservers, processes, bases } +# discovery.ib - LLD информационных баз +# discovery.process - LLD рабочих процессов (rphost) +# discovery.server - LLD рабочих серверов +# +# Аргументы: [ras_server] [cluster_user] [cluster_pwd] +# ras_server по умолчанию localhost:1545 +# Переменные окружения: +# RAC_PATH - путь к rac (иначе ищется в PATH и каталогах установки) +# CORP_LICENSE_PATTERN - ERE КОРП/базовых лицензий, исключаемых из счётчика ПРОФ +# (по умолчанию без интервалов {3} — совместимо с mawk) +# +# Примеры: +# ./1c_cluster_ras.sh json 1c-srv:1545 +# ./1c_cluster_ras.sh discovery.ib + +set -eo pipefail + +MODE="${1:-json}" +RAS_SERVER="${2:-localhost:1545}" +CLUSTER_USER="${3:-}" +CLUSTER_PWD="${4:-}" +CORP_LICENSE_PATTERN="${CORP_LICENSE_PATTERN:-ORG8B ... 500|ORGL8 ... 1}" + +# --- Поиск rac ------------------------------------------------------------ +find_rac() { + if [ -n "${RAC_PATH:-}" ]; then + [ -x "$RAC_PATH" ] || { echo "rac не исполняем: $RAC_PATH" >&2; exit 1; } + printf '%s\n' "$RAC_PATH"; return + fi + if command -v rac >/dev/null 2>&1; then + command -v rac; return + fi + local d f + for d in /opt/1cv8 /opt/1C /usr/local/1cv8 /usr/lib/1cv8; do + [ -d "$d" ] || continue + f=$(find "$d" -type f -name rac 2>/dev/null | sort -r | head -n 1) + if [ -n "$f" ]; then printf '%s\n' "$f"; return; fi + done + echo "rac не найден. Задайте путь через RAC_PATH." >&2 + exit 1 +} + +RACBIN=$(find_rac) + +# Опции аутентификации кластера (только для команд внутри кластера). +AUTH=() +[ -n "$CLUSTER_USER" ] && AUTH+=("--cluster-user=$CLUSTER_USER") +[ -n "$CLUSTER_PWD" ] && AUTH+=("--cluster-pwd=$CLUSTER_PWD") + +# UUID первого кластера (без auth — cluster list его не принимает). +CLUSTER_ID=$("$RACBIN" cluster list "$RAS_SERVER" | awk '$1=="cluster"{print $3; exit}') +[ -n "$CLUSTER_ID" ] || { echo "Кластеры не найдены (проверьте RAS/учётку)." >&2; exit 1; } + +# Команда внутри кластера: rac <объект/команда> --cluster= [auth] . +racc() { "$RACBIN" "$@" "--cluster=$CLUSTER_ID" "${AUTH[@]}" "$RAS_SERVER"; } + +# --- Сбор нужных секций (по режиму — минимум обращений к RAS) -------------- +gather() { + case "$MODE" in + discovery.ib) + echo "@@SECTION infobases"; racc infobase summary list; echo ;; + discovery.process) + echo "@@SECTION processes"; racc process list; echo ;; + discovery.server) + echo "@@SECTION servers"; racc server list; echo ;; + json) + echo "@@SECTION cluster_info"; racc cluster info; echo + echo "@@SECTION servers"; racc server list; echo + echo "@@SECTION infobases"; racc infobase summary list; echo + echo "@@SECTION sessions"; racc session list; echo + echo "@@SECTION connections"; racc connection list; echo + echo "@@SECTION processes"; racc process list; echo + echo "@@SECTION licenses"; racc license list; echo ;; + *) + echo "Неизвестный режим: $MODE" >&2; exit 1 ;; + esac +} + +# --- Разбор rac и генерация JSON (awk, без внешних зависимостей) ----------- +read -r -d '' AWK_PROG <<'AWK' || true +function trim(s){ gsub(/^[ \t\r]+|[ \t\r]+$/,"",s); return s } +function jesc(s, r){ r=s; gsub(/\\/,"\\\\",r); gsub(/"/,"\\\"",r); gsub(/\r/,"",r); gsub(/\n/,"\\n",r); gsub(/\t/,"\\t",r); return r } +function num(s){ return (s ~ /^-?[0-9]+$/) ? s : "0" } +function numf(s, t){ t=s; gsub(/,/,".",t); return (t ~ /^-?[0-9]+(\.[0-9]+)?$/) ? t : "0" } +function booly(s){ return (s=="yes") ? "true" : "false" } + +function clearcur( k){ for(k in cur) delete cur[k]; cur_has=0 } + +function commit( k,i){ + if(!cur_has) return + if(section=="cluster_info"){ for(k in cur) ci[k]=cur[k] } + else if(section=="servers"){ i=++nsrv; + srv_uuid[i]=cur["server"]; srv_name[i]=cur["name"]; srv_host[i]=cur["agent-host"]; + srv_port[i]=cur["agent-port"]; srv_conn[i]=cur["connections-limit"]; + srv_iblim[i]=cur["infobases-limit"]; srv_mem[i]=cur["memory-limit"] } + else if(section=="infobases"){ i=++nib; + ib_uuid[i]=cur["infobase"]; ib_name[i]=cur["name"]; ib_descr[i]=cur["descr"]; + ibname[cur["infobase"]]=cur["name"] } + else if(section=="sessions"){ i=++nsess; + s_uuid[i]=cur["session"]; s_id[i]=cur["session-id"]; s_ib[i]=cur["infobase"]; + s_proc[i]=cur["process"]; s_app[i]=cur["app-id"]; s_user[i]=cur["user-name"]; + s_host[i]=cur["host"]; s_mem[i]=cur["memory-current"]; s_cpu[i]=cur["cpu-time-current"]; + s_db[i]=cur["db-proc-took"]; sidx[cur["session"]]=i } + else if(section=="connections"){ nconn++ } + else if(section=="processes"){ i=++nproc; + p_uuid[i]=cur["process"]; p_pid[i]=cur["pid"]; p_port[i]=cur["port"]; + p_host[i]=cur["host"]; p_started[i]=cur["started-at"]; p_mem[i]=cur["memory-size"]; + p_conn[i]=cur["connections"]; p_run[i]=cur["running"]; p_use[i]=cur["use"]; + p_isen[i]=cur["is-enable"]; p_perf[i]=cur["available-perfomance"]; p_avg[i]=cur["avg-call-time"] } + else if(section=="licenses"){ i=++nlic; l_sess[i]=cur["session"]; l_short[i]=cur["short-presentation"] } + clearcur() +} + +function sessbrief(idx, k){ + if(idx==""||idx==0) return "null" + k=idx + return "{\"infoBase\":\"" jesc(ibname[s_ib[k]]) "\",\"SessionID\":\"" jesc(s_id[k]) \ + "\",\"AppID\":\"" jesc(s_app[k]) "\",\"userName\":\"" jesc(s_user[k]) \ + "\",\"MemoryCurrent\":" num(s_mem[k]) ",\"cpuTimeCurrent\":" num(s_cpu[k]) \ + ",\"dbProcTook\":" num(s_db[k]) "}" +} + +/^@@SECTION /{ commit(); section=$2; clearcur(); next } +/^[ \t\r]*$/{ commit(); next } +{ + idx=index($0,":") + if(idx>0){ + key=trim(substr($0,1,idx-1)) + val=trim(substr($0,idx+1)) + if(key!=""){ cur[key]=val; cur_has=1 } + } +} + +END{ + commit() + + if(mode=="discovery.ib"){ + printf "{\"data\":[" + for(i=1;i<=nib;i++){ if(i>1)printf ","; + printf "{\"{#IBUUID}\":\"%s\",\"{#IBNAME}\":\"%s\",\"{#IBDESCR}\":\"%s\"}", + jesc(ib_uuid[i]), jesc(ib_name[i]), jesc(ib_descr[i]) } + printf "]}\n"; exit + } + if(mode=="discovery.process"){ + printf "{\"data\":[" + for(i=1;i<=nproc;i++){ if(i>1)printf ","; + printf "{\"{#PROCUUID}\":\"%s\",\"{#PROCHOST}\":\"%s\",\"{#PROCPORT}\":\"%s\",\"{#PROCPID}\":\"%s\"}", + jesc(p_uuid[i]), jesc(p_host[i]), jesc(p_port[i]), jesc(p_pid[i]) } + printf "]}\n"; exit + } + if(mode=="discovery.server"){ + printf "{\"data\":[" + for(i=1;i<=nsrv;i++){ if(i>1)printf ","; + printf "{\"{#SRVUUID}\":\"%s\",\"{#SRVNAME}\":\"%s\",\"{#SRVHOST}\":\"%s\"}", + jesc(srv_uuid[i]), jesc(srv_name[i]), jesc(srv_host[i]) } + printf "]}\n"; exit + } + + # --- json: агрегаты по сессиям ------------------------------------- + for(i=1;i<=nlic;i++){ + if(l_sess[i]!=""){ users_count++; licensed[l_sess[i]]=1 + if(l_short[i] !~ corp){ prof_count++; prof_idx[++nprof]=i } } + } + for(i=1;i<=nsess;i++){ + a=s_app[i] + if(a=="1CV8"||a=="1CV8C") clients++ + else if(a=="WSConnection") ws++ + else if(a=="HTTPServiceConnection") http++ + else if(a=="BackgroundJob") jobs++ + else if(a=="COMConnection") com++ + else if(a=="WebServerExtension") web++ + + pu=s_proc[i]; proc_sess[pu]++ + if(licensed[s_uuid[i]]) proc_users[pu]++ + m=num(s_mem[i])+0; if(!(pu in proc_topmem)||m>proc_topmemv[pu]){ proc_topmem[pu]=i; proc_topmemv[pu]=m } + c=num(s_cpu[i])+0; if(!(pu in proc_topcpu)||c>proc_topcpuv[pu]){ proc_topcpu[pu]=i; proc_topcpuv[pu]=c } + d=num(s_db[i])+0; if(!(pu in proc_topdb) ||d>proc_topdbv[pu]) { proc_topdb[pu]=i; proc_topdbv[pu]=d } + + iu=s_ib[i]; ib_sess[iu]++ + if(licensed[s_uuid[i]]) ib_users[iu]++ + if(s_user[i] ~ /^reg[0-9][0-9][0-9]/) reg[iu SUBSEP s_user[i]]++ + } + + # --- cluster -------------------------------------------------------- + printf "{\"cluster\":{" + printf "\"uuid\":\"%s\",", jesc(cluster_id) + printf "\"name\":\"%s\",", jesc(ci["name"]) + printf "\"host\":\"%s\",", jesc(ci["host"]) + printf "\"MainPort\":%s,", num(ci["main-port"]) + printf "\"MaxMemorySize\":%s,", num(ci["max-memory-size"]) + printf "\"KillByMemoryWithDump\":%s,", booly(ci["kill-by-memory-with-dump"]) + printf "\"LoadBalancingMode\":\"%s\",", jesc(ci["load-balancing-mode"]) + printf "\"SessionFaultToleranceLevel\":%s,", num(ci["session-fault-tolerance-level"]) + printf "\"ExpirationTimeout\":%s,", num(ci["expiration-timeout"]) + printf "\"LifeTimeLimit\":%s,", num(ci["lifetime-limit"]) + printf "\"SecurityLevel\":%s,", num(ci["security-level"]) + printf "\"KillProblemProcesses\":%s,", booly(ci["kill-problem-processes"]) + printf "\"workingservers_count\":%d,", nsrv + printf "\"sessions_count\":%d,", nsess + printf "\"connections_count\":%d,", nconn + printf "\"users_count\":%d,", users_count + printf "\"clients_count\":%d,", clients + printf "\"ws_count\":%d,", ws + printf "\"http_count\":%d,", http + printf "\"jobs_count\":%d,", jobs + printf "\"com_count\":%d,", com + printf "\"web_count\":%d,", web + printf "\"prolicense_count\":%d,", prof_count + printf "\"prolicense_users\":[" + pc=0 + for(j=1;j<=nprof;j++){ li=prof_idx[j]; su=l_sess[li]; if(pc++)printf "," + if(su in sidx){ k=sidx[su]; ibn=ibname[s_ib[k]]; un=s_user[k]; ho=s_host[k] } else { ibn=""; un=""; ho="" } + printf "{\"infoBase\":\"%s\",\"userName\":\"%s\",\"host\":\"%s\",\"license\":\"%s\"}", + jesc(ibn), jesc(un), jesc(ho), jesc(l_short[li]) } + printf "]}" + + # --- workingservers ------------------------------------------------- + printf ",\"workingservers\":[" + for(i=1;i<=nsrv;i++){ if(i>1)printf "," + printf "{\"uuid\":\"%s\",\"Name\":\"%s\",\"HostName\":\"%s\",\"MainPort\":%s,\"connections_limit\":%s,\"infobases_limit\":%s,\"memory_limit\":%s}", + jesc(srv_uuid[i]), jesc(srv_name[i]), jesc(srv_host[i]), num(srv_port[i]), + num(srv_conn[i]), num(srv_iblim[i]), num(srv_mem[i]) } + printf "]" + + # --- processes ------------------------------------------------------ + printf ",\"processes\":[" + for(i=1;i<=nproc;i++){ if(i>1)printf ","; pu=p_uuid[i] + ns=(pu in proc_sess)?proc_sess[pu]:0 + nu=(pu in proc_users)?proc_users[pu]:0 + printf "{\"PID\":\"%s\",\"MainPort\":%s,\"HostName\":\"%s\",\"StartedAt\":\"%s\",", + jesc(p_pid[i]), num(p_port[i]), jesc(p_host[i]), jesc(p_started[i]) + printf "\"MemorySize\":%s,\"connections\":%s,\"Running\":%s,\"Use\":\"%s\",\"IsEnable\":%s,", + num(p_mem[i]), num(p_conn[i]), booly(p_run[i]), jesc(p_use[i]), booly(p_isen[i]) + printf "\"AvailablePerf\":%s,\"AvgCallTime\":%s,\"sessions\":%d,\"users\":%d,", + num(p_perf[i]), numf(p_avg[i]), ns, nu + printf "\"session_mem\":%s,\"session_CPU\":%s,\"session_proc_took\":%s}", + sessbrief((pu in proc_topmem)?proc_topmem[pu]:0), + sessbrief((pu in proc_topcpu)?proc_topcpu[pu]:0), + sessbrief((pu in proc_topdb)?proc_topdb[pu]:0) } + printf "]" + + # --- bases ---------------------------------------------------------- + printf ",\"bases\":{\"list\":[" + for(i=1;i<=nib;i++){ if(i>1)printf ","; iu=ib_uuid[i] + bs=(iu in ib_sess)?ib_sess[iu]:0 + bu=(iu in ib_users)?ib_users[iu]:0 + printf "{\"BaseName\":\"%s\",\"Description\":\"%s\",\"sessions\":%d,\"users\":%d,\"reglaments\":[", + jesc(ib_name[i]), jesc(ib_descr[i]), bs, bu + rc=0 + for(key in reg){ split(key,parts,SUBSEP); if(parts[1]==iu){ if(rc++)printf "," + printf "{\"username\":\"%s\",\"count\":%d}", jesc(parts[2]), reg[key] } } + printf "]}" } + printf "]}}\n" +} +AWK + +gather | awk -v mode="$MODE" -v corp="$CORP_LICENSE_PATTERN" -v cluster_id="$CLUSTER_ID" "$AWK_PROG" diff --git a/1c/monitoring-ras/README.md b/1c/monitoring-ras/README.md new file mode 100644 index 0000000..ebc1108 --- /dev/null +++ b/1c/monitoring-ras/README.md @@ -0,0 +1,196 @@ +# Мониторинг кластера 1С 8.3 в Zabbix через RAS/RAC + +Порт сборщика из статьи Infostart +[«Мониторинг кластера 1С 8.3 в Zabbix»](https://infostart.ru/1c/articles/1632627/) +с источника **COM+ (`V83.COMConnector`)** на штатный сервер администрирования +**RAS** (`ras.exe`) и консольный клиент **RAC** (`rac.exe`). + +> Это не альтернатива статье, а её же дорожная карта: в разделе «Планы по +> развитию» автор прямо пишет, что планирует перевод мониторинга **с COM+ на +> RAS/RAC**. Здесь этот переход и реализован. + +## Что делает статья + +Скрипт на PowerShell подключается к агенту кластера через `V83.COMConnector` и +собирает данные в **один JSON-файл** `1c_server_data.json`, который затем парсит +сервер Zabbix (мастер-данные + 3 правила LLD + триггеры + дашборды/Grafana). +Структура JSON — четыре блока: + +1. **cluster** — параметры сервера и кластера + сводка по сеансам; +2. **workingservers** — рабочие серверы кластера и их загрузка; +3. **processes** — рабочие процессы `rphost` и их параметры; +4. **bases.list** — информационные базы кластера и их параметры. + +Коллектор в этой папке отдаёт **тот же JSON той же структуры**, но данные берёт +из RAC. + +## Почему RAS/RAC лучше COM+ + +| | COM+ (`V83.COMConnector`) | RAS/RAC | +|---|---|---| +| Разрядность | COM-объект должен совпадать по битности с процессом PowerShell | не важно, сетевой протокол | +| Где запускать | обычно на самом сервере 1С | с любого хоста с `rac.exe` и доступом к 1545 | +| Регистрация компоненты | нужна регистрация `comcntr.dll` | не нужна | +| PID процессов | статья вынужденно чинит реестр `ProcessNameFormat` и матчит perfmon-счётчики `rphost_` | RAC отдаёт `pid` напрямую — хак больше не нужен | +| Параметры ИБ | статья парсит файл реестра кластера `1CV8Clst.lst` регуляркой | `rac infobase info` (при наличии прав на ИБ) | + +## Соответствие: свойство COM-объекта → поле/команда RAC + +### Блок 1 — cluster + +`rac cluster info --cluster=` (плюс сводка по `session list` / `license list`): + +| Статья (COM) | RAC | +|---|---| +| `MaxMemorySize` | `max-memory-size` | +| `KillByMemoryWithDump` | `kill-by-memory-with-dump` | +| `LoadBalancingMode` | `load-balancing-mode` | +| `SessionFaultToleranceLevel` | `session-fault-tolerance-level` | +| `ExpirationTimeout` | `expiration-timeout` | +| `LifeTimeLimit` | `lifetime-limit` | +| `SecurityLevel` | `security-level` | +| `MainPort` | `main-port` | +| `KillProblemProcesses` | `kill-problem-processes` | +| `sessions_count` | число записей `session list` | +| `users_count` | число выданных клиентских лицензий (`license list` с полем `session`) | +| `clients_count` / `ws_count` / `http_count` / `jobs_count` / `com_count` / `web_count` | группировка `session list` по `app-id` (`1CV8`/`1CV8C`, `WSConnection`, `HTTPServiceConnection`, `BackgroundJob`, `COMConnection`, `WebServerExtension`) | +| `prolicense_count` / `prolicense_users` | `license list`, фильтр по `short-presentation` (КОРП/базовые исключаются, см. `-CorpLicensePattern`) | + +### Блок 2 — workingservers + +`rac server list --cluster=`: `server`, `name`, `agent-host`, `agent-port`, +`connections-limit`, `infobases-limit`, `memory-limit`. +Загрузка CPU по ядрам (`counter_processor_total/12` в статье) — **в RAC +отсутствует**, остаётся на стороне ОС (см. ниже). + +### Блок 3 — processes + +`rac process list --cluster=` + группировка `session list` по `process`: + +| Статья | RAC | +|---|---| +| `PID` / `MainPort` / `HostName` / `StartedAt` | `pid` / `port` / `host` / `started-at` | +| `MemorySize` / `connections` / `Running` / `Use` / `IsEnable` | одноимённые поля | +| `available-perfomance`, `avg-call-time` | одноимённые поля | +| `users` / `sessions` | подсчёт по сгруппированным сессиям процесса | +| `session_mem` / `session_CPU` / `session_proc_took` | top-сессия процесса по `memory-current` / `cpu-time-current` / `db-proc-took` | +| `CPU_Usage` (perfmon `rphost_`) | **нет в RAC** — остаётся на ОС | + +### Блок 4 — bases + +`rac infobase summary list --cluster=` + группировка сессий по `infobase`: + +| Статья | RAC | +|---|---| +| `BaseName` / `Description` | `name` / `descr` | +| `sessions` / `users` | подсчёт по сессиям ИБ | +| `reglaments` | сессии с `user-name ~ ^reg\d{3}`, сгруппированные | +| `Blocked` / `StartBlocking` / `EndBlocking` / `DBServerName` / `DBBaseName` | `rac infobase info --cluster= --infobase= --infobase-user=.. --infobase-pwd=..` → `sessions-deny` / `denied-from` / `denied-to` / `db-server` / `db-name` (требует прав на ИБ; здесь не включено, т.к. нужен пароль каждой базы) | + +## Что принципиально остаётся на стороне ОС/агента + +Этого нет в API администрирования кластера — собирайте штатными средствами +Zabbix-агента (как в статье), RAS их не заменяет: + +| Метрика статьи | Ключ Zabbix | +|---|---| +| Статус службы агента 1С | `service_state["{$CLUSTER1C.SERVICE.NAME}"]` | +| Запущен технологический журнал (`logcfg.xml`) | `vfs.dir.count["...1cv8",".*logcfg.xml$",,file]` | +| Загрузка CPU по ядрам / общая | perfmon-счётчики `\238(N)\6` | +| CPU% на процесс `rphost` | perfmon `\230(rphost_)\6` (при переходе на RAS уже не обязателен) | +| Число ядер сервера | WMI `Win32_ComputerSystem` | + +## Файлы + +Две отдельные реализации коллектора под каждую ОС; **обе выдают идентичный JSON**, +поэтому Zabbix-шаблон и ключи (`onecras.*`) для них общие. + +| Файл | Назначение | +|---|---| +| `1c_cluster_ras.ps1` | Коллектор для **Windows** (Windows PowerShell 5.1+). | +| `1c_cluster_ras.sh` | Коллектор для **Linux** (bash + awk, **без PowerShell**). | +| `userparameter_1c_ras.conf` | UserParameter'ы Zabbix-агента для Windows (`powershell`). | +| `userparameter_1c_ras_linux.conf` | UserParameter'ы для Linux (вызывают `.sh`). | +| `template_1c_cluster_ras.yaml` | Импортируемый шаблон Zabbix 6.0: мастер-айтем, зависимые элементы, 3 LLD, триггеры. | + +Оба коллектора принимают одинаковые режимы (`json`, `discovery.ib`, +`discovery.process`, `discovery.server`) и сами находят `rac`/`rac.exe` (PATH и +типовые каталоги установки); путь переопределяется — на Windows параметром +`-RacPath`, на Linux переменной `RAC_PATH`. + +### Зависимости Linux-версии + +Сознательно минимальные — PowerShell на серверах 1С из коробки нет: + +* `rac` — из состава платформы 1С (`/opt/1cv8/.../rac`); +* `awk` (`mawk`/`gawk`), `bash`, `find`, `sort`, `head` — есть в любом дистрибутиве. + +Никаких `jq`, `python`, `pwsh` — разбор вывода `rac` и сборка JSON целиком на awk. + +## Установка + +### Windows + +1. Убедиться, что RAS запущен как сервис (порт 1545): + ``` + "C:\Program Files\1cv8\<версия>\bin\ras.exe" cluster --service --port=1545 localhost:1540 + ``` +2. Скопировать `1c_cluster_ras.ps1` в `C:\Zabbix\Scripts\`, а + `userparameter_1c_ras.conf` — в каталог конфигов агента. +3. Поднять `Timeout` агента до ~30 c. Проверить: + ``` + powershell -NoProfile -ExecutionPolicy Bypass -File C:\Zabbix\Scripts\1c_cluster_ras.ps1 json + ``` + +### Linux + +1. Убедиться, что RAS запущен (обычно systemd-юнит `srv1cv8-ras`, порт 1545). +2. Скопировать `1c_cluster_ras.sh` в `/etc/zabbix/scripts/` (`chmod +x`), а + `userparameter_1c_ras_linux.conf` — в `/etc/zabbix/zabbix_agentd.d/`. +3. Поднять `Timeout` агента до ~30 c. Проверить: + ``` + /etc/zabbix/scripts/1c_cluster_ras.sh json 1c-srv:1545 + ``` + +4. Импортировать `template_1c_cluster_ras.yaml` в Zabbix и привязать к хосту + (задать макросы `{$RAS.SERVER}`, при необходимости `{$RAS.CLUSTER.USER}` / + `{$RAS.CLUSTER.PWD}`). + +## Схема мониторинга в Zabbix (мастер-айтем) + +Готовый шаблон `template_1c_cluster_ras.yaml` уже реализует всё описанное ниже — +импортируйте его и привяжите к хосту. Раздел объясняет его устройство. + +> Пароль администратора кластера передаётся макросом `{$RAS.CLUSTER.PWD}` — на +> проде задайте его на хосте как **Secret text**, чтобы значение не светилось. + +Как и в статье — один опрос отдаёт весь JSON, метрики достаются предобработкой: + +1. **Мастер-айтем** `onecras.json` (Zabbix agent, интервал 1–2 мин, тип Text). +2. **Зависимые элементы** с предобработкой *JSONPath*: + * `$.cluster.sessions_count`, `$.cluster.users_count`, + `$.cluster.prolicense_count`, `$.cluster.clients_count` и т.д. +3. **LLD инфобаз** `onecras.discovery.ib`, прототип (Dependent): + `$.bases.list[?(@.BaseName=='{#IBNAME}')].sessions.first()`. +4. **LLD процессов** `onecras.discovery.process`, прототипы по + `$.processes[?(@.PID=='{#PROCPID}')].MemorySize.first()` и т.п. +5. **LLD серверов** `onecras.discovery.server`. + +Триггеры (примеры из статьи): превышение памяти `rphost`, рост числа +регламентных `reg000` в рабочее время, срабатывание КОРП-лицензий (`prolicense`), +недоступность RAS (nodata по мастер-айтему). + +## Ограничения + +* Версия `rac`/`rac.exe` должна быть совместима с версией сервера 1С. +* Фильтр КОРП/базовых лицензий (Windows — `-CorpLicensePattern`, Linux — + переменная `CORP_LICENSE_PATTERN`) стоит сверить с фактическим выводом + `rac license list`. На Linux дефолт без интервалов `{3}` (`ORG8B ... 500`) — + совместим с `mawk`; при использовании интервалов нужен `gawk`. +* Единицы `memory-size` зависят от версии платформы — сверяйтесь с консолью + администрирования при настройке порогов. +* Параметры блокировки ИБ и имя БД (`infobase info`) требуют прав на каждую + информационную базу — в коллектор не включены намеренно. +* Linux-версия (`.sh`) протестирована на синтетическом выводе `rac` (парсинг и + JSON валидны); Windows-версия (`.ps1`) вычитана статически. На реальном + кластере сверьте имена полей `rac` — в разных сборках платформы есть отличия. diff --git a/1c/monitoring-ras/template_1c_cluster_ras.yaml b/1c/monitoring-ras/template_1c_cluster_ras.yaml new file mode 100644 index 0000000..5b41619 --- /dev/null +++ b/1c/monitoring-ras/template_1c_cluster_ras.yaml @@ -0,0 +1,447 @@ +zabbix_export: + version: '6.0' + date: '2026-07-12T00:00:00Z' + groups: + - + uuid: 7df96b18c230490a9a0a9e2307226338 + name: Templates + templates: + - + uuid: a1b2c3d4e5f640718293a4b5c6d7e8f9 + template: '1C cluster by RAS' + name: '1C cluster by RAS' + description: | + Мониторинг кластера 1С 8.3 через RAS/RAC (rac.exe/rac). + Порт статьи https://infostart.ru/1c/articles/1632627/ с COM+ на RAS/RAC. + + Требуются на хосте с доступом к RAS (TCP 1545): + Windows: скрипт 1c_cluster_ras.ps1 + userparameter_1c_ras.conf + Linux: скрипт 1c_cluster_ras.ps1 + userparameter_1c_ras_linux.conf (нужен pwsh) + + Один мастер-айтем onecras.json опрашивает RAS, остальные метрики — + зависимые элементы с предобработкой JSONPath. + groups: + - + name: Templates + macros: + - + macro: '{$RAS.SERVER}' + value: 'localhost:1545' + description: 'Адрес RAS в виде host:port' + - + macro: '{$RAS.CLUSTER.USER}' + value: '' + description: 'Администратор кластера (если задан)' + - + macro: '{$RAS.CLUSTER.PWD}' + value: '' + description: 'Пароль администратора кластера' + - + macro: '{$RPHOST.MEM.WARN}' + value: '8000000' + description: 'Порог памяти рабочего процесса rphost (единицы memory-size платформы)' + - + macro: '{$PROLICENSE.MAX}' + value: '10' + description: 'Порог числа ПРОФ-лицензий на КОРП-кластере (см. статью)' + items: + - + uuid: b0010000000000000000000000000001 + name: '1С RAS: сбор данных кластера (JSON)' + type: ZABBIX_ACTIVE + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + delay: '1m' + history: '0' + trends: '0' + value_type: TEXT + description: 'Мастер-айтем: весь кластер одним опросом RAS.' + tags: + - + tag: Application + value: '1С Кластер' + triggers: + - + uuid: c0010000000000000000000000000001 + expression: 'nodata(/1C cluster by RAS/onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"],5m)=1' + name: 'RAS недоступен на {HOST.NAME}' + priority: HIGH + - + uuid: b0010000000000000000000000000002 + name: '1С Кластер: сессий всего' + type: DEPENDENT + key: onecras.cluster.sessions_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.sessions_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000003 + name: '1С Кластер: соединений' + type: DEPENDENT + key: onecras.cluster.connections_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.connections_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000004 + name: '1С Кластер: пользователей (лицензий)' + type: DEPENDENT + key: onecras.cluster.users_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.users_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000005 + name: '1С Кластер: клиентов (1CV8/1CV8C)' + type: DEPENDENT + key: onecras.cluster.clients_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.clients_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000006 + name: '1С Кластер: WEB-сервисов' + type: DEPENDENT + key: onecras.cluster.ws_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.ws_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000007 + name: '1С Кластер: HTTP-сервисов' + type: DEPENDENT + key: onecras.cluster.http_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.http_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000008 + name: '1С Кластер: фоновых заданий' + type: DEPENDENT + key: onecras.cluster.jobs_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.jobs_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b0010000000000000000000000000009 + name: '1С Кластер: COM+ подключений' + type: DEPENDENT + key: onecras.cluster.com_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.com_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b001000000000000000000000000000a + name: '1С Кластер: веб-клиентов' + type: DEPENDENT + key: onecras.cluster.web_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.web_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b001000000000000000000000000000b + name: '1С Кластер: рабочих серверов' + type: DEPENDENT + key: onecras.cluster.workingservers_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.workingservers_count + tags: + - + tag: Application + value: '1С Кластер' + - + uuid: b001000000000000000000000000000c + name: '1С Кластер: лицензий ПРОФ' + type: DEPENDENT + key: onecras.cluster.prolicense_count + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.prolicense_count + tags: + - + tag: Application + value: '1С Кластер' + triggers: + - + uuid: c001000000000000000000000000000c + expression: 'last(/1C cluster by RAS/onecras.cluster.prolicense_count)>{$PROLICENSE.MAX}' + name: 'Слишком много ПРОФ-лицензий на КОРП-кластере {HOST.NAME}' + priority: WARNING + description: 'Из статьи: подключение >10 ПРОФ-ключей ломает функционал КОРП.' + - + uuid: b001000000000000000000000000000d + name: '1С Кластер: допустимый объём памяти' + type: DEPENDENT + key: onecras.cluster.max_memory_size + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - $.cluster.MaxMemorySize + tags: + - + tag: Application + value: '1С Кластер' + discovery_rules: + - + uuid: d0010000000000000000000000000001 + name: 'Обнаружение информационных баз' + type: ZABBIX_ACTIVE + key: 'onecras.discovery.ib["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + delay: '1h' + lifetime: 3d + item_prototypes: + - + uuid: e0010000000000000000000000000001 + name: 'ИБ {#IBNAME}: сессий' + type: DEPENDENT + key: 'onecras.ib.sessions[{#IBNAME}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.bases.list[?(@.BaseName=='{#IBNAME}')].sessions.first()" + tags: + - + tag: Application + value: '1С Базы' + - + uuid: e0010000000000000000000000000002 + name: 'ИБ {#IBNAME}: пользователей' + type: DEPENDENT + key: 'onecras.ib.users[{#IBNAME}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.bases.list[?(@.BaseName=='{#IBNAME}')].users.first()" + tags: + - + tag: Application + value: '1С Базы' + - + uuid: d0010000000000000000000000000002 + name: 'Обнаружение рабочих процессов' + type: ZABBIX_ACTIVE + key: 'onecras.discovery.process["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + delay: '1h' + lifetime: 3d + item_prototypes: + - + uuid: e0010000000000000000000000000011 + name: 'rphost {#PROCHOST}:{#PROCPORT} (PID {#PROCPID}): память' + type: DEPENDENT + key: 'onecras.proc.memory[{#PROCPID}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.processes[?(@.PID=='{#PROCPID}')].MemorySize.first()" + tags: + - + tag: Application + value: '1С Процессы' + trigger_prototypes: + - + uuid: f0010000000000000000000000000011 + expression: 'last(/1C cluster by RAS/onecras.proc.memory[{#PROCPID}])>{$RPHOST.MEM.WARN}' + name: 'Высокая память rphost PID {#PROCPID} на {HOST.NAME}' + priority: WARNING + - + uuid: e0010000000000000000000000000012 + name: 'rphost {#PROCHOST}:{#PROCPORT} (PID {#PROCPID}): соединений' + type: DEPENDENT + key: 'onecras.proc.connections[{#PROCPID}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.processes[?(@.PID=='{#PROCPID}')].connections.first()" + tags: + - + tag: Application + value: '1С Процессы' + - + uuid: e0010000000000000000000000000013 + name: 'rphost {#PROCHOST}:{#PROCPORT} (PID {#PROCPID}): сессий' + type: DEPENDENT + key: 'onecras.proc.sessions[{#PROCPID}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.processes[?(@.PID=='{#PROCPID}')].sessions.first()" + tags: + - + tag: Application + value: '1С Процессы' + - + uuid: e0010000000000000000000000000014 + name: 'rphost {#PROCHOST}:{#PROCPORT} (PID {#PROCPID}): доступная производительность' + type: DEPENDENT + key: 'onecras.proc.availperf[{#PROCPID}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.processes[?(@.PID=='{#PROCPID}')].AvailablePerf.first()" + tags: + - + tag: Application + value: '1С Процессы' + - + uuid: d0010000000000000000000000000003 + name: 'Обнаружение рабочих серверов' + type: ZABBIX_ACTIVE + key: 'onecras.discovery.server["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + delay: '1h' + lifetime: 3d + item_prototypes: + - + uuid: e0010000000000000000000000000021 + name: 'Сервер {#SRVNAME}: лимит соединений' + type: DEPENDENT + key: 'onecras.server.connlimit[{#SRVNAME}]' + delay: '0' + value_type: UNSIGNED + master_item: + key: 'onecras.json["{$RAS.SERVER}","{$RAS.CLUSTER.USER}","{$RAS.CLUSTER.PWD}"]' + preprocessing: + - + type: JSONPATH + parameters: + - "$.workingservers[?(@.Name=='{#SRVNAME}')].connections_limit.first()" + tags: + - + tag: Application + value: '1С Серверы' diff --git a/1c/monitoring-ras/userparameter_1c_ras.conf b/1c/monitoring-ras/userparameter_1c_ras.conf new file mode 100644 index 0000000..b81c624 --- /dev/null +++ b/1c/monitoring-ras/userparameter_1c_ras.conf @@ -0,0 +1,17 @@ +# Мониторинг кластера 1С 8.3 через RAS/RAC — Windows (Zabbix agent + Windows PowerShell). +# Положить в C:\Program Files\Zabbix Agent\zabbix_agent2.d\ (или подключить в zabbix_agentd.conf) +# и перезапустить агент. Скрипт — в C:\Zabbix\Scripts\1c_cluster_ras.ps1 +# +# Аргументы ключа: [, <админ кластера>, <пароль>] — задаются +# из шаблона макросами {$RAS.SERVER}, {$RAS.CLUSTER.USER}, {$RAS.CLUSTER.PWD}. +# +# Мастер-айтем: один вызов = один опрос RAS, из него JSONPath'ом достаются все метрики. +UserParameter=onecras.json[*],powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Zabbix\Scripts\1c_cluster_ras.ps1" json -RasServer "$1" -ClusterUser "$2" -ClusterPwd "$3" + +# LLD: информационные базы, рабочие процессы и рабочие серверы. +UserParameter=onecras.discovery.ib[*],powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Zabbix\Scripts\1c_cluster_ras.ps1" discovery.ib -RasServer "$1" -ClusterUser "$2" -ClusterPwd "$3" +UserParameter=onecras.discovery.process[*],powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Zabbix\Scripts\1c_cluster_ras.ps1" discovery.process -RasServer "$1" -ClusterUser "$2" -ClusterPwd "$3" +UserParameter=onecras.discovery.server[*],powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Zabbix\Scripts\1c_cluster_ras.ps1" discovery.server -RasServer "$1" -ClusterUser "$2" -ClusterPwd "$3" + +# Таймаут агента заведомо должен быть больше времени опроса RAS: +# Timeout=30 diff --git a/1c/monitoring-ras/userparameter_1c_ras_linux.conf b/1c/monitoring-ras/userparameter_1c_ras_linux.conf new file mode 100644 index 0000000..b760455 --- /dev/null +++ b/1c/monitoring-ras/userparameter_1c_ras_linux.conf @@ -0,0 +1,19 @@ +# Мониторинг кластера 1С 8.3 через RAS/RAC — Linux (Zabbix agent + bash/awk). +# Внешних зависимостей нет: нужен только rac (из платформы 1С) и awk + coreutils. +# PowerShell НЕ требуется. +# Положить в /etc/zabbix/zabbix_agentd.d/ (или .../zabbix_agent2.d/) и перезапустить агент. +# Скрипт — /etc/zabbix/scripts/1c_cluster_ras.sh (chmod +x). +# +# Аргументы ключа: [, <админ кластера>, <пароль>] — задаются +# из шаблона макросами {$RAS.SERVER}, {$RAS.CLUSTER.USER}, {$RAS.CLUSTER.PWD}. +# +# Мастер-айтем: один вызов = один опрос RAS, из него JSONPath'ом достаются все метрики. +UserParameter=onecras.json[*],/etc/zabbix/scripts/1c_cluster_ras.sh json "$1" "$2" "$3" + +# LLD: информационные базы, рабочие процессы и рабочие серверы. +UserParameter=onecras.discovery.ib[*],/etc/zabbix/scripts/1c_cluster_ras.sh discovery.ib "$1" "$2" "$3" +UserParameter=onecras.discovery.process[*],/etc/zabbix/scripts/1c_cluster_ras.sh discovery.process "$1" "$2" "$3" +UserParameter=onecras.discovery.server[*],/etc/zabbix/scripts/1c_cluster_ras.sh discovery.server "$1" "$2" "$3" + +# Таймаут агента заведомо должен быть больше времени опроса RAS: +# Timeout=30 diff --git a/zabbix/README.md b/zabbix/README.md index a55d942..c66c06e 100644 --- a/zabbix/README.md +++ b/zabbix/README.md @@ -5,3 +5,6 @@ | Файл | Назначение | |---|---| | `zbx_export_templates.yaml` | Шаблон «Windows hardware by Zabbix agent active»: температуры, SMART, кулеры, напряжения. Требует smartmontools, OpenHardwareMonitor и скрипты `windows.hard.ps1` / `windows.hdd.ps1` на хосте. | + +Мониторинг кластера 1С 8.3 через RAS/RAC — отдельный шаблон и коллектор в +[`../1c/monitoring-ras/`](../1c/monitoring-ras/).