在現代 Web 應用程式開發中,監控扮演著至關重要的角色。本文將以一個名為 mwp-rails 的 Rails 應用程式為例,示範如何使用 Prometheus 監控系統收集應用程式指標,包含使用者刪除、使用者建立等關鍵事件。我們將使用 prometheus-client gem 建立 Prometheus 客戶端,並定義一個 Metrics 模組來管理不同型別的指標,例如計數器、計時器等。接著,我們會將這些指標整合到 Rails 應用程式的控制器和模型中,並設定一個 /metrics 端點,以便 Prometheus 伺服器可以抓取這些指標資料。最後,我們將組態 Prometheus 伺服器,使其能夠定期從我們的 Rails 應用程式收集指標,實作全面的應用程式監控。
第8章:應用程式檢測
應用程式監控簡介
讓我們來看一些應用程式監控的基本原則。首先,在任何良好的應用程式開發方法論中,在構建之前識別您想要構建的內容是一個好主意。監控也不例外。不幸的是,在應用程式開發中有一個常見的反模式,即將監控和其他維運功能(如安全)視為應用程式的附加元件,而不是核心功能。如果您正在為您的應用程式構建規格或使用者故事,請為您的應用程式的每個元件包含監控。不構建指標或監控是一個嚴重的業務和維運風險,可能導致:
- 無法識別或診斷故障。
- 無法衡量應用程式的維運效能。
- 無法衡量應用程式或元件的業務效能和成功,例如跟蹤銷售資料或交易價值。
另一個常見的反模式是檢測不足。我們始終建議您對應用程式進行過度檢測。人們常常抱怨資料太少,但很少有人擔心資料太多。
注意:在儲存容量的限制下,您的監控因超出容量而停止工作顯然是不可取的。通常,將保留時間作為減少儲存而不丟失有用資訊的主要方法是有用的。
第三,如果您使用多個環境(例如開發、測試、預發布和生產),請確保您的監控組態提供標籤,以便您知道資料來自特定的環境。這樣,您可以對監控和指標進行分割槽。我們將在本章後面詳細討論這一點。
內容解密:
- 應用程式監控的重要性:監控是應用程式開發的核心功能之一,不應被視為附加功能。
- 檢測不足的問題:過度檢測通常比檢測不足要好,因為人們更傾向於抱怨資料不足。
- 多環境監控:在多環境設定中,確保監控組態能夠區分不同環境的資料非常重要。
這些原則將幫助您設計和實施有效的應用程式監控策略,從而提高應用程式的可靠性和效能。
第8章:應用程式檢測
應該在哪裡進行檢測?
在應用程式中新增檢測的最佳起點是入口和出口點。例如:
- 測量請求和回應的數量和時間,例如特定網頁或API端點的請求和回應數量及時間。如果正在檢測現有的應用程式,請根據重要性列出特定的頁面或端點,並按重要性順序進行檢測。
- 測量對外部服務和API的呼叫次數和時間,例如應用程式使用資料函式庫、快取或搜尋服務,或使用第三方服務如支付閘道。
- 測量作業排程、執行和其他週期性事件(如cron作業)的次數和時間。
- 測量重要的業務和功能事件的次數和時間,例如使用者建立或交易(如支付和銷售)。
檢測分類別法
應該確保指標按照應用程式、方法、功能或類別似的標記進行分類別和明確標識,以便了解指標的生成位置和內容。第4章中討論了標籤分類別法。
指標
與大多數監控一樣,指標將是應用程式監控的關鍵。那麼應該在應用程式中監控什麼?將關注兩種廣泛的指標型別,儘管這兩種型別之間存在相當大的重疊:
- 應用程式指標:通常測量應用程式碼的狀態和效能。
- 業務指標:通常測量應用程式的價值。例如,在電子商務網站上,可能會測量銷售數量。
本章將探討這兩種型別的指標範例,並指出Prometheus傾向於關注更即時的指標。對於長期業務指標,在許多情況下可能會使用根據事件的系統。
應用程式指標
應用程式指標測量應用程式的效能和狀態。它們包括應用程式終端使用者經驗的特性,如延遲和回應時間。在此基礎上,測量應用程式的吞吐量:請求、請求量、交易和交易時間。
提示:衡量應用程式效能的好例子是前面提到的USE和RED方法以及Google Golden Signals。
還會檢查應用程式的功能和狀態。一個好的例子可能是成功和失敗的登入或錯誤、當機和故障。也可能測量活動(如作業、電子郵件或其他非同步活動)的數量和效能。
業務指標
業務指標是比應用程式指標更高一層的指標。它們通常與應用程式指標相關聯。如果考慮測量對特定服務的請求數量作為應用程式指標,那麼業務指標通常會對請求內容進行處理。應用程式指標的一個例子可能是測量支付交易的延遲;相應的業務指標可能是每個支付交易的價值。業務指標可能包括新使用者/客戶數量、銷售數量、按價值或位置劃分的銷售額,或任何其他有助於衡量業務狀態的指標。
指標放置位置
一旦瞭解要監控和測量的內容,就需要確定指標的位置。在幾乎所有情況下,將這些指標放在程式碼內是最好的選擇,盡可能接近正在嘗試監控或測量的操作。但是,不希望將指標組態內嵌在每個想要記錄指標的地方。相反,希望建立一個實用程式函式庫:一個允許從集中設定建立各種指標的函式。這有時被稱為實用程式模式:一個不要求例項化且只有靜態方法的指標實用程式類別。
實用程式模式
一個常見的模式是使用現有的客戶端建立一個實用程式函式庫或模組。該實用程式函式庫將公開一個API,允許建立和增加指標。然後可以在整個程式碼函式庫中使用此API來檢測感興趣的應用程式區域。
讓我們來看一個例子。我們建立了一些類別似Ruby的程式碼來演示,並假設已經建立了一個名為Metric的實用程式函式庫。
# 清單8.1:一個示例支付方法
include Metric
def pay_user(user, amount)
pay(user.account, amount)
Metric.increment 'payment'
Metric.increment "payment-amount-#{amount.to_i}"
send_payment_notification(user.email)
end
def send_payment_notification(email)
send_email(payment, email)
Metric.increment 'email-payment'
end
內容解密:
include Metric:引入名為Metric的實用程式函式庫,該函式庫提供了一系列靜態方法來處理指標。pay_user方法:定義了一個名為pay_user的方法,該方法接受user和amount兩個引數。它首先呼叫了pay方法進行支付處理,然後透過Metric.increment方法增加了兩個指標:payment和payment-amount-<金額>。最後,它呼叫了send_payment_notification方法傳送支付通知。Metric.increment 'payment':每次成功呼叫pay_user方法(即每次支付操作)時,增加名為payment的計數器,用於記錄支付操作的總次數。Metric.increment "payment-amount-#{amount.to_i}":根據支付金額增加相應的計數器,例如,如果支付金額是100,那麼就會增加名為payment-amount-100的計數器,用於記錄不同金額區間的支付次數。
send_payment_notification方法:定義了一個名為send_payment_notification的方法,該方法接受email作為引數。它呼叫了send_email方法傳送電子郵件通知,並增加了一個名為email-payment的計數器,用於記錄傳送支付通知電子郵件的次數。
在這個例子中,首先包含了Metric實用程式函式庫。可以看到同時指定了應用程式和業務指標。首先定義了一個名為pay_user的方法,該方法接受使用者和金額作為引數。然後使用資料進行了支付,並在第一個方法中增加了兩個指標:
- 一個支付指標:在每次進行支付時增加該指標。
- 一個支付金額指標:該指標按金額記錄每筆支付。
最後,使用第二個方法
send_payment_notification發送了電子郵件,其中增加了第三個指標:email-payment。該指標計算傳送的支付電子郵件數量。
第8章:應用程式監控
外部模式
如果無法控制程式碼函式庫,或無法在程式碼中插入監控或測量指標,又或者有無法更改或更新的舊版應用程式該怎麼辦?此時,需要在應用程式周圍找到下一個最接近的位置。最明顯的位置是應用程式的輸出和外部子系統,例如資料函式庫或快取。
如果應用程式輸出日誌,則可以識別日誌包含的內容,並檢視是否可以使用其內容來測量應用程式的行為。通常,可以透過記錄特定日誌條目的計數來跟蹤事件的頻率。如果應用程式在其他系統中記錄或觸發事件,例如資料函式庫交易、任務排程、電子郵件傳送、身份驗證或授權系統的呼叫、快取或資料儲存,則可以使用這些事件中包含的資料或特定事件的計數來記錄應用程式的效能。
我們將在第9章中進一步討論這一點。
為範例應用程式構建指標
現在,我們已經對監控應用程式有了一些背景,接下來讓我們看一個如何在真實世界中實作它的例子。我們將構建一個利用工具函式庫從應用程式傳送事件的應用程式。我們使用Rails Composer建立了一個範例Rails應用程式,並將其命名為mwp-rails(Monitoring with Prometheus Rails application)。mwp-rails應用程式允許我們建立和刪除使用者並登入應用程式。
新增Prometheus客戶端
首先,我們需要在應用程式中新增對Prometheus的支援,使用根據Ruby的客戶端。prometheus-client gem允許我們在應用程式內建立Prometheus客戶端。
有多個平台的客戶端,包括:
- Go
- Java/JVM
- Python
還有大量第三方客戶端適用於各種框架和語言。
在Gemfile中新增prometheus-client gem
source 'https://rubygems.org'
ruby '2.4.2'
gem 'rails', '5.1.5'
...
gem 'prometheus-client'
...
然後,使用bundle命令安裝新的gem。
$ sudo bundle install
Fetching gem metadata from https://rubygems.org/...
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
...
Installing prometheus-client 0.7.1
...
接下來,我們可以使用Rails控制檯測試客戶端。啟動Rails控制檯並執行以下命令:
$ rails c
Loading development environment (Rails 4.2.4)
[1] pry(main)> prometheus = Prometheus::Client.registry
[2] pry(main)> test_counter = prometheus.counter(:test_counter, 'A test counter')
=> #<Prometheus::Client::Counter:0x00007f9aea051dd8
@base_labels={},
@docstring="A test counter",
@mutex=#<Thread::Mutex:0x00007f9aea051d88>,
@name=:test_counter,
@validator=#<Prometheus::Client::LabelSetValidator:0x00007f9aea051d10 @validated={}>,
@values={}>
[3] pry(main)> test_counter.increment
=> 1.0
建立Prometheus登入檔和指標
prometheus = Prometheus::Client.registry
test_counter = prometheus.counter(:test_counter, 'A test counter')
test_counter.increment
登入檔是Prometheus應用程式監控的核心。每個指標都需要先註冊。我們建立了一個名為prometheus的登入檔,並在其中建立了一個名為test_counter的指標。
新增指標到Rails應用程式
為了避免每次要記錄指標時都手動建立登入檔和指標,我們建立了一個Metrics模組放在lib目錄下,用於簡化指標的建立和管理。
建立Metrics模組
module Metrics
def self.counter(name, docstring, base_labels = {})
provide_metric(name) || registry.counter(name, docstring, base_labels)
end
def self.summary(name, docstring, base_labels = {})
provide_metric(name) || registry.summary(name, docstring, base_labels)
end
def self.gauge(name, docstring, base_labels = {})
provide_metric(name) || registry.gauge(name, docstring, base_labels)
end
def self.histogram(name, docstring, base_labels = {}, buckets = ::Prometheus::Client::Histogram::DEFAULT_BUCKETS)
provide_metric(name) || registry.histogram(name, docstring, base_labels, buckets)
end
private
def self.provide_metric(name)
registry.get(name)
end
def self.registry
@registry || ::Prometheus::Client.registry
end
end
Metrics模組提供了多種指標型別的方法。這些方法會檢查登入檔中是否已存在同名的指標,如果不存在,則建立新的指標。
指標型別的詳細說明
此段落將詳細說明不同型別的指標,包括counter、gauge、histogram和summary等。
counter型別指標詳細說明
test_counter = prometheus.counter(:test_counter, 'A test counter')
counter型別指標用於記錄累計值,例如請求次數等。
程式碼解密:
prometheus.counter方法用於建立一個counter型別指標。:test_counter是指標的名稱,需要是唯一的。'A test counter'是指標的描述,用於說明指標的用途。
第8章:應用程式監控
要對應用程式進行監控,首先需要擴充Rails以載入我們的Metrics函式庫。最簡單的方法是新增一個初始化檔案。
建立Metrics函式庫的初始化檔案
首先,我們需要建立一個新的初始化檔案:
$ touch config/initializers/lib.rb
然後,在這個檔案中引入我們的Metrics函式庫:
# config/initializers/lib.rb
require 'metrics'
內容解密:
這裡我們手動載入metrics函式庫。雖然我們也可以透過設定autoload_paths來自動載入lib目錄下的所有內容,但手動載入可以讓我們更好地控制載入的內容。
新增監控指標
接下來,我們可以在某些方法中新增監控指標。例如,當使用者被刪除時,我們可以增加一個計數器。
def destroy
user = User.find(params[:id])
user.destroy
Metrics.counter(:users_deleted_counter, "Deleted users counter").increment
redirect_to users_path, :notice => "User deleted."
end
內容解密:
在這段程式碼中,我們呼叫了Metrics.counter方法,建立了一個名為:users_deleted_counter的計數器,並給出了描述"Deleted users counter"。然後,我們呼叫了increment方法,使計數器加1。這樣,每當有使用者被刪除時,計數器就會增加。
我們也可以為計數器新增標籤或指定增量值:
.increment({ service: 'foo' }, 2)
這將使計數器增加2,並新增一個名為service、值為foo的標籤。
在User模型中新增計數器
我們也可以在User模型中新增一個計數器,用於記錄新使用者的建立。
class User < ActiveRecord::Base
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
after_create do
Metrics.counter(:user_created_counter, "Users created counter").increment
end
# ...
end
內容解密:
在這裡,我們使用了Active Record的回呼機制,在使用者建立後自動增加一個名為:user_created_counter的計數器。
將監控指標暴露給Prometheus
為了讓Prometheus能夠抓取我們的監控指標,我們需要在config.ru檔案中新增一些設定。
require 'prometheus/middleware/collector'
require 'prometheus/middleware/exporter'
use Prometheus::Middleware::Collector
use Prometheus::Middleware::Exporter
內容解密:
這裡我們引入了Prometheus客戶端的兩個元件:Collector和Exporter。Exporter會建立一個名為/metrics的路由,包含應用程式中定義的所有監控指標。Collector則會收集一些HTTP伺服器的監控指標。
檢視監控指標
當我們存取/metrics端點時,可以看到一些監控指標,例如:
# HELP http_server_requests_total The total number of HTTP requests handled by the Rack application.
http_server_requests_total{code="200",method="get",path="/"} 2.0
# HELP http_server_request_duration_seconds The HTTP response duration of the Rack application.
http_server_request_duration_seconds_bucket{method="get",path="/",le="0.005"} 0.0
http_server_request_duration_seconds_bucket{method="get",path="/",le="0.01"} 0.0
# HELP users_updated_counter Users updated counter
users_updated_counter 1.0
在Prometheus中設定作業來抓取監控指標
接下來,我們需要在Prometheus中設定一個作業來抓取我們的/metrics端點。首先,我們需要建立一個JSON檔案,包含我們的Rails伺服器的主機名稱:
[{
"targets": ["mwp-rails1.example.com", "mwp-rails2.example.com", "mwp-rails3.example.com"]
}]
然後,在prometheus.yml設定檔中新增一個作業:
- job_name: rails
file_sd_configs:
- files:
- targets/rails/*.json
refresh_interval: 5m
內容解密:
這裡我們定義了一個名為rails的作業,使用檔案服務發現機制來抓取我們的Rails伺服器的監控指標。