Développez votre propre Beat

Beats est une plateforme permettant de mettre en place des outils de transport léger pour les données open source. Elle envoie ainsi à Elasticsearch toutes sortes de données qui seront analysées ultérieurement. Notre service comprend Packetbeat pour surveiller le trafic réseau échangé entre vos serveurs, Filebeat pour obtenir les logs de vos serveurs, et le tout nouveau Metricbeat qui récupère à intervalles réguliers des indicateurs provenant de systèmes externes. Si vous avez besoin de collecter d'autres données personnalisées, vous pouvez facilement développer votre propre Beat dans l'environnement libbeat. Nous comptons déjà plus de 25 Beats créées par la communauté.

Nous vous fournissons le pack Générateur de Beat (Beat Generator) qui vous aide à créer votre propre Beat. Dans cet article, vous verrez comment créer votre propre Beat au moyen de ce générateur. Le Beat que nous allons créer aujourd'hui pour nous entraîner s'appelle lsbeat. lsbeat indexe des informations de fichiers et de répertoires, comme la commande Unix ls. Cet article est fait pour Unix, donc si vous êtes sur Windows ou tout autre système d'exploitation (OS), suivez les instructions qui correspondent à votre OS.

Étape 1 : Installation de l'environnement Golang


Les Beats sont écrits en Golang. Pour créer et développer un Beat, il faut que Golang soit installé sur votre machine. Suivez ce guide pour installer Golang. Actuellement, Beats nécessite au moins Golang 1.6. Assurez-vous d'avoir configuré votre variable $GOPATH.

Voyons ensemble le code que nous allons utiliser pour Lsbeat. Il s'agit d'un simple programme Golang qui reçoit un répertoire en argument de ligne de commande et qui liste l'ensemble des fichiers et sous-répertoires contenus dans le répertoire.

package main
import (
    "fmt"
    "io/ioutil"
    "os"
)
func main() {
    //apply run path "." without argument.
    if len(os.Args) == 1 {
        listDir(".")
    } else {
        listDir(os.Args[1])
    }
}
func listDir(dirFile string) {
    files, _ := ioutil.ReadDir(dirFile)
    for _, f := range files {
        t := f.ModTime()
        fmt.Println(f.Name(), dirFile+"/"+f.Name(), f.IsDir(), t, f.Size())
        if f.IsDir() {
            listDir(dirFile + "/" + f.Name())
        }
    }
}

Nous réutiliserons le code de la fonction listDir.

Étape 2 : Génération


Afin de générer notre propre Beat, nous utilisons le Beat Generator. Vous devez d'abord installer cookiecutter : consulter le guide d'installation ici. Une fois cookiecutter installé, nous devons nommer le Beat. Le nom doit être constitué d'un seul mot entièrement en minuscules. Dans cet exemple, nous utilisons lsbeat.

Afin de créer le squelette du Beat, vous devez vous procurer le pack générateur de Beats, disponible sur le répertoire de Beats. Une fois Golang installé, vous pouvez télécharger le Beat Generator à l'aide de la commande go get. Une fois la commande exécutée, tous les fichiers sources seront téléchargés dans $GOPATH/src.

$ go get github.com/elastic/beats

Pour travailler sur une branche stable, récupérez cette branche spécifique.

$ cd $GOPATH/src/github.com/elastic/beats
$ git checkout 5.1

Maintenant, créez votre propre répertoire sous GOPATH, rendez-vous y puis exécutez cookiecutter avec le chemin d'accès du Beat Generator.

$ cd $GOPATH/src/github.com/{user}
$ cookiecutter $GOPATH/src/github.com/elastic/beats/generate/beat

Cookiecutter vous posera plusieurs questions. Pour project_name, entrez lsbeat ; pour github_name, votre nom d'utilisateur github. Les deux questions suivantes concernant beat et beat_path devraient déjà être remplies correctement. Pour la dernière, vous pouvez entrer votre prénom et votre nom.

project_name [Examplebeat]: lsbeat
github_name [your-github-name]: {username}
beat [lsbeat]:
beat_path [github.com/{github id}]:
full_name [Firstname Lastname]: {Full Name}

Désormais, un nouveau répertoire lsbeat devrait apparaître à l'intérieur de notre dossier et contenir plusieurs fichiers. Allons dans ce répertoire et listons les fichiers créés de façon automatique.

$ cd lsbeat
$ tree
.
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── beater
│   └── lsbeat.go
├── config
│   ├── config.go
│   └── config_test.go
├── dev-tools
│   └── packer
│       ├── Makefile
│       ├── beats
│       │   └── lsbeat.yml
│       └── version.yml
├── docs
│   └── index.asciidoc
├── etc
│   ├── beat.yml
│   └── fields.yml
├── glide.yaml
├── lsbeat.template.json
├── main.go
├── main_test.go
└── tests
    └── system
        ├── config
        │   └── lsbeat.yml.j2
        ├── lsbeat.py
        ├── requirements.txt
        └── test_base.py

Nous disposons à présent d'un modèle de base du Beat mais devons encore en récupérer les dépendances et configurer le répertoire Git.

Vous devez d'abord récupérer les dépendances, constituées seulement de libbeat dans notre cas, puis créer les fichiers de configuration et de modèle de base. Nous examinerons plus en détail les fichiers de configuration et de modèle plus tard.

$ make setup

Une fois en possession de votre propre Beat, commencez à le partager avec la communauté en le transférant sur un dépôt GitHub.

Screen Shot 2016-07-13 at 10.53.58 AM.png

Pour envoyer lsbeat sur le dépôt GitHub, exécutez les commandes suivantes :

$ git remote add origin git@github.com:{username}/lsbeat.git
$ git push -u origin master

Nous possédons désormais un Beat complet et avons envoyé sa première version sur GitHub. Développons et exécutons notre Beat puis explorons le code plus en détail.

Étape 3 : Configuration


L'exécution des commandes ci-dessus crée les fichiers lsbeat.yml et lsbeat.template.json automatiquement. Ceux-ci contiennent déjà toutes les configurations de base.

lsbeat.yml:

lsbeat:
  # Defines how often an event is sent to the output
  period: 1s

period est un paramètre de durée inclut par le générateur dans chaque Beat. Il définit le fait que Lsbeat exécute le processus toutes les secondes. Modifions ce paramètre de durée (period) de 1 à 10 secondes et rajoutons un nouveau paramètre de chemin (path) qui représente le chemin du répertoire parent que le programme scannera. Nous pouvons ajouter ces paramètres dans beat.yml situé dans le répertoire etc/. 

lsbeat:
  # Defines how often an event is sent to the output
  period: 10s
  path: "."

Après avoir ajouté ces nouveaux paramètres, nous exécutons la commande make update pour appliquer les modifications faites au fichier de configuration lsbeat.yml. Nous pouvons voir que les nouveaux paramètres que nous avons définis dans etc/beat.yml sont maintenant disponibles dans lsbeat.yml.

$ make update
$ cat lsbeat.yml
################### Lsbeat Configuration Example #########################
############################# Lsbeat ######################################
lsbeat:
  # Defines how often an event is sent to the output
  period: 10s
  path: "."
###############################################################################

Après avoir mis à jour les fichiers de configuration, vous devez éditer config/config.go, de manière à pouvoir ajouter le paramètre path.

package config
import "time"
type Config struct {
    Period time.Duration `config:"period"`
    Path   string        `config:"path"`
}
var DefaultConfig = Config{
    Period: 10 * time.Second,
    Path:   ".",
}

Utilisons les options de configuration par défaut suivantes : 10 secondes pour la durée et répertoire courant (.) pour le répertoire par défaut.

Étape 4 : Ajout de code


Chaque Beat a besoin d'implémenter l'interface Beater, en définissant les fonctions Run() et Stop(). Un guide plus détaillé à propos de l'interface Beater est disponible ici.

Pour cela, il vous faut simplement définir une structure nommée Lsbeat qui définit l'objet Lsbeat chargé d'implémenter l'interface Beater. Puis ajoutons lastIndexTime que nous allons utiliser pour enregistrer les dernières données d'horodatage.

type Lsbeat struct {
    done   chan struct{}
    config config.Config
    client publisher.Client
    lastIndexTime time.Time
}

De plus, chaque Beat a besoin d'implémenter la fonction New(), qui reçoit les données de configuration du Beat et renvoie l'objet Beat au format Lsbeat.

func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
    config := config.DefaultConfig
    if err := cfg.Unpack(&config); err != nil {
        return nil, fmt.Errorf("Error reading config file: %v", err)
    }
    ls := &Lsbeat{
        done:   make(chan struct{}),
        config: config,
    }
    return ls, nil
}

Dans le cas de Lsbeat, nous souhaitons étendre la fonction Run() par défaut afin d'exporter des informations à propos des fichiers et sous-répertoires présents dans un répertoire..

Avant de modifier la fonction Run(), ajoutons d'abord la fonction listDir() au bas du fichier lsbeat.go, qui collecte les informations sur les fichiers et les répertoires. Il génère des événements dont :

  • "@timestamp": common.Time(time.Now())
  • "type": beatname
  • "modtime": common.Time(t)
  • "filename": f.Name()
  • "path": dirFile + "/" + f.Name()
  • "directory": f.IsDir()
  • "filesize": f.Size()

Il indexera tous les noms des fichiers et répertoires la première fois, puis lors des routines suivantes, vérifiera si le fichier ou le répertoire a été créé ou modifié afin d'indexer seulement les nouveaux fichiers et répertoires. Les données d'horodatage de la dernière routine seront enregistrées dans la variable lasIndexTime.

func (bt *Lsbeat) listDir(dirFile string, beatname string) {
    files, _ := ioutil.ReadDir(dirFile)
    for _, f := range files {
        t := f.ModTime()
        path := filepath.Join(dirFile, f.Name())
        if t.After(bt.lastIndexTime) {
            event := common.MapStr{
                "@timestamp": common.Time(time.Now()),
                "type":       beatname,
                "modtime":    common.Time(t),
                "filename":   f.Name(),
                "path":       path,
                "directory":  f.IsDir(),
                "filesize":   f.Size(),
            }
            bt.client.PublishEvent(event)
        }
        if f.IsDir() {
            bt.listDir(path, beatname)
        }
    }
}

Et pensez à ajouter le pack io/ioutil aux bibliothèques d'importation.

import (
    "fmt"
    "io/ioutil"
    "time"
)

Maintenant, occupons-nous de la fonction Run() qui appelle la fonction listDir() et enregistre les données d'horodatage dans la variable lasIndexTime .

func (bt *Lsbeat) Run(b *beat.Beat) error {
    logp.Info("lsbeat is running! Hit CTRL-C to stop it.")
    bt.client = b.Publisher.Connect()
    ticker := time.NewTicker(bt.config.Period)
    for {
        now := time.Now()
        bt.listDir(bt.config.Path, b.Name) // call listDir
        bt.lastIndexTime = now             // mark Timestamp
        logp.Info("Event sent")
        select {
        case <-bt.done:
            return nil
        case <-ticker.C:
        }
    }
}

La fonction Stop() est censée interrompre la boucle d'exécution et il en est de même avec celle générée :

func (bt *Lsbeat) Stop() {
    bt.client.Close()
    close(bt.done)
}

Nous avons presque fini avec le codage. Il nous faut ajouter de nouveaux champs dans le mapping. Ajoutez les informations des champs dans le fichier etc/fields.yml.

- key: lsbeat
  title: LS Beat
  description: 
  fields:
    - name: counter
      type: integer
      required: true
      description: >
        PLEASE UPDATE DOCUMENTATION
    #new fiels added lsbeat
    - name: modtime
      type: date
    - name: filename
      type: text
    - name: path
    - name: directory
      type: boolean
    - name: filesize
      type: long

Puis appliquez les nouvelles mises à jour.

$ make update

filename sera analysé avec le tokenizer nGram. Ajoutons un analyseur personnalisé au fichier lsbeat.template.json, en allant dans les paramètres (« settings »).

{
  "mappings": {
        ...
  },
  "order": 0,
  "settings": {
    "index.refresh_interval": "5s",
    "analysis": {
      "analyzer": {
        "ls_ngram_analyzer": {
          "tokenizer": "ls_ngram_tokenizer"
        }
      },
      "tokenizer": {
        "ls_ngram_tokenizer": {
          "type": "ngram",
          "min_gram": "2",
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
  "template": "lsbeat-*"
}

Étape 5 : Compilation et exécution


Nous pouvons maintenant passer à la compilation et à l'exécution. Exécutez simplement la commande make pour compiler le code et créer le fichier binaire exécutable lsbeat (lsbeat.exe sous Windows).

$ make

Modifiez le fichier lsbeat.yml pour définir le répertoire racine et ainsi lister les fichiers. Dans notre cas, nous définirons $GOPATH pour /Users/ec2-user/go. Assurez-vous de bien entrer le chemin d'accès complet du répertoire.

lsbeat:
  # Defines how often an event is sent to the output
  period: 10s
  path: "/Users/ec2-user/go"

Et assurez-vous également qu'Elasticsearch et Kibana soient lancés sur votre machine. Exécutons Lsbeat et voyons ce qu'il se passe.

$ ./lsbeat

Vous pouvez utiliser l'api _cat pour vérifier si l'index a bien été créé et si les données sont correctement indexées.

cat.png


Nous pouvons voir l'index lsbeat-2016.06.03 ainsi que le nombre de documents. Interrogeons Lsbeat à l'aide du champ filename, qui a été préalablement analysé avec le filtre nGram, en utilisant le mot clé lsbe.

query.png

Eureka ! Félicitations, vous venez de développer votre propre Beat.