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

jeudi 13 février 2014

Couplage nxlog et stack ElasticSearch

Je suis actuellement en train de tester le couple Logstash/ES/Kibana. Dans ce cadre, j'ai voulu regarder différents mécanismes pour remonter des informations venant de mes différentes machines sans avoir pour autant à installer un agent Logstash partout.

J'ai donc commencé à regarder le fonctionnement de nxlog qui permet de faire ce genre de chose.

Mise en place de nxlog

Dans ce qui va suivre, on va remonter les events venant de notre nœud Windows (ici un 2003) dans le serveur Logstash. Le côté intéressant de ce produit est qu'il fonctionne aussi bien sous Windows que Linux et que ce dernier fait de la rétention de message le temps que le serveur Logstash soit de nouveau disponible.

Pour en revenir à nos moutons, ci-dessous un exemple de configuration :

define ROOT C:\Program Files\nxlog

Moduledir %ROOT%\modules
CacheDir %ROOT%\data
Pidfile %ROOT%\data\nxlog.pid
SpoolDir %ROOT%\data
LogFile %ROOT%\data\nxlog.log

<Input in>
    # Module      im_msvistalog
# For windows 2003 and earlier use the following:
    Module      im_mseventlog
</Input>

<Extension json>
    Module      xm_json
</Extension>

<Output logstash>
    Module      om_tcp
    Port        5140
    Host        IP_SERVEUR_LOGSTASH
    Exec        to_json();
</Output>

# Let's tie all pieces together with a NXlog route
<Route local_to_logstash>
    Path        in => logstash
</Route>
En substance, on charge le module im_mseventlog, on indique un point d'envoi (Output logstash) et enfin, on précise un chemin (in => logstash).

Configuration de Logstash

Nous allons maintenant configurer Logstash afin de récupérer les messages de notre ami nxlog.

Pour se faire, nous allons créer un fichier logstash.conf avec une entrée (précisant l'encodage) :

input {
  tcp {
    port => 5140
    type => "nxlog"
    codec => line {
      charset => "CP1252"
    }
  }
}

NB : il est possible d'utiliser le codec json directement dans l'input tcp. Le problème est que l'agent nxlog peut renvoyer plusieurs messages JSON d'un seul coup (notamment dans le cas d'un arrêt/relance du serveur Logstash). Dans ce cas, Logstash n'arrive pas à bien l'interpréter et ne récupère les différents champs.

On va ensuite récupérer le contenu et le traiter comme étant du json :

filter {
  if [type] == "nxlog" {
    json {
      source => "message"
    }
    mutate {
      rename => [ "Message", "message" ]
      # remove_field => [ "champ1", "champ2" ]
    }
  }
}

A noter que nxlog renvoie pas mal de champ. Si vous voulez en supprimer quelques uns, décommenter le champ remove_field ci-dessus.

Il ne nous reste plus qu'à renvoyer le contenu sur la sortie standard pour débogage :

output {
  stdout { debug => true }
}

Reste maintenant à lancer notre agent logstash :

java -jar logstash.jar --conf logstash.conf agent

Exemple de message

Une fois lancé, nous pouvons recevoir nos premiers messages :

{
              "message" => "xxx",
             "@version" => "1",
           "@timestamp" => "2014-02-13T14:53:57.156Z",
                 "type" => "nxlog",
                 "host" => "xxxx:xxx",
            "EventTime" => "2014-02-13 15:53:57",
     "EventTimeWritten" => "2014-02-13 15:53:57",
             "Hostname" => "XXX",
            "EventType" => "AUDIT_SUCCESS",
        "SeverityValue" => 2,
             "Severity" => "INFO",
           "SourceName" => "Security",
             "FileName" => "Security",
              "EventID" => 538,
       "CategoryNumber" => 2,
             "Category" => "Ouverture/Fermeture de session ",
         "RecordNumber" => 410222,
               "Domain" => "XXX",
          "AccountName" => "Administrateur",
          "AccountType" => "User",
    "EventReceivedTime" => "2014-02-13 15:53:57",
     "SourceModuleName" => "in",
     "SourceModuleType" => "im_mseventlog"
}

Il ne reste plus qu'à envoyer tout ça dans le moteur ElasticSearch et le tour est joué !

mardi 1 octobre 2013

Quelques benchs sur Logstash et ElasticSearch

Voilà, je n'ai pas pu y résister. L'autre jour, j'étais tranquillement en train de traîner dans la tribune de rédaction de linuxfr et j'ai aidé à rédiger un article sur ma nouvelle lubie : Logstash, ElasticSearch et Kibana. L'article est relativement pédagogique mais ne pousse pas la réflexion plus loin que la mise en place. Il aurait été intéressant d'ajouter quelques benchs.

Comme de mon côté, je n'avais pas vraiment eu l'occasion de trop pousser le produit non plus (juste au travers un petit POC pas super poussé en terme de volumétrie) je me suis dit qu'après tout ça serait l'occasion de le faire.

Les conditions du test

Comme vous savez sûrement, j'attache énormément d'importance à la pensée Cartésienne. Pour se faire je me dois de vous donner quelques caractéristiques sur la machine sur laquelle je travaille. En voici quelques-unes (ne pas hésiter à me signaler des manquements) :
  • Processeur : AMD Phenom(tm) II X4 965 Processor ;
  • Mémoire : 8 Go - Swap : 8 Go ;
  • Java : OpenJDK (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.13.04.2) ;
  • Taille du fichier : 167997125 (~ 160 Mo) ;
  • Nombre de ligne : 1354848.
Attention : les benchs qui vont être réalisés plus loin sont fait par un néophyte sur ces produits. Si vous pensez que j'ai pu faire des erreurs, n'hésitez pas à me les signaler !

Quelques benchs

Les premiers pas

Pour me faire une idée de la capacité de traitement de logstash, j'ai voulu démarrer par un test très simple : en entrée une socket d'écoute simple et la sortie dans un fichier sans aucun traitement dessus. Pour mémoire, voici le fichier de configuration (bench.conf) que j'ai utilisé :

# Écoute sur le port 2048 de la machine
input {
  tcp {
    port => 2048
  }
}
# Balance le JSON obtenue dans un fichier
output {
  file {
    path => "./bench.json"
  }
}

Le lancement de logstash se fait avec la commande suivante :

java -Xms256m -jar logstash-1.2.1-flatjar.jar agent \
     -f bench.conf

Il ne me reste plus qu'à injecter le contenu de mon fichier dans la socket - ce que je fais à l'aide de la commande nc (netcat) - précédé par la commande time pour avoir une idée du temps que je mets :

time cat /var/log/syslog.1 | nc localhost 2048

Grosso modo, j'arrive donc à un temps de traitement de 41 secondes (soit environ 4 Mo/s ou encore 33045 lignes/s).

Voyons maintenant quelque chose de plus proche de la réalité.

Injection dans un moteur ElasticSearch

J'ai tout d'abord utilisé le moteur ElasticSearch embarqué. La configuration était la suivante :

output {
  elasticsearch {
    embedded => true
  }
}

En lançant le bench, j'obtiens une injection en environ 144 secondes.

Injection dans un moteur ElasticSearch externe

Ici, on va simplement configurer un moteur ElasticSearch afin de le faire tourner dans un process séparé. La configuration est relativement simple puisque nous allons reprendre celle par défaut. Une fois décompresser, le lancement se fait donc avec la commande suivante :

./bin/elasticsearch -f

NB : On procédera également à la suppression des données du moteur entre deux lancements du bench afin d'éviter une influence sur le résultat.

Enfin, du côté de logstash, nous allons simplement modifié la section output de la configuration de la manière suivante :

output {
  elasticsearch {
    host=> "robert"
    cluster => "elasticsearch"
    embedded => false
  }
}

En relançant le traitement, nous obtenons 113 secondes. Bonne nouvelle, c'est mieux.

Quelques modifications de paramètres sur Logstash

Essayons de buffuriser un peu nos écritures afin de voir l'impact que ça pourrait avoir. Le premier sera le paramètre flush_size (nombre maximum d'événement à flusher, par défaut 100). Voici l'impact sur le temps de traitement en fonction de sa valeur :
  • 200 : 114 secondes ;
  • 1000 : 110 secondes ;
  • 10000 : 105 secondes ;
  • 100000 : 101 secondes ;
  • 1000000 : 105 secondes.
Le gain est relatif et on peut voir que nous arrivons à un plafond au delà duquel, nous n'avons plus du tout d'impact sur les performances.

On va maintenant se pencher sur le paramètre idle_flush_time (temps maximum d'attente entre deux flushs, par défaut à 1 seconde). Ici encore, voyons l'impact sur les performances (avec un flush_size à 100000) :
  • 2 s : 104 secondes ;
  • 4 s : 107 secondes.
Bref, ça s'améliore pas. Donc de ce côté ci, on va arrêter les recherches !

Quelques modifications sur ElasticSearch

On va tout d'abord essayer d'espacer les flushs sur disque en bougeant le paramètre index.translog.flush_threshold_period (temps entre deux commits, par défaut 30m). En le passant à 5 s, nous baissons le temps de traitement à 99 secondes.

J'ai ensuite essayé divers modifications en suivant le document suivant : https://gist.github.com/duydo/2427158. Malheureusement, je n'ai jamais trouvé d'autre levier suffisamment efficace pour améliorer les choses. Il semblerait que ma machine manque de puissance pour pouvoir aller plus loin.

Pour conclure

Le but de ce bench était de se faire une idée de la capacité de traitement de notre ami Logstash et de son copain ElasticSearch sur une machine seule. Pour résumer, il faut tout de même se dire qu'on est en mesure de traiter environ 16 000 lignes / secondes ou 1,6 Mo / secondes (ce qui n'est pas mal du tout !). Même si j'ai déjà vu des choses particulièrement atroces qui devait pas être loin de cette volumétrie, je pense que ces produits ont du potentiel.

Autre point, nous sommes sur une infrastructure non distribué avec l'indexeur et le shipper sur la même machine. Je pense qu'il aurait été intéressant de distribuer la charge sur plusieurs machines (notamment pour ElasticSearch). Je vais essayer de voir si je ne pourrais pas récupérer quelques machines pour aller un peu plus loin dans mes tests. Mais en l'état, je pense que je vais arrêter mes tests pour l'instant.