使用Quickwit、Jaeger和Grafana監控您的Rust應用程序
你可能已經看過了Lucas Palmieri的博客文章Are we observable yet? An introduction to Rust telemetry。如果你還沒有看過,我們建議閱讀一下,因為它提供了一個全面的介紹,介紹了如何處理 Rust 代碼中的日志。
然而,僅僅記錄日志可能是不夠的,特別是在分布式架構中。在 Quickwit 中,我們經常使用跟蹤來理解性能瓶頸并提高速度。當我們遇到 Quickwit 的搜索響應緩慢時,我們經常會問自己:是什么導致了減速?是網絡相關的問題,磁盤 I/O 還是過多的 CPU 使用?
在本博客文章中,我們將展示如何為 Rust 應用程序進行測量,并生成跟蹤數據,從 DevOps 視角利用它們。我們的目標將是雙重的:
使用廣泛認可的 Jaeger UI 分析跟蹤,以獲取有關應用程序行為的見解。
從這些跟蹤數據中派生 RED(速率、錯誤和持續時間)指標,并在 Grafana 中監視它們。如果您想進一步了解,我們建議參考以下資源:Weaveworks 的 RED 方法和 Google SRE 書籍中有關監控分布式系統的部分。
現在,讓我們深入介紹步驟,其中我們將涵蓋以下關鍵方面:
將您的跟蹤和指標數據推送到 Quickwit。
在 Jaeger UI 中檢測、診斷和解決問題。
在 Grafana 中監視您的應用程序的 RED 指標(速率、錯誤、持續時間)。
在深入了解之前,請確保您的系統上已安裝并正確運行以下軟件:
Rust 1.68+
Docker
如果您仍在運行舊版本的 Docker,則需要安裝docker-compose。
構建并測量 Rust 應用
我們將使用 Actix Web 框架創建一個基本的 Rust 應用程序。這個應用程序是一個包含單個端點的 Web API。它將從受歡迎的 JSONPlaceholder 公共 Web API 獲取帖子及其評論,并將它們顯示為 JSON。為了更好地了解我們的應用程序生命周期并可能優化它,我們將確保測量以下例程:
從 /posts 獲取帖子。
獲取每個帖子的評論 /posts/1/comments
創建一個名為rust-app-tracing的新目錄。在終端中切換到該目錄,并運行以下命令初始化一個新的 Rust 項目。
cargo new web-api
讓我們還要確保在web-api/Cargo.toml文件中擁有所需的依賴項。
actix-web:用于在 Rust 中構建 Web 應用程序的快速 Web 框架。
actix-web-opentelemetry:actix-web框架的 open-telemetry 擴展。
opentelemetry:Rust 的核心 open-telemetry SDK,包括跟蹤和指標。
opentelemetry-otlp:提供各種 open-telemetry 導出器的 crate。
reqwest:提供一個直觀的 API 來進行 HTTP 請求。
tokio:為我們的應用程序提供異步運行時。
Web API 應用程序代碼
首先,讓我們通過創建一個名為telemetry.rs的文件來配置應用程序跟蹤,我們將在其中處理所有跟蹤配置。
// telemetry.rs ... const SERVICE_NAME: &'static str = "quickwit-jaeger-demo"; pub fn init_telemetry(exporter_endpoint: &str) { // Create a gRPC exporter let exporter = opentelemetry_otlp::new_exporter() .tonic() .with_endpoint(exporter_endpoint); // Define a tracer let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter(exporter) .with_trace_config( trace::new(vec![KeyValue::new( opentelemetry_semantic_conventions::SERVICE_NAME, SERVICE_NAME.to_string(), )])), ) .install_batch(opentelemetry::Tokio) .expect("Error: Failed to initialize the tracer."); // Define a subscriber. let subscriber = Registry::default(); // Level filter layer to filter traces based on level (trace, debug, info, warn, error). let level_filter_layer = EnvFilter::new("INFO")); // Layer for adding our configured tracer. let tracing_layer = tracing_opentelemetry::layer().with_tracer(tracer); // Layer for printing spans to stdout let formatting_layer = BunyanFormattingLayer::new( SERVICE_NAME.to_string(), std::stdout, ); global::new()); subscriber .with(level_filter_layer) .with(tracing_layer) .with(JsonStorageLayer) .with(formatting_layer) .init() }
Copy 接下來,我們將實現我們的 API 端點,并在處理程序函數中添加一些測量代碼。重要的是要注意,我們的重點不在于此應用程序的功能,而在于從應用程序生成有意義且可利用的跟蹤數據。
首先,我們有一些模型文件,允許我們對post和comment進行序列化和反序列化。
//models.rs ... #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Post { pub user_id: i64, pub id: i64, pub title: String, pub body: String, #[serde(default)] pub comments: Vec, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Comment { pub post_id: i64, pub id: i64, pub name: String, pub email: String, pub body: String, }
接下來,讓我們處理 API 端點處理程序。請注意,某些函數上裝飾有instrument屬性。這是我們如何在處理程序函數和它用于執行任務的后續函數上啟用跟蹤的方法。
// lib.rs ... const BASE_API_URL: &'static str = "https://jsonplaceholder.typicode.com"; // The get_post handler #[instrument(level = "info", name = "get_posts", skip_all)] #[get("")] async fn get_posts() -> Result { // Randomly simulate errors in request handling let choices = [200, 400, 401, 200, 500, 501, 200, 500]; let mut rng = rand::thread_rng(); let choice = choices.choose(&mut rng) .unwrap() .clone(); match choice { 400..=401 => Ok(HttpResponse::from_u16(choice).unwrap())), 500..=501 => Ok(HttpResponse::from_u16(choice).unwrap())), _ => { let posts = fetch_posts(20) .await .map_err(actix_web::ErrorInternalServerError)?; Ok(HttpResponse::Ok().json(posts)) } } } // Fetching posts with a limit. #[instrument(level = "info", name = "fetch_posts")] async fn fetch_posts(limit: usize) -> anyhow::Result
在上面的片段中,我們僅發送跟蹤。也可以使用可靠的日志收集器來收集日志并將其發送到Quickwit或其他后端。
使用 Quickwit 收集跟蹤數據
現在我們已經構建了應用程序。讓我們與 Quickwit 一起運行,并確保生成的跟蹤被 Quickwit 索引。 與我們在之前的博客文章中所做的不同,我們將創建一個 docker-compose 文件來簡化 Quickwit、Jaeger 和 Grafana 之間的設置。以下 docker-compose 文件包含所有必要的配置。
QW_ENABLE_OTLP_ENDPOINT:允許 Quickwit 接受和攝取跟蹤和日志數據。
SPAN_STORAGE_TYPE、GRPC_STORAGE_SERVER、QW_ENABLE_JAEGER_ENDPOINT:允許 Jaeger 從 Quickwit 拉取跟蹤和日志以進行分析。
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS:允許我們在 Grafana 中加載特定的插件。
# docker-compose.yaml version: '3' services: quickwit: image: quickwit/quickwit:latest command: run restart: always environment: QW_ENABLE_OTLP_ENDPOINT: true QW_ENABLE_JAEGER_ENDPOINT: true ports: - '7280:7280' - '7281:7281' volumes: - ./qwdata:/quickwit/qwdata jaeger: image: jaegertracing/jaeger-query:latest restart: always depends_on: - quickwit environment: SPAN_STORAGE_TYPE: 'grpc-plugin' GRPC_STORAGE_SERVER: 'quickwit:7281' ports: - '16686:16686' grafana: image: grafana/grafana-enterprise:latest restart: always user: root depends_on: - quickwit environment: GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: 'quickwit-quickwit-datasource' ports: - '3000:3000' volumes: - ./grafana-storage:/var/lib/grafana
有了這個 docker-compose 文件,讓我們在項目目錄中創建所需的目錄以使服務正確運行。創建qwdata目錄以存儲 Quickwit 數據。 然后,下載并將 Quickwit Grafana 數據源插件放置在預期位置。
wget https://github.com/quickwit-oss/quickwit-datasource/releases/download/v0.2.0/quickwit-quickwit-datasource-0.2.0.zip && mkdir -p grafana-storage/plugins && unzip quickwit-quickwit-datasource-0.2.0.zip -d grafana-storage/plugins
現在讓我們通過運行以下命令啟動所有服務(Quickwit、Jaeger、Grafana): 如果沒有問題,現在我們可以運行 Web 應用程序并使用 cURL 幾次命中http://localhost:9000/post端點,以生成一些跟蹤。
curl -X GET http://localhost:9000/post 等待約 10 秒鐘,新的跟蹤將被索引并可供搜索。 您現在可以通過使用 cURL 搜索otel-traces-v0_6索引來檢查 Quickwit 是否已索引跟蹤數據。
curl -X POST http://localhost:7280/api/v1/otel-traces-v0_6/search -H 'Content-Type: application/json' -d '{ "query": "service_name:quickwit-jaeger-demo" }'
您也可以使用 Quickwit UIhttp://localhost:7280/ui/search查看數據。
Jaeger 容器已經在運行中了,可以轉到http://localhost:16686查看我們的應用程序跟蹤。
從上面的截圖可以看出,我們依次為每個帖子獲取評論。也就是說,我們一個接一個地進行了二十次請求。這使得整個請求處理時間更長(上面為 4.39s)。 但我們能不能更好地做? 在 Rust 開發人員擁有的所有優秀工具中,答案是顯而易見的 "是的!"。讓我們利用 Tokio 和 Rustfutures crate的異步流特性,通過并行獲取評論。 讓我們更新我們的fetch_posts函數,以批量并行運行請求,每次同時進行十個請求。這應該可以進一步加速事情。
// Fetching posts with a limit. #[instrument(level = "info", name = "fetch_posts")] async fn fetch_posts(limit: usize) -> anyhow::Result
通過這個改變,你會注意到我們現在處理請求的時間大約為2.46秒,同時你也可以直觀地看到我們的請求處理程序在運行期間最多同時運行了十個fetch_comments請求。
Jaeger 適用于對單個跟蹤進行專注檢查。但如果我們想要監視服務的延遲呢?如果我們想要計算具有給定跟蹤元數據的錯誤或請求的數量呢? 這就是 Grafana 儀表板的用處。我們想要從我們的跟蹤構建 RED 指標并在 Grafana 中可視化它們。 轉到http://localhost:3000/login,使用admin作為用戶名和密碼登錄。 登錄后,我們可以使用新發布的Quickwit 數據源插件連接到 Quickwit 并查詢我們的應用程序跟蹤。
為了使 RED 指標監控過程更加方便,我們準備了一個預配置的Grafana 儀表板供您下載并導入到您的 Grafana 實例中。 該儀表板作為一種強大的工具,用于可視化和理解性能。它包括三個面板:
第一個面板顯示每分鐘的請求數量。
第二個面板顯示每分鐘的錯誤數量。
第三個面板呈現每分鐘請求的持續時間百分位數。
為了觀察這些指標的運行情況,您可以使用 HTTP 基準測試工具,甚至可以使用本教程提供的此腳本發送多個并發請求到您的 Rust 應用程序。 現在讓我們來看一下 Grafana 儀表板的截圖,展示了運行腳本后的指標情況。
就是這樣!在這篇博客文章中,我們超越了基本的日志記錄,深入了解了分布式跟蹤以及如何使用它來監視應用程序性能。 我們構建 Quickwit 的經驗告訴我們,分布式跟蹤對于了解由于調用 S3 或在本地磁盤上讀取數據而失去時間的位置非常重要。我們希望它對您也有所幫助 :) 愉快的編碼和觀察!
審核編輯:湯梓紅
-
cpu
+關注
關注
68文章
10863瀏覽量
211786 -
Web
+關注
關注
2文章
1263瀏覽量
69485 -
API
+關注
關注
2文章
1501瀏覽量
62027 -
應用程序
+關注
關注
37文章
3268瀏覽量
57710 -
Rust
+關注
關注
1文章
228瀏覽量
6610
原文標題:【Rust日報】2023-06-20 使用Quickwit、Jaeger和Grafana監控您的Rust應用程序
文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論