Hindsight est un outil de type ETL qui fonctionne en système de pipelines. Codé en C, il est modulaire et permet via des scripts Lua de traiter de la donnée pour la transformer et la sortir sous divers formats.
Hindsight est sans conteste un remplaçant de choix de l’outil Logstash codé en JRuby (Ruby sur une JVM). Il ne souffre pas des mêmes soucis de performances et l’interface de traitement en Lua permet d’être bien plus flexible que les interfaces Grok de Logstash.
L’architecture se décompose en 3 éléments:
Le transfert d’un élément de l’architecture vers le suivant s’effectue au moyen de message matchers. Lorsqu’un message sort d’un input ou d’un analyseur les analyseurs et sorties vont vérifier que le message correspond à un type d’entrée autorisée, si oui, chacun reçoit le message.
Cette fonctionnalité permet donc à partir d’une entrée ou d’un analyseur d’envoyer le message en multicast à plusieurs analyseurs et/ou plusieurs sorties.
L’input Hindsight est un plugin recevant les données depuis une source de données. Il se contente de lire les données séquentiellement et de les transférer au sein du pipeline. L’input peut avoir plusieurs formes:
Les analyseurs Hindsight sont des outils plus ou moins intelligents qui vont décoder un flux entrant suivant le message matcher et vont le transformer pour le pousser de nouveau dans le pipeline.
Les analyseurs prennent la forme de modules Lua qui vont être injectés dans le runtime.
On peut retrouver plusieurs types de décodeurs:
L’ouput Hindsight est un plugin recevant les données du pipeline correspondant au message matcher configuré. Il peut avoir plusieurs formes:
Une installation Hindsight est composée de 3 éléments:
Hindsight est packagé sur Debian stretch mais la version présente dans le repository est très ancienne. De plus il n’y a pas de package pour les extensions, Hindsight est donc assez inutile.
Je vous propose d’utiliser mon repository autogénéré par un pipeline Gitlab, vous pourrez y trouver les 3 paquets nécessaires packagés.
Ajoutez la clef du repository dans APT:
apt-key adv --fetch-keys https://nerzhul.gitlab.io/debianrepo/repo.key
et ajoutez l’entrée dans /etc/apt/sources.list.d/nerzhul_gitlab_io.list:
deb https://nerzhul.gitlab.io/debianrepo stretch main
Installez Hindsight et les extensions de la LuaSandbox (la sandbox est en dépendance):
apt-get install hindsight luasandbox-extensions
Hindsight est installé sous la forme d’une unit systemd et tournera en utilisateur non privilégié hindsight
Hindsight est conçu de telle manière que toute la configuration peut être rechargée à chaud. Le fichier de configuration global d’Hindsight est le suivant: /etc/hindsight/hindsight.cfg.
Pour charger de la configuration à chaud, il suffit de la placer dans l’un des répertoires de plugins du répertoire load du workdir de Hindsight. Dans le package fourni ci-dessus, le workdir est /var/lib/hindsight
Vous pouvez changer le répertoire de chargement dynamique en modifiant la valeur de la variable sandbox_load_path dans la configuration globale. Si vous la mettez à vide vous désactiverez le chargement à chaud des plugins.
Il peut être également intéressant d’augmenter le nombre de threads pour vos analyseurs en incrémentant la valeur de la variable analysis_threads.
Concentrons nous maintenant sur les plugins.
Dans le workdir d’Hindsight vous trouverez l’arborescence suivante:
Un input plugin se compose à minima de 2 fichiers:
Nous allons ici activer le support syslog.
Dans un premier temps il faut charger le code Lua du plugin d’entrée syslog. Celui-ci est fourni dans le package, il suffit de le copier dans le répertoire de chargement
cp /usr/lib/luasandbox/sandboxes/heka/input/udp.lua /var/lib/hindsight/load/input
Si la configuration dynamique est bien activée, vous devriez voir disparaître le fichier du répertoire et celui-ci devrait se retrouver dans le répertoire run/input
Ensuite on pousse la configuration du plugin syslog afin de charger le module. Créez le fichier input_syslog.cfg avec le contenu suivant, puis poussez le dans le répertoire load/input
filename = "udp.lua"
instruction_limit = 0
-- listen on all interfaces
address = "0.0.0.0"
-- listening port
port = 1514
-- decode the flow
decoder_module = "decoders.syslog"
-- display errors when decoder fails
send_decode_failures = true
Cette configuration définit un listener utilisant le module udp.lua précédemment poussé, le met en écoute sur le port 1514 et décode le flux au moyen du décodeur syslog.
Le module de decoding va analyser le flux en entrée pour le transformer en objet Hindsight découpé suivant le format défini dans le décodeur.
Un output plugin est composé des mêmes élements qu’un input plugin:
Nous allons ici activer une sortie Elasticsearch.
Chargeons tout d’abord le code Lua du plugin, il est présent dans les extensions LuaSandbox du repository ci-dessus:
cp /usr/lib/luasandbox/sandboxes/heka/output/elasticsearch_bulk_api.lua /var/lib/hindsight/load/output
On pousse maintenant la configuration du plugin Elasticsearch.
Créez un fichier output_elasticsearch.cfg avec le contenu ci-dessous, puis poussez le dans le répertoire load/output:
filename = "elasticsearch_bulk_api.lua"
message_matcher = 'Logger == "analysis.syslog_to_es"'
-- Limit memory to 64MB
memory_limit = 64 * 1024 * 1024
ticker_interval = 5
address = "es-cluster-http.example.org"
port = 9200
timeout = 5000
flush_count = 5000
flush_on_shutdown = false
preserve_data = not flush_on_shutdown
discard_on_error = true
max_retry = 1
encoder_module = "encoders.elasticsearch.payload"
encoders_elasticsearch_common = {
es_index_from_timestamp = true,
index = "%{Logger}-%{%Y.%m.%d}",
type_name = "%{Logger}",
}
Ce plugin va pousser dans Elasticsearch en utilisant les API HTTP en mode bulk. L’index créé aura pour nom le nom du logger suivi de la date du jour (YYYY-MM-DD) et les documents auront le type output_elasticsearch.
Autre point intéressant, le ticker_interval et le flush_count. Le premier va définir un intervalle de flush des données côté ES (ici 5 secondes). Le second définit le nombre de documents à pousser avant de flush. Ces deux variables sont à tuner en fonction de la charge sur le cluster ES que vous allouez.
Enfin le dernier point important ici, le message_matcher. Il définit pour quels critères un message multicasté dans le pipeline va être reçu par cette sortie. Ici on demande à ce que le message provienne d’un analyseur appelé syslog_to_es.
Pour finir ajoutons un analyseur. C’est le coeur de notre pipeline Hindsight.
Dans un premier temps créons le fichier de code de l’analyseur (syslog_to_es.lua)
require "cjson"
require "os"
local syslog_msg
function process_message()
syslog_msg = decode_message(read_message("raw"))
-- Transform fields in flat attributes
if syslog_msg["Fields"] ~= nil then
for _, field in pairs(syslog_msg["Fields"]) do
syslog_msg[field.name] = field.value[1]
end
end
if syslog_msg["Timestamp"] ~= nil then
-- Rewrite timestamp for logstash
syslog_msg["@timestamp"] = os.date("%Y-%m-%dT%H:%M:%S.000Z", syslog_msg["Timestamp"] / 1000000000)
-- Drop old timestamp format
syslog_msg["Timestamp"] = nil
end
if syslog_msg["Payload"] ~= nil then
-- Rewrite timestamp for logstash
syslog_msg["@message"] = syslog_msg["Payload"]
-- Drop old timestamp format
syslog_msg["Payload"] = nil
end
-- Remove Uuid and fields
syslog_msg["Uuid"] = nil
syslog_msg["Fields"] = nil
return 0
end
function timer_event()
inject_payload("json", "syslog_msg", cjson.encode(syslog_msg))
syslog_msg = nil
end
Notre analyseur utilise 2 callbacks fonctions d’Hindsight:
Poussez ensuite ce fichier dans le répertoire load/analysis afin qu’il soit chargé.
Créez enfin syslog_to_es.cfg avec le contenu suivant:
filename = "syslog_to_es.lua"
message_matcher = 'Logger == "input.syslog"'
ticker_interval = 5
On définit ici notre analyseur comme utilisant le code lua précédemment poussé, il prend en entrée les messages provenant de notre entrée syslog et pousse les messages régulièrement dans le pipeline, vers notre sortie Elasticsearch.
Pour terminer, déployez la configuration dans /var/lib/hindsight/load/analysis.
Nous avons étudié ici les bases de l’installation d’Hindsight et du fonctionnement des pipelines, comment les brancher dans un cas concret de récolte d’informations syslog pour les pousser vers Elasticsearch, en les convertissant dans un format acceptable pour le frontend Kibana.
Nous verrons dans de prochains articles comment réaliser proprement le parsing de logs au moyen d’analyseurs se branchant sur le décodeur syslog.