Difference between revisions of "Mtr exporter"
Jump to navigation
Jump to search
(3 intermediate revisions by the same user not shown) | |||
Line 127: | Line 127: | ||
- target_label: __address__ | - target_label: __address__ | ||
replacement: 127.0.0.1:9115 # MTR prober address | replacement: 127.0.0.1:9115 # MTR prober address | ||
+ | |||
+ | ``` | ||
+ | |||
+ | |||
+ | # More | ||
+ | |||
+ | ``` | ||
+ | # Stage 1: Build the Go application | ||
+ | FROM golang:1.18 as builder | ||
+ | |||
+ | # Set the working directory inside the container | ||
+ | WORKDIR /app | ||
+ | |||
+ | # Copy go.mod and go.sum files | ||
+ | COPY go.mod go.sum ./ | ||
+ | |||
+ | # Download all dependencies | ||
+ | RUN go mod download | ||
+ | |||
+ | # Copy the source code | ||
+ | COPY . . | ||
+ | |||
+ | # Build the Go application | ||
+ | RUN go build -o mtr_probe . | ||
+ | |||
+ | # Stage 2: Build the final image | ||
+ | FROM debian:bullseye-slim | ||
+ | |||
+ | # Install MTR and necessary utilities | ||
+ | RUN apt-get update && apt-get install -y mtr-tiny wget ca-certificates | ||
+ | |||
+ | # Install Blackbox Exporter | ||
+ | RUN wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.23.0/blackbox_exporter-0.23.0.linux-amd64.tar.gz \ | ||
+ | && tar -xzf blackbox_exporter-0.23.0.linux-amd64.tar.gz \ | ||
+ | && mv blackbox_exporter-0.23.0.linux-amd64/blackbox_exporter /usr/local/bin/ \ | ||
+ | && rm -rf blackbox_exporter-0.23.0.linux-amd64* | ||
+ | |||
+ | # Copy the Go application binary | ||
+ | COPY --from=builder /app/mtr_probe /usr/local/bin/mtr_probe | ||
+ | |||
+ | # Create a blackbox exporter config | ||
+ | COPY blackbox.yml /etc/blackbox_exporter/config.yml | ||
+ | |||
+ | # Expose ports for both services | ||
+ | EXPOSE 9115 9116 | ||
+ | |||
+ | # Start both the Go application and Blackbox Exporter | ||
+ | CMD ["sh", "-c", "mtr_probe & blackbox_exporter --config.file=/etc/blackbox_exporter/config.yml"] | ||
+ | |||
+ | ``` | ||
+ | |||
+ | |||
+ | blacbox.yaml | ||
+ | ``` | ||
+ | modules: | ||
+ | http_2xx: | ||
+ | prober: http | ||
+ | timeout: 5s | ||
+ | http: | ||
+ | valid_http_versions: ["HTTP/1.1", "HTTP/2"] | ||
+ | valid_status_codes: [] # Defaults to 2xx | ||
+ | method: GET | ||
+ | mtr: | ||
+ | prober: mtr | ||
+ | timeout: 30s | ||
+ | mtr: | ||
+ | targets: | ||
+ | - 8.8.8.8 # Example target | ||
+ | |||
+ | ``` | ||
+ | |||
+ | ``` | ||
+ | docker run -d -p 9115:9115 -p 9116:9116 mtr-blackbox-exporter | ||
+ | |||
+ | ``` | ||
+ | |||
+ | # more2 | ||
+ | |||
+ | ``` | ||
+ | package main | ||
+ | |||
+ | import ( | ||
+ | "encoding/json" | ||
+ | "log" | ||
+ | "net/http" | ||
+ | "os/exec" | ||
+ | "strconv" | ||
+ | "strings" | ||
+ | |||
+ | "github.com/gorilla/mux" | ||
+ | "github.com/prometheus/client_golang/prometheus" | ||
+ | "github.com/prometheus/client_golang/prometheus/promhttp" | ||
+ | ) | ||
+ | |||
+ | // Define the MTRResult struct to match MTR JSON output | ||
+ | type MTRResult struct { | ||
+ | Report struct { | ||
+ | Hubs []struct { | ||
+ | Count int `json:"count"` | ||
+ | Loss float64 `json:"Loss%"` | ||
+ | Snt int `json:"Snt"` | ||
+ | Last float64 `json:"Last"` | ||
+ | Avg float64 `json:"Avg"` | ||
+ | Best float64 `json:"Best"` | ||
+ | Wrst float64 `json:"Wrst"` | ||
+ | StDev float64 `json:"StDev"` | ||
+ | Host string `json:"Host"` | ||
+ | } `json:"hubs"` | ||
+ | } `json:"report"` | ||
+ | } | ||
+ | |||
+ | var ( | ||
+ | // Define Prometheus metrics | ||
+ | mtrLoss = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
+ | Name: "mtr_loss_percentage", | ||
+ | Help: "Packet loss percentage reported by MTR", | ||
+ | }, []string{"host", "target"}) | ||
+ | |||
+ | mtrAvg = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
+ | Name: "mtr_avg_latency", | ||
+ | Help: "Average latency reported by MTR", | ||
+ | }, []string{"host", "target"}) | ||
+ | |||
+ | mtrBest = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
+ | Name: "mtr_best_latency", | ||
+ | Help: "Best latency reported by MTR", | ||
+ | }, []string{"host", "target"}) | ||
+ | |||
+ | mtrWrst = prometheus.NewGaugeVec(prometheus.GaugeOpts{ | ||
+ | Name: "mtr_wrst_latency", | ||
+ | Help: "Worst latency reported by MTR", | ||
+ | }, []string{"host", "target"}) | ||
+ | ) | ||
+ | |||
+ | func init() { | ||
+ | // Register Prometheus metrics | ||
+ | prometheus.MustRegister(mtrLoss) | ||
+ | prometheus.MustRegister(mtrAvg) | ||
+ | prometheus.MustRegister(mtrBest) | ||
+ | prometheus.MustRegister(mtrWrst) | ||
+ | } | ||
+ | |||
+ | func runMTR(target string) (MTRResult, error) { | ||
+ | cmd := exec.Command("mtr", "--report", "--json", target) | ||
+ | out, err := cmd.Output() | ||
+ | if err != nil { | ||
+ | return MTRResult{}, err | ||
+ | } | ||
+ | |||
+ | var result MTRResult | ||
+ | err = json.Unmarshal(out, &result) | ||
+ | if err != nil { | ||
+ | return MTRResult{}, err | ||
+ | } | ||
+ | return result, nil | ||
+ | } | ||
+ | |||
+ | func probeHandler(w http.ResponseWriter, r *http.Request) { | ||
+ | target := r.URL.Query().Get("target") | ||
+ | if target == "" { | ||
+ | target = "8.8.8.8" | ||
+ | } | ||
+ | |||
+ | result, err := runMTR(target) | ||
+ | if err != nil { | ||
+ | http.Error(w, err.Error(), http.StatusInternalServerError) | ||
+ | return | ||
+ | } | ||
+ | |||
+ | for _, hub := range result.Report.Hubs { | ||
+ | host := strings.TrimSpace(hub.Host) | ||
+ | mtrLoss.WithLabelValues(host, target).Set(hub.Loss) | ||
+ | mtrAvg.WithLabelValues(host, target).Set(hub.Avg) | ||
+ | mtrBest.WithLabelValues(host, target).Set(hub.Best) | ||
+ | mtrWrst.WithLabelValues(host, target).Set(hub.Wrst) | ||
+ | } | ||
+ | |||
+ | w.Header().Set("Content-Type", "application/json") | ||
+ | json.NewEncoder(w).Encode(result) | ||
+ | } | ||
+ | |||
+ | func main() { | ||
+ | r := mux.NewRouter() | ||
+ | r.HandleFunc("/probe", probeHandler).Methods("GET") | ||
+ | r.Handle("/metrics", promhttp.Handler()) | ||
+ | |||
+ | log.Println("Starting server on :9115") | ||
+ | log.Fatal(http.ListenAndServe(":9115", r)) | ||
+ | } | ||
+ | |||
``` | ``` |
Latest revision as of 19:50, 14 June 2024
mtr_probe.go
package main import ( "encoding/json" "log" "net/http" "os/exec" "github.com/gorilla/mux" ) type MTRResult struct { // Define the structure to match MTR JSON output } func runMTR(target string) (string, error) { cmd := exec.Command("mtr", "--report", "--json", target) out, err := cmd.Output() if err != nil { return "", err } return string(out), nil } func probeHandler(w http.ResponseWriter, r *http.Request) { target := r.URL.Query().Get("target") if target == "" { target = "8.8.8.8" } result, err := runMTR(target) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write([]byte(result)) } func main() { r := mux.NewRouter() r.HandleFunc("/probe", probeHandler).Methods("GET") http.Handle("/", r) log.Println("Starting server on :9115") log.Fatal(http.ListenAndServe(":9115", nil)) }
Dockerfile
# Start from the official Go image FROM golang:1.18 as builder # Set the Current Working Directory inside the container WORKDIR /app # Copy go.mod and go.sum files COPY go.mod go.sum ./ # Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed RUN go mod download # Copy the source from the current directory to the Working Directory inside the container COPY . . # Build the Go app RUN go build -o mtr_probe . # Use a minimal image to run the application FROM debian:bullseye-slim # Install MTR RUN apt-get update && apt-get install -y mtr-tiny # Copy the pre-built binary file from the previous stage COPY --from=builder /app/mtr_probe /usr/local/bin/mtr_probe # Expose port 9115 to the outside world EXPOSE 9115 # Command to run the executable CMD ["mtr_probe"]
go.mod
module mtr_probe go 1.18 require github.com/gorilla/mux v1.8.0
sh
# Build the Docker image docker build -t mtr-probe . # Run the Docker container docker run -d -p 9115:9115 mtr-probe
scrape_configs: - job_name: 'blackbox' metrics_path: /probe params: module: [mtr] # Look for an 'mtr' module in blackbox exporter config. static_configs: - targets: - 'example.com' relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 127.0.0.1:9115 # MTR prober address
More
# Stage 1: Build the Go application FROM golang:1.18 as builder # Set the working directory inside the container WORKDIR /app # Copy go.mod and go.sum files COPY go.mod go.sum ./ # Download all dependencies RUN go mod download # Copy the source code COPY . . # Build the Go application RUN go build -o mtr_probe . # Stage 2: Build the final image FROM debian:bullseye-slim # Install MTR and necessary utilities RUN apt-get update && apt-get install -y mtr-tiny wget ca-certificates # Install Blackbox Exporter RUN wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.23.0/blackbox_exporter-0.23.0.linux-amd64.tar.gz \ && tar -xzf blackbox_exporter-0.23.0.linux-amd64.tar.gz \ && mv blackbox_exporter-0.23.0.linux-amd64/blackbox_exporter /usr/local/bin/ \ && rm -rf blackbox_exporter-0.23.0.linux-amd64* # Copy the Go application binary COPY --from=builder /app/mtr_probe /usr/local/bin/mtr_probe # Create a blackbox exporter config COPY blackbox.yml /etc/blackbox_exporter/config.yml # Expose ports for both services EXPOSE 9115 9116 # Start both the Go application and Blackbox Exporter CMD ["sh", "-c", "mtr_probe & blackbox_exporter --config.file=/etc/blackbox_exporter/config.yml"]
blacbox.yaml
modules: http_2xx: prober: http timeout: 5s http: valid_http_versions: ["HTTP/1.1", "HTTP/2"] valid_status_codes: [] # Defaults to 2xx method: GET mtr: prober: mtr timeout: 30s mtr: targets: - 8.8.8.8 # Example target
docker run -d -p 9115:9115 -p 9116:9116 mtr-blackbox-exporter
more2
package main import ( "encoding/json" "log" "net/http" "os/exec" "strconv" "strings" "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) // Define the MTRResult struct to match MTR JSON output type MTRResult struct { Report struct { Hubs []struct { Count int `json:"count"` Loss float64 `json:"Loss%"` Snt int `json:"Snt"` Last float64 `json:"Last"` Avg float64 `json:"Avg"` Best float64 `json:"Best"` Wrst float64 `json:"Wrst"` StDev float64 `json:"StDev"` Host string `json:"Host"` } `json:"hubs"` } `json:"report"` } var ( // Define Prometheus metrics mtrLoss = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "mtr_loss_percentage", Help: "Packet loss percentage reported by MTR", }, []string{"host", "target"}) mtrAvg = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "mtr_avg_latency", Help: "Average latency reported by MTR", }, []string{"host", "target"}) mtrBest = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "mtr_best_latency", Help: "Best latency reported by MTR", }, []string{"host", "target"}) mtrWrst = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "mtr_wrst_latency", Help: "Worst latency reported by MTR", }, []string{"host", "target"}) ) func init() { // Register Prometheus metrics prometheus.MustRegister(mtrLoss) prometheus.MustRegister(mtrAvg) prometheus.MustRegister(mtrBest) prometheus.MustRegister(mtrWrst) } func runMTR(target string) (MTRResult, error) { cmd := exec.Command("mtr", "--report", "--json", target) out, err := cmd.Output() if err != nil { return MTRResult{}, err } var result MTRResult err = json.Unmarshal(out, &result) if err != nil { return MTRResult{}, err } return result, nil } func probeHandler(w http.ResponseWriter, r *http.Request) { target := r.URL.Query().Get("target") if target == "" { target = "8.8.8.8" } result, err := runMTR(target) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } for _, hub := range result.Report.Hubs { host := strings.TrimSpace(hub.Host) mtrLoss.WithLabelValues(host, target).Set(hub.Loss) mtrAvg.WithLabelValues(host, target).Set(hub.Avg) mtrBest.WithLabelValues(host, target).Set(hub.Best) mtrWrst.WithLabelValues(host, target).Set(hub.Wrst) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) } func main() { r := mux.NewRouter() r.HandleFunc("/probe", probeHandler).Methods("GET") r.Handle("/metrics", promhttp.Handler()) log.Println("Starting server on :9115") log.Fatal(http.ListenAndServe(":9115", r)) }