摘要:官方推薦的方式:按照官方的接口約定,實現新的服務發現方式,將發現的目標和標籤寫到文件中,然後結合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的過程中,服務不會停機

熱更新的加載方法有兩種:

當然新版本的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知識

相關文章