mysqld_exporter是prometheus官方提供的用於監控mysql運行狀態的exporter。其相關信息可以參考:https://github.com/prometheus/mysqld_exporter。
1. 配置
先看一下其配置方式。主要的配置內容分為兩部分,一部分是監控目標mysql的連接信息,另一部分是exporter抓取的監控參數的設置。
首先是連接信息:
連接信息的設置方法有兩種。第一種是通過環境變量設置,例如:
export DATA_SOURCE_NAME='user:password@(hostname:3306)/' ./mysqld_exporter <flags>
另一種方法是通過配置文件進行設置。配置文件會在func parseMycnf()函數中被轉化為與環境變量設置的格式相同。隨后該設置將傳入golang的db庫並進行數據庫連接。
對於兩種設置的優先級,當環境變量存在(長度大於0)時,將不會對配置文件進行解析。
然后是exporter抓取的監控參數的設置:
這里用集合來表示監控參數的范圍。首先exporter中利用scrapers常量記錄了一個默認的采集范圍集合A。
exporter也允許在exporter啟動的時候,通過設置啟動參數來設置采集范圍B。
當集合B不存在時,集合A生效;當集合B存在時,集合B生效,集合A失效。
Prometheus在采集exporter的數據時,可以攜帶一個collect[]參數設定采集范圍C。
當集合C不存在時,Prometheus最終的采集范圍是A或者B(取決於哪個集合生效);當集合C存在時,Prometheus最終的采集范圍時C和A或者B(取決於哪個集合生效)的交集。
2. 工作模式
exporter收集監控數據主要是由Collector實現的。
首先是路由的注冊。注意mysqld_exporter.go的277和278行:
handlerFunc := newHandler(collector.NewMetrics(), enabledScrapers)
http.Handle(*metricPath, promhttp.InstrumentMetricHandler(prometheus.DefaultRegisterer, handlerFunc))
可以看出主要的處理函數在newHandler(),回到162行函數本體。164行是默認的scraper,165行是獲取prometheus帶的collect[]參數。在196-208行,對collect[]進行了處理,並與scraper求了交集。
在210-211行注冊了prometheus的collector,collector的處理入口在/collector/exporter.go的85行New()函數。函數New()返回了一個叫Exporter的結構體。該結構體實現了Prometheus采集的interface,因此在117行的其成員函數Collect()就是采集數據的位置。
Collect()函數調用了126行的scrape()函數。scrape()函數做了一些數據庫初始化的操作后,在160行遍歷了所有scraper,並go func調用所有scraper的Scrape()函數,實現對目標數據的采集。
綜上,對於mysqld_exporter,只有Prometheus在訪問其數據接口時,exporter才對數據庫進行連接並采集數據。對於多個scraper,exporter采取多個協程實現數據的並發采集。(具體的並發能力還要看mysql中為exporter提供的賬號的並發連接數)
3. 定制化
對於單個mysqld_exporter,其內存占用多在幾十M左右。而在實際的應用中,單個exporter實例只能監控單個mysql數據庫是該exporter的一個痛點。
而第2節中介紹的數據采集的特性,其數據接口在未被訪問時幾乎沒有其他動作,因此從性能開銷上來講,利用單個exporter監控多個數據庫並不會存在太大問題。
(當然一個顯然的問題是,多個數據庫的請求是串行還是並行?如果選擇並行,對每個數據庫的每個scraper使用單獨的協程,在協程數量過多的時候對性能會不會產生影響。這是需要深入討論的問題。但是接下來的內容避開了這個問題。)
如何使exporter可以監控多個數據庫實例?一個直接的思路是,在Prometheus訪問數據接口時,多傳入一個instance參數,該參數為監控目標數據庫的地址和端口,例如“localhost:3306”。
那么,當我們處理Prometheus的訪問(即前文提到的newHandler)時,如果解析到了instance參數,就將該instance信息替換掉配置中的數據庫連接信息,這樣就實現了利用Prometheus的配置參數選擇監控的數據庫實例。
通常,Prometheus的配置文件應該類似:
scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - params "collect[]": - ***
但是這樣每次訪問接口只能獲取某個數據庫實例的監控數據。這些數據如何整合到一起?
這時候Prometheus配置中的relabel config就登場了。(這里,具體的說明文檔請參考https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config)
這里我們解釋一下relabel和metrics relabel的區別。relabel是在Prometheus訪問數據接口前生效的,metrics relabel是在接收到數據之后生效的。
relabel config中提供了以下兩個label:
第一個是__address__。通常,我們在配置Prometheus的監控對象時,監控的目標時target。在relabel階段,target會自動傳給__address__,並作為relabel之后Prometheus訪問數據接口的地址。
因此,在relabel階段,我們可以直接將__address__這個label進行replace,這樣就可以重新制定Prometheus訪問的數據接口的地址。
第二個是__param_<name>。即,我們可以在relabel階段,通過對這個label進行處理,實現在訪問數據接口時攜帶指定的參數和內容。
舉例如下:
scrape_configs: - job_name: 'prometheus' static_configs: - targets: - localhost:3306 - localhost:3308 - params "collect[]": - *** relabel_config - source_labels: ['targets'] target_label: __address__ - source_labels: ['__address__'] target_label: __param_instance - source_labels: ['__address__'] replacement: localhost:9104
假設我們在本地3306和3308兩個端口起了兩個mysql,然后再9104起了定制化的exporter。
先看relabel,我們把targets放入__address__,然后將__address__放入__param_instance,這樣原來的target就作為訪問數據接口的參數instance。而訪問接口的地址被replace成localhost:9104。
這樣就通過在exporter的數據接口增加參數,結合Prometheus配置中的relabel,實現了利用單個mysqld_exporter對多個數據庫實例進行監控。
如果需要更深的定制化,比如通過sql語句指定采集的數據等,用mysqld_exporter就不合適了。為了實現這個功能,需要實現一個獨立的Colletor,這樣開發成本較高。
對於自定義sql語句這個需求,可以使用sql_exporter實現。詳情可以參考https://github.com/free/sql_exporter。
