Sonde réseaux en Go
Utilisation d'une base de donnée temporelle pour enregistrer des pings en Go.
Cet article vous présente le développement d'une sonde réseaux (ping vers des hosts) et la sauvegarde des résultats dans une base de donnée temporelle : Influx DB.
InfluxDB est le I dans l'offre TICK de InfluxData. Le T est l'outil d'acquisition des données, le I est pour le stockage, le C pour la restitution graphique (que nous utiliserons également) et le K pour la gestion des alertes.
1. Préparation de la base de donnée
1.1 Installation
Nous procédons à l'installation via Docker par le script ci-dessous:
#!/bin/sh
# Create a Network if not already done, by
# docker network create influxdb
# The first time, generate the default config file by running
# docker run --rm influxdb influxd config > conf/influxdb.conf
docker run -d \
-p 8086:8086 \
--net=influxdb \
-v $PWD/data:/var/lib/influxdb \
-v $PWD/conf/influxdb.conf:/etc/influxdb/influxdb.conf:ro \
--name influxdb \
influxdb -config /etc/influxdb/influxdb.conf
Démarrez le script (après un chmod u+x) et verifiez que le script a bien tourné via $ docker logs -f influxdb
et vous devriez avoir
8888888 .d888 888 8888888b. 888888b.
888 d88P" 888 888 "Y88b 888 "88b
888 888 888 888 888 888 .88P
888 88888b. 888888 888 888 888 888 888 888 888 8888888K.
888 888 "88b 888 888 888 888 Y8bd8P' 888 888 888 "Y88b
888 888 888 888 888 888 888 X88K 888 888 888 888
888 888 888 888 888 Y88b 888 .d8""8b. 888 .d88P 888 d88P
8888888 888 888 888 888 "Y88888 888 888 8888888P" 8888888P"
[I] 2017-12-19T12:15:54Z InfluxDB starting, version 1.4.2, branch 1.4, commit 6d2685d1738277a1c2672fc58df7994627769be6
(...)
[I] 2017-12-19T12:15:54Z Listening for signals
Si vous souhaitez garder la base de donnée après un restart, ajoutez dans le script de démarrage --restart=unless-stopped \
.
1.2 Configuration
Nous créons maintenant un utilisateur et une base pour recuillir nos données.
On se connecte à la base de donnée en mode CLI :
$ docker exec -it influxdb bash
root@725a5f3e929c:/# influx
Connected to http://localhost:8086 version 1.4.2
InfluxDB shell version: 1.4.2
> show databases
name: databases
name
----
_internal
Il n'y a que la base de donnée interne.
Tapez alors
> CREATE DATABASE sonde
> CREATE USER usersonde WITH PASSWORD 'supermotdepasse'
> GRANT ALL ON sonde to usersonde
Placez vous alors dans la base créée et gardez une console ouverte que nous utiliserons pour vérifier les résultats.
> use sonde
Using database sonde
> show measurements
>
qui est vide pour l'instant.
Nous ajountons également une règle sur le temps de conservation des données à 30j. Les données plus vieilles seront automatiquement supprimées.
> create retention policy quatre_semaines on sonde duration 4w replication 1 default
> show retention policies
name duration shardGroupDuration replicaN default
---- -------- ------------------ -------- -------
autogen 0s 168h0m0s 1 false
quatre_semaines 672h0m0s 24h0m0s 1 true
2. Développement du client InfluxDB en Golang
Il faut évidemment que votre environnement Go soit opérationnel.
2.1 Connexion à la base de donnée
Nous créons un exemple de client pour tester la connection à la base de donnée. Dans un fichier sonde.go
package main
import (
"fmt"
"log"
"time"
"github.com/influxdata/influxdb/client/v2"
)
const (
database = "sonde"
username = "usersonde"
password = "supermotdepasse"
)
type pingResult struct {
fromLabel string
toIP string
toLabel string
value float64
}
func main() {
// Test values
pr := pingResult{"localhost", "8.8.8.8", "DNS Google", 1.25}
// Create a new database connection
c := influxDBClient()
// prepare the batch point
bp, err := client.NewBatchPoints(client.BatchPointsConfig{
Database: database,
Precision: "s",
})
if err != nil {
log.Fatalln(err)
}
tags := map[string]string{
"fromLabel": pr.fromLabel,
"toIP": pr.toIP,
"toLabel": pr.toLabel,
}
fields := map[string]interface{}{"ping": pr.value}
pt, err := client.NewPoint("ping", tags, fields, time.Now())
bp.AddPoint(pt)
if err := c.Write(bp); err != nil {
log.Fatalln(err)
}
fmt.Println("Done")
}
func influxDBClient() client.Client {
c, err := client.NewHTTPClient(client.HTTPConfig{
Addr: "http://localhost:8086",
Username: username,
Password: password,
})
if err != nil {
log.Fatalln(err)
}
return c
}
Testons ensuite le bon fonctionnenment en executant le code via $ go run sonde.go
. Il doit simplement afficher "Done".
Dans la console de la base de donnée, nous vérifions que les données sont bien présentes:
> show measurements
name: measurements
name
----
ping
> select * from ping
name: ping
time fromLabel ping toIP toLabel
---- --------- ---- ---- -------
1513687798000000000 localhost 1.25 8.8.8.8 DNS Google
Si vous ne savez pas faire la translation par coeur des dates et temps depuis le 1er Janvier 1970, vous pouvez relancer la console en ajoutant -precision rfc3339
root@725a5f3e929c:/# influx -precision rfc3339
Connected to http://localhost:8086 version 1.4.2
InfluxDB shell version: 1.4.2
> use sonde
Using database sonde
> select * from ping
name: ping
time fromLabel ping toIP toLabel
---- --------- ---- ---- -------
2017-12-19T12:49:58Z localhost 1.25 8.8.8.8 DNS Google
2.2 Fonction ping
Nous allons d'abord déplacer le code de la connexion dans un fichier à part pour plus de visibilité.
Copiez le fichier sonde en tant que pingdb.go par exemple et remplacez la fonction main()
par
// Save a pingResult struct into InfluxDB
func Save(pr pingResult) {
// Create a new database connection
c := influxDBClient()
...
et nous créons également un fichier ping.go.
Nous allons utiliser la librairie fastping que l'on installe via $ go get github.com/tatsushid/go-fastping
Et dans le fichier ping.go, nous créons une fonction Ping qui prend une ipv4 en argument et qui retourne le temps de l'aller/retour (RTT):
package main
import (
"fmt"
"log"
"net"
"time"
"github.com/tatsushid/go-fastping"
)
// Ping send a ping to the ip address
func Ping(ipAddToPing string) time.Duration {
var res *time.Duration
p := fastping.NewPinger()
p.Network("ip")
ra, err := net.ResolveIPAddr("ip4:icmp", ipAddToPing)
if err != nil {
log.Fatalln(err)
}
p.AddIPAddr(ra)
p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
// fmt.Printf("IP Addr: %s receive, RTT: %v\n", addr.String(), rtt)
res = &rtt
}
p.OnIdle = func() {
// fmt.Println("finish")
}
err = p.Run()
if err != nil {
fmt.Println(err)
}
return *res
}
et voilà vos pings sauvegardés dans une base temporelle.
Resources
- Photo de Brooke Campbell