Déployer un cluster Elastic et Kibana avec Vagrant

Cet article vous permettra de déployer, pour vos développements, un cluster Elastic et Kibana avec Vagrant.

Pour le déploiement, nous utiliserons Vagrant avec le provider VirtualBox.

Premiers pas

Avant de commencer le déploiement du cluster, vous aurez besoin :

Préparation du projet

Création de l'environnement local

Dans votre ordinateur, créer le dossier du projet, par exemple vagrant-elastic. Au sein de ce dossier, créer le fichier suivant :

  • Vagrantfile

Le fichier Vagrantfile contiendra l'ensemble des directives et de la configuration nécessaire au déploiement des machines virtuelles du cluster.

Nous aurons également besoin de plusieurs fichiers Bash pour l'installation d'Elastic et de Kibana. Ces fichiers seront présents dans le dossier manifests situé à la racine du projet. Je vous invite maintenant à créer le dossier.

Nous séparons volontairement les fichiers de création des machines virtuelles des fichiers d'installation car ces derniers peuvent être réutilisés dans une instance GCP, un conteneur LXC...

Définition du Vagrantfile

Définition des machines virtuelles

Tout d'abord, nous allons définir les caractéristiques des machines virtuelles de notre cluster. Pour cela nous allons ouvrir le fichier Vagrantfile.

Commençons par ajouter 2 lignes au début du fichier pour indiquer aux éditeurs de code le langage utiliser :

# -*- mode: ruby -*-
# vi: set ft=ruby :

Puis, nous allons définir un tableau qu contiendra les caractéristiques de chaque machine virtuelle :

vms = [
  {
    :type => "elastic",
    :role => "master",
    :hostname => "elastic-d11-master",
    :private_ip => "192.168.0.10",
    :ram => 2048,
    :cpu => 2
  },
  {
    :type => "elastic",
    :role => "node",
    :hostname => "elastic-d11-node-1",
    :private_ip => "192.168.0.11",
    :ram => 2048,
    :cpu => 2
  },
  {
    :type => "elastic",
    :role => "node",
    :hostname => "elastic-d11-node-2",
    :private_ip => "192.168.0.12",
    :ram => 2048,
    :cpu => 2
  },
  {
   :type => "kibana",
   :role => "kibana",
   :hostname => "elastic-d11-kibana",
   :private_ip => "192.168.0.100",
   :ram => 1024,
   :cpu => 2
  }
]

Ce tableau comporte 4 entrées, chacune de ces entrées est une machine virtuelle. Elles ont des caractéristiques différentes :

  • type : La valeur nous permettra de définir le logiciel qui sera installé (Elastic ou Kibana)
  • role : Au sein de notre cluster Elastic, nous aurons des nœuds master et des nœuds node. Les nœuds master seront accessibles depuis l'extérieur. Pour notre article, nous aurons seulement 1 nœud master.
  • hostname : Cette valeur correspondra au nom de la machine.
  • private_ip : Cette valeur correspondra à l'adresse IP privée de notre machine. Une interface réseau sera créée automatiquement pour les échanges entre nos machines virtuelles.
  • ram : Cette valeur permet de définir la quantité de mémoire vive allouée à chaque machine virtuelle.
  • cpu : Ici, nous définissons le nombre de vCPUs pour chacune de nos machines virtuelles.

Directives de déploiement du cluster

Maintenant que nous avons définis l'ensemble des machines virtuelles de notre cluster, nous allons créer les directives permettant leur déploiement. Cette opération sera réalisée dans le fichier Vagrantfile.

À la fin du fichier, ajouter le bloc de configuration ci-dessous :

Vagrant.configure("2") do |config|

end

Le chiffre 2 correspond à la version qui sera utilisée entre la section do et end. Lien vers la documentation.

Dans la section do et end, nous allons créer une boucle each :

Vagrant.configure("2") do |config|
  vms.each do |machine|

  end
end

Cette boucle va parcourir le tableau vms que nous avons créé précédemment. Chaque entrée du tableau sera nommée machine.

Dans la boucle, nous allons définir chaque machine virtuelle, pour ce faire, ajouter la section config.vm :

Vagrant.configure("2") do |config|
  vms.each do |machine|
    config.vm.define machine[:hostname] do |node|

    end
  end
end

Chaque directive qui sera positionnée dans la section config.vm permettra de modifier la configuration de la machine virtuelle. Lien vers la documentation.

En Ruby, le mot-clé machine[:hostname] permet de récupérer le nom d'hôte que nous avons défini dans le hash, dictionnaire clé unique - valeur vms, plus d'informations.

Commençons par définir la box, où l'image qui sera utilisée par nos VMs, le nom d'hôte puis l'adresse IP privée :

Vagrant.configure("2") do |config|
  vms.each do |machine|
    config.vm.define machine[:hostname] do |node|
      node.vm.box = "debian/bullseye64"
      node.vm.hostname = machine[:hostname]
      node.vm.network "private_network", ip: machine[:private_ip]
    end
  end
end

Ici, nous utiliserons l'image debian/bullseye64, soit une Debian 11. Vous pouvez sélectionner d'autres images que vous retrouverez sur le site Vagrant Boxes.

Ajoutons maintenant la directive port_forward qui nous permettra d'ouvrir un port entre la machine virtuelle et la machine hôte. Ceci étant, nous allons conditionner la directive en fonction du rôle de la VM :

Vagrant.configure("2") do |config|
  vms.each do |machine|
    config.vm.define machine[:hostname] do |node|
      node.vm.box = "debian/bullseye64"
      node.vm.hostname = machine[:hostname]
      node.vm.network "private_network", ip: machine[:private_ip]

      if machine[:type] == "elastic" && machine[:role] == "master"
        node.vm.network "forwarded_port", guest: 9200, host: 9200
      end

      if machine[:type] == "kibana"
        node.vm.network "forwarded_port", guest: 5601, host: 5601
      end
    end
  end
end

La première directive ouvrira le port 9200 sur la VM qui est du type elastic et qui a le rôle master. La deuxième directive ouvrira le port 5601 pour permettre l'accès à l'interface Kibana.

Ajoutons maintenant les spécificités du provider virtualbox pour définir le nom de la VM, la vérification des guest additions de VirtualBox, la quantité de mémoire, le nombre de vCPUs.

Les 2 paramètres --natdnshostresolver1 et --natdnsproxy1 permettent forcer l'utilisation du résolveur de noms de l'hôte.

Vagrant.configure("2") do |config|
  vms.each do |machine|
    config.vm.define machine[:hostname] do |node|
      node.vm.box = "debian/bullseye64"
      node.vm.hostname = machine[:hostname]
      node.vm.network "private_network", ip: machine[:private_ip]

      if machine[:type] == "elastic" && machine[:role] == "master"
        node.vm.network "forwarded_port", guest: 9200, host: 9200
      end

      if machine[:type] == "kibana"
        node.vm.network "forwarded_port", guest: 5601, host: 5601
      end

      node.vm.provider "virtualbox" do |v|
        v.name = machine[:hostname]
        v.check_guest_additions = false
        v.memory = machine[:ram]
        v.cpus = machine[:cpu]
        v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
        v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
      end
    end
  end
end

Pour plus d'informations sur le provider virtualbox, je vous invite à consulter la documentation.

Terminons la partie Vagrantfile en ajoutant 3 scripts Bash pour l'installation après la section node.vm.provider :

Vagrant.configure("2") do |config|
  vms.each do |machine|
    config.vm.define machine[:hostname] do |node|
      node.vm.box = "debian/bullseye64"
      node.vm.hostname = machine[:hostname]
      node.vm.network "private_network", ip: machine[:private_ip]

      if machine[:type] == "elastic" && machine[:role] == "master"
        node.vm.network "forwarded_port", guest: 9200, host: 9200
      end

      if machine[:type] == "kibana"
        node.vm.network "forwarded_port", guest: 5601, host: 5601
      end

      node.vm.provider "virtualbox" do |v|
        v.name = machine[:hostname]
        v.check_guest_additions = false
        v.memory = machine[:ram]
        v.cpus = machine[:cpu]
        v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
        v.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
      end

      node.vm.provision "shell", path: "./manifests/install-base.sh"

      if machine[:type] == "elastic"
        node.vm.provision "shell", path: "./manifests/install-elastic.sh"
      end

      if machine[:type] == "kibana"
        node.vm.provision "shell", path: "./manifests/install-kibana.sh"
      end
    end
  end
end

Un premier script permet l'installation des composants de base ainsi que JRE et JDK. Puis les 2 autres scripts installeront Elastic sur l'ensemble des machines de type elastic et Kibana sur les machines de type kibana.

Désormais, le fichier Vagrantfile est complet. La suite de l'article consistera à créer les 3 scripts Bash permettant l'installation d'Elastic et de Kibana.

Définition des scripts d'installation

Dans chaque script, nous avons conditionné une partie des installations, dans le but de ne pas télécharger et installer des composants déjà présents. Il y a un donc un test permettant de vérifier la présence du composant, par exemple, pour tester si JRE est installé, nous exécutons la commande :

java --version > /dev/null 2>&1

Cette commande renvoie la sortie dans /dev/null mais l'ajout de 2>&1 permet de rediriger la sortie STDERR (2) vers STDOUT (1). Pour plus d'informations, vous pouvez consulter cet article.

Après avoir exécuté la commande java --version > /dev/null 2>&1. Nous assignons la sortie $? à une variable :

JRE_IS_INSTALLED=$?

Puis nous réalisons un test :

if [[ $JRE_IS_INSTALLED -ne 0 ]]; then
  echo ">>> Installing JRE"
else
  echo ">>> JRE is already installed!"
fi

Installation des composants de base

Nous allons désormais travailler dans le dossier manifests que nous avons créés au début de l'article. L'ensemble des paquets installés par ce premier script sont nécessaires pour notre cluster, que ce soit pour Elastic ou Kibana. Le fichier est nommé install-base.sh et son contenu est le suivant :

#!/usr/bin/bash

sudo apt-get update -qq && sudo apt-get -y -qq upgrade

# Install Components
sudo apt-get -y -qq install gnupg2 curl apt-transport-https > /dev/null

# Install JRE
# Test if JRE is installed
java --version > /dev/null 2>&1
JRE_IS_INSTALLED=$?

if [[ $JRE_IS_INSTALLED -ne 0 ]]; then
  echo ">>> Installing JRE"
  sudo apt-get -qq -y install default-jre > /dev/null
else
  echo ">>> JRE is already installed!"
fi

# Install JDK
# Test if JDK is installed
javac --version > /dev/null 2>&1
JDK_IS_INSTALLED=$?

if [[ JDK_IS_INSTALLED -ne 0 ]]; then
  echo ">>> Installing JDK"
  sudo apt-get -qq -y install default-jdk > /dev/null
else
  echo ">>> JDK is already installed!"
fi

Tout d'abord, nous devons procéder à une mise à jour des fichiers dans les dépôts APT. Puis à la mise à jour des paquets déjà installés avant d'installer 3 paquets gnupg2, curl et apt-transport-https.

Ensuite, nous testons la présence de JRE, l'environnement d'exécution Java et de la présence du JDK (Kit de développement Java). Dans le cas où les paquets ne sont pas présents, ils seront installés.

Installation d'Elastic

Pour l'installation d'Elastic, nous aurons un script plus complet découpé en 3 parties :

  • Définition des variables
  • Installation d'Elastic
  • Configuration d'Elastic sur chaque nœud

Les variables définies sont les suivantes :

HOSTNAME=$(cat /etc/hostname)
ELASTIC_FILE=elasticsearch-7.16.2-amd64.deb
CLUSTER_NAME=elastic-d11-cluster
ELASTIC_PORT=9200
ELASTIC_MASTER_HOSTNAME="elastic-d11-master"
ELASTIC_NODE1_HOSTNAME="elastic-d11-node-1"
ELASTIC_NODE2_HOSTNAME="elastic-d11-node-2"
  • HOSTNAME sera utilisé pour définir le nom du nœud dans la configuration d'Elastic
  • ELASTIC_FILE permet de spécifier la version d'Elastic à télécharger. Cette variable est également utilisée pour télécharger le fichier SHA du binaire Debian et pour le tester si les fichiers d'installation ont été téléchargés
  • CLUSTER_NAME nomme notre cluster Elastic dans la configuration de l'ensemble des nœuds
  • ELASTIC_PORT permet de spécifier le port d'écoute d'Elastic. Le port 9200 est la valeur par défaut.
  • ELASTIC_MASTER_HOSTNAME, ELASTIC_NODE1_HOSTNAME et ELASTIC_NODE2_HOSTNAME permettront de configurer la liste de tous les nœuds du cluster ainsi que la liste des nœuds master de notre cluster.

La 2ᵉ partie du script correspond à l'installation et au démarrage du service Elastic :

curl 127.0.0.1:9200 > /dev/null 2>&1
ELASTIC_STATUS=$?

if [[ $ELASTIC_STATUS -ne 0 ]]; then
  echo ">>> Check if Elasticsearch is installed"

  sudo systemctl status elasticsearch > /dev/null 2>&1
  IS_ELASTIC_EXIST=$?

  if [[ $IS_ELASTIC_EXIST -ne 0 ]]; then
    echo ">>> Installing Elasticsearch"

    if [ ! -f "$PWD/$ELASTIC_FILE" ]; then
      echo ">>> Download file"
      wget -q https://artifacts.elastic.co/downloads/elasticsearch/"$ELASTIC_FILE"
    fi

    if [ ! -f "$PWD/$ELASTIC_FILE.sha512" ]; then
      echo ">>> Download sha file"
      wget -q https://artifacts.elastic.co/downloads/elasticsearch/"$ELASTIC_FILE.sha512"
    fi

    shasum -a 512 -c "$ELASTIC_FILE.sha512" > /dev/null 2>&1
    SHA_STATUS=$?

    if [[ $SHA_STATUS -eq 0 ]]; then
      sudo dpkg -i "$ELASTIC_FILE"
      echo ">>> Autostart Elasticsearch at boot"
      sudo systemctl daemon-reload && sudo systemctl enable elasticsearch
      echo ">>> Starting Elasticsearch"
      sudo systemctl start elasticsearch
    fi

    rm ./"$ELASTIC_FILE" ./"$ELASTIC_FILE.sha512"
  else
    echo ">>> Starting Elasticsearch"
    sudo systemctl start elasticsearch
  fi
else
  echo ">>> Elasticsearch is already running"
fi

Tout commence par un curl sur le port 9200 de la machine virtuelle. Si le port ne répond pas, nous vérifions si Elastic est installé. Si une erreur est renvoyée dans la sortie, nous lançons l'installation d'Elastic. Mais avant de télécharger le fichier, nous allons vérifier sa présence. Dans le cas contraire, nous lançons le téléchargement. Nous réalisons la même opération pour le fichier SHA. Puis nous lançons la comparaison du paquet Debian téléchargé avec le checksum publiée par Elastic.

Une fois validé, nous lançons l'installation d'Elastic, puis nous configurons le démarrage automatique du service avant de lancer le service Elasticsearch.

La dernière partie du script concerne la configuration d'Elasticsearch, la modification de la configuration est réalisée par la commande sed :

if [[ $HOSTNAME -eq "elastic-d11-master" ]]; then
  echo ">>> Configuring Elasticsearch Master"
  sudo sed -i -e 's/^#network.host.*/network.host: [_local_, _eth0_, _eth1_]/g' /etc/elasticsearch/elasticsearch.yml
else
  echo ">>> Configuring Elasticsearch Nodes"
  sudo sed -i -e 's/^#network.host.*/network.host: [_local_, _eth0_]/g' /etc/elasticsearch/elasticsearch.yml
fi

echo ">>> Configuring Elasticsearch Master & Nodes"
sudo sed -i -e 's/^#node.name.*/node.name: '$HOSTNAME' /g' /etc/elasticsearch/elasticsearch.yml
sudo sed -i -e 's/^#cluster.name.*/cluster.name: '$CLUSTER_NAME'/g' /etc/elasticsearch/elasticsearch.yml
sudo sed -i -e 's/^#http.port.*/http.port: '$ELASTIC_PORT'/g' /etc/elasticsearch/elasticsearch.yml
sudo sed -i -e 's/^#discovery.seed_hosts.*/discovery.seed_hosts: ["'$ELASTIC_MASTER_HOSTNAME'", "'$ELASTIC_NODE1_HOSTNAME'", "'$ELASTIC_NODE2_HOSTNAME'"]/g' /etc/elasticsearch/elasticsearch.yml
sudo sed -i -e 's/^#cluster.initial_master_nodes.*/cluster.initial_master_nodes: ["'$ELASTIC_MASTER_HOSTNAME'"]/g' /etc/elasticsearch/elasticsearch.yml

echo ">>> Restarting Elasticsearch"
sudo systemctl restart elasticsearch

Nous commençons par configurer les interfaces réseaux qui seront utilisées par Elastic. Les interfaces réseaux ne sont pas identiques entre les différents types de nœud, car le nœud maître possède une interface réseau supplémentaire permettant de réaliser un port-forward sur la machine hôte.

Ensuite, la configuration est commune quel que soit le nœud, nous définissons le nom du nœud, le nom du cluster, le port d'écoute...

Nous terminons ensuite par le redémarrage du nœud dans le but de mettre à jour à la configuration.

Par défaut, la sécurité est désactivée, vous pouvez configurer la sécurité en vous aidant de la documentation Elastic. Cette partie ne sera pas traitée dans cet article.

Installation de Kibana

Le script d'installation de Kibana a une structure identique au script d'installation d'Elastic. Seule la configuration est différente, elle est décrite après le contenu du fichier :

#!/usr/bin/bash

KIBANA_FILE=kibana-7.16.2-amd64.deb
KIBANA_PORT=5601
KIBANA_HOST=0.0.0.0
KIBANA_HOSTNAME=elastic-d11-kibana
ELASTIC_HOST="http:\/\/192.168.0.10:9200"

curl 127.0.0.1:5601 > /dev/null 2>&1
KIBANA_STATUS=$?

if [[ $KIBANA_STATUS -ne 0 ]]; then
  echo ">>> Check if Kibana is installed"

  sudo systemctl status kibana > /dev/null 2>&1
  IS_KIBANA_EXIST=$?

  if [[ $IS_KIBANA_EXIST -ne 0 ]]; then
    echo ">>> Installing Kibana"

    if [ ! -f "$PWD/$KIBANA_FILE" ]; then
      echo ">>> Download file"
      wget -q https://artifacts.elastic.co/downloads/kibana/"$KIBANA_FILE"
    fi

    if [ ! -f "$PWD/$KIBANA_FILE.sha512" ]; then
      echo ">>> Download sha file"
      wget -q https://artifacts.elastic.co/downloads/kibana/"$KIBANA_FILE.sha512"
    fi

    shasum -a 512 -c "$KIBANA_FILE.sha512" > /dev/null 2>&1
    SHA_STATUS=$?

    if [[ $SHA_STATUS -eq 0 ]]; then
      sudo dpkg -i "$KIBANA_FILE"
      echo ">>> Autostart Kibana at boot"
      sudo systemctl daemon-reload && sudo systemctl enable kibana
      echo ">>> Starting Kibana"
      sudo systemctl start kibana
    fi

    rm ./"$KIBANA_FILE" ./"$KIBANA_FILE.sha512"
  else
    echo ">>> Starting Kibana"
    sudo systemctl start kibana
  fi
else
  echo ">>> Kibana is already running"
fi

echo ">>> Configuring Kibana"
sudo sed -i -e 's/^#server.port.*/server.port: '$KIBANA_PORT'/g' /etc/kibana/kibana.yml
sudo sed -i -e 's/^#server.host.*/server.host: "'$KIBANA_HOST'"/g' /etc/kibana/kibana.yml
sudo sed -i -e 's/^#server.name.*/server.name: "'$KIBANA_HOSTNAME'"/g' /etc/kibana/kibana.yml
sudo sed -i -e 's/^#elasticsearch.hosts.*/elasticsearch.hosts: "'$ELASTIC_HOST'"/g' /etc/kibana/kibana.yml

echo ">>> Restarting Kibana"
sudo systemctl restart kibana

La configuration de Kibana est plus simple comparée à Elastic. Nous commençons par définir le port d'écoute de l'interface graphique de Kibana, puis l'adresse IP du serveur ainsi que le nom de la machine.

Nous terminons par préciser à Kibana l'adresse IP de la machine master du cluster Elastic.

Déploiement du cluster

Maintenant que nous avons terminé la définition du fichier Vagrantfile et que nous avons scripté l'installation des paquets requis d'Elastic et de Kibana. Nous pouvons lancer le déploiement de notre cluster grâce à la commande :

vagrant up

Une fois les machines virtuelles créées, vous avez la possibilité de modifier les scripts à votre convenance et mettre à jour la configuration de vos machines virtuelles avec la commande :

vagrant up --provision

Lorsque Vagrant aura terminé de créer le cluster, vous pouvez tester en ouvrant le navigateur de votre machine hôte sur les adresses http://<votre-adresse-ip>:9200 et http://<votre-adresse-ip>:5601.

Le résultat de la première adresse sera un objet JSON indiquant le nom du cluster, de la machine... et la seconde devrait vous afficher l'interface graphique de Kibana.

Destruction du cluster

Pour détruire le cluster, il est nécessaire de stopper l'ensemble des machines avec la commande

vagrant halt

Puis la commande pour détruire les machines virtuelles est :

vagrant destroy

Cette commande vous demandera une confirmation avant de procéder à la suppression.