Difference between revisions of "Mtr exporter"
Jump to navigation
Jump to search
(Created page with "``` package main import ( "encoding/json" "log" "net/http" "os/exec" "github.com/gorilla/mux" ) type MTRResult struct { // Define the structure to match MTR JSON outp...") |
|||
| (4 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| + | |||
| + | mtr_probe.go | ||
``` | ``` | ||
package main | package main | ||
| Line 125: | 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))
}