一. 前言
kubectl top 可以很方便地查看node匆浙、pod的實(shí)時(shí)資源使用情況:如CPU刃泌、內(nèi)存昧穿。這篇文章會(huì)介紹其數(shù)據(jù)鏈路和實(shí)現(xiàn)原理勺远,同時(shí)借kubectl top 闡述 k8s 中的監(jiān)控體系,窺一斑而知全豹时鸵。最后會(huì)解釋常見(jiàn)的一些問(wèn)題:
- kubectl top 為什么會(huì)報(bào)錯(cuò)胶逢?
- kubectl top node 怎么計(jì)算厅瞎,和節(jié)點(diǎn)上直接 top 有什么區(qū)別?
- kubectl top pod 怎么計(jì)算初坠,包含 pause 嗎和簸?
- kubectl top pod 和exec 進(jìn)入 pod 后看到的 top 不一樣?
- kubectl top pod 和 docker stats得到的值為什么不同碟刺?
以下命令的運(yùn)行環(huán)境為:
- k8s 1.8
- k8s 1.13
二. 使用
kubectl top 是基礎(chǔ)命令锁保,但是需要部署配套的組件才能獲取到監(jiān)控值
- 1.8以下:部署 heapter
- 1.8以上:部署 metric-server
kubectl top node: 查看node的使用情況
kubectl top pod: 查看 pod 的使用情況
不指定pod 名稱(chēng),則顯示命名空間下所有 pod半沽,--containers可以顯示 pod 內(nèi)所有的container
指標(biāo)含義:
- 和k8s中的request爽柒、limit一致,CPU單位100m=0.1 內(nèi)存單位1Mi=1024Ki
- pod的內(nèi)存值是其實(shí)際使用量抄囚,也是做limit限制時(shí)判斷oom的依據(jù)霉赡。pod的使用量等于其所有業(yè)務(wù)容器的總和橄务,不包括 pause 容器幔托,值等于cadvisr中的container_memory_working_set_bytes指標(biāo)
- node的值并不等于該node 上所有 pod 值的總和,也不等于直接在機(jī)器上運(yùn)行 top 或 free 看到的值
三. 實(shí)現(xiàn)原理
3.1 數(shù)據(jù)鏈路
kubectl top 蜂挪、 k8s dashboard 以及 HPA 等調(diào)度組件使用的數(shù)據(jù)是一樣重挑,數(shù)據(jù)鏈路如下:
使用 heapster 時(shí):apiserver 會(huì)直接將metric請(qǐng)求通過(guò) proxy 的方式轉(zhuǎn)發(fā)給集群內(nèi)的 hepaster 服務(wù)。
而使用 metrics-server 時(shí):apiserver是通過(guò)/apis/metrics.k8s.io/的地址訪(fǎng)問(wèn)metric
這里可以對(duì)比下kubect get pod時(shí)的日志:
3.2 metric api
可以發(fā)現(xiàn)棠涮,heapster使用的是 proxy 轉(zhuǎn)發(fā)谬哀,而 metric-server 和普通 pod都是使用 api/xx 的資源接口,heapster采用的這種 proxy 方式是有問(wèn)題的:
- proxy只是代理請(qǐng)求严肪,一般用于問(wèn)題排查史煎,不夠穩(wěn)定,且版本不可控
- heapster的接口不能像apiserver一樣有完整的鑒權(quán)以及client集成驳糯,兩邊都維護(hù)的話(huà)代價(jià)高篇梭,如generic apiserver)
- pod 的監(jiān)控?cái)?shù)據(jù)是核心指標(biāo)(HPA調(diào)度),應(yīng)該和 pod 本身?yè)碛型鹊匚辉褪啵?metric應(yīng)該作為一種資源存在恬偷,如metrics.k8s.io 的形式,稱(chēng)之為 Metric Api
于是官方從 1.8 版本開(kāi)始逐步廢棄 heapster帘睦,并提出了上邊Metric api 的概念袍患,而metrics-server 就是這種概念下官方的一種實(shí)現(xiàn),用于從 kubelet獲取指標(biāo)竣付,替換掉之前的 heapster
3.3 kube-aggregator
有了metrics-server組件诡延,采集到了需要的數(shù)據(jù),也暴露了接口古胆,但走到這一步和 heapster 其實(shí)沒(méi)有區(qū)別肆良,最關(guān)鍵的一步就是如何將打到apiserver的/apis/metrics.k8s.io
請(qǐng)求轉(zhuǎn)發(fā)給metrics-server組件?解決方案就是:kube-aggregator。
kube-aggregator是對(duì) apiserver 的有力擴(kuò)展妖滔,它允許k8s的開(kāi)發(fā)人員編寫(xiě)一個(gè)自己的服務(wù)隧哮,并把這個(gè)服務(wù)注冊(cè)到k8s的api里面,即擴(kuò)展 API座舍,metric-server其實(shí)在 1.7版本就已經(jīng)完成了沮翔,只是在等kube-aggregator的出現(xiàn)。
kube-aggregator是 apiserver 中的實(shí)現(xiàn)曲秉,有些 k8s 版本默認(rèn)沒(méi)開(kāi)啟采蚀,你可以加上這些配置
來(lái)開(kāi)啟。他的核心功能是動(dòng)態(tài)注冊(cè)承二、發(fā)現(xiàn)匯總榆鼠、安全代理。
如metric-server注冊(cè) pod 和 node 時(shí):
3.4 監(jiān)控體系
在提出 metric api 的概念時(shí)亥鸠,官方頁(yè)提出了新的監(jiān)控體系妆够,監(jiān)控資源被分為了2種:
Core metrics(核心指標(biāo)):從 Kubelet、cAdvisor 等獲取度量數(shù)據(jù)负蚊,再由metrics-server提供給 Dashboard、HPA 控制器等使用伤极。
Custom Metrics(自定義指標(biāo)):由Prometheus Adapter提供API custom.metrics.k8s.io彼硫,由此可支持任意Prometheus采集到的指標(biāo)串绩。
核心指標(biāo)只包含node和pod的cpu剪芍、內(nèi)存等状共,一般來(lái)說(shuō),核心指標(biāo)作HPA已經(jīng)足夠,但如果想根據(jù)自定義指標(biāo):如請(qǐng)求qps/5xx錯(cuò)誤數(shù)來(lái)實(shí)現(xiàn)HPA呵扛,就需要使用自定義指標(biāo)了每庆。
目前Kubernetes中自定義指標(biāo)一般由Prometheus來(lái)提供,再利用k8s-prometheus-adpater聚合到apiserver今穿,實(shí)現(xiàn)和核心指標(biāo)(metric-server)同樣的效果缤灵。
3.5 kubelet
前面提到,無(wú)論是 heapster還是 metric-server蓝晒,都只是數(shù)據(jù)的中轉(zhuǎn)和聚合腮出,兩者都是調(diào)用的 kubelet 的 api 接口獲取的數(shù)據(jù),而 kubelet 代碼中實(shí)際采集指標(biāo)的是 cadvisor 模塊芝薇,你可以在 node 節(jié)點(diǎn)訪(fǎng)問(wèn) 10255 端口 (read-only-port)獲取監(jiān)控?cái)?shù)據(jù):
- Kubelet Summary metrics: 127.0.0.1:10255/metrics胚嘲,暴露 node、pod 匯總數(shù)據(jù)
- Cadvisor metrics: 127.0.0.1:10255/metrics/cadvisor洛二,暴露 container 維度數(shù)據(jù)
示例馋劈,容器的內(nèi)存使用量:
kubelet雖然提供了 metric 接口攻锰,但實(shí)際監(jiān)控邏輯由內(nèi)置的cAdvisor模塊負(fù)責(zé),演變過(guò)程如下:
- 從k8s 1.6開(kāi)始妓雾,kubernetes將cAdvisor開(kāi)始集成在kubelet中娶吞,不需要單獨(dú)配置
- 從k8s 1.7開(kāi)始,Kubelet metrics API 不再包含 cadvisor metrics械姻,而是提供了一個(gè)獨(dú)立的 API 接口來(lái)做匯總
- 從k8s 1.12開(kāi)始寝志,cadvisor 監(jiān)聽(tīng)的端口在k8s中被刪除,所有監(jiān)控?cái)?shù)據(jù)統(tǒng)一由Kubelet的API提供
到這里為止策添,k8s范圍內(nèi)的監(jiān)控體系就結(jié)束了材部,如果你想繼續(xù)了解cadvisor和 cgroup 的內(nèi)容,可以向下閱讀
3.6 cadvisor
cadvisor由谷歌開(kāi)源唯竹,使用Go開(kāi)發(fā)乐导,項(xiàng)目地址也是google/cadvisor,cadvisor不僅可以搜集一臺(tái)機(jī)器上所有運(yùn)行的容器信息浸颓,包括CPU使用情況物臂、內(nèi)存使用情況、網(wǎng)絡(luò)吞吐量及文件系統(tǒng)使用情況产上,還提供基礎(chǔ)查詢(xún)界面和http接口棵磷,方便其他組件進(jìn)行數(shù)據(jù)抓取。在K8S中集成在Kubelet里作為默認(rèn)啟動(dòng)項(xiàng)晋涣,k8s官方標(biāo)配仪媒。
cadvisor 拿到的數(shù)據(jù)結(jié)構(gòu)示例:
核心邏輯:
通過(guò)new出來(lái)的memoryStorage以及sysfs實(shí)例,創(chuàng)建一個(gè)manager實(shí)例谢鹊,manager的interface中定義了許多用于獲取容器和machine信息的函數(shù)
cadvisor的指標(biāo)解讀:cgroup-v1
cadvisor獲取指標(biāo)時(shí)實(shí)際調(diào)用的是 runc/libcontainer庫(kù)算吩,而libcontainer是對(duì) cgroup文件 的封裝,即 cadvsior也只是個(gè)轉(zhuǎn)發(fā)者佃扼,它的數(shù)據(jù)來(lái)自于cgroup文件偎巢。
3.7 cgroup
cgroup文件中的值是監(jiān)控?cái)?shù)據(jù)的最終來(lái)源,如
mem usage的值兼耀,來(lái)自于
/sys/fs/cgroup/memory/docker/[containerId]/memory.usage_in_bytes
如果沒(méi)限制內(nèi)存压昼,Limit = machine_mem,否則來(lái)自于
/sys/fs/cgroup/memory/docker/[id]/memory.limit_in_bytes
內(nèi)存使用率 = memory.usage_in_bytes/memory.limit_in_bytes
一般情況下瘤运,cgroup文件夾下的內(nèi)容包括CPU窍霞、內(nèi)存、磁盤(pán)尽超、網(wǎng)絡(luò)等信息:
如memory下的幾個(gè)常用的指標(biāo)含義:
memory.stat中的信息是最全的:
原理到這里結(jié)束官撼,這里解釋下最開(kāi)始的kubectl top 的幾個(gè)問(wèn)題:
四. 問(wèn)題
4.1 kubectl top 為什么會(huì)報(bào)錯(cuò)
一般情況下 top 報(bào)錯(cuò)有以下幾種:
- 沒(méi)有部署 heapster 或者 metric-server,或者pod運(yùn)行異常似谁,可以排查對(duì)應(yīng) pod日志
- 要看的pod 剛剛建出來(lái)傲绣,還沒(méi)來(lái)得及采集指標(biāo)掠哥,報(bào) not found 錯(cuò)誤,默認(rèn)1 分鐘
- 以上兩種都不是秃诵,可以檢查下kubelet 的 10255 端口是否開(kāi)放续搀,默認(rèn)情況下會(huì)使用這個(gè)只讀端口獲取指標(biāo),也可以在heapster或metric-server的配置中增加證書(shū)菠净,換成 10250 認(rèn)證端口
4.2 kubectl top pod 內(nèi)存怎么計(jì)算禁舷,包含 pause容器 嗎
每次啟動(dòng) pod,都會(huì)有一個(gè) pause 容器毅往,既然是容器就一定有資源消耗(一般在 2-3M 的內(nèi)存)牵咙,cgroup 文件中,業(yè)務(wù)容器和 pause 容器都在同一個(gè) pod的文件夾下攀唯。
但 cadvisor 在查詢(xún) pod 的內(nèi)存使用量時(shí)洁桌,是先獲取了 pod 下的container列表,再逐個(gè)獲取container的內(nèi)存占用侯嘀,不過(guò)這里的 container 列表并沒(méi)有包含 pause另凌,因此最終 top pod 的結(jié)果也不包含 pause 容器
pod 的內(nèi)存使用量計(jì)算
kubectl top pod 得到的內(nèi)存使用量鳖昌,并不是cadvisor 中的container_memory_usage_bytes芽唇,而是container_memory_working_set_bytes栗涂,計(jì)算方式為:
container_memory_usage_bytes == container_memory_rss + container_memory_cache + kernel memory
container_memory_working_set_bytes = container_memory_usage_bytes - total_inactive_file(未激活的匿名緩存頁(yè))
container_memory_working_set_bytes是容器真實(shí)使用的內(nèi)存量意系,也是limit限制時(shí)的 oom 判斷依據(jù)
cadvisor 中的 container_memory_usage_bytes對(duì)應(yīng) cgroup 中的 memory.usage_in_bytes文件,但container_memory_working_set_bytes并沒(méi)有具體的文件搏熄,他的計(jì)算邏輯在 cadvisor 的代碼中惭载,如下:
同理越妈,node 的內(nèi)存使用量也是container_memory_working_set_bytes
4.3 kubectl top node 怎么計(jì)算错沃,和節(jié)點(diǎn)上直接 top 有什么區(qū)別
kubectl top node得到的 cpu 和內(nèi)存值栅组,并不是節(jié)點(diǎn)上所有 pod 的總和,不要直接相加枢析。top node是機(jī)器上cgroup根目錄下的匯總統(tǒng)計(jì)
在機(jī)器上直接 top命令看到的值和 kubectl top node 不能直接對(duì)比,因?yàn)橛?jì)算邏輯不同刃麸,如內(nèi)存醒叁,大致的對(duì)應(yīng)關(guān)系是(前者是機(jī)器上 top,后者是kubectl top):
rss + cache = (in)active_anon + (in)active_file
4.4 kubectl top pod 和exec 進(jìn)入 pod 后看到的 top 不一樣
top命令的差異和上邊 一致泊业,無(wú)法直接對(duì)比把沼,同時(shí),就算你對(duì) pod 做了limit 限制吁伺,pod 內(nèi)的 top 看到的內(nèi)存和 cpu總量仍然是機(jī)器總量饮睬,并不是pod 可分配量
- 進(jìn)程的RSS為進(jìn)程使用的所有物理內(nèi)存(file_rss+anon_rss),即Anonymous pages+Mapped apges(包含共享內(nèi)存)
- cgroup RSS為(anonymous and swap cache memory)篮奄,不包含共享內(nèi)存捆愁。兩者都不包含file cache
4.5 kubectl top pod 和 docker stats得到的值為什么不同割去?
docker stats dockerID 可以看到容器當(dāng)前的使用量:
如果你的 pod中只有一個(gè) container,你會(huì)發(fā)現(xiàn)docker stats 值不等于kubectl top 的值昼丑,既不等于 container_memory_usage_bytes呻逆,也不等于container_memory_working_set_bytes。
因?yàn)閐ocker stats 和 cadvisor 的計(jì)算方式不同菩帝,總體值會(huì)小于 kubectl top:計(jì)算邏輯是:
docker stats = container_memory_usage_bytes - container_memory_cache
五. 后記
一般情況下咖城,我們并不需要時(shí)刻關(guān)心node 或 pod 的使用量,因?yàn)橛屑鹤詣?dòng)擴(kuò)縮容(cluster-autoscaler)和pod 水平擴(kuò)縮容(HPA)來(lái)應(yīng)對(duì)這兩種資源變化呼奢,資源指標(biāo)的意義更適合使用prometheus來(lái)持久化 cadvisor 的數(shù)據(jù)宜雀,用于回溯歷史或者發(fā)送報(bào)警。
關(guān)于prometheus的內(nèi)容可以看容器監(jiān)控系列
其他補(bǔ)充:
- 雖然kubectl top help中顯示支持Storage握础,但直到 1.16 版本仍然不支持
- 1.13 之前需要 heapster州袒,1.13 以后需要metric-server,這部分kubectl top help的輸出 有誤弓候,里面只提到了heapster
- k8s dashboard 中的監(jiān)控圖默認(rèn)使用的是 heapster郎哭,切換為 metric-server后數(shù)據(jù)會(huì)異常,需要多部署一個(gè)metric-server-scraper 的 pod 來(lái)做接口轉(zhuǎn)換菇存,具體參考 pr:https://github.com/kubernetes/dashboard/pull/3504
六. 參考資料
- https://github.com/kubernetes-sigs/metrics-server/issues/193
- https://github.com/kubernetes/kubernetes/pull/83247
- https://www.cnblogs.com/liuhongru/p/11215447.html
- https://github.com/DirectXMan12/k8s-prometheus-adapter/blob/master/docs/walkthrough.md#quantity-values
- https://github.com/fabric8io/kansible/blob/master/vendor/k8s.io/kubernetes/docs/design/resources.md
- https://erdong.site/linux/system/computer-unit-conversion.html
- https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
- https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/6/html/resource_management_guide/sec-memory
- https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
- https://www.cnblogs.com/liuhongru/p/11215447.html
- https://github.com/moby/moby/issues/10824
- https://github.com/docker/cli/pull/80