聽說你不知道如何監(jiān)控Node服務(wù)的內(nèi)存?
剛開始,先拋出一個問題:
❝ 你知道你們生產(chǎn)環(huán)境的 Node 服務(wù)平時占用內(nèi)存多少嗎?或者說是多少量級? ❞
山月在面試 Node 候選人時,這個問題足夠篩掉一半的自稱Node精通者,不過沒有回答上來,我往往會再補充一個問題,以免漏掉優(yōu)秀的無線上經(jīng)驗的候選人:
❝ 如何知道某個進程消耗多少內(nèi)存?[1] ❞
「當(dāng)使用 Node 在生產(chǎn)環(huán)境作為服務(wù)器語言時,并發(fā)量過大或者代碼問題造成 OOM (out of memory) 或者 CPU 滿載這些都是服務(wù)器中常見的問題,此時通過監(jiān)控 CPU 及內(nèi)存,再結(jié)合日志及 Release 就很容易發(fā)現(xiàn)問題?!?/p>
本章將介紹如何監(jiān)控本地環(huán)境及生產(chǎn)環(huán)境的內(nèi)存變化
一個 Node 應(yīng)用實例
所以,如何動態(tài)監(jiān)控一個 Node 進程的內(nèi)存變化呢?
以下是一個 Node Server 的示例,并且是一個有內(nèi)存泄漏問題的示例,并且是山月在生產(chǎn)環(huán)境定位了很久的問題的精簡版。
❝ 那次內(nèi)存泄漏問題中,導(dǎo)致單個容器中的內(nèi)存從原先的 400M 暴漲到 700M,在 800M 的容器資源限制下偶爾會發(fā)生 OOM,導(dǎo)致重啟。一時沒有定位到問題 (發(fā)現(xiàn)問題過遲,半個月前的時序數(shù)據(jù)已被吞沒,于是未定位到 Release),于是把資源限制上調(diào)到 1000M。后發(fā)現(xiàn)是由 ctx.request 掛載了數(shù)據(jù)庫某個大字段而致 ❞
- const Koa = require('koa')
- const app = new Koa()
- function getData () {
- return Array.from(Array(1000)).map(x => 10086)
- }
- app.use(async (ctx, next) => {
- ctx.data = getData()
- await next()
- })
- app.use(ctx => {
- ctx.body = 'hello, world'
- })
- app.listen(3200, () => console.log('Port: 3200'))
進程內(nèi)存監(jiān)控
一些問題需要在本地及測試環(huán)境得到及時扼殺,來避免在生產(chǎn)環(huán)境造成更大的影響。那么了解在本地如何監(jiān)控內(nèi)存就至關(guān)重要。
pidstat 是 sysstat 系列 linux 性能調(diào)試工具的一個包,竟然用它來調(diào)試 linux 的性能問題,包括內(nèi)存,網(wǎng)絡(luò),IO,CPU 等。
「這不僅試用與 node,而且適用于一切進程,包括 python,java 以及 go」
- # -r: 指輸出內(nèi)存指標
- # -p: 指定 pid
- # 1: 每一秒輸出一次
- # 100: 輸出100次
- $ pidstat -r -p pid 1 100
而在使用 pidstat 之前,需要先找到進程的 pid
如何找到 Node 進程的 pid
在 node 中可以通過 process.pid 來找到進程的 pid
- > process.pid
- 16425
雖然通過寫代碼可以找到 pid,但是具有侵入性,不太實用。那如何通過非侵入的手段找到 pid 呢?有兩種辦法
- 通過多余的參數(shù)結(jié)合 ps 定位進程
- 通過端口號結(jié)合 lsof 定位進程
- $ node index.js shanyue
- # 第一種方法:通過多余的參數(shù)快速定位 pid
- $ ps -ef | grep shanyue
- root 31796 23839 1 16:38 pts/5 00:00:00 node index.js shanyue
- # 第二種方法:通過端口號定位 pid
- lsof -i:3200
- COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
- node 31796 root 20u IPv6 235987334 0t0 TCP *:tick-port (LISTEN)
使用 pidstat 監(jiān)控內(nèi)存
從以上代碼中可以知道,node 服務(wù)的 pid 為 31796,為了可以觀察到內(nèi)存的動態(tài)變化,再施加一個壓力測試
- $ ab -c 10000 -n 1000000 http://localhost:3200/
- # -r: 指輸出內(nèi)存指標
- # -p: 指定 pid
- # 1: 每一秒輸出一次
- # 100: 輸出100次
- $ pidstat -r -p 31796 1 100
- Linux 3.10.0-957.21.3.el7.x86_64 (shuifeng) 2020年07月02日 _x86_64_ (2 CPU)
- UID PID minflt/s majflt/s VSZ RSS %MEM Command
- 19時20分39秒 0 11401 0.00 0.00 566768 19800 0.12 node
- 19時20分40秒 0 11401 0.00 0.00 566768 19800 0.12 node
- 19時20分41秒 0 11401 9667.00 0.00 579024 37792 0.23 node
- 19時20分42秒 0 11401 11311.00 0.00 600716 59988 0.37 node
- 19時20分43秒 0 11401 5417.82 0.00 611420 70900 0.44 node
- 19時20分44秒 0 11401 3901.00 0.00 627292 85928 0.53 node
- 19時20分45秒 0 11401 1560.00 0.00 621660 81208 0.50 node
- 19時20分46秒 0 11401 2390.00 0.00 623964 83696 0.51 node
- 19時20分47秒 0 11401 1764.00 0.00 625500 85204 0.52 node
對于輸出指標的含義如下
- RSS: Resident Set Size,常駐內(nèi)存集,可理解為內(nèi)存,這就是我們需要監(jiān)控的內(nèi)存指標
- VSZ: virtual size,虛擬內(nèi)存
從輸出可以看出,「當(dāng)施加了壓力測試后,內(nèi)存由 19M 漲到了 85M?!?/p>
使用 top 監(jiān)控內(nèi)存
pidstat 是屬于 sysstat 下的 linux 性能工具,但在 mac 中,如何定位內(nèi)存的變化?
此時可以使用 top/htop
- $ htop -p 31796
使用 htop 監(jiān)控內(nèi)存
生產(chǎn)環(huán)境內(nèi)存監(jiān)控
由于目前生產(chǎn)環(huán)境大都部署在 k8s,「因此生產(chǎn)環(huán)境對于某個應(yīng)用的內(nèi)存監(jiān)控本質(zhì)上是 k8s 對于某個 workload/deployment 的內(nèi)存監(jiān)控」,關(guān)于內(nèi)存監(jiān)控 metric 的數(shù)據(jù)流向大致如下:
k8s -> metric server -> prometheus -> grafana
架構(gòu)圖如下:
❝ 以上圖片取自以下文章
- Kubernetes Monitoring with Prometheus[2]
- Kubernetes monitoring architecture[3] ❞
最終能夠在 grafana 中收集到某一應(yīng)用的內(nèi)存監(jiān)控實時圖:
由于本部分設(shè)計內(nèi)容過多,我將在以下的章節(jié)中進行介紹
「這不僅僅適用于 node 服務(wù),而且適用于一切 k8s 上的 workload」
總結(jié)
本章介紹了關(guān)于 Node 服務(wù)的內(nèi)存在本地環(huán)境及生產(chǎn)環(huán)境的監(jiān)控
- 本地使用 htop/top 或者 pidstat 監(jiān)控進程內(nèi)存
- 生產(chǎn)環(huán)境使用 k8s/metric-server/prometheus/grafana 監(jiān)控 node 整個應(yīng)用的內(nèi)存
當(dāng)監(jiān)控到某一服務(wù)發(fā)生內(nèi)存泄漏后,如何解決問題?因此接下來的文章將會講到
- 生產(chǎn)環(huán)境是如何監(jiān)控整個應(yīng)用的內(nèi)存的
- 當(dāng)生產(chǎn)環(huán)境發(fā)生 OOM 后,如何快速定位
- 真實生產(chǎn)環(huán)境若干 OOM 的示例定位