# 閒聊設計 Slo 與 Sli

最近在閱讀 [Implementing Service Level Objectives](https://www.amazon.com/Implementing-Service-Level-Objectives-Practical/dp/1492076813) 這本書，作者 [Alex](https://www.alex-hidalgo.com/) 參與過 [The Site Reliability Workbook](https://www.oreilly.com/library/view/the-site-reliability/9781492029496/) 某些章節的寫作，所以很多概念跟之前幾本 SRE 相關書籍是有關連的。

這文章想介紹的是其中的第 12 章 A Worked Example，以及將學到的概念用在 OpenTelemetry Demo 的電商系統上。

![](https://m.media-amazon.com/images/I/91bW4csmp5L.AC_CR0%2C0%2C0%2C0_SX960_SY720.jpg align="left")

---

# A Worked Example

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737102390898/24ce6bb7-2dff-424c-be46-4db20e19d3a1.png align="center")

這是書中的零售網站架構，讓我來把這圖做個劃分

之前的章節一直提到 **SLO 應該作為用戶旅程的工具。**

所以這裡提出了一個核心觀點：SLO 應該基於用戶旅程來定義，這樣可以更好地對齊技術目標與業務需求。 將用戶分為三類：

1. **外部客戶**（如網站用戶）
    
2. **其他服務作為用戶**（如內部依賴的微服務）
    
3. **內部用戶**（如公司員工）
    

***外部客戶：瀏覽和搜索產品***

* **用戶需求**：客戶需要能夠快速瀏覽和搜索產品。
    
* **相關組件**：包括 CDN、Web 應用、產品數據庫、緩存、庫存微服務等。
    
* **示例 SLO**：
    
    * **首頁加載延遲**：99.9% 的請求在 2,000 毫秒內返回 2xx、3xx 或 4xx 狀態碼。
        
    * **搜索結果**：99.8% 的搜索請求在 4,000 毫秒內返回 2xx 或 3xx 狀態碼。
        

> **關鍵觀點**：
> 
> * SLO 應該考慮用戶的耐心，例如用戶對搜索結果的等待容忍度可能高於普通頁面加載。
>     
> * 需要平衡性能與用戶體驗。
>     

***其他服務作為用戶：購買產品***

* **用戶需求**：支付流程需要快速且可靠，因為這直接影響收入。
    
* **相關組件**：包括支付微服務、外部支付供應商等。
    
* **示例 SLO**：
    
    * **結賬成功率**：99.99% 的結賬請求在首次嘗試時成功。
        
    * **整體支付工作流程**：99.93% 的結賬請求在整個流程中首次嘗試成功。
        

> **關鍵觀點**：
> 
> * 當內部服務依賴外部供應商時，內部 SLO 必須考慮供應商的 SLA。
>     
> * 需要計算多個目標百分比的相互影響，並設計適當的重試邏輯。
>     

***內部用戶***

* **用戶需求**：內部工具（如數據分析應用和 Wiki）需要穩定可靠，否則會影響員工效率。
    
* **相關組件**：包括數據處理管道、內部應用、內部 Wiki 等。
    
* **示例 SLO**：
    
    * **數據導出**：90% 的數據導出嘗試在一個月內成功。
        
    * **內部 Wiki 可用性**：工作時間內可用性達到 99.9%。
        

> **關鍵觀點**：
> 
> * 即使是內部工具，也需要可靠性目標，否則會影響員工的工作滿意度。
>     
> * 簡單的 SLO（如「是否可用」）對於第三方或開源軟體可能已足夠。
>     

因此我們能把整個系統架構按照上面的案例畫分成這三區。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737103500506/d5df9559-722c-4350-8835-633de2859242.png align="center")

每個我們在更細緻的探索它們應用案例相關的 SLO 與 SLI

***外部客戶***

#### **瀏覽和搜索產品**

**首頁加載延遲SLI**：

* HTTP 響應時間是否小於 2000 ms。
    
* HTTP 響應狀態碼是否為 2xx、3xx 或 4xx。
    

SLO︰99.9% 的請求應在 2000 毫秒內返回成功狀態。

```yaml
(
  sum(rate(http_request_duration_seconds_bucket{le="2", status=~"2..|3..
"}[5m]))
  /
  sum(rate(http_request_duration_seconds_count[5m]))
) * 100
```

#### **搜索結果**

**SLI**：

* 搜索請求是否在 4000 ms內完成。
    
* 搜索請求是否返回 2xx 或 3xx。
    

**SLO**：

99.8% 的搜索請求應在 4000 ms內返回成功狀態。

```yaml
# 計算符合 SLO 的成功率
(
  sum(rate(search_request_duration_seconds_bucket{le="4", status=~"2..|3.."}[5m]))
  /
  sum(rate(search_request_duration_seconds_count[5m]))
) * 100
```

> **為什麼明明圈起來的還有 Cache 和 Database，SLO 這裡卻沒用到呢？**
> 
> 1. **SLI 的核心目標是反映用戶體驗：**
>     
>     * SLI 的設計是為了量化用戶旅程的成功與否，例如「首頁是否快速加載」或「搜索結果是否快速返回」。這些是用戶直接感受到的指標。
>         
>     * **DB** 和 **Cache** 是支持這些用戶操作的後端組件，對用戶來說是透明的。因此，我們通常不會直接將它們作為 SLI，而是通過高層次的指標（如 HTTP 響應時間）間接反映它們的性能。
>         
> 2. **內部組件的健康狀況通常通過內部監控來衡量：**
>     
>     * **DB 和 Cache** 的性能（例如查詢延遲、cache hit rate等）可以作為內部的技術指標進行監控，但它們更適合作為服務**可靠性**的支持性指標，而不是直接的 SLI。
>         
>     * 如果 DB 或 Cache 出現問題，這些問題會反映在高層次的用戶操作（如頁面加載時間或搜索結果返回時間）中。
>         
> 3. **避免過於細化 SLI：**
>     
>     * 如果將每個內部組件都單獨定義 SLI，會導致指標過於分散，難以管理和解釋。
>         
>     * 我們的目標是關注用戶價值，而不是每個內部組件的細節。
>         
> 
> 但我們可以設計 [Dashboard link](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/manage-dashboard-links/#panel-links) ，讓高階指標儀表板、連結至相關的細顆粒度的系統組件儀表板，協助分析。
> 
> ```yaml
> -------------------------------------------------------
> |      高層次指標（SLO）：首頁加載時間                |
> |   [首頁加載時間：99.9% 在 2000ms 內完成]           |
> -------------------------------------------------------
> |  相關內部指標（SLI）：                              |
> |   - DB 查詢延遲（99% 在 100ms 內完成）             |
> |   - Cache 命中率（95%）                            |
> |   - 外部 API 響應時間（99% 在 200ms 內完成）       |
> -------------------------------------------------------
> ```
> 
> 回呼一下小弟自己的出版內容[OpenTelemetry 入門指南 第 9 章](https://www.tenlong.com.tw/products/9786263338739)中曾提到，儀表板的設計，而 SLO 這樣的分層設計概念也是一樣的。
> 
> ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737123367928/c0c3d859-d9e7-41e0-bd45-7796c16b8d88.png align="center")
> 
> ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737123373546/a2a53617-840f-4d2a-8002-06c9f346fe5c.png align="center")

[***其他依賴的外部服務***](https://www.tenlong.com.tw/products/9786263338739)

整個支付流程（包括結賬、支付微服務、外部支付供應商）成功完成的比例。

支付流程成功率 = 支付微服務成功率 × 外部支付 API 成功率

### **結賬成功率**

結賬成功率是指結賬請求中返回成功狀態（如 HTTP 2xx）的比例。

**支付微服務**

**SLI**：

* 支付請求是否在 1000 ms 內完成並返回成功狀態碼（2xx）。
    
* 支付請求是否在首次嘗試時成功。
    

**SLO**：

* 99.99% 的支付請求應在 1000 ms 內成功完成。
    
* 99.95% 的支付請求應在整個支付流程中成功。
    

#### **外部供應商 API**

**SLI**：

* 外部 API 請求是否在 2000 ms 內返回成功狀態碼（2xx 或 3xx）。
    
* 外部 API 可用性是否達到指定 SLA。
    

**SLO**：

* 99.9% 的外部 API 請求應在 2000 ms 內返回成功狀態。
    
* 外部 API 每月可用性應達到 99.95%。
    

```yaml
# 計算支付流程成功率
(
  sum(rate(payment_service_requests_total{status="success"}[5m]))
  /
  sum(rate(payment_service_requests_total[5m]))
)
*
(
  sum(rate(external_payment_api_requests_total{status="success"}[5m]))
  /
  sum(rate(external_payment_api_requests_total[5m]))
)
```

> ***為什麼支付這裡需要多個 SLI？***
> 
> 1. **系統的複雜性**  
>     現代系統（如微服務架構）涉及多個組件和依賴（如結賬微服務、支付微服務、外部支付供應商等）。單一的 SLI 無法捕捉所有這些組件的行為和性能。
>     
> 2. **不同層面的健康狀態**  
>     每個組件的可靠性（如資料庫、API、Cache、負載均衡器等）都可能影響整體的用戶體驗。需要針對每個層面定義單獨的 SLI，才能精確定位問題。
>     
> 3. **從用戶體驗到內部依賴的監控**
>     
>     * **用戶體驗層面**：如支付流程的成功率。
>         
>     * **內部依賴層面**：如支付微服務的延遲、外部支付 API 的可用性、database 查詢的成功率等。
>         
>     * **基礎設施層面**：如負載均衡器的響應時間、Cache hit rate等。
>         
> 
> 只有當這些低層級的 SLI 都達到預期時，高層級的 SLO 才可能被滿足。

> ***SLO 和多個 SLI 的關係***
> 
> **1\. 單一 SLO，依賴多個 SLI**
> 
> 一個高層級的 SLO（如支付流程成功率 99.93%）通常依賴多個 SLI 的表現。例如：
> 
> * 支付流程成功率 = **支付微服務成功率** × **外部支付 API 成功率**。
>     
> * 如果任意一個 SLI 表現不佳，整個支付流程的 SLO 就可能無法達成。
>     
> 
> **2\. 多個 SLO，分別追蹤不同維度**
> 
> 某些情況下，您可能需要為不同的關鍵指標設置單獨的 SLO。例如：
> 
> * **支付流程成功率 SLO**：99.93%。
>     
> * **支付微服務延遲 SLO**：95% 的請求延遲小於 1000 毫秒。
>     
> * **外部支付 API 可用性 SLO**：99.9%。
>     
> 
> 這樣可以更清晰地定義每個組件的責任範圍。

> ***如何有效地管理多個 SLI？***
> 
> 1. **分層管理**
>     
>     * **高層級 SLI**：直接影響用戶體驗的指標，例如支付流程成功率。
>         
>     * **低層級 SLI**：內部系統的性能指標，例如支付微服務的延遲、外部支付 API 的可用性等。
>         
> 2. **自動化監控與告警** 使用工具（如 Prometheus 和 Grafana）來自動化監控多個 SLI，並設置告警：
>     
>     * 當高層級 SLI（如支付流程成功率）下降時，觸發告警。
>         
>     * 當低層級 SLI（如 API 延遲）異常時，觸發更具體的告警。
>         
> 3. **關聯分析** 在高層級 SLI 出現問題時，通過關聯分析低層級 SLI，快速定位問題源頭。
>     

***內部使用者***

內部使用者的主要操作流程可以分為以下幾個階段：

1. **資料查詢與處理（透過 Microservice D 和 Reporting 系統）**
    
2. **報表生成與查看（Reporting 系統）**
    

#### **資料查詢與處理**

**用戶行為：** 使用者透過業務應用發送查詢請求，Microservice D 從資料庫中提取資料並進行處理，返回結果。

* **SLI：查詢所需報表與資料成功率**
    
    * 定義：查詢請求成功返回結果的比例。
        
    * 測量方式：`成功查詢請求數 / 總查詢請求數`
        
    * SLO：**99.8% 的查詢請求需成功**
        
    * 如果太常查詢不到所需的即時資料也有可能是 ETL 的前半段出現問題。
        
* **SLI：查詢延遲**
    
    * 定義：從用戶發送查詢請求到結果返回的時間。
        
    * 測量方式：`查詢完成時間 - 查詢請求時間`
        
    * SLO：
        
        * **P80：** 80% 的查詢需在 **700ms** 內完成。
            
        * **P99：** 99% 的查詢需在 **1.5s** 內完成。
            

> **內部用戶的範疇是否需要再細分？**  
> 例如，內部用戶可以分為「技術團隊」（如開發、運維）和「非技術團隊」（如業務部門）。這兩者的需求可能不同，例如：
> 
> \* 技術團隊更關注內部系統的可用性（如 CI/CD 管道、監控工具）。
> 
> \* 非技術團隊更關注業務支持工具的穩定性（如報表系統、CRM 系統）。

---

# OpenTelemetry Demo 專案

Demo 專案剛好也是一個電商網站，其系統架構更加複雜一些。

![](https://github.com/tedmax100/OpenTelemetryEntryBeook/blob/main/%E5%90%84%E7%AB%A0%E7%AF%80%E5%9C%96%E7%89%87/%E5%9C%968-2.png?raw=true align="left")

先將用戶分類

1. **訪客用戶 (Guest User)**
    
2. **購物用戶 (Shopping User)**
    
3. **支付用戶 (Payment User)**
    

***用戶類型與場景***

**訪客用戶 (Guest User)**

1. * 未登入的用戶，瀏覽產品、查看推薦商品。
        
    * 主要行為：瀏覽、產品搜索、推薦商品點擊。
        
2. **購物用戶 (Shopping User)**
    
    * 正在進行購物的用戶，將商品加入購物車，進行結賬。
        
    * 主要行為：加入購物車、結賬、選擇運送方式。
        
3. **支付用戶 (Payment User)**
    
    * 完成購物並進行支付的用戶。
        
    * 主要行為：提供支付信息、完成支付、收到訂單確認。
        

就能再根據這些場景去了解對應到哪些內部系統。

***用戶場景***

1. **產品瀏覽場景**
    
    * 用戶進入網站或應用，瀏覽產品目錄，查看產品詳情，並獲得推薦商品。
        
    * 涉及的服務：`frontend`、`productcatalogservice`、`recommendationservice`。
        
2. **購物車場景**
    
    * 用戶將商品加入購物車，並檢視購物車內容。
        
    * 涉及的服務：`frontend`、`cartservice`、`currencyservice`。
        
3. **結賬場景**
    
    * 用戶提交購物車，進行結賬，選擇運送方式，並確認訂單。
        
    * 涉及的服務：`checkoutservice`、`cartservice`、`quoteservice`、`shippingservice`、`frauddetectionservice`。
        
4. **支付場景**
    
    * 用戶提供支付信息，完成支付，並收到訂單確認郵件。
        
    * 涉及的服務：`paymentservice`、`emailservice`、`accountingservice`。
        

## **設計用戶旅程的 SLO 與 SLI**

**(1) *產品瀏覽場景***

**目標 (SLO)**：

* 產品瀏覽頁面加載成功率 ≥ 99.95%
    
* 推薦商品 API 響應延遲 ≤ 200ms (P95)
    

**相關 SLI**：

1. **成功率**：
    
    * `frontend` 調用 `productcatalogservice` 和 `recommendationservice` 的成功請求數 / 總請求數  
        PromQL：
        
    
    ```yaml
    sum(rate(http_requests_total{service="frontend", status="2xx"}[5m])) 
    / 
    sum(rate(http_requests_total{service="frontend"}[5m]))
    ```
    
2. **延遲**：
    
    * `recommendationservice` 的 P95 響應時間  
        PromQL：
        
    
    ```yaml
    histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{service="recommendationservice"}[5m]))
    ```
    

---

**(2) *購物車場景***

**目標 (SLO)**：

* 加入購物車成功率 ≥ 99.9%
    
* 購物車頁面加載延遲 ≤ 300ms (P95)
    

**相關 SLI**：

1. **成功率**：
    
    * `cartservice` 的成功請求數 / 總請求數  
        PromQL：
        
    
    ```yaml
    sum(rate(grpc_requests_total{service="cartservice", status="success"}[5m])) 
    / 
    sum(rate(grpc_requests_total{service="cartservice"}[5m]))
    ```
    
2. **延遲**：
    
    * `cartservice` 的 P95 響應時間  
        PromQL：
        
    
    ```yaml
    histogram_quantile(0.95, rate(grpc_request_duration_seconds_bucket{service="cartservice"}[5m]))
    ```
    

---

**(3) *結賬場景***

**目標 (SLO)**：

* 結賬成功率 ≥ 99.93%
    
* 結賬流程完成時間 ≤ 1秒 (P95)
    

**相關 SLI**：

1. **成功率**：
    
    * `checkoutservice` 的成功請求數 / 總請求數  
        PromQL：
        
    
    ```yaml
    sum(rate(grpc_requests_total{service="checkoutservice", status="success"}[5m])) 
    / 
    sum(rate(grpc_requests_total{service="checkoutservice"}[5m]))
    ```
    
2. **延遲**：
    
    * `checkoutservice` 的 P95 響應時間  
        PromQL：
        
    
    ```yaml
    histogram_quantile(0.95, rate(grpc_request_duration_seconds_bucket{service="checkoutservice"}[5m]))
    ```
    

---

**(4) *支付場景***

**目標 (SLO)**：

* 支付成功率 ≥ 99.9%
    
* 訂單確認郵件發送成功率 ≥ 99.8%
    

**相關 SLI**：

1. **支付成功率**：
    
    * `paymentservice` 的成功請求數 / 總請求數  
        PromQL：
        
    
    ```yaml
    sum(rate(grpc_requests_total{service="paymentservice", status="success"}[5m])) 
    / 
    sum(rate(grpc_requests_total{service="paymentservice"}[5m]))
    ```
    
2. **郵件發送成功率**：
    
    * `emailservice` 的成功請求數 / 總請求數  
        PromQL：
        
    
    ```yaml
    sum(rate(grpc_requests_total{service="emailservice", status="success"}[5m])) 
    / 
    sum(rate(grpc_requests_total{service="emailservice"}[5m]))
    ```
    

透過將使用者分類，給出業務場景，就能對照到系統架構與組件的依賴關係圖中，去設計出適合的使用者相關的 SLO。

## **總結**

設計用戶旅程的 SLO 與 SLI 主要基於以下原則：

1. **核心行為**：針對用戶的關鍵操作（如瀏覽、購物、支付）設計 SLI。
    
2. **多層次指標**：從成功率、延遲和吞吐量等多角度衡量系統性能。
    
3. **場景分解**：根據不同用戶場景拆分指標，確保每個場景都有清晰的目標 (SLO)。
    

這樣的設計能幫助您清楚了解系統在不同用戶場景下的表現，並快速定位問題來源。
