我們被一個 kong 的性能 bug 折騰了一個通宵
故事背景
在 Erda 的技術架構中,我們使用了 kong 作為 API 網關的技術選型。因其具備高併發低延時的特性,同時結合了 Kubernetes Ingress Controller,基於雲原生的聲明式配置方式,能夠實現豐富的 API 策略。
在我們最早交付的集羣中,kong 還是較為早期的 0.14 版本,隨着業務層面對安全的要求日益趨增,我們需要基於 kong 實現安全插件,幫助系統能夠具備更好的安全能力。由於較為早期的 0.14 版本不能使用 go-pluginserver 來擴展 kong 的插件機制,我們不得不在古老的集羣中將 kong 升級為相對較新的 2.2.0 版本。
升級過程就不在此贅述了,基本就是照着官方文檔一步步順利的升級上去,但是在升級上去之後的幾天裏,我們的 SRE 團隊收到了非常密集的諮詢甚至是聲討,部署在該集羣上的業務間歇性的無法訪問,延遲非常高。
一系列失敗的嘗試
參數調優
最開始為了快速修復這個問題,我們對 kong 的 NGINX_WORKER_PROCESSES
、MEM_CACHE_SIZE
、 DB_UPDATE_FREQUENCY
、WORKER_STATE_UPDATE_FREQUENCY
參數以及 postgres 的 work_mem
、 share_buffers
都進行了適當的調優。
但是,沒有任何效果 😓。
清理數據
由於這個集羣的歷史原因,會頻繁的註冊或者刪除 api 數據,因此集羣中累計了大約 5 萬多條 route 或者 service 數據。
我們懷疑是數據裏量大導致的性能下降,於是結合 erda 中的數據對 kong 中的歷史數據進行刪除,在刪除的過程中出現的刪除較慢並且同時 kong 的性能急劇下降的現象。
反覆測試了幾次後我們確定了 「只要調用 admin 的接口導致 kong 性能下降」 這一結論,跟社區的這個問題非常相似,鏈接如下:
http://github.com/Kong/kong/issues/7543
kong 實例的讀寫分離
確定了是 admin 接口的原因後,我們決定將 admin 跟業務的 kong 實例分開,希望 admin 的調用不會影響到業務的正常流量訪問,期待達到 kong 的 admin 接口慢就慢吧,但是不要影響業務的訪問性能。
然而,沒有任何效果😫。
postgres 遷移 RDS
kong 層面的努力無果之後,我們在測試過程中同時觀察到了當調用 admin 接口試,postgres 的進程也增多了很多,CPU使用率也漲了起來,也是決定將 pg 遷移到 更為專業的RDS中。
還是,沒有任何效果😭。
回滾
最終我們還是回滾到了 0.14 版本,追求暫時“心靈的寧靜”。
至此,線上的嘗試基本搞一段落,也大致摸清了問題復現的條件,於是我們決定在線下構造一個環境來繼續找出問題的原因。
問題的復現之路
我們將出問題的 kong 的 postgres 數據導一份到開發環境中,模擬 「調用 admin 接口是性能急劇下降」的情況,並尋找解決之道。
導入數據
我們將有問題的集羣中的 postgre 的數據備份之後然後在一個新的集羣中導入:
psql -h 127.0.0.1 -U kong < kong.sql
並且開啟 kong 的 prometheus 插件, 方便使用 grafana 來查看性能圖標:
curl -X POST http://10.97.4.116:8001/plugins --data "name=prometheus"
現象一
調用 admin 服務的 / 同樣的慢,跟線上的現象吻合,當數據量大的時候調用 admin 的 / 目錄需要耗費更多的時間。
curl http://10.97.4.116:8001
現象二
然後我們來模擬在線上遇到的調用 admin 接口後業務訪問性能變差的現象,先調用 admin 接口創建個業務的 api,以供測試,我們創建了一個 service 以及一個 routes:
curl -i -X POST http://10.97.4.116:8001/services/ -d 'name=baidu2' -d 'url=http://www.baidu.com'
curl -i -X POST http://10.97.4.116:8001/services/baidu2/routes \
-d "name=test2" \
-d "paths[1]=/baidu2"
後面可以使用 curl http://10.97.4.116:8000/baidu2
來模擬業務接口進行測試。
準備 admin 接口的測試腳本, 創建並刪除一個 service/route,中間穿插一個 service list。
#!/bin/bash
curl -i -X POST http://10.97.4.116:8001/services/ -d 'name=baidu' -d 'url=http://www.baidu.com'
curl -i -X POST http://10.97.4.116:8001/services/baidu/routes \
-d "name=test" \
-d "paths[1]=/baidu"
curl -s http://10.97.4.116:8001/services
curl -i -X DELETE http://10.97.4.116:8001/services/baidu/routes/test
然後持續調用該腳本:
for i in `seq 1 100`; do sh 1.sh ; done
在持續調用該腳本的過程中去訪問一個業務的接口,會發現非常慢,跟線上的現象完全吻合。
curl http://10.97.4.116:8000/baidu2
PS:精簡腳本,後只觸發一條寫入,或者刪除也會觸發該現象
伴隨現象
- kong 實例的 cpu 跟 mem 都持續上漲,且當 admin 接口調用結束後此現象依然沒有結束。mem 上漲到一定程度會到時 nginx worker 進程 oom 掉,然後重啟,這個也許是訪問慢的原因;
- 我們設置了
KONG_NGINX_WORKER_PROCESSES
為 4,並且為 pod 的內存為 4G 的時候,pod 的整體內存會穩定在 2.3G, 但是 調用 admin 接口試,pod 的內存就會一直上漲至超過 4G,觸發 worker 的 OOM,於是我將 pod 的內存調整到了 8G。再次調用 admin 接口,發現 pod 的內存依然一直上漲,只是上漲到了 4.11 G 就結束了,這似乎意味着我們是要設置 pod 的內存為KONG_NGINX_WORKER_PROCESSES
兩倍,這個問題就解決了(但是還有個重要的問題是為什麼調用一次 admin 接口,會導致內存漲了那麼多); - 另外,當我持續調用 admin 接口的時候, 最終的內存會持續增長並且穩定到 6.9G。
這個時候我們將問題抽象一下:
調用「kong admin 接口」導致內存一直上漲,進而觸發 oom 導致 worker 被 kill 掉,最終導致業務訪問慢。
繼續調查內存是被什麼佔用了:
我使用 pmap -x [pid]
前後兩次查看了 worker 進程的內存分佈,變化的是第二張如中框起來來的部分,從地址上看整塊內存都被換過了,但是將內存數據導出並且字符串化之後,沒有什麼有效的信息可供進一步排查。
結論
- 該問題跟 kong 的升級(0.14 --> 2.2.0) 沒有關係,直接使用 2.2.0 版本也會有這個問題;
- kong 每隔
worker_state_update_frequency
時間後會在內存中重建 router,一旦開始重建就會導致 內存上漲,看了下代碼問題出在了Router.new
的方法這裏,會申請 lrucache 但是使用後沒有 flush_all,根據最新的 2.8.1 版本的 lrucache 進行了釋放問題依然存在; - 也就是 kong 的
Router.new
方法裏的其他邏輯到時的內存上漲;
- 這也就表明這個問題是 kong 存在的一個性能 bug,及時在最新的版本中依然存在,當 route 跟 service 達到某個量級的時候會出現調用 admin 接口,導致 kong 的 worker 內存迅速上漲,使得 oom 進而引發業務訪問性能變差的現象,臨時的解決辦法可以是減少
NGINX_WORKER_PROCESSES
的數量並且增加 kong pod 的內存量,保證調用 admin 接口後需要的內存足夠使用不觸發 oom,才能保證業務的正常使用。
最後,我們會在 http://github.com/Kong/kong/issues/7543 這個 issue 中將該現象補充進去,歡迎大家持續關注,一起討論~
更多技術乾貨請關注【爾達 Erda】公眾號,與眾多開源愛好者共同成長~
- 我們被一個 kong 的性能 bug 折騰了一個通宵
- 我們被一個 kong 的性能 bug 折騰了一個通宵
- 一文詳解|Go 分佈式鏈路追蹤實現原理
- 用更雲原生的方式做診斷|大規模 K8s 集羣診斷利器深度解析
- 用更雲原生的方式做診斷|大規模 K8s 集羣診斷利器深度解析
- 深入探索雲原生流水線的架構設計
- 終極套娃 2.0|雲原生 PaaS 平台的可觀測性實踐分享
- 深度好文|探尋雲原生時代應用研發新模式
- 一文詳解|雲原生下的指標與日誌採集
- 雲原生下的指標與日誌採集
- 開發者故事|朝九晚六大小周,我就是快樂的技術人
- 「Spark從精通到重新入門(二)」Spark中不可不知的動態資源分配
- 「Spark從精通到重新入門(一)」Spark 中不可不知的動態優化
- 大規模 K8s 集羣管理經驗分享 · 上篇
- 為構建大型複雜系統而生的微服務框架 Erda Infra
- 一文搞懂指標採集利器 Telegraf
- Erda 1.1 版本發佈|3 大亮點特性最新解讀
- 聊一聊在阿里做了 8 年研發後,我對打造大型工程研發團隊的再思考
- 如何使用 Kind 快速創建 K8s 集羣?
- 超好玩:使用 Erda 構建部署應用是什麼體驗?