監控系統在這裏特指對數據中心的監控,主要針對數據中心內的硬件和軟件進行監控和告警。企業的 IT 架構逐步從傳統的物理服務器,遷移到以虛擬機爲主導的 IaaS 雲。不管基礎架構如何調整,都離不開監控系統的支持。html
不只如此。愈來愈複雜的數據中心環境對監控系統提出了更愈來愈高的要求:須要監控不一樣的對象,例如容器,分佈式存儲,SDN網絡,分佈式系統。各類應用程序等,種類繁多,還須要採集和存儲大量的監控數據,例如天天數TB數據的採集彙總。以及基於這些監控數據的智能分析,告警及預警等。node
在每一個企業的數據中心內,或多或少都會使用一些開源或者商業的監控系統。從監控對象的角度來看,能夠將監控分爲網絡監控,存儲監控,服務器監控和應用監控等,由於須要監控數據中心的各個方面。因此監控系統須要作到面面俱到,在數據中心中充當「天眼「角色。linux
網絡性能監控:主要涉及網絡監測,網絡實時流量監控(網絡延遲、訪問量、成功率)和歷史數據統計、彙總和歷史數據分析等功能。ios
網絡***檢測:主要針對內網或者外網的網絡***。如DDoS***的。經過分析異常流量來肯定網絡***行爲。web
設備監控:主要針對數據中心內的多種網絡設備進行監控。包括路由器,防火牆和交換機等硬件設備,能夠經過snmp等協議收集數據。正則表達式
存儲性能監控方面:存儲一般監控塊的讀寫速率,IOPS。讀寫延遲,磁盤用量等;文件存儲一般監控文件系統inode。讀寫速度、目錄權限等。數據庫
存儲系統監控方面:不一樣的存儲系統有不一樣的指標,例如,對於ceph存儲須要監控OSD, MON的運行狀態,各類狀態pg的數量以及集羣IOPS等信息。後端
存儲設備監控方面:對於構建在x86服務器上的存儲設備,設備監控經過每一個存儲節點上的採集器統一收集磁盤、SSD、網卡等設備信息;存儲廠商以黑盒方式提供商業存儲設備,一般自帶監控功能,可監控設備的運行狀態,性能和容量的。api
CPU:涉及整個 CPU 的使用量、用戶態百分比、內核態百分比,每一個 CPU 的使用量、等待隊列長度、I/O 等待百分比、CPU 消耗最多的進程、上下文切換次數、緩存命中率等。瀏覽器
內存:涉及內存的使用量、剩餘量、內存佔用最高的進程、交換分區大小、缺頁異常等。
網絡 I/O:涉及每一個網卡的上行流量、下行流量、網絡延遲、丟包率等。
磁盤 I/O:涉及硬盤的讀寫速率、IOPS、磁盤用量、讀寫延遲等。
消息中間件: RabbitMQ、Kafka
Web 服務中間件:Tomcat、Jetty
緩存中間件:Redis、Memcached
數據庫中間件:MySQL、PostgreSQL
APM主要是針對應用程序的監控,包括應用程序的運行狀態監控,性能監控,日誌監控及調用鏈跟蹤等。調用鏈跟蹤是指追蹤整個請求過程(從用戶發送請求,一般指瀏覽器或者應用客戶端)到後端API服務以及API服務和關聯的中間件,或者其餘組件之間的調用,構建出一個完整的調用拓撲結構,不只如此,APM 還能夠監控組件內部方法的調用層次(Controller-->service-->Dao)獲取每一個函數的執行耗時,從而爲性能調優提供數據支撐。
應用程序監控工具除了有 Pinpoint,還有 Twitter 開源的 Zipkin,Apache SkyWalking,美團開源的 CAT等。
經過 APM 除了能夠截獲方法調用,還能夠截獲TCP、HTTP網絡請求,從而得到執行耗時最長的方法和 SQL 語句、延遲最大的 API 的信息。
Prometheus 是一套開源的系統監控報警框架。它啓發於 Google 的 borgmon 監控系統,由工做在 SoundCloud 的 google 前員工在 2012 年建立,做爲社區開源項目進行開發,並於 2015 年正式發佈。2016 年,Prometheus 正式加入 Cloud Native Computing Foundation,成爲受歡迎度僅次於 Kubernetes 的項目。
強大的多維度數據模型:
Prometheus 生態圈中包含了多個組件,其中許多組件是可選的:
從這個架構圖,也能夠看出 Prometheus 的主要模塊包含, Server, Exporters, Pushgateway, PromQL, Alertmanager, WebUI 等。
它大體使用邏輯是這樣:
Prometheus 適用於記錄文本格式的時間序列,它既適用於以機器爲中心的監控,也適用於高度動態的面向服務架構的監控。在微服務的世界中,它對多維數據收集和查詢的支持有特殊優點。Prometheus 是專爲提升系統可靠性而設計的,它能夠在斷電期間快速診斷問題,每一個 Prometheus Server 都是相互獨立的,不依賴於網絡存儲或其餘遠程服務。當基礎架構出現故障時,你能夠經過 Prometheus 快速定位故障點,並且不會消耗大量的基礎架構資源。
Prometheus 很是重視可靠性,即便在出現故障的狀況下,你也能夠隨時查看有關係統的可用統計信息。若是你須要百分之百的準確度,例如按請求數量計費,那麼 Prometheus 不太適合你,由於它收集的數據可能不夠詳細完整。這種狀況下,你最好使用其餘系統來收集和分析數據以進行計費,並使用 Prometheus 來監控系統的其他部分。
Prometheus 全部採集的監控數據均以指標(metric)的形式保存在內置的時間序列數據庫當中(TSDB):屬於同一指標名稱,同一標籤集合的、有時間戳標記的數據流。除了存儲的時間序列,Prometheus 還能夠根據查詢請求產生臨時的、衍生的時間序列做爲返回結果。
每一條時間序列由指標名稱(Metrics Name)以及一組標籤(鍵值對)惟一標識。其中指標的名稱(metric name)能夠反映被監控樣本的含義(例如,http_requests_total
— 表示當前系統接收到的 HTTP 請求總量),指標名稱只能由 ASCII 字符、數字、下劃線以及冒號組成,同時必須匹配正則表達式 [a-zA-Z_:][a-zA-Z0-9_:]*
。
[info] 注意
冒號用來表示用戶自定義的記錄規則,不能在 exporter 中或監控對象直接暴露的指標中使用冒號來定義指標名稱。
經過使用標籤,Prometheus 開啓了強大的多維數據模型:對於相同的指標名稱,經過不一樣標籤列表的集合,會造成特定的度量維度實例(例如:全部包含度量名稱爲 /api/tracks
的 http 請求,打上 method=POST
的標籤,就會造成具體的 http 請求)。該查詢語言在這些指標和標籤列表的基礎上進行過濾和聚合。改變任何度量指標上的任何標籤值(包括添加或刪除指標),都會建立新的時間序列。
標籤的名稱只能由 ASCII 字符、數字以及下劃線組成並知足正則表達式 [a-zA-Z_][a-zA-Z0-9_]*
。其中以 __
做爲前綴的標籤,是系統保留的關鍵字,只能在系統內部使用。標籤的值則能夠包含任何 Unicode
編碼的字符。
在時間序列中的每個點稱爲一個樣本(sample),樣本由如下三部分組成:
經過以下表達方式表示指定指標名稱和指定標籤集合的時間序列:
<metric name>{<label name>=<label value>, ...}
例如,指標名稱爲 api_http_requests_total
,標籤爲 method="POST"
和 handler="/messages"
的時間序列能夠表示爲:
api_http_requests_total{method="POST", handler="/messages"}
這與 OpenTSDB 中使用的標記法相同。
Prometheus 的客戶端庫中提供了四種核心的指標類型。但這些類型只是在客戶端庫(客戶端能夠根據不一樣的數據類型調用不一樣的 API 接口)和在線協議中,實際在 Prometheus server 中並不對指標類型進行區分,而是簡單地把這些指標統一視爲無類型的時間序列。不過,未來咱們會努力改變這一現狀的。
例如 Prometheus server 中 http_requests_total
, 表示 Prometheus 處理的 http 請求總數,咱們可使用 delta
, 很容易獲得任意區間數據的增量,這個會在 PromQL 一節中細講。
例如 Prometheus server 中 go_goroutines
, 表示 Prometheus 當前 goroutines 的數量。
例如,查詢 prometheus_http_request_duration_seconds_sum{handler="/api/v1/query",instance="localhost:9090",job="prometheus"}時,返回結果以下:
Prometheus 中,將任意一個獨立的數據源(target)稱之爲實例(instance)。包含相同類型的實例的集合稱之爲做業(job)。 以下是一個含有四個重複實例的做業:
- job: api-server - instance 1: 1.2.3.4:5670 - instance 2: 1.2.3.4:5671 - instance 3: 5.6.7.8:5670 - instance 4: 5.6.7.8:5671
自生成標籤和時序
Prometheus 在採集數據的同時,會自動在時序的基礎上添加標籤,做爲數據源(target)的標識,以便區分:
job
: The configured job name that the target belongs to.instance
: The <host>:<port>
part of the target's URL that was scraped.若是其中任一標籤已經在此前採集的數據中存在,那麼將會根據 honor_labels
設置選項來決定新標籤。詳見官網解釋: scrape configuration documentation
對每個實例而言,Prometheus 按照如下時序來存儲所採集的數據樣本:
up{job="<job-name>", instance="<instance-id>"}
: 1 表示該實例正常工做up{job="<job-name>", instance="<instance-id>"}
: 0 表示該實例故障
scrape_duration_seconds{job="<job-name>", instance="<instance-id>"}
表示拉取數據的時間間隔
scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"}
表示採用重定義標籤(relabeling)操做後仍然剩餘的樣本數
scrape_samples_scraped{job="<job-name>", instance="<instance-id>"}
表示從該數據源獲取的樣本數其中 up
時序能夠有效應用於監控該實例是否正常工做。
在前言中,簡單介紹了咱們選擇 Prometheus 的理由,以及使用後給咱們帶來的好處。
在這裏主要和其餘監控方案對比,方便你們更好的瞭解 Prometheus。
在討論 Exporter 以前,有必要先介紹一下 Prometheus 文本數據格式,由於一個 Exporter 本質上就是將收集的數據,轉化爲對應的文本格式,並提供 http 請求。
Exporter 收集的數據轉化的文本內容以行 (\n
) 爲單位,空行將被忽略, 文本內容最後一行爲空行
文本內容,若是以 #
開頭一般表示註釋。
# HELP
開頭表示 metric 幫助說明。# TYPE
開頭表示定義 metric 類型,包含 counter
, gauge
, histogram
, summary
, 和 untyped
類型。內容若是不以 #
開頭,表示採樣數據。它一般緊挨着類型定義行,知足如下格式:
metric_name [ "{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}" ] value [ timestamp ]
下面是一個完整的例子:
# HELP http_requests_total The total number of HTTP requests. # TYPE http_requests_total counter http_requests_total{method="post",code="200"} 1027 1395066363000 http_requests_total{method="post",code="400"} 3 1395066363000 # Escaping in label values: msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9 # Minimalistic line: metric_without_timestamp_and_labels 12.47 # A weird metric from before the epoch: something_weird{problem="division by zero"} +Inf -3982045 # A histogram, which has a pretty complex representation in the text format: # HELP http_request_duration_seconds A histogram of the request duration. # TYPE http_request_duration_seconds histogram http_request_duration_seconds_bucket{le="0.05"} 24054 http_request_duration_seconds_bucket{le="0.1"} 33444 http_request_duration_seconds_bucket{le="0.2"} 100392 http_request_duration_seconds_bucket{le="0.5"} 129389 http_request_duration_seconds_bucket{le="1"} 133988 http_request_duration_seconds_bucket{le="+Inf"} 144320 http_request_duration_seconds_sum 53423 http_request_duration_seconds_count 144320 # Finally a summary, which has a complex representation, too: # HELP rpc_duration_seconds A summary of the RPC duration in seconds. # TYPE rpc_duration_seconds summary rpc_duration_seconds{quantile="0.01"} 3102 rpc_duration_seconds{quantile="0.05"} 3272 rpc_duration_seconds{quantile="0.5"} 4773 rpc_duration_seconds{quantile="0.9"} 9001 rpc_duration_seconds{quantile="0.99"} 76656 rpc_duration_seconds_sum 1.7560473e+07 rpc_duration_seconds_count 2693
須要特別注意的是,假設採樣數據 metric 叫作 x
, 若是 x
是 histogram
或 summary
類型必需知足如下條件:
x_sum
。x_count
。summary
類型的採樣數據的 quantile 應表示爲 x{quantile="y"}
。histogram
類型的採樣分區統計數據將表示爲 x_bucket{le="y"}
。histogram
類型的採樣必須包含 x_bucket{le="+Inf"}
, 它的值等於 x_count
的值。summary
和 historam
中 quantile
和 le
必需按從小到大順序排列。收集到 node_exporter 的數據後,咱們可使用 PromQL 進行一些業務查詢和監控,下面是一些比較常見的查詢。
注意:如下查詢均以單個節點做爲例子,若是你們想查看全部節點,將 instance="xxx"
去掉便可。
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
avg by (instance, mode) (irate(node_cpu_seconds_total[5m])) * 100
node_load1{instance="xxx"} // 1分鐘負載 node_load5{instance="xxx"} // 5分鐘負載 node_load15{instance="xxx"} // 15分鐘負載
100 - ((node_memory_MemFree_bytes+node_memory_Cached_bytes+node_memory_Buffers_bytes)/node_memory_MemTotal_bytes) * 100
100 - node_filesystem_free{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} / node_filesystem_size{instance="xxx",fstype!~"rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*"} * 100
或者你也能夠直接使用 {fstype="xxx"} 來指定想查看的磁盤信息
// 上行帶寬 sum by (instance) (irate(node_network_receive_bytes{instance="xxx",device!~"bond.*?|lo"}[5m])/128) // 下行帶寬 sum by (instance) (irate(node_network_transmit_bytes{instance="xxx",device!~"bond.*?|lo"}[5m])/128)
// 入包量 sum by (instance) (rate(node_network_receive_bytes{instance="xxx",device!="lo"}[5m])) // 出包量 sum by (instance) (rate(node_network_transmit_bytes{instance="xxx",device!="lo"}[5m]))