Руководство по снятию снапшотов GlobalScheduler

Содержание

Руководство по снятию снапшотов GlobalScheduler#

Назначение#

Процедура снятия снапшотов (thread dump) процесса GlobalScheduler позволяет диагностировать проблемы производительности, зависания потоков и дедлоки в системе Global ERP.

Примечание

Снятие снапшотов требуется при следующих симптомах:

  • Зависание задач планировщика.

  • Высокая нагрузка на CPU процесса GlobalScheduler.

  • Увеличивающееся потребление памяти.

  • Ошибки выполнения запланированных заданий.

  • Длительное выполнение задач без прогресса.

Общие предварительные требования#

  • Установленная Java Development Kit (JDK) той же версии, что используется для запуска GlobalScheduler.

  • Знание PID процесса Java GlobalScheduler.

  • Права доступа к процессу GlobalScheduler.

Часть 1: Снятие снапшотов в Kubernetes#

Предварительные требования для Kubernetes#

  • Доступ к кластеру Kubernetes с правами на выполнение kubectl exec.

  • Настроенный kubeconfig для namespace gs-ctk.

Шаг 1.1: Поиск пода GlobalScheduler#

kubectl get pods -n gs-ctk

Пример вывода:

NAME                                               READY   STATUS    RESTARTS        AGE
gs-cluster-1-global-scheduler-7d49bffb5f-d9llp     2/2     Running   2 (3m12s ago)   18h
gs-cluster-1-global-server-excl-cc4d9c647-jshzq    2/2     Running   5 (3m29s ago)   18h
gs-cluster-1-global-server-share-95bc95764-lql4x   2/2     Running   6 (3m12s ago)   18h
gs-cluster-1-grafana-0                             1/1     Running   1 (3m12s ago)   18h
gs-cluster-1-haproxy-6d4b44cf94-lzlj2              2/2     Running   2 (3m29s ago)   18h
gs-cluster-1-rabbitmq-0                            1/1     Running   1 (3m29s ago)   18h
nsctl-6f59bdc5c8-b8vsv                             1/1     Running   1 (3m29s ago)   18h

Шаг 1.2: Определение PID процесса Java#

# Подключение к поду и поиск PID
kubectl exec -it gs-cluster-1-global-scheduler-7d49bffb5f-d9llp -n gs-ctk -c globalscheduler -- /bin/bash

# Поиск PID процесса Java (обычно дочерний от python3)
ps aux | grep java
# ИЛИ
pstree -p 1

Пример вывода:

python3(1)─┬─java(27)─┬─{java}(35)

В данном случае PID процесса Java = 27.

Шаг 1.3: Снятие thread dump в Kubernetes#

# Определение переменных
POD_NAME="gs-cluster-1-global-scheduler-7d49bffb5f-d9llp"
NAMESPACE="gs-ctk"
JAVA_PID=27

# Снятие одиночного дампа
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > thread_dump_k8s_$(date +%Y%m%d_%H%M%S).txt

# Снятие нескольких дампов с интервалом
for i in {1..3}; do
    echo "Снятие дампа $i в $(date)"
    kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > thread_dump_k8s_${i}_$(date +%Y%m%d_%H%M%S).txt
    sleep 10
done

Шаг 1.4: Снятие heap dump в Kubernetes#

# Снятие heap dump
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jmap -dump:live,format=b,file=/tmp/heap_dump.hprof $JAVA_PID"

# Копирование с пода
kubectl cp $NAMESPACE/$POD_NAME:/tmp/heap_dump.hprof ./heap_dump_k8s_$(date +%Y%m%d_%H%M%S).hprof -c globalscheduler

# Очистка
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "rm -f /tmp/heap_dump.hprof"

Часть 2: Снятие снапшотов на автономном сервере#

Предварительные требования для автономного сервера#

  • Права sudo пользователя или доступ под пользователем global.

  • Знание пути к установке GlobalScheduler (обычно /opt/global/globalserver).

Шаг 2.1: Определение PID процесса GlobalScheduler#

# Определение пода через systemd
sudo systemctl status globalscheduler
PID=$(systemctl show globalscheduler --property=MainPID | cut -d= -f2)

# Проверка PID
echo "PID процесса GlobalScheduler: $PID"
# PID должен совпадать с тем что вывел syst

Шаг 2.2: Определение пути к Java#

# Проверка используемой Java
sudo ls -l /proc/$PID/exe

# Проверка установленных версий Java
update-alternatives --list java

Шаг 2.3: Снятие thread dump на автономном сервере#

# Снятие одиночного дампа
sudo $JAVA_HOME/bin/jstack $PID > /tmp/thread_dump_standalone_$(date +%Y%m%d_%H%M%S).txt

# Снятие нескольких дампов
for i in {1..3}; do
    echo "Снятие дампа $i в $(date)"
    sudo $JAVA_HOME/bin/jstack $PID > /tmp/thread_dump_standalone_${i}_$(date +%Y%m%d_%H%M%S).txt
    sleep 10
done

Шаг 2.4: Снятие heap dump на автономном сервере#

# Снятие heap dump
sudo $JAVA_HOME/bin/jmap -dump:live,format=b,file=/tmp/heap_dump_standalone_$(date +%Y%m%d_%H%M%S).hprof $PID

# Проверка размера
ls -lh /tmp/heap_dump_*.hprof

Предупреждение

Снятие heap dump может занять значительное время и потребовать много места на диске (обычно несколько гигабайт). Убедитесь, что в /tmp достаточно свободного места.

Часть 3: Анализ полученных дампов#

Быстрый анализ через командную строку#

# Для анализа перейдите в директорию с дампами
cd /path/to/dumps

# Поиск заблокированных потоков
grep -c "BLOCKED" thread_dump_*.txt

# Поиск deadlock
grep -l "deadlock" thread_dump_*.txt

# Подсчет потоков по состоянию
grep "java.lang.Thread.State" thread_dump_*.txt | awk '{print $2}' | sort | uniq -c | sort -nr

# Анализ состояния всех потоков
for file in thread_dump_*.txt; do
    echo "=== $file ==="
    grep "java.lang.Thread.State" "$file" | awk '{print $2}' | sort | uniq -c
    echo
done

Часть 4: Интерпретация результатов для GlobalScheduler#

Типичные сценарии проблем#

Сценарий 1: Дедлок в базе данных#

Found one Java-level deadlock:
=============================
"Thread-15":
  waiting to lock monitor 0x00007f8c4c00b8e0 (object 0x00000000f8a8f8a0, a org.postgresql.core.v3.QueryExecutorImpl)

Решение: Проверка длительных транзакций в БД PostgreSQL.

Сценарий 2: Блокировка ресурсов планировщика#

"QuartzScheduler_Worker-10" #25 prio=5 os_prio=0 tid=0x00007f8c54010800 nid=0x5e3a waiting for monitor entry [0x00007f8c4a1fe000]
   java.lang.Thread.State: BLOCKED (on object monitor)

Решение: Увеличение размера пула потоков в настройках Quartz.

Сценарий 3: Проблемы с подключением к Global ERP#

"Thread-8" #20 prio=5 os_prio=0 tid=0x00007f8c5420a800 nid=0x5f1c runnable [0x00007f8c4a3ff000]
   java.lang.Thread.State: RUNNABLE
   at java.net.SocketInputStream.socketRead0(Native Method)

Решение: Проверка подключения к SOAP-сервису Global ERP.

Часть 5: Устранение распространенных проблем#

Общие проблемы и решения#

Проблема: «Permission denied» при выполнении jstack/jmap#

В Kubernetes: Убедитесь, что у пользователя есть права на выполнение kubectl exec в namespace gs-ctk.

На автономном сервере: Выполняйте команды с правами пользователя global:

sudo -u global jstack $PID

Проблема: Процесс Java не найден#

В Kubernetes: Проверьте PID процесса Java командой внутри пода:

kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- ps aux | grep java

На автономном сервере: Убедитесь, что GlobalScheduler запущен:

sudo systemctl status globalscheduler

Проблема: jstack/jmap не найдены#

Решение: Укажите полный путь к утилитам JDK:

# В Kubernetes
/root/globalserver/workspace/local/jdk21/bin/jstack $PID

# На автономном сервере  
/usr/local/jdk-21/bin/jstack $PID
# ИЛИ
/usr/lib/jvm/java-21-openjdk-amd64/bin/jstack $PID

Проблема: Недостаточно места для heap dump#

Решение: Используйте директорию с достаточным местом:

# В Kubernetes
kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jmap -dump:live,format=b,file=/opt/global/temp/heap_dump.hprof $JAVA_PID"

# На автономном сервере  
sudo -u global jmap -dump:live,format=b,file=/opt/global/temp/heap_dump.hprof $PID

Часть 6: Дополнительные диагностические команды#

Для автономного сервера#

# Мониторинг системных ресурсов
top -p $PID
htop -p $PID

# Память процесса
pmap -x $PID | tail -1

# Открытые файлы
lsof -p $PID | wc -l

# Логи GlobalScheduler
sudo journalctl -u globalscheduler -f
sudo tail -f /opt/global/globalserver/logs/globalscheduler.log

Для Kubernetes#

# Логи пода
kubectl logs -n gs-ctk $POD_NAME -c globalscheduler -f

# Мониторинг ресурсов
kubectl top pod -n gs-ctk $POD_NAME

# Описание пода для диагностики
kubectl describe pod -n gs-ctk $POD_NAME

# Проверка состояния контейнера
kubectl get pod $POD_NAME -n gs-ctk -o jsonpath='{.status.containerStatuses[0].ready}'

Часть 7: Автоматический мониторинг и снятие дампов#

Скрипт для автоматического снятия дампов при высокой нагрузке (Kubernetes)#

#!/bin/bash
NAMESPACE="gs-ctk"
POD_LABEL="app=globalscheduler"
POD_NAME="gs-cluster-1-global-scheduler-7d49bffb5f-d9llp"
CPU_THRESHOLD=80
MEMORY_THRESHOLD=85

while true; do
	
    if [ -z "$POD_NAME" ]; then
        echo "$(date): Pod not found"
        sleep 60
        continue
    fi
    
    # Получаем использование CPU и памяти
    POD_STATS=$(kubectl top pod -n $NAMESPACE $POD_NAME --no-headers 2>/dev/null)
    
    if [ $? -eq 0 ]; then
        CPU_USAGE=$(echo $POD_STATS | awk '{print $2}' | sed 's/%//')
        MEMORY_USAGE=$(echo $POD_STATS | awk '{print $3}' | sed 's/%//')
        
        echo "$(date): Pod $POD_NAME - CPU: ${CPU_USAGE}%, Memory: ${MEMORY_USAGE}%"
        
        # Проверяем пороги
        if [ $CPU_USAGE -gt $CPU_THRESHOLD ] || [ $MEMORY_USAGE -gt $MEMORY_THRESHOLD ]; then
            echo "$(date): High resource usage detected! Taking emergency thread dump..."
            
            # Получаем PID Java процесса
            JAVA_PID=$(kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- ps aux | grep java | grep -v grep | awk '{print $2}')
            
            if [ -n "$JAVA_PID" ]; then
                TIMESTAMP=$(date +%Y%m%d_%H%M%S)
                kubectl exec $POD_NAME -n $NAMESPACE -c globalscheduler -- /bin/bash -c "/root/globalserver/workspace/local/jdk21/bin/jstack $JAVA_PID" > emergency_dump_${TIMESTAMP}.txt
                echo "$(date): Emergency dump saved to emergency_dump_${TIMESTAMP}.txt"
            fi
        fi
    else
        echo "$(date): Failed to get pod metrics"
    fi
    
    sleep 60
done

Часть 8: Дополнительные инструменты анализа#

# Визуальные инструменты (требуют GUI)
jvisualvm
jmc

Совет

Для комплексного анализа рекомендуется снимать несколько дампов с интервалом 10–15 секунд и сравнивать их для выявления изменяющихся состояний потоков. Всегда сохраняйте дампы с временными метками для последующего анализа.

При анализе обращайте внимание на:

  1. Количество блокированных потоков (BLOCKED).

  2. Наличие deadlock.

  3. Потоки, находящиеся в состоянии RUNNABLE продолжительное время.

  4. Рост количества потоков со временем.

  5. Потоки, ожидающие доступа к одним и тем же мониторам.