﻿# 快速上手 Grafana k6 壓力測試工具

如果要問我哪一套壓力測試工具最好用，我會說 [k6](https://k6.io/) 絕對是開發者體驗最好的一種，他可以讓你用簡單易懂的 JavaScript 就可以撰寫出各種壓力測試的情境，而且我們這幾年的專案也用了幾次，感覺相當不錯。這篇文章我就帶大家快速上手這套 k6 怎麼用。

### 安裝 k6 執行檔

k6 有個執行檔需要下載或安裝，透過 Docker 執行也可以，你可以從官網的 [Installation](https://k6.io/docs/get-started/installation/) 頁面找到完整的安裝說明。

以下我示範 [Chocolatey](https://chocolatey.org/install) 的安裝方法：

```bash
choco install k6 -y
```

![k6](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a3c58dd4817d789db243-93dfdf1e-2422-4caa-9f90-ee7976b91b44.webp)

### 體驗 k6 的開發過程

由於 k6 只是一個壓力測試的執行器，所有的壓測邏輯都要靠 JS 腳本來撰寫，不過程式碼還蠻直覺的，不會很難上手，以下是體驗的步驟。

1.  撰寫測試情境 ( `test.js` )
    
    k6 的測試腳本就是一份很純的 JavaScript / ES6 modules 程式碼：
    
    ```javascript
    import http from 'k6/http';
    import { sleep } from 'k6';
    
    export default function () {
        http.get('https://test.k6.io');
        sleep(1);
    }
    ```
    
    > 這裡的 [test.k6.io](https://test.k6.io/) 就是 k6 特別提供給大家做壓力測試的網站。
    
    這裡最重要的程式碼片段，就是以下這段 k6 的初始化腳本，當壓力測試開始時，每位虛擬使用者(VU)都會去執行這個 `export default function () {}` 函式，而且每位 VU 在每次 iteration 只會執行一次。
    
    ```javascript
    export default function () {
        // ...
    }
    ```
    
2.  執行測試情境 (預設使用 1 個虛擬使用者進行測試)
    
    ```bash
    k6 run test.js
    ```
    
    執行後你會得到以下測試結果：
    
    ![k6 run test.js](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/f076da5843b6d975725b-9f31bbac-34c0-4591-8ebe-ebd27c6ce94d.webp)
    
    測試結果的資訊含量頗大，需要好好消化一下！
    
    -   `scenario`: 測試情境
        
    -   `VUs`: 虛擬使用者人數 (Virtual Users)
        
    -   `iterations`: 迭代數，也就是重複測試的次數
        
    -   `data_received`: 從「受測目標」接收到的傳輸量
        
    -   `data_sent`: 發送到「受測目標」的傳輸量
        
    -   `http_req_blocked`: 在發出要求前 TCP 連線的等待時間 (等候有空的 TCP 連線)
        
    -   `http_req_connecting`: 建立到「測試目標」的 TCP 連線時間
        
    -   `http_req_duration`: 整個 HTTP 的往返時間 (不含 DNS 查詢時間)
        
        ( `http_req_duration` = `http_req_sending` + `http_req_waiting` + `http_req_receiving` )
        
    -   `http_req_failed`: 失敗率
        
    -   `http_req_receiving`: 從「受測目標」接收到數據的時間
        
    -   `http_req_sending`: 發送數據到「受測目標」的傳輸時間
        
    -   `http_req_tls_handshaking`: 進行 TLS 交握的時間
        
    -   `http_req_waiting`: 等待伺服器回應的時間，也就是俗稱的 `time to first byte` (`TTFB`) 時間
        
    -   `http_reqs`: 總共發出了多少 HTTP 要求
        
    -   `iteration_duration`: 一次完整 iteration 的時間
        
    -   `iterations`: 完成幾次 iteration
        
    -   `vus`: 虛擬使用者人數
        
    -   `vus_max`: 同時最大與最小虛擬使用者人數
        
    
    > 完整的計量指標可參考 [Built-in metrics](https://k6.io/docs/using-k6/metrics/reference/) 文件。
    
3.  在既有的測試情境下進行壓力測試
    
    以下命令可以利用 10 VUs 進行為期 30 秒的壓力測試：
    
    ```bash
    k6 run --vus 10 --duration 30s test.js
    ```
    
    這個命令利用 10 位虛擬使用者，不間斷的持續執行 `test.js` 中的測試情境，就無窮迴圈一直跑、一直跑，跑到 `duration` 時間結束為止。
    
    ![k6 run --vus 10 --duration 30s test.js](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/fda7685db94faedd68b8-993b7c27-fe57-47b9-8481-e6f3cc9755cb.webp)
    
    > 注意: 由於我們的測試腳本有寫到 `sleep(1);` 這個邏輯，這意味著每位 VU 在執行完 `http.get('https://test.k6.io');` 的時候，會停頓 1 秒鐘的時間。
    
    以下命令可以利用 10 VUs 進行總共 100 次 iterations 的壓力測試：
    
    ```bash
    k6 run --vus=10 --iterations=100 test.js
    ```
    
4.  你可以將壓力測試的參數寫在程式碼中
    
    你只要在程式碼中加入以下程式碼片段即可：
    
    ```javascript
    export const options = {
        vus: 10,
        duration: '30s',
    };
    ```
    
    現在完整的程式碼如下：
    
    ```javascript
    import http from 'k6/http';
    import { sleep } from 'k6';
    
    export default function () {
        http.get('https://test.k6.io');
        sleep(1);
    }
    
    export const options = {
        vus: 10,
        duration: '30s',
    };
    ```
    
    執行測試也只要這樣下指令：
    
    ```bash
    k6 run test.js
    ```
    
    是不是非常直覺！是不是對開發人員特別友善！👍
    
    > 備註: 完整的 `options` 設定可以參考官網的 [Options reference](https://k6.io/docs/using-k6/k6-options/reference/) 文件。
    
5.  嘗試更細緻的壓測情境
    
    壓力測試有所謂 Ramp up 與 Ramp down 的測試方法，也就是透過不斷遞增或遞減的壓力，對「受測對象」進行不同程度的壓測。
    
    如下 `options` 的壓力情境設定，可以這樣解釋：
    
    -   花 30 秒的時間，從 1 VU 慢慢成長到 20 VUs
    -   然後再花 1 分 30 秒的時間，漸漸的從 20 VUs 降低到 10 VUs
    -   最後花 20 秒的時間，從 10 VUs 降低到 0 VUs，也就是結束壓測！
    
    ```javascript
    export const options = {
        stages: [
            { duration: '30s', target: 20 },
            { duration: '1m30s', target: 10 },
            { duration: '20s', target: 0 },
        ],
    };
    ```
    
    壓力測試的過程，可以透過 `check` 函式檢查 HTTP 回應是否為成功，你可以自行定義檢查的條件。
    
    ```javascript
    import http from 'k6/http';
    import { check, sleep } from 'k6';
    
    export const options = {
        stages: [
            { duration: '30s', target: 20 },
            { duration: '1m30s', target: 10 },
            { duration: '20s', target: 0 },
        ],
    };
    
    export default function () {
        const res = http.get('https://httpbin.test.k6.io/');
        check(res, { 'status was 200': (r) => r.status == 200 });
        sleep(1);
    }
    ```
    
    測試結果就會額外出現新的 `checks` 欄位，顯示壓測過程的實際回應狀況！
    
    ![status was 200 - checks](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3d6515824dfce053011c-4e7dbca2-f7c4-40e8-953a-32be50615d89.webp)
    

### 相關連結

-   [Load testing for engineering teams | Grafana k6](https://k6.io/)
-   [Demo website for load testing](https://test.k6.io/)
-   [k6 Documentation](https://k6.io/docs/)
-   [k6 JavaScript API](https://k6.io/docs/javascript-api/)
