首页
导航
统计
留言
更多
壁纸
直播
关于
推荐
星的魔法
星的导航页
谷歌一下
镜像国内下载站
大模型国内下载站
docker镜像国内下载站
腾讯视频
Search
1
Ubuntu安装 kubeadm 部署k8s 1.30
214 阅读
2
kubeadm 部署k8s 1.30
132 阅读
3
rockylinux 9.3详细安装drbd
131 阅读
4
rockylinux 9.3详细安装drbd+keepalived
119 阅读
5
ceshi
82 阅读
默认分类
日记
linux
docker
k8s
ELK
Jenkins
Grafana
Harbor
Prometheus
Cepf
k8s安装
Gitlab
traefik
sonarqube
OpenTelemetry
MinIOn
Containerd进阶使用
ArgoCD
golang
Git
Python
Web开发
HTML和CSS
JavaScript
对象模型
公司
登录
/
注册
Search
标签搜索
k8s
linux
docker
drbd+keepalivde
ansible
dcoker
webhook
星
累计撰写
117
篇文章
累计收到
940
条评论
首页
栏目
默认分类
日记
linux
docker
k8s
ELK
Jenkins
Grafana
Harbor
Prometheus
Cepf
k8s安装
Gitlab
traefik
sonarqube
OpenTelemetry
MinIOn
Containerd进阶使用
ArgoCD
golang
Git
Python
Web开发
HTML和CSS
JavaScript
对象模型
公司
页面
导航
统计
留言
壁纸
直播
关于
推荐
星的魔法
星的导航页
谷歌一下
镜像国内下载站
大模型国内下载站
docker镜像国内下载站
腾讯视频
搜索到
2
篇与
的结果
2025-09-06
容器化 + 上线到 K8s + 被 Prom 抓取
容器化 + 上线到 K8s + 被 Prom 抓取 目标:Exporter 以容器形式部署到集群,并被 Prometheus 抓取。 任务 构建镜像并 push:make docker IMG=…。 应用 k8s/deployment.yaml + Service;为你的命名空间调好 selector。 如果你集群用 Prometheus Operator,创建 ServiceMonitor 让它自动发现 scrape 目标。 Prometheus target 变为 Up,图表能看到你的新指标。 若用 Operator,ServiceMonitor 生效(label 选择器正确)。一、准备条件你有 Docker、kubectl 权限,能访问集群。 Harbor 私有仓库可用(如果是私有,需要在 K8s 里创建 imagePullSecret,步骤见第 4 步)。 Exporter 监听 0.0.0.0:9090 且有 /metrics(若没有,见“可选:最小 main.go”)。二、(可选)最小 main.go(若你项目还没暴露 /metrics)在你的可执行入口里加入(或新建 main.go):package main import ( "log" "net/http" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { reg := prometheus.NewRegistry() reg.MustRegister(collectors.NewGoCollector(), collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) // TODO: 在这里注册你自己的指标 reg.MustRegister(yourMetric) mux := http.NewServeMux() mux.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) //(若你有 /live、/ready 就更好) mux.HandleFunc("/live", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) log.Println("listening on :9090") log.Fatal(http.ListenAndServe(":9090", mux)) } 三、Dockerfile(多阶段构建,体积小、非 root 运行) 在项目根目录新建 Dockerfile:root@k8s-03:/woke/go-password-validator-main# cat Dockerfile # ---- build ---- FROM golang:1.23-alpine AS build WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . # 如果入口在仓库根目录的 main.go: RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -ldflags="-s -w" -o /out/exporter . # ---- runtime ---- FROM gcr.io/distroless/static:nonroot USER nonroot:nonroot COPY --from=build /out/exporter /exporter EXPOSE 9090 ENTRYPOINT ["/exporter"] 四、Makefile 目标 在项目根目录的 Makefile 增加:root@k8s-03:/woke/go-password-validator-main# cat Makefile # ---------- 现有的目标,保留 ---------- test: go test ./... fmt: go fmt ./... vet: go vet ./... install-lint: # 建议用 go install(Go 1.16+) go install golang.org/x/lint/golint@latest @go env GOPATH >/dev/null lint: go list ./... | grep -v /vendor/ | xargs -L1 golint -set_exit_status install-staticcheck: go install honnef.co/go/tools/cmd/staticcheck@latest staticcheck: staticcheck -f stylish ./... # ---------- Docker 相关,新增 ---------- REGISTRY ?= 192.168.30.180:30003 REPO ?= test/go TAG ?= $(shell date +%Y%m%d-%H%M%S) IMG := $(REGISTRY)/$(REPO):$(TAG) .PHONY: docker docker-build docker-push docker: docker-build docker-push docker-build: docker build -t $(IMG) . docker-push: docker push $(IMG) 使用:# 登录你的 Harbor docker login harbor.example.com # 构建并 push(替换成你自己的 Harbor 项目/镜像名) make docker IMG=harbor.example.com/ops/go-password-exporter:v0.1.0 #如果配置好了Makefile文件 可以直接执行 make docker 也可以像上面这样定制五、Harbor 私有仓库拉取凭据(K8s imagePullSecret)注意:imagePullSecret 是命名空间级的,需要在哪个 ns 拉镜像,就在哪个 ns 创建;要全局通用只能在每个 ns 都创建(或用准入控制器自动注入)。#示例,在 monitoring 命名空间创建: kubectl -n monitoring create secret docker-registry harbor-cred \ --docker-server=192.168.30.180:30003 \ --docker-username='admin' \ --docker-password='Harbor12345' \ --docker-email='ops@example.com' 六、部署到 K8s(Deployment + Service)#deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: pass-validator-exporter namespace: monitoring labels: app.kubernetes.io/name: pass-validator-exporter spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: pass-validator-exporter template: metadata: labels: app.kubernetes.io/name: pass-validator-exporter spec: imagePullSecrets: - name: harbor-cred containers: - name: exporter image: 192.168.30.180:30003/test/go:20250905-154053 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 9090 readinessProbe: httpGet: { path: /ready, port: http } initialDelaySeconds: 3 periodSeconds: 5 livenessProbe: httpGet: { path: /live, port: http } initialDelaySeconds: 10 periodSeconds: 10 resources: requests: { cpu: "50m", memory: "64Mi" } limits: { cpu: "200m", memory: "128Mi" } securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65532#service.yaml apiVersion: v1 kind: Service metadata: name: pass-validator-exporter namespace: monitoring labels: app.kubernetes.io/name: pass-validator-exporter # 若你是“裸 Prometheus + 注解发现”,可打开以下注解: # annotations: # prometheus.io/scrape: "true" # prometheus.io/path: "/metrics" # prometheus.io/port: "9090" spec: type: ClusterIP selector: app.kubernetes.io/name: pass-validator-exporter ports: - name: http port: 9090 targetPort: http kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml # 验证: kubectl -n monitoring get pods -l app.kubernetes.io/name=pass-validator-exporter -w kubectl -n monitoring get svc pass-validator-exporter # 本地看指标(可选): kubectl -n monitoring port-forward svc/pass-validator-exporter 19090:9090 curl http://127.0.0.1:19090/metrics | head七、让 Prometheus 抓取方案 A:你在用 Prometheus Operator / kube-prometheus-stack(推荐) 关键点:ServiceMonitor 的 selector 要能匹配到 Service 的 label;同时 ServiceMonitor 自己的 label 要匹配到 Prometheus 实例的 serviceMonitorSelector。大多数 Helm 安装用的 label 是 release: kube-prometheus-stack(按你的集群实际为准)。# 看 Prometheus 实例的 selector 要求(取第一个 Prometheus 资源) kubectl -n monitoring get prometheus -o jsonpath='{.items[0].spec.serviceMonitorSelector.matchLabels}{"\n"}' # 一般会看到 {"release":"<你的 Helm release 名>"}#创建 servicemonitor.yaml 如果上面没有查到直接删除 labels: # release: kube-prometheus-stack # ←改成你 Prometheus 实例在选的那个 label apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: pass-validator-exporter namespace: monitoring labels: release: kube-prometheus-stack # ←改成你 Prometheus 实例在选的那个 label spec: namespaceSelector: matchNames: ["monitoring"] selector: matchLabels: app.kubernetes.io/name: pass-validator-exporter # 要和 Service 的 label 完全一致 endpoints: - port: http # 要和 Service 端口名一致 path: /metrics interval: 15s scrapeTimeout: 10s#应用并验证: kubectl apply -f servicemonitor.yaml # 在 Prometheus UI -> Status -> Targets 里应该出现你的 target,并且是 UP。 #启动pod测试集群内获取是否正常 kubectl -n monitoring run tmp-curl --rm -it --image=curlimages/curl --restart=Never \ -- http://pass-validator-exporter.monitoring.svc:9090/metrics | head #可以在浏览器IP+端口访问 kubectl -n monitoring port-forward --address 0.0.0.0 svc/prometheus-k8s 9090:9090方案 B:裸 Prometheus(没装 Operator)你需要在 Prometheus 的 prometheus.yml 里启用 kubernetes_sd_configs,并用注解过滤(上面 Service 已给注解示例)。典型片段如下(放到你的 scrape_configs 里):- job_name: 'kubernetes-services' kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: "true" - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target: __metrics_path__ regex: "(.+)" - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] action: replace target: __address__ regex: (.+?)(?::\d+)?;(\d+) replacement: $1:$2 #改好配置后重启 Prometheus,去 Targets 看是否 UP。八、迭代上线(改代码后快速发版)# 1) 构建并推送新镜像 make docker IMG=harbor.example.com/ops/go-password-exporter:v0.1.1 # 2) 滚动更新 Deployment(或直接改 yaml) kubectl -n monitoring set image deploy/pass-validator-exporter exporter=harbor.example.com/ops/go-password-exporter:v0.1.1 kubectl -n monitoring rollout status deploy/pass-validator-exporter 九、验收清单Prometheus Targets 里看到 pass-validator-exporter → UP。 在 Prometheus 或 Grafana 中能查询到你的新指标(例如:your_metric_name)。 如果是 Operator:ServiceMonitor 的 labels.release 与 Prometheus 实例选择器 匹配;ServiceMonitor.spec.selector.matchLabels 与 Service 的 labels 匹配;endpoints.port 与 Service 端口名 一致。十、常见坑速查镜像拉不下来:Deployment 没配置 imagePullSecrets,或 secret 在错的命名空间。 Target 一直 DOWN:容器没监听 0.0.0.0;端口/路径不一致;Service 选择器写错;endpoints.port 名字不对。 Operator 抓不到:ServiceMonitor 的 labels 与 Prometheus 的 serviceMonitorSelector 不匹配。 /metrics 超时:指标收集阻塞(锁/IO);建议拆分收集逻辑并设置 scrapeTimeout。 权限:Exporter 是 HTTP 只读,不需要额外 RBAC。
2025年09月06日
3 阅读
0 评论
0 点赞
2025-08-25
Exporte + 基础指标
一、解压 & 目录说明unzip sys-exporter.zip -d sys-exporter cd sys-exporter tree -L 3 # . # ├── Makefile # ├── README.md # ├── cmd/sys-exporter/main.go # ├── go.mod # └── internal/collectors/ # ├── disk_usage.go # ├── disk_usage_test.go # ├── net_conn.go # ├── net_conn_test.go # ├── proc_rss.go # └── proc_rss_test.go 关键点: 使用自定义 Registry,同时注册 process 和 go collector。 /metrics 使用 promhttp.HandlerFor(reg, promhttp.HandlerOpts{EnableOpenMetrics:true})。 自带 /live、/ready,/ready 会尝试 Gather(),失败返回 503。 已挂好 /debug/pprof/*,方便做 CPU/内存采样。 三个示例指标: sys_mount_usage_ratio{mountpoint="/data"} (附带 sys_mount_used_bytes / sys_mount_total_bytes) sys_network_open_sockets{proto="tcp|udp", family="ipv4|ipv6"} sys_process_resident_memory_bytes(Exporter 进程自身 RSS)二、运行脚手架(本地 make run) 2.1 前置准备(只做一次)#下载go wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz #解压go sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz # 使用 vim: sudo vim ~/.bashrc #在文件的最后,添加以下几行: export PATH=$PATH:/usr/local/go/bin export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin #立即生效 source ~/.bashrc #验证安装 go version #创建工作目录 mkdir -p ~/work && cd ~/work2.2一键跑起来(/metrics、/live、/ready)# 可选:拉依赖 make tidy # 启动 如果没有data目录 需要提前创建 make run # 日志应看到:sys-exporter listening on :9090 (mount=/data)#报错 root@k8s-01:~/woke/sys-exporter# make run GO111MODULE=on go run ./cmd/sys-exporter 2025/08/25 07:47:24 sys-exporter listening on :9090 (mount=/data) root@k8s-01:~/woke/sys-exporter# make test GO111MODULE=on go test ./... -race -count=1 # github.com/example/sys-exporter/internal/collectors [github.com/example/sys-exporter/internal/collectors.test] internal/collectors/disk_usage_test.go:22:37: undefined: testutil.NewGaugeFromDesc ? github.com/example/sys-exporter/cmd/sys-exporter [no test files] FAIL github.com/example/sys-exporter/internal/collectors [build failed] FAIL make: *** [Makefile:10: test] Error 1 root@k8s-01:~/woke/sys-exporter 这是单测里用了不存在的函数导致编译失败。prometheus/testutil 并没有 NewGaugeFromDesc 这个 API(我在示例里多写了一句检查 ratio 的代码)。解决方法:把那几行删掉,用 CollectAndCompare 做断言就够了。 #报错 root\@k8s-01:\~/woke/sys-exporter# make test GO111MODULE=on go test ./... -race -count=1 ? github.com/example/sys-exporter/cmd/sys-exporter \[no test files] \--- FAIL: TestProcRSSCollector (0.00s) proc\_rss\_test.go:31: metrics mismatch: +# HELP sys\_process\_resident\_memory\_bytes Resident memory (RSS) of the exporter process in bytes. +# TYPE sys\_process\_resident\_memory\_bytes gauge +sys\_process\_resident\_memory\_bytes 2.097152e+06 FAIL FAIL github.com/example/sys-exporter/internal/collectors 0.020s FAIL make: \*\*\* \[Makefile:10: test] Error 1 root\@k8s-01:~~/woke/sys-exporter# make tidy go mod tidy root\@k8s-01:~~/woke/sys-exporter# make test GO111MODULE=on go test ./... -race -count=1 ? github.com/example/sys-exporter/cmd/sys-exporter \[no test files] \--- FAIL: TestProcRSSCollector (0.00s) proc\_rss\_test.go:31: metrics mismatch: +# HELP sys\_process\_resident\_memory\_bytes Resident memory (RSS) of the exporter process in bytes. +# TYPE sys\_process\_resident\_memory\_bytes gauge +sys\_process\_resident\_memory\_bytes 2.097152e+06 FAIL FAIL github.com/example/sys-exporter/internal/collectors 0.018s FAIL make: \*\*\* \[Makefile:10: test] Error 1 root\@k8s-01:\~/woke/sys-exporter# cat internal/collectors/disk\_usage\_test.go package collectors import ( "strings" "testing" #解决办法 用下面的内容覆盖 internal/collectors/disk_usage_test.go: package collectors import ( "strings" "testing" "github.com/prometheus/client_golang/prometheus/testutil" ) func TestDiskUsageCollector_Metrics(t *testing.T) { mock := func(path string) (total, avail uint64, err error) { // 100 total, 25 available -> used=75, ratio=0.75 return 100, 25, nil } c := NewDiskUsageCollector("/data", mock) expected := # HELP sys_mount_total_bytes Total bytes for a mountpoint. # TYPE sys_mount_total_bytes gauge sys_mount_total_bytes{mountpoint="/data"} 100 # HELP sys_mount_used_bytes Used bytes for a mountpoint. # TYPE sys_mount_used_bytes gauge sys_mount_used_bytes{mountpoint="/data"} 75 # HELP sys_mount_usage_ratio Disk usage ratio (used/total) for a mountpoint. # TYPE sys_mount_usage_ratio gauge sys_mount_usage_ratio{mountpoint="/data"} 0.75 if err := testutil.CollectAndCompare(c, strings.NewReader(expected)); err != nil { t.Fatalf("unexpected metrics diff: %v", err) } } "github.com/prometheus/client_golang/prometheus/testutil" ) func TestDiskUsageCollector\_Metrics(t \*testing.T) { mock := func(path string) (total, avail uint64, err error) { // 100 total, 25 available -> used=75, ratio=0.75 return 100, 25, nil } c := NewDiskUsageCollector("/data", mock) expected := # HELP sys\_mount\_total\_bytes Total bytes for a mountpoint. # TYPE sys\_mount\_total\_bytes gauge sys\_mount\_total\_bytes{mountpoint="/data"} 100 # HELP sys\_mount\_used\_bytes Used bytes for a mountpoint. # TYPE sys\_mount\_used\_bytes gauge sys\_mount\_used\_bytes{mountpoint="/data"} 75 # HELP sys\_mount\_usage\_ratio Disk usage ratio (used/total) for a mountpoint. # TYPE sys\_mount\_usage\_ratio gauge sys\_mount\_usage\_ratio{mountpoint="/data"} 0.75 \ if err := testutil.CollectAndCompare(c, strings.NewReader(expected)); err != nil { t.Fatalf("unexpected metrics diff: %v", err) } } 你这个失败点在于单测期望的数值格式和PID 路径。 Prometheus 的文本输出里,浮点数经常是科学计数法(2.097152e+06),而你测试里写的是 2097152,导致对比失败。 你的测试里把 status 路径硬编码成 /fake/123/status,但实际 os.Getpid() 不一定是 123,最好用真实 PID 组路径。 把 internal/collectors/proc_rss_test.go 改成下面这样(两点都修了): package collectors import ( "os" "path/filepath" "strings" "testing" "github.com/prometheus/client_golang/prometheus/testutil" ) func TestProcRSSCollector(t *testing.T) { root := "/fake" // 使用真实 PID 组路径,避免硬编码 123 statusPath := filepath.Join(root, strconv.Itoa(os.Getpid()), "status") data := "Name:\tapp\nVmRSS:\t2048 kB\n" // 2048 KB => 2,097,152 bytes // 只要命中 <root>/<pid>/status 就返回我们伪造的数据 reader := func(path string) ([]byte, error) { if path == statusPath { return []byte(data), nil } // 返回空数据,不要报错,Collector 会自己忽略 return []byte(""), nil } c := NewProcRSSCollector(root, reader) // 注意:Prometheus 文本输出常是科学计数法,这里用 e+06 exp := # HELP sys_process_resident_memory_bytes Resident memory (RSS) of the exporter process in bytes. # TYPE sys_process_resident_memory_bytes gauge sys_process_resident_memory_bytes 2.097152e+06 if err := testutil.CollectAndCompare(c, strings.NewReader(exp)); err != nil { t.Fatalf("metrics mismatch: %v", err) } }#报错 root\@k8s-01:~~/woke/sys-exporter# vi internal/collectors/disk\_usage\_test.go root\@k8s-01:~~/woke/sys-exporter# ls cmd go.mod go.sum internal Makefile README.md root\@k8s-01:~~/woke/sys-exporter# go clean -testcache root\@k8s-01:~~/woke/sys-exporter# make test GO111MODULE=on go test ./... -race -count=1 # github.com/example/sys-exporter/internal/collectors \[github.com/example/sys-exporter/internal/collectors.test] internal/collectors/proc\_rss\_test.go:12:6: TestProcRSSCollector redeclared in this block internal/collectors/disk\_usage\_test.go:12:6: other declaration of TestProcRSSCollector internal/collectors/disk\_usage\_test.go:15:36: undefined: strconv ? github.com/example/sys-exporter/cmd/sys-exporter \[no test files] FAIL github.com/example/sys-exporter/internal/collectors \[build failed] FAIL make: \*\*\* \[Makefile:10: test] Error 1 root\@k8s-01:\~/woke/sys-exporter# cat internal/collectors/disk\_usage\_test.go package collectors import ( "os" "path/filepath" "strings" "testing" "github.com/prometheus/client_golang/prometheus/testutil" ) func TestProcRSSCollector(t \*testing.T) { root := "/fake" // 使用真实 PID 组路径,避免硬编码 123 statusPath := filepath.Join(root, strconv.Itoa(os.Getpid()), "status") data := "Name:\tapp\nVmRSS:\t2048 kB\n" // 2048 KB => 2,097,152 bytes // 只要命中 <root>/<pid>/status 就返回我们伪造的数据 reader := func(path string) ([]byte, error) { if path == statusPath { return []byte(data), nil } // 返回空数据,不要报错,Collector 会自己忽略 return []byte(""), nil } c := NewProcRSSCollector(root, reader) // 注意:Prometheus 文本输出常是科学计数法,这里用 e+06 exp := ` # HELP sys\_process\_resident\_memory\_bytes Resident memory (RSS) of the exporter process in bytes. # TYPE sys\_process\_resident\_memory\_bytes gauge sys\_process\_resident\_memory\_bytes 2.097152e+06 \` if err := testutil.CollectAndCompare(c, strings.NewReader(exp)); err != nil { t.Fatalf("metrics mismatch: %v", err) } } 你把 disk_usage_test.go 覆盖成了 proc_rss_test.go 的内容,所以现在: 出现了重复的 TestProcRSSCollector(两个文件里同名测试)。 且缺少 strconv 的 import 还原 disk_usage_test.go 把它恢复成“磁盘使用率”的测试(不要放 RSS 的测试): // internal/collectors/disk_usage_test.go package collectors import ( "strings" "testing" "github.com/prometheus/client_golang/prometheus/testutil" ) func TestDiskUsageCollector_Metrics(t *testing.T) { mock := func(path string) (total, avail uint64, err error) { // 100 total, 25 available -> used=75, ratio=0.75 return 100, 25, nil } c := NewDiskUsageCollector("/data", mock) expected := ` # HELP sys_mount_total_bytes Total bytes for a mountpoint. # TYPE sys_mount_total_bytes gauge sys_mount_total_bytes{mountpoint="/data"} 100 # HELP sys_mount_used_bytes Used bytes for a mountpoint. # TYPE sys_mount_used_bytes gauge sys_mount_used_bytes{mountpoint="/data"} 75 # HELP sys_mount_usage_ratio Disk usage ratio (used/total) for a mountpoint. # TYPE sys_mount_usage_ratio gauge sys_mount_usage_ratio{mountpoint="/data"} 0.75 ` if err := testutil.CollectAndCompare(c, strings.NewReader(expected)); err != nil { t.Fatalf("unexpected metrics diff: %v", err) } } 修正 proc_rss_test.go 确保它只包含 RSS 的测试,且 加上 strconv 的 import: // internal/collectors/proc_rss_test.go package collectors import ( "os" "path/filepath" "strconv" "strings" "testing" "github.com/prometheus/client_golang/prometheus/testutil" ) func TestProcRSSCollector(t *testing.T) { root := "/fake" statusPath := filepath.Join(root, strconv.Itoa(os.Getpid()), "status") data := "Name:\tapp\nVmRSS:\t2048 kB\n" // 2048 KB => 2,097,152 bytes reader := func(path string) ([]byte, error) { if path == statusPath { return []byte(data), nil } return []byte(""), nil } c := NewProcRSSCollector(root, reader) exp := ` # HELP sys_process_resident_memory_bytes Resident memory (RSS) of the exporter process in bytes. # TYPE sys_process_resident_memory_bytes gauge sys_process_resident_memory_bytes 2.097152e+06 ` if err := testutil.CollectAndCompare(c, strings.NewReader(exp)); err != nil { t.Fatalf("metrics mismatch: %v", err) } } #正常 root@k8s-01:~/woke/sys-exporter# go clean -testcache make test GO111MODULE=on go test ./... -race -count=1 ? github.com/example/sys-exporter/cmd/sys-exporter [no test files] ok github.com/example/sys-exporter/internal/collectors 1.019s#报错 root@k8s-01:~/woke/sys-exporter# make bench GO111MODULE=on go test ./... -run=^$ -bench=. -benchmem -count=1 -cpuprofile cpu.out -memprofile mem.out cannot use -cpuprofile flag with multiple packages make: *** [Makefile:13: bench] Error 1 #cannot use -cpuprofile flag with multiple packages 意思是:你下了 -cpuprofile 参数,但 go test ./... 会一次性跑多个包(cmd/... 和 internal/...),Go 不允许把多个包的基准同时写到一个 cpu.out 里。 解决方法 只 bench 你有基准的那个包(internal/collectors),而不是所有 ./...。 bench: GO111MODULE=on go test ./internal/collectors -run=^$$ -bench=. -benchmem -count=1 -cpuprofile cpu.out -memprofile mem.out @echo "" @echo "CPU profile: cpu.out | Mem profile: mem.out" @echo "Open an interactive view:" @echo " go tool pprof -http=:9999 cpu.out" 执行make bench 能正常生成 cpu.out 和 mem.out#报错 root\@k8s-01:\~/woke/sys-exporter# make bench GO111MODULE=on go test ./internal/collectors -run=^\$ -bench=. -benchmem -count=1 -cpuprofile cpu.out -memprofile mem.out PASS ok github.com/example/sys-exporter/internal/collectors 0.252s CPU profile: cpu.out | Mem profile: mem.out Open an interactive view: go tool pprof -http=:9999 cpu.out root\@k8s-01:~~/woke/sys-exporter# ls cmd collectors.test cpu.out go.mod go.sum internal Makefile mem.out README.md root\@k8s-01:~~/woke/sys-exporter# go tool pprof -http=:9999 cpu.out Serving web UI on [http://localhost:9999](http://localhost:9999) Couldn't find a suitable web browser! Set the BROWSER environment variable to your desired browser. 浏览器[http://192.168.30.180:9999/](http://192.168.30.180:9999/) 打不开 这是正常现象:现在的 pprof Web UI 只监听在本机回环地址(localhost),所以你从别的机器用 http://192.168.30.180:9999 打不开 #解决 go tool pprof -http=0.0.0.0:9999 cpu.out 如果浏览器打开以后显示:Could not execute dot; may need to install graphviz. 那就需要安装 Graphviz sudo apt-get install -y graphviz sudo yum install -y graphviz 重新打开 pprof Web go tool pprof -http=0.0.0.0:9999 cpu.out 这时点击 “View → Graph” 或 “Flame Graph” 就能显示图像了。 root@k8s-01:~/woke/sys-exporter# go tool pprof -http=0.0.0.0:9999 cpu.out Serving web UI on http://0.0.0.0:9999 Couldn't find a suitable web browser! Set the BROWSER environment variable to your desired browser. cd ~/woke/sys-exporter LISTEN_ADDR=":9090" MOUNTPOINT="/data" make run # 看到日志:sys-exporter listening on :9090 (mount=/data)# 抓在线 profile 并用 Web UI 展示 用正确的 URL抓在线 profile 在 exporter 正常监听后再执行(仍然在服务器上): go tool pprof -http=0.0.0.0:9999 http://localhost:9090/debug/pprof/profile?seconds=15 注意要点: -http=0.0.0.0:9999 只是 Web UI 的监听地址; profile 的 URL 要作为最后一个参数单独给出(别写到 -http= 里); 如果你把 exporter 绑在了别的端口,比如 :9100,就把 URL 换成: http://localhost:9100/debug/pprof/profile?seconds=15
2025年08月25日
5 阅读
0 评论
0 点赞