錯誤現(xiàn)象
a. 終端重復報警消息:?NMI watchdog: BUG: soft lockup - CPU#xx stuck for 22s!
b. dmesg提示kernel:?SLUB: Unable to allocate memory on node -1
c. crash log顯示:
SLUB: Unable to allocate memory on node -1 (gfp=0xd0)
? cache: sock_inode_cache(158:8aff71103b350fcc15a20dcad154986160ce07496a9f70fa4aff83e7716b0936), object size: 632, buffer size: 640, default order: 3, min order: 0
? node 0: slabs: 8, objs: 363, free: 0
? node 1: slabs: 17, objs: 867, free: 0
socket: no more sockets
IPv6: Failed to initialize the ICMP6 control socket (err -23)
BUG: unable to handle kernel NULL pointer dereference at 0000000000000020
IP: [<ffffffff894dfb20>] ip6mr_sk_done+0x60/0xf0
.......
[<ffffffff894c984d>] rawv6_close+0x1d/0x40
[<ffffffff8946cf5d>] inet_release+0x7d/0x90
[<ffffffff894a67d0>] inet6_release+0x30/0x40
[<ffffffff893cba35>] sock_release+0x25/0x90
[<ffffffff893d302c>] sk_release_kernel+0x2c/0x60
[<ffffffff894cbbf5>] icmpv6_sk_init+0x125/0x140
[<ffffffff893e0794>] ops_init+0x44/0x150
[<ffffffff893e0943>] setup_net+0xa3/0x160
[<ffffffff893e10e5>] copy_net_ns+0xb5/0x180
[<ffffffff88ebfe89>] create_new_namespaces+0xf9/0x180
[<ffffffff88ec00ca>] unshare_nsproxy_namespaces+0x5a/0xc0
[<ffffffff88e90c13>] SyS_unshare+0x193/0x300
[<ffffffff8951f7d5>] system_call_fastpath+0x1c/0x21
操作系統(tǒng)版本是CentOS Linux release 7.5.1804法严, 內(nèi)核為3.10.0-862.el7.x86_64,k8s版本是v1.12.3竟痰,Docker版本Docker version 18.06.1-ce兄猩。
問題分析及解決方案
首先我們看到內(nèi)核提示的錯誤信息與內(nèi)存分配有關(guān)侠坎,我們首先考慮這可能是一個內(nèi)存泄露問題峻仇。同時弓颈,當終端不斷彈出sock lockup錯誤信息時,手動stop docker service之后消息立馬不再彈出涩维,因而我們也懷疑這是docker造成的問題。
問題先從Linux的內(nèi)存管理機制說起袁波。在Linux中瓦阐,內(nèi)存分為內(nèi)核內(nèi)存及用戶內(nèi)存,內(nèi)核內(nèi)存設(shè)計為專用于Linux內(nèi)核中系統(tǒng)服務(wù)使用篷牌,是不可swap的睡蟋,因而內(nèi)核內(nèi)存資源對Linux系統(tǒng)來說是非常寶貴的。然而枷颊,正因為這種設(shè)計戳杀,現(xiàn)實中也存在很多針對內(nèi)核內(nèi)存資源的攻擊该面,例如惡意進程可以通過不斷地fork新進程從而耗盡系統(tǒng)資源,從而造成系統(tǒng)異承趴ǎ或崩潰隔缀,這就是所謂的“fork bomb”。
為了防止出現(xiàn)“fork bomb”傍菇,社區(qū)中就有提議通過linux內(nèi)核限制cgroup中的kmem容量使用從而限制惡意進程的行為猾瘸,即kernel memory accounting機制。當我們通過memory cgroup限制應用的內(nèi)存使用時丢习,我們不但需要限制應用對用戶內(nèi)存的使用牵触,也需要限制應用對內(nèi)核內(nèi)存的使用。kernel memory accounting機制為cgroup的內(nèi)存限制增加了stack pages(例如新進程創(chuàng)建)咐低、slab pages(SLAB/SLUB分配器使用的內(nèi)存)揽思、sockets memory pressure、tcp memory pressure等见擦,內(nèi)核文檔中有詳細的描述钉汗。kernel memory accounting機制為cgroup提供了memory.kmem.usage_in_bytes配置項用于限制內(nèi)核內(nèi)存的使用,這個配置項與memory.limit_in_bytes(總內(nèi)存限額)配合使用存在下面三種情形锡宋,在lwn文章中有詳細的介紹:
????a. memory.kmem.limit_in_bytes == unlimited:這種情形不限制內(nèi)核內(nèi)存的使用儡湾。
????b. memory.kmem.limit_in_bytes < memory.limit_in_bytes:在這種情形下,我們詳細的指定了內(nèi)核內(nèi)存的上限是多少执俩。
????c. memory.kmem.limit_in_bytes >= memory.limit_in_bytes:在這種情形下徐钠,我們只關(guān)心包括內(nèi)核內(nèi)存及用戶內(nèi)存在內(nèi)的內(nèi)存的總使用情況,并不關(guān)心內(nèi)核內(nèi)存實際使用了多少役首。在實際使用中尝丐,通常是這種情形,我們將memory.kmem.limit_in_bytes設(shè)置成大于memory.limit_in_bytes衡奥,從而只限制應用的總內(nèi)存使用爹袁。
k8s從1.9版本開始runc默認Enable了kernel memory accounting,即當容器應用設(shè)置了memory limit時矮固,容器的memory cgroup中將為memory.kmem.limit_in_bytes設(shè)置整形最大值失息,從而使能內(nèi)核內(nèi)存限制機制。我們通過查看memory.kmem.slabinfo的信息即可判斷在當前cgroup中kernel memory accounting是否使能档址,如果未使能盹兢,訪問這個文件將報輸入輸出錯誤。
然而守伸,在4.0以下版本的Linux內(nèi)核對kernel memory accounting的支持并不完善绎秒,社區(qū)中逐漸爆出了slab leak causing a crash when using kmem control group問題,同時runc社區(qū)以及Kubernetes社區(qū)中也出現(xiàn)了同樣的問題尼摹,Enabling kmem accounting can break applications on CentOS7见芹、application crash due to k8s 1.9.x open the kernel memory accounting by default剂娄。對于這個問題,在內(nèi)核低于4版本的系統(tǒng)中給docker容器設(shè)置kernel-memory限制時將docker將給出以下提示:
You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.
這個問題也是我們這次遇到問題的根本原因玄呛。kmem的泄露導致系統(tǒng)中的服務(wù)內(nèi)存申請失敗阅懦,從而造成了內(nèi)核的soft lock。這個問題的解決方法在Kernel kmem leak caused by newer versions of Docker這篇文章中有很詳細的介紹把鉴。我們解決的方案是將docker版本升級故黑,版本特性包括Disable kmem accounting in runc on RHEL/CentOS (docker/escalation#614, docker/escalation#692) docker/engine#121。
更多我的文章可以查看我的github博客
添加好友我們可以聊更多相關(guān)話題二維碼