前言
Prometheus 生態裡有很多采集器負責各類監控資料的採集,其中使用最廣泛的,顯然是 node-exporter,負責 Linux、BSD 等系統的常規監控指標的採集,比如 CPU、記憶體、硬碟、網路、IO 等。其 github 地址是:https://github.com/prometheus/node_exporter 。很多人都用過,但對其細節未必清楚。
我想寫一個小專欄,透過 node-exporter 這個採集器,講解各類指標的含義、採集方法、使用場景、注意事項。順帶講解 Linux 的很多觀測手段。想必對於初中級研發、運維人員都會有幫助,專欄依舊會放到星球裡,爭取讓星球的資源慢慢更為豐富起來。這塊寫完之後,後面可以繼續 mysql、redis 等相關的專欄,一點點磕。
安裝 node-exporter
要想方便除錯,理解整個知識,建議還是要把 node-exporter 的程式碼下載下來,能夠本地編譯執行。這裡我做一個簡單演示,我的電腦是 Mac,M1 晶片,首先下載 go 安裝包(https://go.dev/dl/):https://go.dev/dl/go1.22.2.darwin-arm64.tar.gz。一般使用 tar.gz 的檔案就好,不用 pkg。
cd /Users/ulric/works/tgz
wget https://go.dev/dl/go1.22.2.darwin-arm64.tar.gz
tar -zxf go1.22.2.darwin-arm64.tar.gz
操作如上,/Users/ulric/works/tgz/go
這個目錄就是 go 的安裝目錄,然後配置環境變數:
export GOROOT=/Users/ulric/works/tgz/go
export GOPATH=/Users/ulric/works/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
GOROOT 是 go 的安裝目錄,GOPATH 是 go 的工作目錄,PATH 是環境變數,這樣配置之後,就可以使用 go 命令了。上面的幾行命令可以儲存在 ~/.bash_profile
或者 ~/.zshrc
裡,這樣每次開啟終端都會自動載入。
驗證 go 環境是否正常安裝:
% go version
go version go1.22.2 darwin/arm64
然後下載 node-exporter 的程式碼:
cd /Users/ulric/works
git clone https://github.com/prometheus/node_exporter.git
然後就可以編譯了,如果你的網路環境不好,編譯之前可以設定代理:
export GOPROXY=https://goproxy.cn,direct
cd /Users/ulric/works/node_exporter
go build
如果一切正常,就可以執行 node_exporter 做測試了,我先看看其版本:
ulric@ulric-flashcat node_exporter % ./node_exporter --version
node_exporter, version (branch: , revision: 0d3400ebc976e14d5b87db276bb2ec32f55b4052)
build user:
build date:
go version: go1.22.2
platform: darwin/arm64
tags: unknown
如上,就完成了 node-exporter 的原始碼安裝。
啟動 node-exporter
生產環境啟動 node-exporter,通常是透過 systemd 等方式啟動,咱們這裡為了學習方便,就直接把程序啟動在前臺即可:
ulric@ulric-flashcat node_exporter % ./node_exporter --log.level=debug
ts=2024-05-23T04:08:01.560Z caller=node_exporter.go:193 level=info msg="Starting node_exporter" version="(version=, branch=, revision=0d3400ebc976e14d5b87db276bb2ec32f55b4052)"
ts=2024-05-23T04:08:01.560Z caller=node_exporter.go:194 level=info msg="Build context" build_context="(go=go1.22.2, platform=darwin/arm64, user=, date=, tags=unknown)"
ts=2024-05-23T04:08:01.561Z caller=node_exporter.go:199 level=debug msg="Go MAXPROCS" procs=1
ts=2024-05-23T04:08:01.561Z caller=filesystem_common.go:111 level=info collector=filesystem msg="Parsed flag --collector.filesystem.mount-points-exclude" flag=^/(dev)($|/)
ts=2024-05-23T04:08:01.562Z caller=filesystem_common.go:113 level=info collector=filesystem msg="Parsed flag --collector.filesystem.fs-types-exclude" flag=^devfs$
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:111 level=info msg="Enabled collectors"
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=boottime
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=cpu
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=diskstats
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=filesystem
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=loadavg
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=meminfo
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=netdev
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=os
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=powersupplyclass
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=textfile
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=thermal
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=time
ts=2024-05-23T04:08:01.562Z caller=node_exporter.go:118 level=info collector=uname
ts=2024-05-23T04:08:01.565Z caller=tls_config.go:313 level=info msg="Listening on" address=[::]:9100
ts=2024-05-23T04:08:01.565Z caller=tls_config.go:316 level=info msg="TLS is disabled." http2=false address=[::]:9100
可以看到,node-exporter 啟動成功,監聽在 9100 埠,可以透過瀏覽器訪問:http://localhost:9100/metrics 檢視採集到的指標。或者透過 curl 命令:
curl -s http://localhost:9100/metrics
在我的本地 Mac 上,大概會採集 610 個指標,包括:
- go 字首的指標:這是 node-exporter 程序本身的一些指標,比如 gc 耗時、記憶體使用等
- node 字首的指標:機器的一些常規指標,比如 CPU、記憶體、硬碟、網路、IO 等,咱們後面重點研究這類指標
- promhttp 字首的指標:node-exporter 的 http 服務的一些指標,比如請求次數
node-exporter 啟動引數
./node_exporter --help
可以檢視 node-exporter 的啟動引數,主要引數:
- collector 字首的指標:控制是否啟用某個採集器,node-exporter 內建了多個採集器,比如 cpu、meminfo、ntp 等都是不同的採集器;collector 字首的還有一些引數是控制各個採集器具體行為的,比如
--collector.ntp.server
控制 ntp 採集器的 ntp 伺服器地址 - web 字首的指標:控制 node-exporter 的 http 服務,比如
--web.listen-address
控制監聽地址,--web.telemetry-path
控制暴露指標資料的 API 路徑 - log 字首的指標:控制日誌列印,比如
--log.level
控制日誌級別
大量引數都是圍繞 collector 的,因為 node-exporter 的核心就是採集器,不同的採集器負責不同的指標採集。有部分 collector 是預設開啟的,有部分是預設關閉的,README 中有詳細說明。對於那些預設關閉的 collector,如果你想啟用,就要小心測試了,看看採集耗時、對機器的資源佔用的影響等。
node-exporter 原始碼結構
程式碼倉庫根目錄下,有個 node_exporter.go,main 函式入口就在這裡。collector 目錄下是各個外掛的實現,比如 meminfo 相關的:
ulric@ulric-flashcat collector % ll meminfo*
-rw-r--r-- 1 ulric staff 1998 12 18 17:20 meminfo.go
-rw-r--r-- 1 ulric staff 2515 12 18 17:20 meminfo_darwin.go
-rw-r--r-- 1 ulric staff 1853 12 18 17:20 meminfo_linux.go
-rw-r--r-- 1 ulric staff 1163 12 18 17:20 meminfo_linux_test.go
-rw-r--r-- 1 ulric staff 1520 12 18 17:20 meminfo_netbsd.go
-rw-r--r-- 1 ulric staff 4655 12 18 17:20 meminfo_numa_linux.go
-rw-r--r-- 1 ulric staff 2950 12 18 17:20 meminfo_numa_linux_test.go
-rw-r--r-- 1 ulric staff 2483 12 18 17:20 meminfo_openbsd.go
-rw-r--r-- 1 ulric staff 2336 12 18 17:20 meminfo_openbsd_amd64.go
這些原始碼檔案分成了很多不同的字尾,這是因為不同的系統,meminfo 的實現是不同的,go 語言透過字尾來區分不同的系統,比如 meminfo_darwin.go 是 Mac 系統的實現,meminfo_linux.go 是 Linux 系統的實現。
不同的外掛,都會有個 init()
函式,這個函式會在 node-exporter 啟動的時候被呼叫,用來註冊外掛。比如 meminfo 外掛:
func init() {
registerCollector("meminfo", defaultEnabled, NewMeminfoCollector)
}
所謂的外掛註冊,核心就是把各個外掛的資訊(名稱、是否啟用、工廠函式)儲存在全域性變數中,這樣一來,node-exporter 啟動的時候,就可以根據這些資訊,動態建立外掛例項,然後呼叫採集函式,採集指標。典型的外掛化設計思路。
外掛在 node-exporter 中抽象為一個 interface,只有一個 Update 函式:
type Collector interface {
// Get new metrics and expose them via prometheus registry.
Update(ch chan<- prometheus.Metric) error
}
比如記憶體採集外掛 meminfo,就實現了這個介面:
func (c *meminfoCollector) Update(ch chan<- prometheus.Metric) error {
var metricType prometheus.ValueType
memInfo, err := c.getMemInfo()
if err != nil {
return fmt.Errorf("couldn't get meminfo: %w", err)
}
level.Debug(c.logger).Log("msg", "Set node_mem", "memInfo", memInfo)
for k, v := range memInfo {
if strings.HasSuffix(k, "_total") {
metricType = prometheus.CounterValue
} else {
metricType = prometheus.GaugeValue
}
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
prometheus.BuildFQName(namespace, memInfoSubsystem, k),
fmt.Sprintf("Memory information field %s.", k),
nil, nil,
),
metricType, v,
)
}
return nil
}
node-exporter 框架層面,會建立 prometheus.Metric 型別的 channel,作為一個監控資料接收器,傳給 Update,各個外掛實現 Update 函式,把採集到的指標資料寫入 channel,node-exporter 框架層面,會把這些資料透過 /metrics
介面暴露出來。
小結
作為專欄第一篇,對 node-exporter 整體做了一些介紹,包括其定位、安裝方式、啟動引數、原始碼結構等。後續會逐個外掛詳細講解,一起揭開 Linux 監控資料的神秘面紗,看看這些資料是如何採集的,用來幹啥的,有啥坑,以及一些重要指標的含義。