摘要:診斷修復(fù)不穩(wěn)定的關(guān)鍵詞社區(qū)相關(guān)問(wèn)題起源薛定諤平臺(tái)是我司開(kāi)發(fā)的基于建立的一套自動(dòng)化測(cè)試框架,提供各種能力,同時(shí)也提供自動(dòng)化的測(cè)試,各類(lèi)異常監(jiān)控告警以及自動(dòng)輸出測(cè)試報(bào)告等功能。
作者:張文博
Kubernetes(K8s)是一個(gè)開(kāi)源容器編排系統(tǒng),可自動(dòng)執(zhí)行應(yīng)用程序部署、擴(kuò)展和管理。它是云原生世界的操作系統(tǒng)。 K8s 或操作系統(tǒng)中的任何缺陷都可能使用戶進(jìn)程存在風(fēng)險(xiǎn)。作為 PingCAP EE(效率工程)團(tuán)隊(duì),我們?cè)?K8s 中測(cè)試 TiDB Operator(一個(gè)創(chuàng)建和管理 TiDB 集群的工具)時(shí),發(fā)現(xiàn)了兩個(gè) Linux 內(nèi)核錯(cuò)誤。這些錯(cuò)誤已經(jīng)困擾我們很長(zhǎng)一段時(shí)間,并沒(méi)有在整個(gè) K8s 社區(qū)中徹底修復(fù)。
經(jīng)過(guò)廣泛的調(diào)查和診斷,我們已經(jīng)確定了處理這些問(wèn)題的方法。在這篇文章中,我們將與大家分享這些解決方法。不過(guò),盡管這些方法很有用,但我們認(rèn)為這只是權(quán)宜之策,相信未來(lái)會(huì)有更優(yōu)雅的解決方案,也期望 K8s 社區(qū)、RHEL 和 CentOS 可以在不久的將來(lái)徹底修復(fù)這些問(wèn)題。
Bug #1: 診斷修復(fù)不穩(wěn)定的 Kmem Accounting關(guān)鍵詞:SLUB: Unable to allocate memory on node -1
社區(qū)相關(guān) Issue:
github.com/kubernetes/…
github.com/opencontain…
support.mesosphere.com/s/article/C…
問(wèn)題起源薛定諤平臺(tái)是我司開(kāi)發(fā)的基于 K8s 建立的一套自動(dòng)化測(cè)試框架,提供各種 Chaos 能力,同時(shí)也提供自動(dòng)化的 Bench 測(cè)試,各類(lèi)異常監(jiān)控、告警以及自動(dòng)輸出測(cè)試報(bào)告等功能。我們發(fā)現(xiàn) TiKV 在薛定諤平臺(tái)上做 OLTP 測(cè)試時(shí)偶爾會(huì)發(fā)生 I/O 性能抖動(dòng),但從下面幾項(xiàng)來(lái)看未發(fā)現(xiàn)異常:
TiKV 和 RocksDB 的日志
CPU 使用率
內(nèi)存和磁盤(pán)等負(fù)載信息
只能偶爾看到 dmesg 命令執(zhí)行的結(jié)果中包含一些 “SLUB: Unable to allocate memory on node -1” 信息。
問(wèn)題分析我們使用 perf-tools 中的 funcslower trace 來(lái)執(zhí)行較慢的內(nèi)核函數(shù)并調(diào)整內(nèi)核參數(shù) hung_task_timeout_secs 閾值,抓取到了一些 TiKV 執(zhí)行寫(xiě)操作時(shí)的內(nèi)核路徑信息:
從上圖的信息中可以看到 I/O 抖動(dòng)和文件系統(tǒng)執(zhí)行 writepage 有關(guān)。同時(shí)捕獲到性能抖動(dòng)的前后,在 node 內(nèi)存資源充足的情況下,dmesg 返回的結(jié)果也會(huì)出現(xiàn)大量 “SLUB: Unable to allocate memory on node -1” 的信息。
從 hung_task 輸出的 call stack 信息結(jié)合內(nèi)核代碼發(fā)現(xiàn),內(nèi)核在執(zhí)行 bvec_alloc 函數(shù)分配 bio_vec 對(duì)象時(shí),會(huì)先嘗試通過(guò) kmem_cache_alloc 進(jìn)行分配,kmem_cache_alloc 失敗后,再進(jìn)行 fallback 嘗試從 mempool 中進(jìn)行分配,而在 mempool 內(nèi)部會(huì)先嘗試執(zhí)行 pool->alloc 回調(diào)進(jìn)行分配,pool->alloc 分配失敗后,內(nèi)核會(huì)將進(jìn)程設(shè)置為不可中斷狀態(tài)并放入等待隊(duì)列中進(jìn)行等待,當(dāng)其他進(jìn)程向 mempool 歸還內(nèi)存或定時(shí)器超時(shí)(5s) 后,進(jìn)程調(diào)度器會(huì)喚醒該進(jìn)程進(jìn)行重試 ,這個(gè)等待時(shí)間和我們業(yè)務(wù)監(jiān)控的抖動(dòng)延遲相符。
但是我們?cè)趧?chuàng)建 Docker 容器時(shí),并沒(méi)有設(shè)置 kmem limit,為什么還會(huì)有 kmem 不足的問(wèn)題呢?為了確定 kmem limit 是否被設(shè)置,我們進(jìn)入 cgroup memory controller 對(duì)容器的 kmem 信息進(jìn)行查看,發(fā)現(xiàn) kmem 的統(tǒng)計(jì)信息被開(kāi)啟了, 但 limit 值設(shè)置的非常大。
我們已知 kmem accounting 在 RHEL 3.10 版本內(nèi)核上是不穩(wěn)定的,因此懷疑 SLUB 分配失敗是由內(nèi)核 bug 引起的,搜索 kernel patch 信息我們發(fā)現(xiàn)確實(shí)是內(nèi)核 bug, 在社區(qū)高版本內(nèi)核中已修復(fù):
slub: make dead caches discard free slabs immediately
同時(shí)還有一個(gè) namespace 泄漏問(wèn)題也和 kmem accounting 有關(guān):
mm: memcontrol: fix cgroup creation failure after many small jobs
那么是誰(shuí)開(kāi)啟了 kmem accounting 功能呢?我們使用 bcc 中的 opensnoop 工具對(duì) kmem 配置文件進(jìn)行監(jiān)控,捕獲到修改者 runc 。從 K8s 代碼上可以確認(rèn)是 K8s 依賴的 runc 項(xiàng)目默認(rèn)開(kāi)啟了 kmem accounting。
解決方案通過(guò)上述分析,我們要么升級(jí)到高版本內(nèi)核,要么在啟動(dòng)容器的時(shí)候禁用 kmem accounting 功能,目前 runc 已提供條件編譯選項(xiàng),可以通過(guò) Build Tags 來(lái)禁用 kmem accounting,關(guān)閉后我們測(cè)試發(fā)現(xiàn)抖動(dòng)情況消失了,namespace 泄漏問(wèn)題和 SLUB 分配失敗的問(wèn)題也消失了。
操作步驟我們需要在 kubelet 和 docker 上都將 kmem account 功能關(guān)閉。
kubelet 需要重新編譯,不同的版本有不同的方式。
如果 kubelet 版本是 v1.14 及以上,則可以通過(guò)在編譯 kubelet 的時(shí)候加上 Build Tags 來(lái)關(guān)閉 kmem account:
$ git clone --branch v1.14.1 --single-branch --depth 1 [https://github.com/kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
$ cd kubernetes
$ KUBE_GIT_VERSION=v1.14.1 ./build/run.sh make kubelet GOFLAGS="-tags=nokmem"
但如果 kubelet 版本是 v1.13 及以下,則無(wú)法通過(guò)在編譯 kubelet 的時(shí)候加 Build Tags 來(lái)關(guān)閉,需要重新編譯 kubelet,步驟如下。
首先下載 Kubernetes 代碼:
$ git clone --branch v1.12.8 --single-branch --depth 1 https://github.com/kubernetes/kubernetes
$ cd kubernetes
然后手動(dòng)將開(kāi)啟 kmem account 功能的 兩個(gè)函數(shù) 替換成 下面這樣:
func EnableKernelMemoryAccounting(path string) error {
return nil
}
func setKernelMemory(path string, kernelMemoryLimit int64) error {
return nil
}
之后重新編譯 kubelet:
$ KUBE_GIT_VERSION=v1.12.8 ./build/run.sh make kubelet
編譯好的 kubelet 在 ./_output/dockerized/bin/$GOOS/$GOARCH/kubelet 中。
同時(shí)需要升級(jí) docker-ce 到 18.09.1 以上,此版本 docker 已經(jīng)將 runc 的 kmem account 功能關(guān)閉。
最后需要重啟機(jī)器。
驗(yàn)證方法是查看新創(chuàng)建的 pod 的所有 container 已關(guān)閉 kmem,如果為下面結(jié)果則已關(guān)閉:
$ cat /sys/fs/cgroup/memory/kubepods/burstable/podBug #2:診斷修復(fù)網(wǎng)絡(luò)設(shè)備引用計(jì)數(shù)泄漏問(wèn)題/ /memory.kmem.slabinfo cat: memory.kmem.slabinfo: Input/output error
關(guān)鍵詞:kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1
社區(qū)相關(guān) Issue:
github.com/kubernetes/…
github.com/projectcali…
github.com/moby/moby/i…
問(wèn)題起源我們的薛定諤分布式測(cè)試集群運(yùn)行一段時(shí)間后,經(jīng)常會(huì)持續(xù)出現(xiàn)“kernel:unregister_netdevice: waiting for eth0 to become free. Usage count = 1” 問(wèn)題,并會(huì)導(dǎo)致多個(gè)進(jìn)程進(jìn)入不可中斷狀態(tài),只能通過(guò)重啟服務(wù)器來(lái)解決。
問(wèn)題分析通過(guò)使用 crash 工具對(duì) vmcore 進(jìn)行分析,我們發(fā)現(xiàn)內(nèi)核線程阻塞在 netdev_wait_allrefs 函數(shù),無(wú)限循環(huán)等待 dev->refcnt 降為 0。由于 pod 已經(jīng)釋放了,因此懷疑是引用計(jì)數(shù)泄漏問(wèn)題。我們查找 K8s issue 后發(fā)現(xiàn)問(wèn)題出在內(nèi)核上,但這個(gè)問(wèn)題沒(méi)有簡(jiǎn)單的穩(wěn)定可靠復(fù)現(xiàn)方法,且在社區(qū)高版本內(nèi)核上依然會(huì)出現(xiàn)這個(gè)問(wèn)題。
為避免每次出現(xiàn)問(wèn)題都需要重啟服務(wù)器,我們開(kāi)發(fā)一個(gè)內(nèi)核模塊,當(dāng)發(fā)現(xiàn) net_device 引用計(jì)數(shù)已泄漏時(shí),將引用計(jì)數(shù)清 0 后移除此內(nèi)核模塊(避免誤刪除其他非引用計(jì)數(shù)泄漏的網(wǎng)卡)。為了避免每次手動(dòng)清理,我們寫(xiě)了一個(gè)監(jiān)控腳本,周期性自動(dòng)執(zhí)行這個(gè)操作。但此方案仍然存在缺陷:
引用計(jì)數(shù)的泄漏和監(jiān)控發(fā)現(xiàn)之間存在一定的延遲,在這段延遲中 K8s 系統(tǒng)可能會(huì)出現(xiàn)其他問(wèn)題;
在內(nèi)核模塊中很難判斷是否是引用計(jì)數(shù)泄漏,netdev_wait_allrefs 會(huì)通過(guò) Notification Chains 向所有的消息訂閱者不斷重試發(fā)布 NETDEV_UNREGISTER 和 NETDEV_UNREGISTER_FINAL 消息,而經(jīng)過(guò) trace 發(fā)現(xiàn)消息的訂閱者多達(dá) 22 個(gè),而去弄清這 22 個(gè)訂閱者注冊(cè)的每個(gè)回調(diào)函數(shù)的處理邏輯來(lái)判斷是否有辦法避免誤判也不是一件簡(jiǎn)單的事。
解決方案在我們準(zhǔn)備深入到每個(gè)訂閱者注冊(cè)的回調(diào)函數(shù)邏輯的同時(shí),我們也在持續(xù)關(guān)注 kernel patch 和 RHEL 的進(jìn)展,發(fā)現(xiàn) RHEL 的 solutions:3659011 有了一個(gè)更新,提到 upstream 提交的一個(gè) patch:
route: set the deleted fnhe fnhe_daddr to 0 in ip_del_fnhe to fix a race
在嘗試以 hotfix 的方式為內(nèi)核打上此補(bǔ)丁后,我們持續(xù)測(cè)試了 1 周,問(wèn)題沒(méi)有再?gòu)?fù)現(xiàn)。我們向 RHEL 反饋測(cè)試信息,得知他們已經(jīng)開(kāi)始對(duì)此 patch 進(jìn)行 backport。
操作步驟推薦內(nèi)核版本 Centos 7.6 kernel-3.10.0-957 及以上。
安裝 kpatch 及 kpatch-build 依賴:
UNAME=$(uname -r)
sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel
sudo yum install pesign yum-utils zlib-devel
binutils-devel newt-devel python-devel perl-ExtUtils-Embed
audit-libs audit-libs-devel numactl-devel pciutils-devel bison
# enable CentOS 7 debug repo
sudo yum-config-manager --enable debug
sudo yum-builddep kernel-${UNAME%.*}
sudo debuginfo-install kernel-${UNAME%.*}
# optional, but highly recommended - enable EPEL 7
sudo yum install ccache
ccache --max-size=5G
安裝 kpatch 及 kpatch-build:
git clone https://github.com/dynup/kpatch && cd kpatch
make
sudo make install
systemctl enable kpatch
下載并構(gòu)建熱補(bǔ)丁內(nèi)核模塊:
curl -SOL https://raw.githubusercontent.com/pingcap/kdt/master/kpatchs/route.patch
kpatch-build -t vmlinux route.patch (編譯生成內(nèi)核模塊)
mkdir -p /var/lib/kpatch/${UNAME}
cp -a livepatch-route.ko /var/lib/kpatch/${UNAME}
systemctl restart kpatch (Loads the kernel module)
kpatch list (Checks the loaded module)
雖然我們修復(fù)了這些內(nèi)核錯(cuò)誤,但是未來(lái)應(yīng)該會(huì)有更好的解決方案。對(duì)于 Bug#1,我們希望 K8s 社區(qū)可以為 kubelet 提供一個(gè)參數(shù),以允許用戶禁用或啟用 kmem account 功能。對(duì)于 Bug#2,最佳解決方案是由 RHEL 和 CentOS 修復(fù)內(nèi)核錯(cuò)誤,希望 TiDB 用戶將 CentOS 升級(jí)到新版后,不必再擔(dān)心這個(gè)問(wèn)題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7861.html
摘要:經(jīng)過(guò)廣泛的調(diào)查和診斷,我們已經(jīng)確定了處理這些問(wèn)題的方法。我們發(fā)現(xiàn)在薛定諤平臺(tái)上做測(cè)試時(shí)偶爾會(huì)發(fā)生性能抖動(dòng),但從下面幾項(xiàng)來(lái)看未發(fā)現(xiàn)異常和的日志使用率內(nèi)存和磁盤(pán)等負(fù)載信息只能偶爾看到命令執(zhí)行的結(jié)果中包含一些信息。 作者:張文博 Kubernetes(K8s)是一個(gè)開(kāi)源容器編排系統(tǒng),可自動(dòng)執(zhí)行應(yīng)用程序部署、擴(kuò)展和管理。它是云原生世界的操作系統(tǒng)。 K8s 或操作系統(tǒng)中的任何缺陷都可能使用戶進(jìn)程...
摘要:經(jīng)過(guò)廣泛的調(diào)查和診斷,我們已經(jīng)確定了處理這些問(wèn)題的方法。我們發(fā)現(xiàn)在薛定諤平臺(tái)上做測(cè)試時(shí)偶爾會(huì)發(fā)生性能抖動(dòng),但從下面幾項(xiàng)來(lái)看未發(fā)現(xiàn)異常和的日志使用率內(nèi)存和磁盤(pán)等負(fù)載信息只能偶爾看到命令執(zhí)行的結(jié)果中包含一些信息。 作者:張文博 Kubernetes(K8s)是一個(gè)開(kāi)源容器編排系統(tǒng),可自動(dòng)執(zhí)行應(yīng)用程序部署、擴(kuò)展和管理。它是云原生世界的操作系統(tǒng)。 K8s 或操作系統(tǒng)中的任何缺陷都可能使用戶進(jìn)程...
摘要:中國(guó)論壇提案征集月日截止論壇讓用戶開(kāi)發(fā)人員從業(yè)人員匯聚一堂,面對(duì)面進(jìn)行交流合作。贊助方案出爐多元化獎(jiǎng)學(xué)金現(xiàn)正接受申請(qǐng)即將首次合體落地中國(guó) PingCAP將其TiDB數(shù)據(jù)庫(kù)平臺(tái)押注在云原生上 showImg(https://segmentfault.com/img/bVbogKp?w=508&h=477); 公司:PingCAP地點(diǎn):中國(guó)北京和加利福尼亞州圣馬特奧行業(yè):軟件 挑戰(zhàn) 流行的...
摘要:中國(guó)論壇提案征集月日截止論壇讓用戶開(kāi)發(fā)人員從業(yè)人員匯聚一堂,面對(duì)面進(jìn)行交流合作。贊助方案出爐多元化獎(jiǎng)學(xué)金現(xiàn)正接受申請(qǐng)即將首次合體落地中國(guó) PingCAP將其TiDB數(shù)據(jù)庫(kù)平臺(tái)押注在云原生上 showImg(https://segmentfault.com/img/bVbogKp?w=508&h=477); 公司:PingCAP地點(diǎn):中國(guó)北京和加利福尼亞州圣馬特奧行業(yè):軟件 挑戰(zhàn) 流行的...
閱讀 2898·2021-09-22 15:20
閱讀 2968·2021-09-22 15:19
閱讀 3471·2021-09-22 15:15
閱讀 2406·2021-09-08 09:35
閱讀 2385·2019-08-30 15:44
閱讀 3015·2019-08-30 10:50
閱讀 3744·2019-08-29 16:25
閱讀 1596·2019-08-26 13:55