Ce TP sert d’introduction au Big Data. Pour appréhender ce concept, l’application M2M sera prise pour exemple.
Durant le TP, l’application simulera des flux de données. L’objectif est de mettre en place une stack ELK qui permettra d’observer les tendances et caractéristiques de l’application. Cette stack sera démarrée sur Kubernetes à côté de l’application M2M.
Avant de commencer le TP, il faut bien se mettre dans le namespace M2M:
$ kubectl config set-context --current --namespace=m2m
Structure ELK
Voici l’architecture d’une stack ELK. Elle est composée de:
- Elasticsearch qui est le moteur d’indexation et de recherche de donnée.
- Logstash qui est un agrégateur de log permettant de formater des données pour les envoyer à Elasticsearch
- Kibana qui est l’interface de visualisation des données
Cette stack sera lancé comme pour le TP précédent sur Kubernetes. Elle sera configurée comme pourrait l’être une application en production.
La stack ELK étant déportée de l’application, elle sera déployée à partir d’un dossier différent. L’application ayant un dossier de déploiement k8s-app, on choisira un dossier k8s-elk pour ELK.
Elasticsearch
Avant toute chose, il faut démarrer un cluster Elasticsearch qui est le cœur de cette stack. Elasticsearch peut être démarré en standalone. Mais vu que nous utiliserons Kubernetes, nous allons démarrer un cluster.
Pour ce faire, Elasticsearch a besoin d’un ou plusieurs nœuds maître, et de connaitre leurs IP. Ce mode de construction de cluster ressemble à celui utilisé par Hazelcast vu dans le TP précédent.
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: elastic
name: elastic
namespace: m2m
spec:
serviceName: "elastic"
replicas: 1
selector:
matchLabels:
app: elastic
template:
metadata:
labels:
app: elastic
spec:
initContainers:
- name: init-sysctl
image: busybox
imagePullPolicy: IfNotPresent
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
containers:
- name: elasticsearch
securityContext:
privileged: true
capabilities:
add:
- IPC_LOCK
- SYS_RESOURCE
image: elasticsearch:7.9.2
resources:
requests:
memory: 2Gi
cpu: 1
limits:
memory: 3Gi
cpu: 1
env:
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: cluster.name
value: m2m
- name: network.host
value: "0.0.0.0"
- name: discovery.seed_hosts
value: "elastic-0.elastic"
# value: "elastic-0.elastic,elastic-1.elastic"
- name: cluster.initial_master_nodes
value: "elastic-0"
# value: "elastic-0,elastic-1"
- name: bootstrap.memory_lock
value: "false"
- name: ES_JAVA_OPTS
value: -Xms2g -Xmx2g
ports:
- containerPort: 9200
- containerPort: 9300
Le cluster Elasticsearch sera accessible via elastic-0.elastic, elastic-1.elastic… suivant le nombre d’instances qui sont démarrées.
Créer un fichier de déploiement elasticsearch.yml dans le dossier k8s-elk. Ce déploiement devra contenir le cluster StatefulSet, ainsi que le Service rest (port 9200) et cluster (port 9300).
Pour connaitre l’état du cluster:
$ kubectl exec -ti elastic-0 -- curl -XGET 'http://localhost:9200/_cluster/health?pretty=true'
De manière générale, Elasticsearch est prévu pour fonctionner en mode rest. Tous les outils qui s’y connectent exécute des commandes, soit pour pousser des données, soit pour en récupérer.
Cette API est documentée. Par exemple la commande utilisée pour obtenir le status du cluster.
Logstash application M2M
La configuration du service Kubernetes Logstash est déjà présent dans le dossier k8s-elk: logstash.yml. Dans cette configuration, 2 Logstash ou plus peuvent être lancés. Ils sont indépendants les uns des autres, à l’inverse d’Elasticsearch. On peut donc en lancer autant que nécessaire suivant la charge. Pour le besoin du TP, 1 sera largement suffisant.
Néanmoins, Logstash va avoir besoin d’un fichier de configuration pour savoir comment il devra ajouter les données à Elasticsearch.
Comme présenté sur le schéma de la structure ELK, c’est le front et le backend qui enverront leurs métriques au format JSON aux Logstash. Ces métriques sont envoyées grâce à log4j.
Le front n’enverra que des données techniques. C’est-à-dire le détail des sessions client, le nombre d’octets échangé… Dans le fichier de configuration log4j.xml de ce composant, vous devez voir 2 sections pour les sorties vers Logstash:
- FRONT_REQ qui envoie le détail de chaque requête envoyées par les embarqués.
- FRONT_SESS qui donne les informations sur le déroulement d’une session avec un embarqué.
Pour le Backend, il enverra des données techniques en plus de données applicatives. Il enverra donc comme le front le détail sur les sessions, message avec celui-ci. Dans le fichier de configuration log4j.xml de ce composant, vous devez voir 3 sections pour les sorties vers Logstash:
- BACK_REQ qui envoie le détail de chaque requête envoyées par les fronts.
- BACK_SESS qui donne les informations sur le déroulement d’une session avec un front.
- APP qui donne des informations sur l’application.
Vérifier que ces sections sont bien présentes et non commenté. Aussi veiller à ce que log4j envois les métriques vers logstash:9500.
Logstash devra donc être à l’écoute d’un port en entrée et accepter du JSON en entrée
input {
tcp {
port => 9500
codec => json
}
}
filter {
date {
match => [ "timeMillis", "UNIX_MS" ]
}
}
filter {
json {
source => "message"
}
}
Pour l’output de logstash, il faut conditionner la sortie pour séparer les différentes métriques. Pour cela, il faut utiliser une condition sur le loggerName. Les cinq sorties auront comme cela 5 noms d’index différents:
- req-front-*
- sess-front-*
- req-back-*
- sess-back-*
- app-*
output {
stdout {
codec => rubydebug
}
if [loggerName]=="FRONT-REQ" {
elasticsearch {
hosts => [ "elastic:9200" ]
index => "req-front-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
}
else if [loggerName]=="FRONT-SESS" {
elasticsearch {
hosts => [ "elastic:9200" ]
index => "sess-front-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
}
else if [loggerName]=="BACK-REQ" {
elasticsearch {
hosts => [ "elastic:9200" ]
index => "req-back-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
}
else if [loggerName]=="BACK-SESS" {
elasticsearch {
hosts => [ "elastic:9200" ]
index => "sess-back-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
} else if [loggerName]=="APP" {
elasticsearch {
hosts => [ "elastic:9200" ]
index => "app-%{+YYYY.MM.dd}"
document_type => "log4j_type"
}
}
}
Lorsque vous avez créé le fichier de configuration logs.conf, il faut le prendre en compte.
Dans docker-compose, il aurait fallu mapper le fichier de configuration comme volume: -v logs.conf:/usr/share/logstash/config/logstash.yml
Avec Kubernetes, il faut le charger en tant que configmap dans Kubernetes.
$ kubectl create configmap logstash-config --namespace=m2m --from-file=logstash/logs.conf
La configuration logstash-config se retrouve dans le fichier Kubernetes de logstash k8s-elk/logstash.yml
Lancer l’application et la stack ELK. Vérifier que tout démarre correctement. Les services ne doivent pas planter ni redémarrer. Si c’est le cas, vérifier les logs.
Pour lancer la stack ELK:
$ kubectl create -f k8s-elk
Pour vérifier que Logstash a poussé des données dans Elasticsearch, les index doivent être créés:
$ kubectl exec -ti elastic-0 -- curl -XGET 'http://localhost:9200/_all/_mapping'
Kibana
Le service Kibana est déjà présent dans k8s-elk, il doit donc déjà être lancé. Pour obtenir son interface:
- Avec Minikube:
$ minikube service -n m2m kibana
- Avec Kubernetes:
$ open "http://localhost:$(kubectl get services | grep kibana | awk '{print $5}' | awk -F':' '{print $2}' | sed 's#/TCP##')"
Il met du temps à démarrer. Il faut être patient. La plupart du temps, il indique Kibana server is not ready yet depuis son interface. Si le message persiste, le problème vient peut-être du cluster Elasticsearch.
Lorsque la stack ELK est démarrée, vous pouvez démarrer l’application:
$ kubectl create -f k8s-app
Paramétrage des index
La première chose à faire lorsque Kibana est lancé sur un nouveau cluster, c’est de créer les index. Les index permettront à Kibana de savoir comment indexer les données avec les différents champs qu’elles contiennent.
Lors de la configuration de Logstash, 5 index ont été configurés. Parmi ces 5 index, certains ont le même format:
- sess-front-* et sess-back-*
- req-front-* et req-back-*
Pour ces index, nous allons les considérer comme communs, et nous les différencierons grâce à l’origine (front ou backend). Donc nous allons créer 3 index:
- sess-* pour indexer les tickets de sessions du front et du Backend.
- req-* afin d’indexer les requêtes reçus par le front et le Backend.
- app-* pour indexer les données de l’application fourni par le Backend.
Pour les ajouter, il faut aller dans le menu Index patterns dans Kibana. Ce menu est disponible dans Management > Stack Management > Index Patterns dans le menu déroulant de gauche.
Si les index patterns ne sont pas visibles, c’est qu’ils ne sont pas contenus par Elasticsearch. Dans ce cas, il faut vérifier que l’application envoie bien des métriques.
Lorsque les patterns sont ajoutés, il faut spécifier le Time field. C’est sur ce champ que Kibana se basera pour l’indice de temps. Le champ qui indique le timestamp dans les messages que l’application envoie est @timestamp
Discover
Quand tout est mis en place, les métriques devraient être visibles dans Discover. Ce mode permet d’observer toutes les métriques dans une forme brute.
En sélectionnent l’index req-* ajouter un filtre pour n’afficher que les requestType: /m2m/happybeer/kegload
Discover est pratique, mais lorsque beaucoup de grosses données sont présentes, il est impossible de trouver quelque chose sans utiliser des filtres très spécifiques.
Dashboard
La vraie force d’une stack ELK est de pouvoir créer des graphiques sur toutes les données qui sont présentes. L’objectif du TP est de créer 2 dashboards:
- Les données techniques pour l’exploitation de la plateforme, convenant aux personnes du run.
- Les données applicatives pour le business de la plateforme, convenant aux clients ou à l’équipe commerciale.
Données techniques
La première métrique à ajouter est le nombre de messages traiter par l’application.
Pour ce faire, il faut utiliser le pattern req-*. Dans le menu Kibana > Visualize. Créer une nouvelle visualisation.
Beaucoup de types de visualisations sont disponibles. Pour le moment, la plus simple Metric fera l’affaire. Par défaut, lorsque la métrique est créée, elle nous donne le nombre de requête qui a été reçu par le front et le backend.
Ce que l’on veut c’est le nombre de requête reçu par le front. Donc il faut ajouter un filtre.
Ajouter un filtre sur le loggerName = FRONT_REQ.
Pour connaitre les valeurs à mettre pour le filtre, le plus simple est d’aller dans l’onglet Discover et de regarder ce que l’on veut.
Maintenant, il faudrait afficher le nombre de message reçu par le front et le backend pour comparer la valeur. Pour ce faire, il faut faire un split sur la donnée.
Lors de la création de la visualisation, il y a un menu Buckets.
Ajouter un Split Group, avec une agrégation Terms sur le champ loggerName.
Lorsque le split est fait, 2 métriques s’affiche. Une pour le front, et une pour le backend. Ce n’est pas très visuel. Donc il faudrait plutôt utiliser une autre représentation. Par exemple un Pie, Horizontal Bar, ou Vertical Bar.
Refaite une visualisation, mais avec un autre type de métriques plus adapté
Une fois que vous avez fait vos visualisations, vous pouvez créer un dashboard les regroupant. Les dashboards peuvent être créés à partir du menu Kibana > Dashboard.
Il y a beaucoup de métriques, de paramètres disponibles… Le plus simple est d’essayer de faire vous-mêmes vos métriques. La plupart du temps, une première ébauche est faite, et c’est grâce aux retours utilisateurs que les dashboard s’enrichisse.
Ajouter des métriques techniques (req-* et sess-*). Les métriques techniques intéressantes seraient le volume de données échangé, les durées de sessions, temps de réponses…
Données applicatives
Ajouter des métriques applicatives (app-*). Les métriques applicatives pourraient être la consommation moyenne par tireuses, la répartition des bières consommées…
Autres services
Dans sa version payante, la stack ELK propose beaucoup de services intéressants. Pour aller plus loin dans l’analyse de données, cela peut être une alternative viable à d’autres solutions.