利用Prometheus 打造企業分佈式監控平臺(2)--服務發現
摘要:官方推薦的方式:按照官方的接口約定,實現新的服務發現方式,將發現的目標和標籤寫到文件中,然後結合prometheus本身支持的file_sd方式。爲了解決這個問題,最近,Prometheus存儲庫中的文檔目錄進行了一些小的代碼更改,並提供了一個示例,以實現自定義服務發現集成,而無需將其合併到Prometheus主分支中。
服務發現在Wikipedia的描述是:
Service discovery is the automatic detection of devices and services offered by these devices on a computer network.
換句話說,它允許應用程序動態發現服務,而不是在應用程序配置中靜態定義服務。
對於Prometheus,可以使用多種方法進行服務發現,包括雲提供商API(例如AWS,Azure,GCE,Openstack),基於DNS的發現(使用SRV記錄)以及查詢Kubernetes API中正在運行的服務。
可想而知,在目前雲原生環境下,應用具備高度彈性,通過靜態配置監控目標的行爲是多麼的低效。
Hotreload
當然Prometheus 提供了Hotreload機制,在配置文件變更的時候,可以通知Prometheus進行reload。而且在reload的過程中,服務不會停機
熱更新的加載方法有兩種:
- kill -hub pid
- curl -X POST http://localhost :9090/-/reload
當然新版本的Prometheus 的熱加載功能默認是關閉的,你需要在Prometheus的啓動參數中,添加如下參數:
--web.enable-lifecycle
但是這種方式,並不是最優雅的,你需要維護整個配置文件。
服務發現
除靜態配置之外,Prometheus 已經支持瞭如下的服務發現方式:
# List of Azure service discovery configurations. azure_sd_configs: [ - <azure_sd_config> ... ] # List of Consul service discovery configurations. consul_sd_configs: [ - <consul_sd_config> ... ] # List of DNS service discovery configurations. dns_sd_configs: [ - <dns_sd_config> ... ] # List of EC2 service discovery configurations. ec2_sd_configs: [ - <ec2_sd_config> ... ] # List of OpenStack service discovery configurations. openstack_sd_configs: [ - <openstack_sd_config> ... ] # List of file service discovery configurations. file_sd_configs: [ - <file_sd_config> ... ] # List of GCE service discovery configurations. gce_sd_configs: [ - <gce_sd_config> ... ] # List of Kubernetes service discovery configurations. kubernetes_sd_configs: [ - <kubernetes_sd_config> ... ] # List of Marathon service discovery configurations. marathon_sd_configs: [ - <marathon_sd_config> ... ] # List of AirBnB's Nerve service discovery configurations. nerve_sd_configs: [ - <nerve_sd_config> ... ] # List of Zookeeper Serverset service discovery configurations. serverset_sd_configs: [ - <serverset_sd_config> ... ] # List of Triton service discovery configurations. triton_sd_configs: [ - <triton_sd_config> ... ]
下圖是一個Prometheus + consul sd 的架構。對於線上環境我們可能會劃分爲:dev, stage, prod不同的集羣。每一個集羣運行多個主機節點,每個服務器節點上運行一個Node Exporter實例。Node Exporter實例會自動註冊到Consul中,而Prometheus則根據Consul返回的Node Exporter實例信息動態的維護Target列表,從而向這些Target輪詢監控數據。
當然目前官方對於增加新的服務發現方式比較慎重,與Alertmanager 通知類型情況類似,官方不希望新的不穩定服務發現方式會影響Prometheus自身的穩定性。
能夠與其他SD機制(例如Docker Swarm)集成是源源不斷的需求。爲了解決這個問題,最近,Prometheus存儲庫中的文檔目錄進行了一些小的代碼更改,並提供了一個示例,以實現自定義服務發現集成,而無需將其合併到Prometheus主分支中。
例如,我在實際落地Prometheus的工程中,增加了下面兩種服務發現方式:
- 基於負載均衡器服務自動發現:根據負載均衡器的地址,獲取後端服務器組。在Cloud上,後端服務器組大多要配置HPA,決定了需要動態的發現。
- 基於自動伸縮組的服務自動發現:有些任務型工作負載,根據工作負載來擴縮worker節點數,比如根據消息隊列的消息堆積數,來擴縮消費者的數量。此時根據自動伸縮組,獲取實際運行的Worker節點。
如何實現自定義服務發現?
官方推薦的方式:按照官方的接口約定,實現新的服務發現方式,將發現的目標和標籤寫到文件中,然後結合prometheus本身支持的file_sd方式。Prometheus會定期到指定文件中獲取最新的目標。
如下所示:
scrape_configs: - job_name: "custom-sd" scrape_interval: "15s" file_sd_configs: - files: - /path/to/custom_sd.json
Adapter
首先了解一下adapter.go文件,您可以複製此文件以實現自定義SD實現。
// Adapter runs an unknown service discovery implementation and converts its target groups // to JSON and writes to a file for file_sd. type Adapter struct { ctx context.Context disc discovery.Discoverer groups map[string]*customSD manager *discovery.Manager output string name string logger log.Logger } // Run starts a Discovery Manager and the custom service discovery implementation. func (a *Adapter) Run() { go a.manager.Run() a.manager.StartCustomProvider(a.ctx, a.name, a.disc) go a.runCustomSD(a.ctx) }
Adapter利用 Discovery.Manager
在goroutine中啓動自定義SD提供程序的Run函數。Manager有一個通道,自定義SD將向其發送更新。這些更新包含SD目標。groups字段包含所有目標和標籤。
type customSD struct { Targets []string `json:"targets"` Labels map[string]string `json:"labels"` }
存在這個 customSD
結構主要是爲了幫助我們將內部Prometheus targetgroup.Group
結構轉換爲JSON以用於 file_sd
格式。
運行時,Adapter將在通道上監聽來自我們的自定義SD實現的更新,接收到更新後,它將解析 targetgroup.Groups
到另一個映射 [string]* customSD
中,並將其存儲在Adapter,如果兩者不同,我們將新組分配給Adapter結構,並將它們作爲JSON寫入輸出文件中。
自定義SD實現
現在我們要實際使用Adapter來實現我們自己的自定義SD。完整的工作示例位於此處的 examples目錄 中。
在這裏,您可以看到我們導入Adapter代碼“ github.com/prometheus/prometheus/documentation/examples/custom-sd/adapter”以及其他一些Prometheus庫。爲了編寫自定義SD,我們需要一個實現Discoverer接口:
// Discoverer provides information about target groups. It maintains a set // of sources from which TargetGroups can originate. Whenever a discovery provider // detects a potential change, it sends the TargetGroup through its channel. // // Discoverer does not know if an actual change happened. // It does guarantee that it sends the new TargetGroup whenever a change happens. // // Discoverers should initially send a full set of all discoverable TargetGroups. type Discoverer interface { // Run hands a channel to the discovery provider(consul,dns etc) through which it can send // updated target groups. // Must returns if the context gets canceled. It should not close the update // channel on returning. Run(ctx context.Context, up chan<- []*targetgroup.Group) }
我們實際上只需要實現一個函數 Run(ctx context.Context,up chan <-[] * targetgroup.Group)
。這是Adapter代碼中的管理器將在goroutine中調用的函數。Run函數包含了知道何時退出的上下文,並傳遞了用於發送目標組更新的通道。
查看提供的示例中的Run函數,我們可以看到在另一個SD的實現中需要做的一些關鍵事情。
總結
如果實際場景中,公司已經有統一的服務註冊中心或是配置中心,那麼完全可以自定義SD,這樣的好處是,利用了現有的基礎設施,實現無縫對接。
另外一點是,Prometheus 中kubernetes SD的方式,對於容器化部署的業務,更加簡單。
歡迎關注我們的微信公衆號,每天學習Go知識