iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
Software Development

微服務奇兵:30天Quarkus特訓營系列 第 27

開發概念建置- 微服務監控設計大小事

  • 分享至 

  • xImage
  •  

在前一章節,我們通過一個實際案例討論了微服務,相信讀者對於它的演進設計脈絡已經有了一定的了解,並對於單體架構和微服務的差異有了具體的體會。不過,微服務涉及的技術範圍相當廣泛,每個主題都可以深入探討,甚至寫成一本書。因此,這章節將對微服務架構設計中常見的問題進行論述介紹,讀者可根據這些主題進行進一步的研究和實踐。

問題排查挑戰

還記得前一章節提到的線上學習平台案例(第22篇)嗎?我們將業務功能拆分成多個服務。首要遇到的問題便是排查問題的複雜性。因為在單體架構中,所有功能都集中在同一個系統內,排查問題時只需要專注在這個系統的內部邏輯。但當我們採用微服務架構時,系統被拆分成多個獨立的服務,這些服務透過API或消息隊列進行交互,因此當某個功能出現異常時,定位問題的範圍不再單純。稍微列一下設計上有哪些做法

健康檢查(Health Checks)

當設計一個API服務時,確保它一直正常運作是非常重要的。具體作法通常由監控工具或平台(如 Kubernetes、Load Balancer)來實現。主要會有兩部分來完成

健康檢查端點 : 服務部分,程式內部會提供一個健康檢查的端點(例如 /health)。這個端點回傳應用程式的狀態,例如 "OK" 或 "Unhealthy"。

監控框架或工具 : 監控部分外部工具會定期訪問健康檢查端點,例如常見的 Prometheus + Grafana。而框架部分 .NET 與 Spring Boot 都有簡易輕鬆增加 /health 端點的設定。例如Quarkus設置(使用quarkus-smallrye-health)會如下

// 步驟1: 加入依賴
// 在你的pom.xml文件中加入以下依賴:
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-health</artifactId>
</dependency>

// 步驟2: 建置健康檢查類
// 創建一個簡單的健康檢查類:
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;

@Liveness
public class SimpleHealthCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.up("Simple health check");
    }
}

監控工具部分,實作面有蠻多不同方式,稍微條例較常見的方式如下

  • Prometheus 監控 : 透過 /metrics 端點提供服務指標(請求數量、回應時間、錯誤率、記憶體使用量、CPU 使用率)。定期主動向被監控的應用程式發送 HTTP 請求,結合 Alertmanager 發送警報通知。
  • Kubernetes 原生健康檢查
    • Liveness Probe : 檢查容器(服務)是否正在執行。
    • Readiness Probe : 檢查容器(服務)是否準備好接收流量。
    • Startup Probe : 檢查應用程式(服務)是否已經啟動完成。

分散式追踪(Distributed Tracing)

當服務數量增加時,僅依靠Health Check可能不足以全面了解系統狀況。另外若要了解全面系統狀況,請求跨多個微服務時,追踪請求流向就會變得至關重要。Distributed Tracing有幾個重要目標如下

  • 視覺化請求流程:追蹤請求如何在不同服務間傳遞
  • 效能分析:識別系統中的瓶頸和延遲較高的服務
  • 錯誤追蹤:快速定位錯誤發生的確切位置
  • 系統相依性分析:了解服務間的相依關係

稍微示意Grafana在Distributed Tracing具體呈現是什麼樣子

https://ithelp.ithome.com.tw/upload/images/20240928/20115895JKzpYLNF5P.png

追蹤詳情如下:

  1. 可以看到mythical-requester 發起了一個請求,總耗時約 12.75ms。
  2. mythical-server 接收到 POST 請求,處理時間約 10.86ms。
  3. mythical-server 執行了幾個中間件操作,其中一個操作(pg.queryn)耗時較長,約 5.77ms。
  4. mythical-server 還執行了 HTTP POST、tcp connect 和 dns lookup 等操作。
  5. mythical-requester 在請求結束後執行了一些 log 操作。

紅色圓圈標記的操作可能表示發生了錯誤,包括 mythical-requester 的主要操作和 mythical-server 的 pg.queryn 操作。

實現的話,這部分較常使用的工具為OpenTelemetry ,他為一個開源的可觀測性框架,用於產生、蒐集和匯出遙測資料(包括追蹤、指標和日誌),一般會搭配Prometheus 和 Grafana一起使用。稍微解釋一下這三者的關係

  • OpenTelemetry : 資料蒐集標準化,提供統一的框架來蒐集分散式追蹤(Distributed Tracing)、度量(Metrics)和日誌(Logs)。可以將資料傳送到各種後端系統,包括 Prometheus
  • Prometheus : 資料收集和分析工具,使用PromQL配合Grafana做視覺化監控儀表板。
  • Grafana : 視覺化和儀表板工具,可連接多種資料來源,包括 Prometheus,展示關鍵效能指標(KPI)並具有告警管理功能。

OpenTelemetry 蒐集和標準化來自應用程式的遙測資料,然後傳送到 Prometheus 進行儲存和查詢。Grafana 然後連接到 Prometheus,使用其資料建立視覺化儀表板。簡單呈現如下圖

https://ithelp.ithome.com.tw/upload/images/20240928/201158957U9f8WCLXY.png

用程式(WebApi)產生遙測資料,OpenTelemetry 負責收集和標準化這些資料後送給Prometheus 作為時間序列資料庫儲存和管理。而Grafana 提供了友善的使用者介面來視覺化呈現這些資料。

基本上OpenTelemetry 算是一個成熟的框架解決方案,它不僅僅是一個工具,而且提供一套完整的架構來處理可觀測性(Observability)的問題。如

  • 標準化 API:提供統一的 API,讓開發者可以在不同的程式語言和平台上,輕鬆收集各種遙測資料(例如:日誌、指標、追蹤資料)。
  • 資料模型:定義了一個標準化的資料模型,確保來自不同來源的遙測資料能夠一致地表示與處理,提升整合與分析的效率。
  • 收集器(Collector):一個獨立的組件,負責接收、處理及匯出遙測資料。它可以部署在本地或雲端,用來處理大量的資料流量,並輸出到不同的後端系統(例如:Prometheus、Jaeger 等)。
  • 自動檢測(Auto-instrumentation): 支援常見的框架和函式庫(ex:Spring Boot)自動插入檢測程式碼,開發者不需要手動撰寫大量追蹤或監控相關的程式碼,減少了整合的工作量。
  • 擴展性:開發者可以根據具體需求,自定義資料匯出器或資料處理邏輯,讓框架適應不同的應用場景。(ex : 你可能需要根據不同的租戶或應用程式,篩選或修改遙測資料。例如在多租戶 SaaS 系統中,你可以為每個客戶標記不同的 trace 或 metric,以便後續的查詢與分析。)

因為時間不多…這邊請AI幫我產一個 Quarkus範例

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

// 定義一個 RESTful 資源,路徑為 "/example"
@Path("/example")
public class ExampleResource {

    // 注入 OpenTelemetry 的 Tracer 實例
    @Inject
    Tracer tracer;

    // 宣告一個長整數計數器,用於記錄請求次數
    private final LongCounter requestCounter;

    // 建構子
    public ExampleResource() {
        // 取得 OpenTelemetry 的 Meter 實例
        Meter meter = GlobalOpenTelemetry.getMeter("io.example");
        
        // 建立一個長整數計數器
        requestCounter = meter
                .counterBuilder("requests")  // 計數器名稱
                .setDescription("計算請求次數")  // 設定描述
                .setUnit("次")  // 設定單位
                .build();  // 建立計數器
    }

    // 定義一個 GET 方法端點
    @GET
    @Produces(MediaType.TEXT_PLAIN)  // 指定回應的媒體類型為純文字
    public String exampleEndpoint() {
        // 建立一個新的 span
        Span span = tracer.spanBuilder("example-operation").startSpan();
        
        // 使用 try-with-resources 確保 span 在方法結束時正確關閉
        try (Scope scope = span.makeCurrent()) {
            // 增加請求計數
            requestCounter.add(1);

            // 為 span 添加一個自定義屬性
            span.setAttribute("custom.attribute", "某個值");

            // 模擬一些處理時間
            simulateWork();

            return "你好, OpenTelemetry!";
        } finally {
            // 確保 span 被正確結束
            span.end();
        }
    }

    // 模擬工作負載的私有方法
    private void simulateWork() {
        try {
            // 模擬處理時間為 100 毫秒
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // 如果線程被中斷,重新設置中斷狀態
            Thread.currentThread().interrupt();
        }
    }
}

requestCounter:這是一個長整數計數器(LongCounter),用於計算對 "/example" 端點的請求次數。每次有請求進入 exampleEndpoint 方法時,這個計數器就會增加 1。這個 metric 可以幫助監控 API 的使用頻率和負載情況。

集中式日誌管理(Centralized Logging)

完成了分散式追蹤的介紹後,還有另一個至關重要的可觀測性工具需要關注,那就是集中式日誌管理(Centralized Logging)。雖然分散式追蹤能夠幫助我們在多個微服務間追蹤請求的整個生命週期,了解應用程序的性能和依賴關係,但實際的運行狀態、錯誤以及系統行為的細節通常需要從日誌中查看。

在一個分散式微服務架構下運行時,每個服務都會產生自己的日誌資料。如果我們沒有將這些日誌進行集中管理,開發者就必須登錄到每個服務的節點上來查看各自的日誌。常見的解決方案有

  • ELK Stack : 由 Elasticsearch、Logstash 和 Kibana 組成

    • Elasticsearch : 分散式的搜索和分析引擎,負責存儲和檢索日誌資料。
    • Logstash : 日誌的資料處理管道工具,負責從多種來源收集資料、進行資料轉換(如過濾、格式化)後,再發送到 Elasticsearch 或其他儲存系統。
    • Kibana : 展示和分析存儲在 Elasticsearch 中的資料。

    流程上,Logstash 從應用程式、系統或其他數據源收集日誌。將收集到的資料進行處理並傳輸到 Elasticsearch。Elasticsearch 存儲和索引這些日誌,提供高效的檢索功能。最後使用 Kibana 進行可視化和分析,通過查詢和儀表板來檢視系統狀況。

不知道我有沒有記錯,印象Logstash 普遍評價不好用,好像偏向使用Fluent Bit…有記錯請指正我一下

  • Loki : 由 Grafana Labs 開發的一款專門用於日誌管理的工具,主要針對日誌進行標籤化存儲和檢索,而不是全文索引。為輕量級且高效的日誌管理系統的解決方案。Loki 是一個自帶搜尋與日誌管理的服務,負責收集、存儲和索引日誌資料,而 Grafana 則負責視覺化這些日誌資料。Loki本身是存儲和檢索使用,collect部分常見會與Promtail 或是Fluent Bit搭配使用。

Elasticsearch 的檢索能力非常強大,但索引成本高,簡單來說Elasticsearch 需要對每一條日誌進行全面索引,以實現快速和靈活的查詢。所以當日誌資料量非常龐大時,索引的構建和維護會消耗大量的存儲和計算資源。如果系統並不需要進行複雜的全文檢索,而是只需要基於元資料或標籤來進行檢索(例如:只查找與某個服務、某個 Pod 相關的日誌)那Loki會是比較好的選擇。

稍微用網路的架構圖示意實際Loki架構如下

https://ithelp.ithome.com.tw/upload/images/20240928/20115895T732S6tsg7.png

K8s 叢集透過Fluentbit collect將log push到Loki,同時,Prometheus 負責拉取叢集的效能指標。最後,藉由 Grafana 儀表板整合呈現這些日誌和指標,方便營運團隊進行系統監控和問題排查。

服務網格(Service Mesh)

講完集中式日誌管理,還有一個重要觀念為Service Mesh,用於更精細的流量管理和安全控制。簡單來說,大型的微服務架構中,各個服務之間的請求流量會變得非常龐大,手動管理這些請求的流向和控制會變得越來越困難(ex:使用配置文件的方式來記錄各個服務的地址)。Service Mesh此時就用來處理,去解決項目如下列

  1. 服務間的溝通 : 確保每個服務之間的請求能夠順暢傳輸。

    1. 每個服務的旁邊會安裝一個「sidecar proxy」,負責處理所有的請求和響應。
  2. 流量管理 : 自動化處理流量管理,確保微服務架構的穩定性和高效運行。 (由Service Discovery, Load Balancing, Traffic Routing三個關鍵功能達成)。

    1. Service Discovery : 透過內建的服務發現功能,自動追蹤所有服務的實例,動態調整流量的路由,無需手動配置。例如,當某個服務增加了新的實例,Service Mesh 會自動將請求分發到新的實例上。
    2. Load Balancing : 自動進行負載平衡,確保請求能均勻分配到每個服務實例上(ex :Round Robin,Least Connections )。
    3. Traffic Routing : 根據請求的具體內容或條件來選擇不同的路徑,。例如,在做藍綠部署或金絲雀發布時,Service Mesh 能將部分流量路由到新版本的服務,而大部分流量依然發送到舊版本,以便觀察新版本是否穩定。此外,還能根據請求的來源、用戶類型或負載狀況來進行流量分配
  3. 安全性 : 確保所有的服務之間的通信都是加密且可靠的。

    1. mTLS(相互傳輸層安全協定): 透過 mTLS 來加密微服務之間的所有通信。mTLS 不僅加密資料,還能驗證通訊雙方的身份,確保只有經過認證的服務才能互相通信。
    2. 沒有 Service Mesh 的情況下,開發者需要手動在每個服務中實現身份驗證、授權和加密通信等功能。,Service Mesh 可以統一在 sidecar 代理中處理這些需求,要求所有服務之間的通信都必須經過身份驗證,並加密通信內容。
  4. 整合 : 與日誌管理和分散式追蹤工具的整合。

    1. Service Mesh 可以與常見的日誌管理系統(如 ELK StackLoki)無縫整合。自動將每個請求的日誌資料傳送到這些系統,並提供詳細的請求路徑、延遲時間、失敗率等數據,方便開發者監控和排查系統問題。
    2. Service Mesh 可以自動收集和匯報所有跨服務的請求追蹤數據,無需開發者手動編寫追蹤邏輯。
    3. Service Mesh 提供的流量管理功能,運維團隊能夠清楚地看到系統中每個服務的請求模式、負載情況以及錯誤率。

一樣稍微以現有網路上的架構圖去描述,https://ithelp.ithome.com.tw/upload/images/20240928/201158950aBIxsNxgy.png

圖中將 Service Mesh 分為兩個主要層次:控制層(Control Plane) 和 資料層(Data Plane)。控制層負責設定和管理網路配置、路由規則與安全策略,並將這些規則下發給資料層中的代理(Proxy)。

資料層部分由每個服務旁邊的 sidecar proxy 組成,負責處理所有進出服務的流量,執行負載平衡、安全加密(如 mTLS)、流量路由等功能。這樣的架構確保了服務之間的通信安全、穩定,並且方便管理和監控。


上一篇
開發觀念建置-var使用
下一篇
開發-Quarkus急迫上手-Part1
系列文
微服務奇兵:30天Quarkus特訓營30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言