Affichage des articles dont le libellé est WebLogic. Afficher tous les articles
Affichage des articles dont le libellé est WebLogic. Afficher tous les articles

mardi 20 septembre 2011

Template wlsagent pour PNP4Nagios

Il y a de ça peu de temps, j'ai présenté l'utilisation de wlsagent pour le suivi sous nagios d'instance WebLogic. J'avais également donné quelques exemples de graphique sous PNP4Nagios mais sans donner le template de présentation :



Le template est maintenant disponible à l'adresse suivante : http://docs.pnp4nagios.org/templates/check_wlsagent

Il faut copier-coller ce fichier dans le répertoire template de PNP4Nagios (en principe, quelque chose comme /usr/local/pnp4nagios/share/templates/) et se débrouiller pour que PNP4Nagios arrive à faire le lien entre le plugin de lancement et le template.

Personnellement, comme j'appelle tout au travers de NRPE, j'ai simplement créé un doublon de la commande check_nrpe en l'appelant check_wlsagent :
# 'check_wlsagent' command definition
define command{
  command_name check_wlsagent
  command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c $ARG1$
}
J'appelle ensuite mes surveillances à l'aide de commande de ce genre :
# Application WebLogic à surveiller
define service{
  use generic-service
  host_name remotehost
  service_description Application hyperimportante
check_command check_wlsagent!check_application_hyperimportante
}

mercredi 7 septembre 2011

Mise en place de wlsagent

J'ai déjà parlé sur ce blog d'intégrer une surveillance des indicateurs interne de WebLogic sous nagios. Nous allons voir ensemble un nouvel exemple de plugin particulièrement intéressant que j'ai eu l'occasion de déployer : wlsagent.

Installation de l'agent wlsagent

Il nous tout d'abord récupérer une version récente de notre plugin :
yannig@portable:~/wlsagent$ svn checkout http://wlsagent.googlecode.com/svn/trunk/src src
Procédons maintenant à la récupération des librairies nécessaires au fonctionnement de wlsagent :

  • jetty-continuation-7.3.1.v20110307.jar
  • jetty-http-7.3.1.v20110307.jar
  • jetty-io-7.3.1.v20110307.jar
  • jetty-security-7.3.1.v20110307.jar
  • jetty-server-7.3.1.v20110307.jar
  • jetty-servlet-7.3.1.v20110307.jar
  • jetty-util-7.3.1.v20110307.jar
  • servlet-api-2.5.jar
  • wlclient.jar
  • wljmxclient.jar

Tous les jars de type jetty et servlet-api font partie du package jetty que vous pouvez récupérer à l'addresse suivante : http://www.eclipse.org/jetty/downloads.php

Déposons maintenant ces fichiers dans le répertoire lib, à la même hauteur que le répertoire src des fichiers sources de wlsagent. Concernant les fichiers wlclient.jar et wljmxclient.jar, vous les trouverez dans les binaires de WebLogic.

Récupérons ensuite le fichier de build ant suivant :


 name="wlsagent" basedir="." default="build">

     name="src.dir"     value="src"/>
     name="build.dir"   value="build"/>
     name="classes.dir" value="${build.dir}/classes"/>
     name="jar.dir"     value="${build.dir}/jar"/>
     name="lib.dir"     value="lib"/>
     name="main-class"  value="net.wait4it.wlsagent.WlsAgent"/>

     id="classpath">
         dir="${lib.dir}" includes="**/*.jar"/>
    

     name="clean">
         dir="${build.dir}"/>
    

     name="compile">
         dir="${classes.dir}"/>
         srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="true"/>
    

     name="jar" depends="compile">
         dir="${jar.dir}"/>
         destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
            
                 name="Main-Class" value="${main-class}"/>
            
        
    

     name="build" depends="clean,jar"/>

Lançons ensuite le build avec ant, vous devriez obtenir le fichier wlsagent.jar dans le répertoire build/jar.

Passons maintenant à la création du fichier de démarrage de wlsagent :

#!/bin/bash
JAVA_HOME="/usr/lib/jvm/java-6-openjdk"
CURRENT_PATH=$(dirname $0)
LIB_PATH=${CURRENT_PATH}/lib
CLASSPATH=${CURRENT_PATH}/wlsagent.jar

for jar in $(find ${LIB_PATH} -name '*.jar'); do
  CLASSPATH=${CLASSPATH}:${jar};
done

start()
{
  ${JAVA_HOME}/bin/java -Xmx32m -cp ${CLASSPATH} net.wait4it.wlsagent.WlsAgent "9090" > $CURRENT_PATH/wlsagent.log 2>&1 &  echo $! > $CURRENT_PATH/wlsagent.pid
}

stop()
{
  kill $(< $CURRENT_PATH/wlsagent.pid)
}

case $1 in
  stop) stop ;;
  start) start ;;
  restart) stop ; sleep 1 ; start ;;
  status) ps -fp $(< $CURRENT_PATH/wlsagent.pid) ;;
  *) echo "Expected : start/stop/restart/status" ;;
esac
Il ne nous reste plus qu'à lancer le tout et de vérifier le bon démarrage de l'agent :
yannig@portable:~/wlsagent$ ./wlsagent.sh start
yannig@portable-ots:~/wlsagent$ ps -ef | grep wlsagent | grep -v grep
yannig    3922     1  0 10:45 pts/2    00:00:04 /usr/lib/jvm/java-6-openjdk/bin/java -Xmx32m -cp ./wlsagent.jar:.[...]servlet-api-2.5.jar net.wait4it.wlsagent.WlsAgent 9090
Un coup d'oeil dans la log pour vérifier le bon démarrage de l'agent :
yannig@portable:~/wlsagent$ more wlsagent.log 
2011-09-03 10:45:49.961:INFO::jetty-7.3.1.v20110307
2011-09-03 10:45:50.069:INFO::started o.e.j.s.ServletContextHandler{/wlsagent,null}
2011-09-03 10:45:50.222:INFO::Started SelectChannelConnector@0.0.0.0:9090
Tout est prêt ! Nous allons maintenant pouvoir lancer nos tests de communication.

Utilisation du plugin wlsagent

Avant toute chose il faut créer un utilisateur nagios faisant parti du groupe Monitors dans le domaine WebLogic.

On lance ensuite une requête sur l'agent à l'aide d'un wget :
wget -q -O - "http://localhost:9090/wlsagent/WlsAgent?hostname=$(hostname)&port=7028&username=nagios&password=xxx&jvm=UsedMemory;80;90"
0|AdminServer: status OK|HeapSize=370M;;;0;910 UsedMemory=210M;;;0;910

Ici, pas de problème la communication se passe bien. Il suffit ensuite d'intégrer cette surveillance dans nagios pour obtenir une collecte. Pour se faire, vous pouvez récupérer un script d'invocation à l'emplacement suivant : http://code.google.com/p/wlsagent/wiki/Invocation 

Il vous faudra également un outil de collecte des données de performance comme PNP4Nagios. Ci-dessous un exemple de résultat d'intégration à la suite de 2 semaines de collecte :


Pour info, il est possible de remonter les éléments suivants :
  • Threads sur la JVM ;
  • Nombre de transactions en cours sur la JVM ;
  • Nombre de requête / seconde (throughput) ;
  • État des connexion JDBC ;
  • Nombre de session utilisateur ;
  • Et enfin, l'utilisation des queues JMS.
Activité datasource de l'application
Pour gérer ces surveillances, il est nécessaire de rajouter des options pour préciser les éléments à surveiller dans nagios. Pour plus de détail, se reporter à la page suivante : http://code.google.com/p/wlsagent/

dimanche 20 février 2011

Exemple d'utilisation du répartiteur de charge Haproxy

Il y a de ça quelques années, j'ai eu l'occasion de travailler sur le répartiteur de charge HAProxy. Le point fort de cet outil est qu'il est très simple à mettre en oeuvre. Nous l'avions à l'époque mis en place pour une boucle de charge avec des serveurs d'applications Java WebLogic.

L'architecture


L'application pour laquelle nous devions mettre en place cette solution était un Webservice hébergé sur des serveurs d'application Weblogic. Il est important de souligner que cette application n'avait pas de notion de session et la persistance s'appuyait sur la librairie hibernate.

Afin de faire face au trafic (supérieur à 300 transactions/secondes), il avait été décidé d'héberger cette application sur 32 d'instances Weblogics réparties sur 4 machines. Nous devions trouver une solution permettant de répartir la charge tout en offrant des possibilités de maintenance de certains noeuds de la boucle de charge. Ceci nous permettait également de faire des évolutions applicatives en pleine journée.

Pourquoi utiliser ce type de répartiteur de charge


Nous disposions de matériel dédié à la répartition de charge (type Alteon) qui ne gère que la répartition de charge au niveau TCP. Cette situation n'était pas satisfaisante puisque nous avions besoin de pouvoir gérer certaines informations au niveau du HTTP (Keepalive et gestion boucle de charge). De plus, les applications venant consulter ce webservice avaient tendance à garder leur connexion HTTP ouverte empêchant la sortie d'instance Weblogic de la boucle de charge.

C'est là où intervient le HAProxy puisque cet outil de répartition de charge comprend le HTTP. Nous pouvions donc nous en servir pour désactiver le Keepalive et garantir qu'à chaque requête, nos clients passe par une nouvelle connexion.

A noter que la désactivation du Keepalive avait un surcoût au niveau CPU : 1 à 2% (sur un V890 équipé de 8 Ultra IV à 1.3 Ghz). Il est important de qualifier ce coût supplémentaire avec des benchs pour ne pas avoir de mauvaise surprise.

Le HAProxy était capable de scrupter une page présente sur le serveur WebLogic lui indiquant s'il doit le prendre en compte où non. Pour cela nous avions créé une page Web (page JSP dans une WAR) nous permettant de débrayer automatiquement une instance via le dépot d'un fichier dans le répertoire properties de nos instances Weblogic.

Création d'une application Web pour gérer la validité d'une instance


Nous allons tout d'abord créer un petit programme java qui va gérer un état d'une page en fonction d'un fichier properties :

package haproxy;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javax.servlet.Servlet;
import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HaproxyTestServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (isAlive()) {
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().print("OK");
return;
} else {
resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,"Service non disponible");
}
}
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (isAlive()) {
resp.setStatus(HttpServletResponse.SC_OK);
} else {
resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
}
private boolean isAlive() {
URL url = this.getClass().getResource("/haproxy.down");
if (url != null) {
return false;
}
return true;
}
}


Il nous faut maintenant mapper cette applet au niveau de l'application Web en alimentant le fichier web.xml avec le contenu suivant :

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp">
<display-name>TestHaproxy</display-name>
<servlet>
<servlet-name>HaproxyTestServlet</servlet-name>
<display-name>HaproxyTestServlet</display-name>
<servlet-class>haproxy.HaproxyTestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HaproxyTestServlet</servlet-name>
<url-pattern>/HaproxyTestServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>


Reste à la mettre en place et de voir le résultat de son déploiement en consultant l'application en rentrant son URL (http://monserver:monport/haproxy/HaproxyTestServlet). Pour désactiver une instance ? Rien de plus simple : créer un fichier haproxy.down dans le répertoire des properties de l'instance. Remettre une instance dans la boucle ? Simplement en supprimant le fichier haproxy.down

Mise en place de la solution


Il faut d'abord créer un fichier de configuration pour notre HAProxy (haproxy.cfg).

Ce dernier contient l'adresse et le port d'écoute (ici host:8888) ainsi que de nombreux paramètres sur son fonctionnement. Ci-dessous un début de fichier de configuration intéressant :

global
maxconn 4096
daemon
nbproc 2
pidfile /var/run/haproxy-private.pid

listen proxy host:8888
log global
mode http
option httplog
option dontlognull
cookie COOKIEID insert indirect nocache
balance roundrobin


La liste des serveurs dans la boucle de répartition de charge est représenté par la liste de ligne commençant par server.

server inst11 web1:7011 check inter 2000 rise 2 fall 5
server inst21 web2:7021 check inter 2000 rise 2 fall 5
server inst31 web3:7031 check inter 2000 rise 2 fall 5
server inst41 web4:7041 check inter 2000 rise 2 fall 5
[...]
server inst18 web1:7018 check inter 2000 rise 2 fall 5
server inst28 web2:7028 check inter 2000 rise 2 fall 5
server inst38 web3:7038 check inter 2000 rise 2 fall 5
server inst48 web4:7048 check inter 2000 rise 2 fall 5

Indiquons également l'emplacement de notre page de test ainsi que nos paramètres de répartition de charge :

monitor-uri /haproxy/HaproxyTestServlet
retries 3
redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000


Enfin, dans le cas où vous voudriez désactiver le Keepalive HTTP, rajoutez les lignes suivantes :

reqidel ^Connection: # disable keep-alive
reqadd Connection:\ close
rspidel ^Connection:
rspadd Connection:\ close


Il ne nous reste plus qu'à lancer le démon haproxy à l'aide de la commande suivante :
haproxy -D -f haproxy.cfg


Conclusion


Dans cet article, nous n'avons vu qu'une toute petite partie des capacités de ce répartiteur de charge. Son auteur (Willy Tarreau) a également inclu des fonctionnalités très intéressantes comme :
- la gestion d'IPV6 (v1.2)
- la gestion des cookies applicatifs pour garder le contexte d'un client vers un seul serveur (v1.2)
- la reconfiguration à chaud (v1.2)
- la répartition de charge avec poids (v1.2)
- la page de diagnostique (v1.2)
- la gestion des contenus statiques/dynamiques (v1.3)

Pour en savoir plus : http://haproxy.1wt.eu/#desc

mercredi 23 juin 2010

Supervision de WebLogic à l'aide de nagios

Afin de surveiller la consommation mémoire de nos instances WebLogic, j'ai commencé à écrire un plugin se basant sur le protocole SNMP.

Pour ceux qui ne le saurait pas, WebLogic est un serveur d'application Java propriétaire.

Ce besoin de surveillance vient du fait que les serveurs les hébergeants (des Sun V890 avec 40 Go de mémoire) sont sur le point d'arriver à saturation mémoire. Pour cette raison, nous avons donc essayé de savoir quelles instances consommées et à quelle hauteur.

Il faut dire que nous avons constaté que la mémoire allouée aux JVMs étaient le double de la mémoire physique ! Pour info, la machine héberge une centaine d'instance WebLogic en version 8.1 et Java 1.4. Comme vous vous en doutez, nous avons prévu de faire le ménage dans nos instances. Mais pour ça, il nous faudrait un petit état des lieux, d'où l'écriture d'un script.

Passons maintenant à l'écriture du script.

Premier point, il faut se débrouiller pour activer l'agent SNMP au niveau du domaine WebLogic. Nous pourrons ainsi interroger le serveur sans avoir à faire de couteuses interrogations à base de programme en java. Nous allons ensuite nous baser sur l'outil snmpwalk.

Pour mémoire, une page de référence de la MIB de ce serveur d'application est disponible à l'adresse suivante : http://download.oracle.com/docs/cd/E13222_01/wls/docs90/snmp/index.html

Commençons tout d'abord par interroger l'agent SNMP WebLogic en ligne de commande :
drayan@robinet:~$ snmpwalk -m ./BEA-WEBLOGIC-MIB.mib -c public -v 1 192.168.0.10:7061 .1.3.6.1.4.1.140.625.180
La branche .1.3.6.1.4.1.140.625.180 correspond aux informations sur les threads Java actifs. Nous pouvons également interroger la branche .1.3.6.1.4.1.140.625.340.1 pour récupérer la quantité de mémoire consommée (jvmRuntimeHeapSizeCurrent). A noter que pour connaître les instances présentes dans le domaine WebLogic, je fais appel à la branche 1.3.6.1.4.1.140.625.361.1.

Une fois enrobé tout ceci j'obtiens le script suivant :
#!/usr/bin/perl
use strict;
use File::Basename;
use Sys::Hostname;
use Getopt::Long;

my $verbose = 0;
my $show_help = 0;
my $show_version = 0;
my $snmp_address = 0;
my $instance = 0;
my $snmp_cmd = "snmpwalk -m ".dirname($0)."/../share/BEA-WEBLOGIC-MIB.mib";
my $show_only = ".*";

sub usage {
my ($msg) = @_;
$msg = "" if(!$msg);
my $name = basename($0);
print "Usage :
\$ $name [OPTION]

Get runtime information about WebLogic instance
-h --help display this help and exit.
--intance <SRVNAME>[,<>] name of the instances to check.
--only <REGEXP> show only instance corresponding to regexp.
--snmp <HOST[:PORT]> address (and port) of the snmp server.
-v --verbose verbose.

Examples:
\$ $name --port 10861 --instance srvback11,srvback12,srvback13,srvback14
".($msg ? "\n".$msg."\n" : "")
}

GetOptions(
"V" => \$show_version,
"version" => \$show_version,
"h" => \$show_help,
"help" => \$show_help,
"v" => \$verbose,
"verbose" => \$verbose,
"snmp=s" => \$snmp_address,
"instance=s" => \$instance,
"only=s" => \$show_only,
);

if($show_help) { usage(); exit(0); }
die("Please specify snmp address (--snmp host:port)") if(!$snmp_address);

if(!$instance) {
my(%instance_name, %instance_state) = ((), ());
my @tmp = ();
my ($i, $j) = (0, 0);
foreach(`$snmp_cmd -c public -v 1 $snmp_address 1.3.6.1.4.1.140.625.361.1 2> /dev/null`) {
if(/BEA-WEBLOGIC-MIB::serverLifeCycleRuntimeName.*STRING: "(\w+)"/) {
$instance_name{$i++} = $1;
} elsif(/BEA-WEBLOGIC-MIB::serverLifeCycleRuntimeState.*STRING: "(\w+)"/) {
my $label = $instance_name{$j++};
$instance_state{$label} = $1;
if($1 =~ /RUNNING/) {
print "$label is RUNNING\n" if($verbose);
push(@tmp, $label);
} else {
print "Ignoring $label\n" if($verbose);
}
}
}
$instance = join(",", @tmp);
}

my $perfdata = "";
foreach my $srv(sort(split(/,/, $instance))) {
next if(!($srv =~ /$show_only/));
my @result = `$snmp_cmd -c public\@$srv -v 1 $snmp_address .1.3.6.1.4.1.140.625.340.1 2> /dev/null`;
my($label, $current_max_heap, $free_heap);
foreach(@result) {
if(/BEA-WEBLOGIC-MIB::jvmRuntimeName.*STRING:\s+"(\w+)"/) { $label = $1; }
elsif(/BEA-WEBLOGIC-MIB::jvmRuntimeHeapFreeCurrent.*INTEGER:\s+(\d+)/) { $free_heap = $1; }
elsif(/BEA-WEBLOGIC-MIB::jvmRuntimeHeapSizeCurrent.*INTEGER:\s+(\d+)/) {
$current_max_heap = $1;
$current_max_heap = int($current_max_heap / 1048576 + .5);
$free_heap = int($free_heap / 1048576 + .5);
my $used = $current_max_heap - $free_heap;
$perfdata .= " $label=$used;$current_max_heap;$current_max_heap;0;$current_max_heap";
}
}
}

print "WebLogic memory OK|$perfdata\n";
On configure notre agent pour accepter un nouveau type de commande :
command[check_weblogic_memory]=/usr/lib/nagios/plugins/check_weblogic --snmp localhost:7061
On configure un nouveau service sous Nagios :
define service {
host_name machine-weblogic
use generic-service
service_description Suivi memoire WebLogic
check_command check_nrpe!check_weblogic_memory
}
Et hop ! Au bout de quelques jours, vous obtenez le résultat suivant (en combinaison avec pnp4nagios) :