起源
當開發http 介面的時候,往往我們會關心開發的server能承受多少壓力,這時候一個比較常用的工具是 apache bench。一部分情況下ab工具確實能滿足需求,但是很多時候並不能,需要分散式式測試工具。
壓力測試比較關心的是產生壓力
、獲取測試物件的如TPS、響應時延等效能資料
和主機資源如cpu,memory消耗資料
以定位效能瓶頸,簡單的單機測試工具並不能很好的滿足這些需求。
我們可以選擇雲平臺的分散式測試工具,比如騰訊的wetest,阿里雲也有類似產品。但是這種產品往往收費不菲。也可以選擇類似的開源產品,比如locust。但是調研發現,這種開源產品往往比較簡單,或者過於陳舊。google官網有一個壓力測試例子,用的就是locust。大家可以看一下這個工具,基於python,功能非常簡陋,master,slave模式,不支援線上編輯指令碼,修改測試要重啟。
如何設計這樣的壓力測試工具
k8s是目前比較流行的容器編排系統,是否可以在k8s上自己做一個分散式測試工具呢。當然可以用google推薦的做法,在k8s上執行master slave 模式的locust,又或者自己動手做一個。
既然執行在k8s上,那麼這個測試工具實際上關心的事就比較簡單了:k8s已經實現了排程,資源監控,我們的工具只需要定義指令碼,執行指令碼,統計測試結果,收集測試過程的資源消耗。
需要說明的是,測試工具執行在k8s上只是為了利用k8s的基礎設施,簡化工具設計,事實上測試的物件可以是執行在k8s上或者在k8s外的任何服務。
使用golang做測試指令碼
選擇golang作為測試指令碼的原因一是語言成熟,語法簡單,二是goroutine很方便,很容易把壓力打上去,三是即有編譯型語言的高效能,同時又像指令碼一樣能夠快速執行(編譯很快)。
golang本身是編譯型語言,不是指令碼語言,執行要先編譯,但是因為編譯很快,實際上很容易當成一個指令碼來執行,比如這個例子。或者顯式的執行go build,go run,像docker/distribution專案的dockerfile這樣。那麼要實現一個"動態"執行golang的工具要做的就是:定義一種型別的任務,使用者的測試指令碼只要實現這種任務的interface,客戶端就可以裝載這種指令碼編譯執行。具體舉例如下。
// 1. 定義一個TTask作為interface
type TTask interface {
Name() string
Run() int
}
// 2. 使用者的測試指令碼實現這個interface
// New ....
func newhello() task.TTask {
return &hellotask{}
}
type hellotask struct {
}
func (h *hellotask) Name() string {
return "task"
}
func (h *hellotask) Run() int {
time.Sleep(time.Microsecond * 10)
return 0
}
// 3. 實現一種註冊式的外掛機制,讓使用者的任務註冊進來
var tasksets = make(map[string]NewFunc, 0)
func Register(newfunc NewFunc) {
t := newfunc()
_, registered := tasksets[t.Name()]
if registered {
panic(fmt.Sprintf("TTask named %s already registered", t.Name()))
}
tasksets[t.Name()] = newfunc
}
// 使用者指令碼中要註冊他的指令碼
Register(task.NewFunc(newhello))
// 4. 測試客戶端不關心使用者指令碼的實現細節,執行tasksets裡面TTask就可以了
func main() {
for _, f := range tasksets {
f().Run()
...
}
}複製程式碼
一個更復雜一點的例子在這裡。
使用k8s的基礎設施
首先實現測試任務,使用job是很合適不過的,設定parallelism併發執行。
其次要實現任務的動態新增和掛載,可以使用k8s的configmap來實現。使用configmap來儲存使用者的指令碼,執行agent的時候將指令碼自動掛載到agent的容器對應路徑,容器啟動指令碼中加入build流程,這樣就能很方便的實現一種"動態"的執行golang指令碼的效果了。
當壓力測試使用多個節點的時候,我們往往需要同時觀測測試客戶端和服務端的cpu等資源監控,因為客戶端已經天然的執行在k8s上了,可以直接使用k8s的監控設施。
另外測試工具的設計並沒有侷限在測試執行在k8s上的server,但是如果剛好,被測試的物件也執行在k8s上,那麼也可以很方便的或者server的宿主機metrics,如果不是,那麼server端就需要裝一個收集metric的deamon實現同樣的效果了。
效果
dashboard支援檢視測試任務,每個任務有一個最近執行的記錄和建立時間。
編輯一個測試專案,測試指令碼是用golang編輯的,需要實現一個TTaskSet 的interface。支援設定任務的goroutine和執行時間,權重,設定權重之後goroutine數量會在多個taskset之間分配,一個taskset又可以新增多個task。taskset併發執行,一個taskset中的task序列執行,這樣設計的好處是可以滿足使用者併發,序列,帶context的序列多種需求,非常靈活。任務可以選擇執行的節點,多個節點併發測試。
執行完測試的效果,目前還沒有加入測試中的client,server資源監控,但是簡單的測試統計已經有了。如圖是測試的一個執行在1G虛擬機器的nginx容器的測試結果。測試結果同時有各個節點的執行結果和彙總結果,同時繪製latency的百分點陣圖。
完整專案地址在 github.com/arlert/ymir 歡迎拍磚。