Pull to refresh

Мониторинг «на коленке» – использование Cacti для контроля Jаva приложений

Reading time 9 min
Views 47K
В статье описывается решение для мониторинга с использованием Cacti на примере задачи анализа и контроля потребления ресурсов большого Java-приложения.

Передо мной стояла задача – в краткие сроки предложить меры по стабилизации большого трехзвенного Java-приложения, имеющего проблемы с потреблением памяти и производительностью. Времени, как обычно, мало: 1-2 недели на все. На фирме отсутствовала подходящая инфраструктура мониторинга приложений, и в мою задачу не входило ее создавать. Вариант с использованием JConsole не подходил из-за необходимости анализировать потребление за продолжительное время и смотреть его после возможных внезапных перезагрузок приложений.

В одной из фирм, где я работал, было реализовано впечатляющее по удобству и простоте решение для мониторинга Java-приложений на основе RRD Tool. Состояло оно из несложной надстройки на perl-скриптах, обеспечивающих сбор и отображение данных через HTTP и ряда доработок-агентов сбора данных в самом приложении. Для меня это стало идеей решения, однако, времени на написание обвязки над RRD у меня не было.

После аккуратного поиска нашелся бесплатный инструмент, реализующий необходимую мне надстройку – Cacti. Cacti это приложение, написанное в инфраструктуре Apache-PHP-MySql, позволяющее настраивать сбор и отображение данных мониторинга на основе веб-интерфейса. Разобраться с ним оказалось несложно, пару дней для подъема инфраструктуры, затем настройка и дописывание агентов сбора данных и все.

Далее в статье подробно описывается решение, позволившие решить мою задачу и, в конце концов, провести успешную стабилизацию приложения на фирме.



Что нужно для работы:
  • Cacti 0.8.7i (самая свежая версия на момент реализации решения)
  • Apache 2.2.21 (движок для Cacti)
  • PHP 5.3.8 (платформа, на которой написана Cacti)
  • MySQL 5.5 (хранение настроек Cacti)
  • RRDTool 1.2.30 (отрисовка диаграмм и хранение данных мониторинга)

(указаны версии, на которых запустился мониторинг, подойдет любая работающая связку Apache-PHP-MySql)

Опишу принцип работы мониторинга на Cacti: при помощи «назначенных заданий» Windows (или cron в unix) производится периодический запуск опроса (polling) агентов сбора данных, которыми в моем случае были: сама JVM (потребление памяти) и специализированные доработки приложения. Собранные сведения размещаются в базе данных RRD – т.е. в циклических буферах в виде файлов. Далее, накопленные данные из RRD можно просматривать через веб-интерфейс Cacti в различных масштабах в разрезе минут, часов, дней, месяцев, др.

План работы – поднять всю необходимую инфраструктуру, адаптировать анализируемые приложения для сбора данные и настроить сбор и вывод данных в Cacti.

Настройка инфраструктуры

PHP


Добавить путь к php.exe в переменную PATH, тот же путь прописать в переменной PHPRC

Скопировать файл php.ini-production в php.ini и внести в php.ini следующие изменения:

Раскомментировать строчки:
extension_dir = c:\php\ext
extension=php_mysql.dll
extension=php_snmp.dll
extension=php_sockets.dll
cgi.force_redirect = 0
date.timezone = "Europe/Moscow"


Apache

Добавить в conf\httpd.conf следующие строки:
LoadModule php5_module c:\php\php5apache2_2.dll
AddType application/x-httpd-php .php
DirectoryIndex index.html index.htm index.php


MySql

Добавить путь к mysql.exe в переменную PATH

Создание схемы cacti:
mysql --user=root --password create cacti


Импортируем структуру данных схемы cacti
mysql --user=root --password cacti < c:\apache2\htdocs\cacti\cacti.sql


Создаем пользователя cactiuser:
mysql --user=root --password 


Далее в командной строке MySql:
mysql> create user cactiuser@localhost IDENTIFIED BY ’cactiuser’
mysql> GRANT ALL ON cacti.* TO cactiuser@localhost;
mysql> flush privileges;


Для быстрой проверки работоспособности связки php-mysql-apache я использовал следующих скрипт php:
<?php
mysql_connect( "localhost", "cactiuser", "cactiuser" ) or die("Can't connect");
mysql_query( "USE cacti" ) or die("Can't select mysql database");
echo "Success\n";
?>


Его надо положить в файлик с именем, например, testphp.php, скопировать в директорию Apache htdocs/ и загрузить страничку localhost:8080/testphp.php. Должна появиться надпись «Success».

Настройка Cacti


Разархировать дистрибутив cacti в директорию Apache /htdocs.
Проверить что файл cacti /include/config.php содержит следующие строки:

$database_default = "cacti";
$database_hostname = "localhost";
$database_username = "cactiuser";
$database_password = "cactiuser";
$database_port = "3306";


Зайти на адрес localhost:8080/cacti/ под логином admin/admin.

В настройках Settings->Paths задать пути к внешним утилитам (рекомендуется использовать пути в стиле Unix, например c:/php/php.exe).

Настроить запуск команды «php cacti/poller.php» каждые 5 минут (через Windows Scheduled Tasks). У меня для этого используется батничек:
start /MIN php.exe cacti\poller.php


Настройка Cacti Spine (опционально: это poller, написанный на C++, который используется для ускорения опроса, рекомендуется Cacti)
Разархивировать spine архив в директорию cacti убедиться что spine.conf содержит следующие строки:

DB_Host 127.0.0.1 # строго не localhost!!!
DB_Database cacti
DB_User cactiuser
DB_Password cactiuser
DB_Port 3306


Выбор и настройка сбора данных


Я пробовал два способа сбора данных – SNMP и опрос JMX сервера в составе JVM и приложений. SNMP поддерживается Cacti и его разумно использовать, если нужно смотреть только использование памяти JVM и, нужно это сделать очень быстро. Я начал с SNMP, но после первых успехов перешел на JMX. Cacti не поддерживает JMX, поэтому за дополнительную гибкость надо платить — требуются усилия по написанию приемной и ответной части на Java.

Ниже привожу соответствующий код.

Код для опроса JVM по состоянию памяти (аналогично пишется код для произвольного JMX-сервера):

import java.io.Closeable;
import java.io.IOException;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;


public class JvmHeapJmxClient {
    public static void main(String[] args) {
        JMXConnector jmxc = null;
        try {
            String jmxHost = args[0];
            String jmxPort = args[1];
            String jmxConnectionString = "service:jmx:rmi:///jndi/rmi://" + jmxHost + ":" + jmxPort + "/jmxrmi";
            
            JMXServiceURL url = new JMXServiceURL(jmxConnectionString);
            jmxc = JMXConnectorFactory.connect(url, null);
            MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

            ObjectName memoryMBeanName = new ObjectName("java.lang:type=Memory");
            MemoryMXBean memoryMBeanProxy = JMX.newMXBeanProxy(mbsc, memoryMBeanName, MemoryMXBean.class, true);
            MemoryUsage memoryUsage = memoryMBeanProxy.getHeapMemoryUsage();
            
            echo(
                "used:" + memoryUsage.getUsed() + " " +
                "committed:" + memoryUsage.getCommitted() + " " +
                "init:" + memoryUsage.getInit() + " " +
                "max:" + memoryUsage.getMax());
            
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            closeStream(jmxc);
        }
    }

    private static void echo(String msg) {
        System.out.println(msg);
    }

    private static void closeStream(Closeable stream) {
        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


Код Java для запуска JMX. Все эти сложности нужны для фиксирования порта и имени хоста, что требуется при наличии межсетевого экрана. Для каждого сервиса требуется по 2 порта, как видно из кода – один для http доступа, другой для RMI. Распределение портов очевидно можно сделать по-другому (в данном случае RMI-порт = http-порт + 1), в том числе явно указать оба порта. В строку запуска приложений нужно будет добавить следующие слова (jmxagent – jar-файл с кодом агента):

-Djmxagent.port=<SERVER_JMX_PORT> -Djmxagent.host=<SERVER_HOST> -javaagent:jmxagent.jar


import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.HashMap;

import javax.management.MBeanServer;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class JmxFirewallAgent {

    private JmxFirewallAgent() { }

    public static void premain(String agentArgs) throws IOException {
        try {
            final int rmiRegistryPort = Integer.parseInt(System.getProperty("jmxagent.port"));
            LocateRegistry.createRegistry(rmiRegistryPort);
    
            final int rmiServerPort = rmiRegistryPort + 1;
    
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            HashMap<String,Object> env = new HashMap<String,Object>();
            final String hostname = System.getProperty("jmxagent.host");        
            JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://" + hostname + ":" + rmiServerPort + "/jndi/rmi://" + hostname + ":" + rmiRegistryPort    + "/jmxrmi");
            
            JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
            cs.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


Далее идут скриншоты наиболее важных настроек Cacti.

Настройка опроса – важный момент в общих настройках Cacti. Я использую Spine, опрос запускается каждые 5 минут, данные снимаются каждые 10 секунд. Смотрите далее настройки RRD в составе Cacti – их требуется продумать заранее, т.к. далее менять без перезаписи базы RRD проблематично. Здесь неплохая подборка советов по этим настройкам:



Мои настройки RRD.



Пример содержимого одной настройки:



Настраивам Device – это сервер с IP-адресом, с которого будут забираться данные. У нас несколько серверов, на каждом из них запущено несколько сервисов Java.



Настраиваем Data Input Method – задание способа сбора данных. В моем случае это батничек, который запускает Java программу опроса JMX (ее исходные коды были приведены выше.) Входные параметры у него это адрес и порт сервера, т.е. Java-приложения.



Пример описания входного параметра – в данном случае адрес сервера. Используется предопределенное ключевое слово – hostname, которое автоматически заполняется Cacti. Второй параметр будет заполняться для каждой настройки сбора данных Data Source, как будет видно дальше.



Выходные параметры, которые возвращает батничек. Cacti понимает их в формате:
ключ1:значение1 ключ2:значение2


Моя программа возвращает 4 выходных значения, вот пример задания одного из них (commited):



Далее создаем Data Template – шаблон для задания источников данных. Самая важная настройка в Cacti, на мой взгляд. Шаблон связывается с настройкой базы RRD, в нем указываются параметры для хранения в базе (Data Source Item) и дополнительные настройки, в данном случае это jmx host и jmx port, первый, как уже говорилось, заполняется автоматически, второй будет заполняться на каждом источнике данных, т.е. источник данных будет соответствовать одному Java-приложению.



На основе шаблона создается Data Source – источник данных, которые затем можно будет отображать на диаграммах Graph. В источнике указывает Device, к которому надо подключаться для сбора данных, шаблон источника, имя и месторасположение базы данных RRD. В моем случае также требуется указать дополнительный параметр – порт JMX. Важный момент – при существенных обновлениях настроек сбора данных в Data Source или Data Template (например, удаление-добавление параметров Data Source Item) требуется пересоздание базы данных RRD. Это можно сделать руками вне Cacti (я до этого не добрался) или пересоздать Data Source с потерей всех предыдущих данных. Это, видимо, самое неприятная особенность связки Cacti-RRD, с которой мне пришлось столкнуться.



Приступаем к отображению данных путем задания шаблонов диаграмм Graph Template. Настройки в основном задают способ визуализации данных. Требуется указать параметры из Data Template, которые требуется выводить на диаграмме и в каком виде. Насколько я понимаю все эти настройки – непосредственная оболочка над командным интерфейсом RRD.



Пример задания параметра Data Source. Я использую для отображения потребления памяти 5 элементов на диаграмме: Макс, мин, и текущее потребление в виде сплошной закраски (порядок вывода важен!), затем два параметра – максимальное выделенное и зарезервированное потребление в виде линий. Пример, см. ниже.



Диаграмма Graph – задается шаблон, сервер и задание соответствия между конкретным Data Source и паметрами шаблона диаграммы:



Пример окончательной диаграммы использующей все заданные настройки: Видно что потреблениев среднем на уровне 3Гб, однако довольно часто достигает максимальной выделенной границы в 6Гб (-Xmx). Данные можно смотреть с произвольной детальностью (от 1 тика сбора до 2лет, как на примере и больше). Все это зависит от заданных настроек базы RRD. С моими настройками мне вполне хватило информации для решения проблем с потреблением.



На этом описание моего решения заканчивается. Подчеркиваю, что основная идея решения – это скорость реализации при высоком качестве полученного результата. Вопросы и идеи по улучшению приветствуются.

Ту часть настроек, которая касается моих специфических параметров производительности я приводить не стал, поскольку они полностью аналогичны уже описанным.

Спасибо за внимание!

Выводы:


Cacti позволил мне успешно решить задачу быстрой настройки мониторинга Java-приложений. Если вам нужно быстро с нуля сделать мониторинг – рекомендую это как один из рабочих вариантов. Если в ваши планы входит построение долгосрочного мониторинга, то, на мой взгляд, имеет смысл посмотреть на следующие варианты: внедрять мощные системы типа Nagios, либо писать свою специализированную надстройку над RRD. Плюсы и минусы Cacti:

Минусы решения:

  • Довольно быстрое нарастание количества однотипных настроек в случае большого числа сред и серверов приложений Java.
  • Ограниченная производительность «неродных» JMX решений для Cacti.


Плюсы решения:

  • Высокая скорость развертывания при минимальном дополнительном кодировании
  • Простота и удобство интерфейса просмотра диаграмм и их настройки (не нужно сразу учить что-то сложное)
Tags:
Hubs:
+13
Comments 10
Comments Comments 10

Articles