Laisser un commentaire

Analyser les logs avec Graylog

Graylog est un système qui permet de centraliser, monitorer et d’analyser les logs. Ainsi, les logs de vos différents serveurs et applications se retrouvent tous consultables au même endroit. Cela permet bien évidemment une grosse économie de temps, mais également de facilement pouvoir comparer et corréler les logs de vos différents services entre eux. Enfin, il est possible de configurer des analyses automatiques qui seront ainsi capables de vous avertir en temps réel si l’un des paramètres que vous spécifiez est atteint. Convaincu ? Alors allons-y !

Grand concurrent de la célèbre stack Elasticsearch, Graylog est conçu dès le départ à 100% pour la gestion des logs. Il est donc dédié à 100% à cet usage et cela se ressent au niveau de son interface. Cependant, les deux compétiteurs ont aussi des points communs : ils sont tous deux open source, mais surtout, ils utilisent tous les deux le même moteur d’indexation, à savoir, Elasticsearch. Vous disposez ainsi de tableaux de bords regroupant vos métriques critiques tout en ayant la possibilité de rechercher efficacement parmi des térabytes de données (admettez que c’est mieux qu’un grep !).

L’installation

Graylog peut être installé de multiples manières : de la compilation des sources à l’automatisation complète via des conteneurs Docker, tout y est. Il suffit de jeter un œil au Git de Graylog pour s’en convaincre. Il y a même un package omnibus qui se charge d’installer toute la stack pour vous !

Nous n’allons pas ici compiler l’ensemble mais nous utiliserons les paquets de notre distribution (Debian flavoured dans le cas présent). Cela nous permettra d’avoir un bon niveau de compréhension de l’architecture d’ensemble et d’être à l’aise avec sa configuration.

Graylog possède deux dépendances, MongoDB et Elasticsearch. Nous allons donc commencer par installer ces derniers. Pour l’exemple, nous serons sur une machine Ubuntu en mode standalone, c’est à dire que tout se trouve sur un seul et même serveur.

MongoDB

La plupart des paquets des distributions sont clairement à la traîne pour Mongo. Il faut donc ajouter le repo contenant les versions les plus récentes. Selon votre distribution et afin de rester le plus à jour possible, le mieux est d’aller voir directement les instructions sur le site officiel. Néanmoins, dans le cas de Debian 8 et d’Ubuntu 16.04, et à l’heure où j’écris ces lignes, voici les commandes à exécuter :

apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927

# pour debian
echo "deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.2 main" | tee /etc/apt/sources.list.d/mongodb-org-3.2.list

# pour ubuntu
echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.2.list

apt-get update
apt-get install -y mongodb-org

Pour Ubuntu 16.04, il faudra également créer manuellement le script systemd. Dans /lib/systemd/system/mongod.service :

[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target
Documentation=https://docs.mongodb.org/manual

[Service]
User=mongodb
Group=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf

[Install]
WantedBy=multi-user.target

Il n’y a rien de particulier à configurer concernant Mongo. Puisqu’on est en local, la connexion peut-être laissée sans mot de passe.

Elasticsearch

Graylog 2.x nécessite Elasticsearch 2.3.x est compatible avec les dernières versions d’Elasticsearch, lequel nécessite Java 8. Installons donc open-jdk dans la mesure où il est déjà libre et déjà présent dans nos dépôts (contrairement au JDK d’Oracle).

apt-get install openjdk-8-jre-headless

Il ne nous reste plus qu’à installer Elastic. Là encore, plusieurs solutions s’offrent à nous, je vous invite évidemment à consulter la page d’installation d’ES afin d’obtenir les instructions les plus à jour et adaptées à votre distribution. Me concernant, je trouve que le plus pratique est de recourir aux dépôts afin de pouvoir également profiter des mises à jour.

# ajout de le clef & repo update et install
wget -qO - https://packages.elastic.co/GPG-KEY-elasticsearch | apt-key add -
echo "deb http://packages.elastic.co/elasticsearch/2.x/debian stable main" | tee -a /etc/apt/sources.list.d/elasticsearch-2.x.list
apt-get update
apt-get install -y elasticsearch

Ensuite, pour qu’ES démarre automatiquement au boot de la machine, il faut configurer les script d’initialisation. Systemd tend à être le nouveau standard (Debian 8, Ubuntu 16.04 et dérivés) mais sysV est encore utilisé sur de très nombreux systèmes.

# s'assurer que ce petit monde se lance au boot
# systemd
systemctl daemon-reload
systemctl enable elasticsearch.service

# SysV init
update-rc.d elasticsearch defaults

On ira ici modifier le fichier de configuration se trouvant à /etc/elasticsearch/elasticsearch.yml :

# modifications directes via sed
sed -i -r 's/# cluster.name: [a-zA-Z0-9-]+/ cluster.name: graylog/' /etc/elasticsearch/elasticsearch.yml
sed -i -r 's/# node.name: [a-zA-Z0-9]+-1/ node.name: graylog-server/' /etc/elasticsearch/elasticsearch.yml
sed -i 's/# node.max_local_storage_nodes: 1/ node.max_local_storage_nodes: 1/' /etc/elasticsearch/elasticsearch.yml

En résumé, on définit le nom du cluster ainsi que celui de la node (dans notre cas nous avons un cluster d’une seule node) et on précise à ES que l’on veut avoir une seule node maximum. Il n’y a, dans le cas présent, pas d’intérêt à avoir plus d’une node sur le même serveur.

Graylog serveur

Maintenant que nos dépendances sont satisfaites, entrons dans le vif du sujet, j’ai cité, Graylog ! Une fois n’est pas coutume, vous pouvez consulter les instructions sur la page de Graylog afin d’avoir les détails pour votre propre distribution. Pour nos amis Debian et Ubuntu, voici les commandes :

apt-get install apt-transport-https
wget https://packages.graylog2.org/repo/packages/graylog-2.0-repository_latest.deb
dpkg -i dpkg -i graylog-2.0-repository_latest.deb

apt-get update
apt-get install -y graylog-server

Évidemment, nous voulons que Graylog démarre au boot. Pour les OS utilisant systemd, il faudra donc faire systemctl enable graylog-server, pour SysV c’est update-rc.d graylog-server defaults 95 10.

Ici aussi, nous allons devoir modifier un peu la configuration par défaut dans /etc/graylog/server/server.conf :

# utilisation d'une chaine pour saler les mots de passe des utilisateurs enregistrés
# on peut la générer avec la commande : openssl rand -base64 32
sed -i 's/password_secret =/password_secret = MOT_DE_PASSE/'

# le login de l'utilisateur root
sed -i 's/#root_username = admin/#root_username = VOTRE_LOGIN/'

# Il s'agit ici du mot de passe du compte root hashé avec sha2
# on peut le générer avec la commande :
# echo -n "MOT_DE_PASSE_ROOT" | shasum -a 256 | awk '{print $1}'
sed -i 's/root_password_sha2 =/root_password_sha2 = MOT_DE_PASSE_HASH/' /etc/graylog/server/server.conf

# email du compte root
sed -i 's/#root_email = ""/root_email = "ROOT_EMAIL"/' /etc/graylog/server/server.conf

# configuration de la timezone
# CET pour UTC+1 soit Paris et autres capitales Européennes
sed -i 's/#root_timezone = UTC/root_timezone = CET/' /etc/graylog/server/server.conf

# nombre de shards ES, comme nous n'avons qu'une node, ce n'est pas utile d'avoir plusieurs shards
sed -i 's/elasticsearch_shards = 4/elasticsearch_shards = 1/' /etc/graylog/server/server.conf

# ici, idem, on sait immédiatement où trouver les nodes de notre cluster (un seul en localhost !)
sed -i 's/#elasticsearch_discovery_zen_ping_multicast_enabled = false/elasticsearch_discovery_zen_ping_multicast_enabled = false/' /etc/graylog/server/server.conf
sed -i 's/#elasticsearch_discovery_zen_ping_unicast_hosts = 127.0.0.1:9300', replace: 'elasticsearch_discovery_zen_ping_unicast_hosts = 127.0.0.1:9300/' /etc/graylog/server/server.conf

Vous êtes bien entendu encouragés à regarder plus en détails l’ensemble du fichier de configuration, il est dans l’ensemble assez explicite. Par ailleurs, nous ne l’avons pas mentionné ici mais l’API peut-être passée en TLS aussi.

Graylog supporte le TLS, aussi bien pour l’API que pour le serveur web. Pour ma part, comme à mon habitude, je préfère laisser comme cela, placer un reverse proxy devant et effectuer le chiffrement au niveau du reverse. Sachez néanmoins que, comme Graylog expose son API, votre navigateur effectue directement les requêtes auprès de l’API pour récupérer les données. Ainsi, je créé deux sous-domaines, un pour l’interface web, par exemple graylog.buzut.fr et un pour l’API api.graylog.buzut.fr.

Pour que cette configuration soit fonctionnelle avec un reverse, il faut aussi configurer le web_endpoint_uri. Dans le cas d’un sous domaine avec “api”, nous aurions donc :

web_endpoint_uri = https://api.graylog.buzut.fr:443/

Ensuite, n’oubliez pas de créer les deux vhosts au niveau de votre proxy et le tour est joué !

Faites péter les logs !

Une fois que tout est en place, il ne reste plus qu’à rapatrier les logs de vos différents serveurs et applications vers Graylog. Pour cela, il faut avant tout ouvrir un input afin que votre serveur écoute sur un port donné.

Vous vous rendez dans system / inputs > inputs, vous choisissez un type d’input, ici le GELF TCP, vous pouvez définir vos ports, choisir d’utiliser le TLS etc.

interface de graylog, choisir un input
Définir un input dans Graylog
De nombreuses solutions s'offrent à vous pour envoyer vos logs vers Graylog. Vous pouvez configurer rsyslog ou syslog-ng pour qu'il envoie directement votre syslog sur le bon serveur, vous pouvez utiliser logstash ou fluentd afin de lire des fichiers de logs et les envoyer au bon endroit. Il existe aussi de nombreuses librairies qui permettent d'envoyer vos logs directement depuis le cœur de vos applications. Que ce soit en php, en nodejs, en ruby etc.

Je fais ici une petite digression, mais sachez que si vous voulez rapidement tester la connectivité, vous pouvez vous aidez de CURL ou de Netcat pour des connexions en HTTP et TCP ou UDP respectivement.

# connexion HTTP, petit coup de curl
curl -XPOST http://graylog.server.com:12202/gelf -p0 -d '{"version": "1.1", "host":"api.buzut.fr", "short_message":"Hello there", "full_message":"Backtrace here\n\nmore stuff"}'

# et en UDP (notez l'option -u)
echo -e '{"version": "1.1", "host":"api.buzut.fr", "short_message":"Hello there", "full_message":"Backtrace here\n\nmore stuff"}' | nc -u graylog.server.com 12202

# enfin en tcp
echo -e '{"version": "1.1", "host":"api.buzut.fr", "short_message":"Hello there", "full_message":"Backtrace here\n\nmore stuff"}\0' | nc graylog.server.com 12202

Vous noterez qu’en TCP, on ajoute un \0 – le null character – afin de délimiter les packets TCP les uns des autres, dans un flux qui est continu. En effet, ce problème n’existe pas en UDP qui possède des paquets de taille fixe.

Gelf s’intègre à de nombreuses solutions, par exemple, si vous utilisez Winston comme outil de logging, plusieurs solutions s’offrent à vous pour (dont une dont je suis l’auteur) avoir un transport spécial Graylog directement dans Winston vous pouvez configurer Gelf comme un transport et automatiquement envoyer tous les logs de votre application vers Graylog.

Dans l’absolu, tous les logs n’ont pas besoin d’être centralisés. Pour ma part, je récupère les logs d’erreurs du serveur web s’il y en a un, le syslog et enfin, bien évidemment, les logs d’erreurs de mes applications (géré directement dans le code de l’app). Pour ce qui est du syslog, j’ai écrit un petit collecteur en nodejs afin de lire le fichier de logs (syslog, Apache2 ou Nginx error.log) et d’envoyer au fur et à mesure tout ça à Graylog au format GELF via TCP ou HTTP.

Le module s’installe très simplement avec npm install -g log2gelf et vous n’avez ensuite qu’à le lancer au démarrage du serveur. Par exemple, pour envoyer le syslog, je créé un petit script qui s’assure que log2gelf est bien lancé et je l’appelle régulièrement via une crontab :

#!/bin/bash

if ! ps aux | grep -v grep | grep "/usr/bin/nodejs /usr/bin/log2gelf"
then
/usr/bin/log2gelf bztBlog graylog.buzut.fr 12201 tcp true syslog /var/log/syslog 2>&1 | logger &
fi

Ce script, que je place dans /usr/local/sbin/startLog2Gelf.sh est appelé toutes les minutes via la crontab de root :

* * * * * /usr/local/sbin/startLog2Gelf.sh

Graylog au quotidien

À ce stade, nous avons configuré Graylog sur notre serveur de monitoring et nous avons un ou plusieurs serveurs qui envoient des données vers Graylog. Parfait.

Pour l’instant, la seule manière d’exploiter ces données est de se rendre dans l’interface de Graylog et d’y effectuer des recherches. Nous en conviendrons aisément, c’est déjà pas mal. Mais au travers des dashboard et des streams, Graylog peut offrir bien plus !

Les dashboards

Le terme est tellement courant que vous devez déjà avoir une idée de l’utilité de la chose. Dans Graylog, un dashboard permet de sauvegarder des requêtes et d’afficher, sur le dashboard donc, les résultats de ces requêtes actualisés en temps réel. On pourra donc avoir un dashboard présentant le compte des messages ou alertes d’un certain programme.

dashboard de graylog
Compte et répartition des messages de MySQLd et du kernel
Plusieurs types de widgets sont disponibles dans les dashboard :compteur de résultat et histogramme, tels que présentés dans la capture ci-dessus, il est possible de calculer également des moyennes (temps moyen de réponse d'un serveur web par ex. ou encore d'afficher des diagrammes circulaires.

Il suffit de se rendre dans l’onglet dashboard et de cliquer sur “Create new” pour créer un nouveau dashboard, vide. Ensuite, lorsque vous effectuer des recherches, il vous faudra exporter les widgets vers le dashboard de votre choix. C’est tout !

Pour en savoir plus sur les dashboard et les différents widgets disponibles, je vous invite à lire la doc dédiée.

Les streams

Moins évidents que les dashboards, les streams permettent d’analyser et de traiter les logs en temps réel. On compte deux principaux avantages aux streams. Le premier est une fonction de routing. Chaque stream présente en effet des conditions, ainsi, chaque log sera routé – ou pas – dans un stream.

Si par exemple je défini qu’un stream ne contient que les messages de la crontab, lorsque je me rends sur le stream en question, je n’ai que les messages en question. La grosse différence en terme de performance avec une recherche à posteriori sur des conditions comparables est que les streams sont traités en temps réel. Ainsi, sur un nombre de logs important, une recherche nécessitera une puissance de calcul importante et nécessitera potentiellement du temps, tandis que pour le stream, tout est déjà pre-calculé. On peut ainsi effectuer d’autres recherches, de manière plus flexible et plus rapide, dans le stream.

Le second avantage est qu’avec les streams, il est possible de déclencher des alertes automatiquement (email, slack, etc.). Vous pouvez déclencher une alerte au dessus d’un certains nombre de messages par unité de temps, où lors de la présence d’un certain message dans les logs, ou lorsque la moyenne d’une valeur numérique dépasse un certain seuil. Il existe deux types d’alertes, les alertes email, et le http callbacks qui permettent donc d’interagir avec toute api exposée en http (d’où slack).

En dernier lieu – on en aura peut-être moins souvent l’usage – les streams permettent de rediriger du contenu via GELF ou stdout. De même que pour les dashboards, je vous laisse le soin de visiter la doc associée aux streams pour en savoir plus.

Conclusion

Graylog est un système global et, s’il nécessite un minimum de configuration, il saura ensuite se montrer d’une puissance redoutable, tant dans la prévention grâce à l’alerting, que dans la recherche détaillée après avarie ou simplement pour diagnostiquer un problème. Puisqu’il utilise ElasticSearch comme moteur de stockage et d’indexation, la recherche précise parmi des TB de logs ne pose aucun problème.

Avez-vous pu installer le tout facilement ? La prise en main a-t-elle été intuitive ou est-ce bataillez-vous avec l’interface ? N’hésitez pas à faire part de vos commentaires.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```