NFS/HTTP 监控 Exporter 技术文档

NFS/HTTP 监控 Exporter 技术文档

一、组件概述

本Exporter基于Prometheus监控体系开发,主要用于实时监控以下两类目标状态:

  1. HTTP服务可用性
  2. NFS TCP端口可达性

二、配置文件说明

1. 配置文件格式

config.yaml需包含以下结构:

yamlCopy Codeexporter:
  port: "9090"  # Exporter监听端口

http:
  url: "http://example.com/health"  # 被监控的HTTP服务地址

tcp:
  server: "nfs-server:2049"  # NFS服务地址(含端口)

三、监控指标说明

1. HTTP健康指标

# promql 指标

http_port_up{target="http://example.com/health"}
  • 类型‌:Gauge
  • 取值范围
    • 1:服务可达且返回HTTP 200
    • 0:服务不可达或返回非200状态码‌
  • 探测频率‌:10秒/次

源代码:

// checkHealth 对目标地址进行探活检查
func checkHealth(target string, wg *sync.WaitGroup) {
    defer wg.Done()
    // 超时时间 5 秒
    client := http.Client{
        Timeout: 5 * time.Second,
    }
    for {
        // 检查传入的 url 是否健康,如果错误为空,并且状态码为 200 将 isHealthy 值修改为 true
        resp, err := client.Get(target)
        isHealthy := false
        if err == nil && resp.StatusCode == 200 {
            isHealthy = true
        }
        healthGauge.WithLabelValues(target).Set(float64(btoi(isHealthy)))
        time.Sleep(10 * time.Second) // 每10秒检查一次
    }
}

2. NFS端口状态指标

# promql
nfs_tcp_port_up{target="nfs-server:2049"}
  • 类型‌:Gauge
  • 取值范围
    • 1:TCP连接成功
    • 0:TCP连接失败‌
  • 探测频率‌:15秒/次

源代码:

func probeNFSPort(ctx context.Context, target string, wg *sync.WaitGroup) {
    defer wg.Done()
    // 超时时长 3 秒
    dialer := &net.Dialer{Timeout: 3 * time.Second}
    for {
        // go 协程
        select {
        case <-ctx.Done():
            return
        default:
            // 指定 tcp 协议,和传入 nfs 信息做链接探活
            conn, err := dialer.DialContext(ctx, "tcp", target)
            if err == nil {
                // 如果 err 为空 value 等于 1
                nfsPortUp.WithLabelValues(target).Set(1)
                conn.Close()
            } else {
                // 否则为 0
                nfsPortUp.WithLabelValues(target).Set(0)
            }
            time.Sleep(15 * time.Second) // 探测间隔
        }
    }
}

四、实现细节

1. 并发控制机制

  • 采用sync.WaitGroup实现goroutine生命周期管理
  • 通过context.Context实现优雅退出控制‌

2. 网络探测实现

检测类型 实现方式
HTTP健康检查 使用http.Client发送GET请求,超时时间5秒‌4
TCP端口检测 通过net.Dialer实现TCP拨测,超时时间3秒‌5

3. 指标初始化

goCopy Codefunc init() {
    prometheus.MustRegister(nfsPortUp)  // 注册NFS端口指标
    prometheus.MustRegister(healthGauge)  // 注册健康指标‌:ml-citation{ref="3,4" data="citationList"}
}

五、源代码

yaml 配置:

exporter:
  port: 9120

http:
  # 如果不是 443和80, URL+指定PORT
  url: "https://www.baidu.com:443"

tcp:
  # tcp 服务
  server: "10.0.0.200:2049"

go 代码

package main

import (
    "context"
    "fmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "github.com/spf13/viper"
    "net"
    "net/http"
    "sync"
    "time"
)

var (
    nfsPortUp = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "nfs_tcp_port_up",
            Help: "NFS port  (1=up, 0=down)",
        },
        []string{"target"},
    )

    healthGauge = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "http_port_up",
            Help: "Health status of the host (1=up, 0=down)",
        },
        []string{"target"},
    )
)

func init() {
    prometheus.MustRegister(nfsPortUp)
    prometheus.MustRegister(healthGauge)

}

// exporter config
func Exporter_Config() string {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")

    // 读取配置文件
    if err := viper.ReadInConfig(); err != nil {
        fmt.Println("Error reading config file, %s", err)
    }

    return viper.GetString("exporter.port")

}

func Web_Config() string {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")

    // 读取配置文件
    if err := viper.ReadInConfig(); err != nil {
        fmt.Println("Error reading config file, %s", err)
    }

    return viper.GetString("http.url")

}

func Nfs_Config() string {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")

    // 读取配置文件
    if err := viper.ReadInConfig(); err != nil {
        fmt.Println("Error reading config file, %s", err)
    }

    return viper.GetString("tcp.server")

}

// checkHealth 对目标地址进行探活检查
func checkHealth(target string, wg *sync.WaitGroup) {
    defer wg.Done()
    client := http.Client{
        Timeout: 5 * time.Second,
    }
    for {
        resp, err := client.Get(target)
        isHealthy := false
        if err == nil && resp.StatusCode == 200 {
            isHealthy = true
        }
        healthGauge.WithLabelValues(target).Set(float64(btoi(isHealthy)))
        time.Sleep(10 * time.Second) // 每10秒检查一次
    }
}

func probeNFSPort(ctx context.Context, target string, wg *sync.WaitGroup) {
    defer wg.Done()
    dialer := &net.Dialer{Timeout: 3 * time.Second}
    for {
        select {
        case <-ctx.Done():
            return
        default:
            conn, err := dialer.DialContext(ctx, "tcp", target)
            if err == nil {
                nfsPortUp.WithLabelValues(target).Set(1)
                conn.Close()
            } else {
                nfsPortUp.WithLabelValues(target).Set(0)
            }
            time.Sleep(15 * time.Second) // 探测间隔
        }
    }
}

// btoi (true 转换为 1,false 转换为 0) 。
func btoi(b bool) int {
    if b {
        return 1
    }
    return 0
}

func main() {
    fmt.Println(Web_Config())
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    var wg sync.WaitGroup

    wg.Add(1)
    go checkHealth(Web_Config(), &wg)
    go probeNFSPort(ctx, Nfs_Config(), &wg)
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":"+Exporter_Config(), nil)
}

img_v3_02k1_7af40217-1446-4dfb-9d8c-fd04e197842g

五、运行

1. 启动命令

./exporter

2. 访问端点

http://127.0.0.1:9090/metrics

六、docker image 制作

# 使用官方的 Golang 基础镜像进行构建
FROM golang:alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制源代码到容器中
COPY exporter-demo .

# 编译 Golang 程序
RUN go build -o dial_exporter .

# 使用一个更小的基础镜像来运行应用
FROM alpine:latest

# 设置工作目录
WORKDIR /root/

# 从构建阶段复制编译好的二进制文件
COPY --from=builder /app/dial_exporter .

# 从构建阶段复制配置文件到容器中
COPY --from=builder /app/config.yaml .

# 暴露端口(根据你的应用需求)
EXPOSE 9120

# 启动应用
CMD ["./dial_exporter"]

七、K8S 部署

编写 configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: dial-exporter-config
  namespace: test-vm
data:
  config.yaml: |
    exporter:
      port: 9120
    http:
      # 如果不是 443和80, URL+指定PORT
      url: "https://www.xxx.com:443"
    tcp:
      # tcp 服务
      server: "x.x.x.x:2049"

编写 deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dial-exporter
  namespace: test-vm
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dial-exporter
  template:
    metadata:
      labels:
        app: dial-exporter
    spec:
      containers:
      - name: dial-exporter
        image: nfs_exporter:v1
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9120
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
          requests:
            cpu: 100m
            memory: 100Mi
        volumeMounts:
        - name: config-volume
          mountPath: /root/config.yaml
          subPath: config.yaml
      volumes:
      - name: config-volume
        configMap:
          name: dial-exporter-config

创建 service 文件

apiVersion: v1
kind: Service
metadata:
  labels:
    # 用于给 endpoint 实现标签绑定
    app: dial-exporter
  name: dial-exporter
  namespace: test-vm
spec:
  selector:
    app: dial-exporter
  ports:
  - name: metrics
    protocol: TCP
    port: 9120
    targetPort: 9120

应用这些配置

kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

八、对接Prometheus

由于这里 Prometheus 是部署在 K8S 中所以需要 sm 资源将其暴露对应 endpoint

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app.kubernetes.io/vendor: kubesphere
    app: dial-exporter
  name: dial-exporter
  namespace: kubesphere-monitoring-system
spec:
  namespaceSelector:
    matchNames:
    - test-vm
  # selector 字段中匹配 endpoint 的标签
  selector:
    matchLabels:
      app: dial-exporter
  endpoints:
  - interval: 1m
    port: metrics       # 匹配 svc port 名称
    path: /metrics      # 匹配监控指标路径
  jobLabel: app
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇