docker 容器terminal失败

关键一句话:docker 容器的teminal失败,一定是等待资源导致的,不管是pid资源,还是内存资源。本文主要讲因为内存资源导致进程D状态,然后导致teminal容器失败。

目前在集群中,cpu占用率其实一直较低,也就是说,load高目前都是因为D状态的进程多,或者说D状态的进程时间比较久,这种情况下,如果该进程归属的容器需要terminal,则会失败。

总结的规律是:

1.有的集群,由于kmem的泄露,导致容器的runc有时候会在创建的时候出现申请内存失败而在内核态循环的情况,这种情况,只要增加对应容器的内存就能解决。然后升级runc不默认开启kmem的记账。

2.普通的业务集群,没有开启kmem记账,那么当用户态占用内存较多达到limit时,会因为申请不到内存而在内核态循环,而触发了oom,

常见的堆栈如下:

[<ffffffffba3da5f5>] wait_iff_congested+0x135/0x150
[<ffffffffba3cce5a>] shrink_inactive_list+0x30a/0x5d0
[<ffffffffba3cd815>] shrink_lruvec+0x385/0x730
[<ffffffffba3cdc36>] shrink_zone+0x76/0x1a0
[<ffffffffba3ce140>] do_try_to_free_pages+0xf0/0x4e0
[<ffffffffba3ce62c>] try_to_free_pages+0xfc/0x180
[<ffffffffba47941e>] free_more_memory+0xae/0x100
[<ffffffffba47a73b>] __getblk+0x15b/0x300---这个地方会有一个循环
[<ffffffffc075bd43>] __ext4_get_inode_loc+0xe3/0x3c0 [ext4]
[<ffffffffc075e49d>] ext4_get_inode_loc+0x1d/0x20 [ext4]
[<ffffffffc0760256>] ext4_reserve_inode_write+0x26/0xa0 [ext4]
[<ffffffffc0760323>] ext4_mark_inode_dirty+0x53/0x210 [ext4]
[<ffffffffc0763b20>] ext4_dirty_inode+0x40/0x60 [ext4]
[<ffffffffba4715ad>] __mark_inode_dirty+0x16d/0x270
[<ffffffffba45f6a9>] update_time+0x89/0xd0
[<ffffffffba45f790>] file_update_time+0xa0/0xf0
[<ffffffffc0763d4c>] ext4_page_mkwrite+0x6c/0x470 [ext4]
[<ffffffffba3e5a3a>] do_page_mkwrite+0x8a/0xe0
[<ffffffffba3e60d6>] do_shared_fault.isra.62+0x86/0x280
[<ffffffffba3ea664>] handle_pte_fault+0xe4/0xd10
[<ffffffffba3ed3ad>] handle_mm_fault+0x39d/0x9b0
[<ffffffffba971603>] __do_page_fault+0x203/0x4f0
[<ffffffffba971925>] do_page_fault+0x35/0x90
[<ffffffffba96d768>] page_fault+0x28/0x30
[<ffffffffffffffff>] 0xffffffffffffffff

这种就是典型的由于pagefault进来,然后因为申请内存不到而在内核态死循环的。此时pagefault的oom的执行需要

mem_cgroup_oom_enable,同时,也要求mem_cgroup_oom_synchronize能够顺利执行才会最终杀进程,而很多时候循环在

__getblk 的循环中,如果当前进程被选择为需要oom的进程,则必须退出循环才能处理。所以出现了软死锁,这个非常关键,我们要跳出循环才能执行oom杀进程,所以不是没有oom,

而是oom在这种情况下没法执行。

另外,不是所有的pagefault进来都是这个堆栈,比如是因为写时复制的原因进来,这个时候堆栈大多数情况下是如下:

PID: 112272 TASK: ffff9ae9eaff8000 CPU: 21 COMMAND: "runc:[2:INIT]"
#0 [ffff9adf48673608] __schedule at ffffffffb6769a72
#1 [ffff9adf48673698] schedule at ffffffffb6769f19
#2 [ffff9adf486736a8] schedule_timeout at ffffffffb6767968
#3 [ffff9adf48673758] io_schedule_timeout at ffffffffb67695ed
#4 [ffff9adf48673788] wait_iff_congested at ffffffffb61da5f5
#5 [ffff9adf486737e8] shrink_inactive_list at ffffffffb61cce5a
#6 [ffff9adf486738b0] shrink_lruvec at ffffffffb61cd815
#7 [ffff9adf486739b0] shrink_zone at ffffffffb61cdc36
#8 [ffff9adf48673a08] do_try_to_free_pages at ffffffffb61ce140
#9 [ffff9adf48673a80] try_to_free_mem_cgroup_pages at ffffffffb61ce78a
#10 [ffff9adf48673b18] mem_cgroup_reclaim at ffffffffb6234d1e
#11 [ffff9adf48673b58] __mem_cgroup_try_charge at ffffffffb62356dc
#12 [ffff9adf48673c10] mem_cgroup_charge_common at ffffffffb6236049
#13 [ffff9adf48673c58] wp_page_copy at ffffffffb61e6b50
#14 [ffff9adf48673cc8] do_wp_page at ffffffffb61e8f6b
#15 [ffff9adf48673d70] handle_pte_fault at ffffffffb61ea8fd
#16 [ffff9adf48673e08] handle_mm_fault at ffffffffb61ed3ad
#17 [ffff9adf48673eb0] __do_page_fault at ffffffffb6771603
#18 [ffff9adf48673f20] do_page_fault at ffffffffb6771925
#19 [ffff9adf48673f50] page_fault at ffffffffb676d768

这种情况下,runc会由于写时复制而出现没有内存的情况,返回值一般是:

mem_cgroup_charge_common return=0xfffffffffffffff4,这个就是-12了,也就是没有内存。

这种情况虽然不会出现死循环,但是由于用户态确实需要访问写时复制的page,这样会导致它长期地持有

mmap_sem这把锁,一方面会导致load升高,其他等待这把锁的会处于D状态,另一方面,runc的阻塞,也会导致docker exec 卡住。

3.还有一种load高,是因为内存翻转导致,比如提交的io飞了,一直在等待某个page释放等,这种概率虽然低,但是一旦遇到很难定位,我在oppo短短半年就遇到3次,都是mce

检测不出来,但是堆栈明显是内存翻转,后来升级bios解决。

4.很多ps进程阻塞,这个也是表象,ps阻塞还出现过读锁堵塞读,只要是因为防止写饥饿,因为虽然读者持有sem的读端,当写者来写时,排队会阻塞后端再来的读。

既然说到load高,如果是cpu占用高,则需要分析cpu占用,如果是D状态,比如说之前一直出现的lxcfs导致的fuse的D状态,则一方面会导致load升高,另外一方面就会导致

进程杀不掉,也就是container 在teminnal的时候会出现阻塞。前段时间一直报的重启lxcfs来规避,就是fuse阻塞较多,后来运维发现lxcfs复位也不能解决所有D状态,这个是很

正常的,因为D状态的触发路径有很多,fuse只是占了其中一部分而已。

原文地址:https://www.cnblogs.com/10087622blog/p/13581921.html