Ou on arrive enfin elasticsearch

Après la génération des access log au format JSON avec apache, puis la génération des acces log au format JSON avec varnish nous avons continué avec un premier routage syslog logstash pour enregistre provisoirement dans redis

Acte 4 scène 1: Pourquoi Redis en temporisation

Nous allons maintenant extraire de redis qui est là que pour offrir un buffer avant l’enregistrement dans eslasticsearch.

Durant les pics d’audience propre a une journée normale sur le site, le volume de log est trop important pour être directement injecté dans eslasticsearch. Durant quelques minutes logstash patine, le load cpu du serveur monte, les disques grattes comme des fous. Les logs étant envoyé en udp sont perdu car aucun processus logstash n’est disponible.
j’avais des graphique en peigne, avec pic et blanc d’info alterné toute les 3 a 4 minutes.

L’enregistrement dans redis est rapide car sans traitement et sans écriture disque, le tout sur un serveur aux ressources sous utilisé.

Le logstash chargé de l’intégration (que nous allons voir dans un autre billet) intègre à son rythme, sans rien perdre, sans engorgement. Au final il peux y avoir un retard à l’affichage car les données les plus fraîches présentable date de 10 à 15 min mais sans perte. Au moment où l’audience deviens plus calme (après 21h par exemple) le retard est rattrapé. Le monitoring en temps réel en soufre mais pas le data mining.

Acte 4 scène 2: Sortir de Redis avec logstash.

Depuis le serveur hébergeant eslasticsearch nous allons configurer un logstash pour l’alimenter en allant piocher dans le redis, filtrer les données des access log puis verser tout cela dans eslasticsearch

input

Aller piocher dans redis est tout aussi simple qui écrire dedans. Je lance deux thread logstatsh qui traiterons les logs par parquet de 1000 enregistrement. Comme depuis leur génération les access logs sont format JSON on le précise pour logstash.

input {
 redis {
 host => "Redis.tld"
 port => 6379
 data_type => "list"
 batch_count => 1000
 threads => 2
 key => "logstash"
 codec => json
 }
}

filter

Avant de tout verser dans eslasticsearch un peu de filtrage s’impose. Il faut entre autre éviter le caractère « – » que eslasticsearch n’apprécie pas. Ce que je fais dès la première ligne sur le champ clientip

   if [clientip] == "-" { mutate { remove => "[clientip]" } }

J’utilise également la Geo IP (la DB est sur maxmind. Il y’a aussi un paquet debian geoip-database) en créant le champs GeoIP en l’alimentant grâce au champ clientip mais seulement si il ne contient pas une IP privé rfc1918

if [clientip] and [clientip] !~ "(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)|(^169\.254\.)" {
        geoip {
                add_tag =>; [ "GeoIP" ]
                database => "/opt/logstash/GeoLiteCity.dat"
                source => "clientip"
        }
    }

Pour le reste je ne fais que le nettoyage du caractère « – » que eslasticsearch n’apprécie pas.

Voici la section filter en entier :

filter {
   if [clientip] == "-" { mutate { remove => "[clientip]" } }
   if [clientip] and [clientip] !~ "(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)|(^169\.254\.)" {
        geoip {
                add_tag => [ "GeoIP" ]
                database =>; "/opt/logstash/GeoLiteCity.dat"
                source => "clientip"
        }
    }
    if [useragent] != "-" and [useragent] != "" {
      useragent {
        add_tag => [ "UA" ]
        source => "useragent"
      }
    }
    if [bytes] == 0 { mutate { remove => "[bytes]" } }
    if [bytes] == "-" { mutate { remove => "[bytes]" } }
    if [geoip][city_name]      == "" { mutate { remove =>; "[geoip][city_name]" } }
    if [geoip][continent_code] == "" { mutate { remove => "[geoip][continent_code]" } }
    if [geoip][country_code2]  == "" { mutate { remove => "[geoip][country_code2]" } }
    if [geoip][country_code3]  == "" { mutate { remove => "[geoip][country_code3]" } }
    if [geoip][country_name]   == "" { mutate { remove => "[geoip][country_name]" } }
    if [geoip][latitude]       == "" { mutate { remove => "[geoip][latitude]" } }
    if [geoip][longitude]      == "" { mutate { remove => "[geoip][longitude]" } }
    if [geoip][postal_code]    == "" { mutate { remove => "[geoip][postal_code]" } }
    if [geoip][region_name]    == "" { mutate { remove => "[geoip][region_name]" } }
    if [geoip][time_zone]      == "" { mutate { remove => "[geoip][time_zone]" } }
    if [urlquery]              == "" { mutate { remove => "urlquery" } }
    if [method]    =~ "(HEAD|OPTIONS)" { mutate { remove => "method" } }
    if [useragent] == "-"              { mutate { remove => "useragent" } }
    if [referer]   == "-"              { mutate { remove => "referer" } }
    if "UA" in [tags] {
        if [device] == "Other" { mutate { remove => "device" } }
        if [name]   == "Other" { mutate { remove => "name" } }
        if [os]     == "Other" { mutate { remove => "os" } }
    }
}

Mapping

Oui il n’y a pas de spécification de mapping des données dans elasticsearch. Seule mapping dynamique par défaut de elasticsearch est utilisé.  Cela débouche sur une utilisation peux sembler magique Une des qualités à Elasticsearch, comparé par exemple à un serveur  tel que Solr, est son mapping dynamique. Dans ce cas, au vu de la simplicité du JSON, j’aurais pu en faire un.  Mais utilisé le mapping dynamique à apporté réel gain de temps (surtout celui d’apprendre à maîtriser cette notion). Cependant, des modifications ceci à des limites et nous verrons plus tard qu’une utilisation poussée de Kibana deviens difficile sans un mapping des données sur mesure.

Un simple curl sur votre serveur vous donnera la mapping dynamique par défaut utilisé.

curl 'http://VOTRE-SERVEUR-ELASTICSEARCH:9200/_template'

4 réflexions sur “ Ou on arrive enfin elasticsearch ”

  1. Très intéressant 🙂
    Petite précision : sauf erreur de ma part, solr est à présent capable de créer des champs dynamiques 🙂

  2. Possible en version 4.10 … pas eu le temps de tester l’utilisation de solr, je me contente de le mettre à disposition du dev 🙂

  3. Oualala Nicolas j’ai commencé un nouveau travail et j’ai un peu lâché l’affaire. Tu souhaiterais voir quoi dans cette suite ?

Laisser un commentaire