k8s oom告警解决记录

8 篇文章 1 订阅

oom告警问题
背景:容器中执行c++的运行和评测,如存在如下代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>

int main ()
{
     char *p = NULL;
     int count = 1;
     while(1){
         p = (char *)malloc(1024*1024*10);
         if(!p){
              printf("malloc error!\n");
              return -1;
         }
         memset(p, 0, 1024*1024*10);
         printf("malloc %dM memory\n", 10*count++);
         usleep(500000);
     }
}

c++运行指令梳理:
第一层:项目中test.yaml配置文件:timeout控制超时退出
timeout -s 9 60 execute_cpp.sh 

第二层:编译 g++ -m32
execute_cpp.sh: 
g++ -m32 -lm -g -w -o 和 unshare_run.sh

第三层:沙箱使用
unshare_run.sh:
unshare --fork -p -n --mount-proc -r run.sh

第四层:运行编译后的cpp等代码
run.sh:
ulimit -t ${limitTime}
ulimit -m ${limitMemory}
ulimit -s ${limitStackSize}
ulimit -u ${limitMaxProcesses}
#执行
#./${executeFile}/main.out
./main.out


告警容器:coding-editor-channel-noirun
测试:cpp-run
pod给定资源内存1500M时
运行界面报错信息:
.....
malloc 1190M memory
Killed
程序占用资源超过限制,已停止运行

修改pod给定资源内存4G时
报错信息:
....
malloc 119000M memory
Killed
程序占用资源超过限制,已停止运行


场景1:如果直接修改成每次分配10M,容器限制内存4G,shell脚本ulimit -m
错误提示:程序占用资源超过限制,已停止运行  
最终错误内容如下:
/home/user_118/bin/run.sh: line 30:     5 Killed                  ./main.out

timeout -s 9 60 execute_cpp.sh /nas.... 
含义:9 SGGKILL信号,
-s:超时则发送信号
60, 超时时间 超过会自动停止进程
https://blog.csdn.net/yunweimao/article/details/106688074
https://www.linuxprobe.com/linux-time-limit.html

本质原因:
从14:14:29.982 执行到14:15:30.672,超过了timeout的中的60s,所以超时被kill了。此时并没有达到容器内存上限4G。

所以引出问题1:该场景下提示有问题


场景2:修改成每次分配100M,容器限制内存1536M,shell脚本ulimit -m
则程序报错信息为:
malloc 100M memory
....
malloc 1500M memory
/home/user_15/bin/run.sh: line 30:     5 Killed                  ./main.out
程序占用资源超过限制,已停止运行

同时阿里云产生告警,告警信息如下:
pod was OOM killed. node:cn-zhangjiakou.10.158.224.147 pod:coding-editor-channel-noirun-c7d97d4dc-dcxvj namespace:peiyou-xiaohoucode uuid:59530596-1019-418c-978a-7c7eb742a422

说明:
此时执行耗时是6秒,不再是时间限制问题
此时ulimit -m未生效,实际生效的是容器对内存的限制

解决:修改 ulimit -v为ulimit -m
直接运行代码:
run相关的机器:
1 写到cpp文件中 如a.cpp
2 编译: g++ -m32 a.cpp,可以得到编译后的文件a.out
3 测试ulimit -v和-m
ulimit -v 128000
./a.out
得到对应的结果


场景3:修改成每次分配100M,容器限制内存1536M,shell脚本ulimit -m,删除memset(p, 0, n);
执行结果如下:
malloc 100M memory
.....
malloc 4000M memory
malloc error!
执行完成

说明:此时cpp能够申请的最大虚拟内存是4G????且容器给的1.5G,所以物理内存是用到的时候才会分配,容器中分配的是物理内存。

待处理问题:
为什么最大申请虚拟内存是4G?(利用虚拟内存技术让每个进程都有4GB 互不干涉的虚拟地址空间。)
https://blog.csdn.net/weixin_44747933/article/details/105803508

下一个工作,找到阿里云的支持,询问测试环境为什么没有告警????

运行评测内存限制问题
ulimit 运行使用,限制虚拟内存 代码中声明的是虚拟内存
isolate 评测使用,限制的是物理内存???
所以评测时,即使声明了很大的内存依然没问题
所以会产生如下现象和问题:
同一段代码,运行不通过,但是评测可能通过,是否有方案解决?


运行:ulimit -m没有限制住内存,修改成-v就可以了,能限制住虚拟内存的申请和使用
评测:isolate --cg-m限制内存生效,会被k8s识别成容器的oom,其实是子进程的;增加-m参数,让-m生效就不会被k8s误判了


但是评测还是有OOM告警问题?
    public static final String RUN_COMPILE_VARIABLE_COMMAND = "isolate --cg -s -b {0} -M " +
            "{1} " +
            "-t {2} -x {3} -w {4} -k {5} -p{6} --cg-mem={7} --no-cg-timing -f {8} " +
            "-E HOME={9} " +
            "-E LANG -E LANGUAGE -E LC_ALL " +
            "--dir=/lib32/ " +
            "--dir=/libx32/ " +
            "-d '/etc':'noexec' --run " +
            "-- " +
            "./a.out < " +
            "{10} > " +
            "{11} 2> " +
            "{12}";

isolate指令详解:
http://www.ucw.cz/moe/isolate.1.html

--cg Enable use of control groups
-s, --silent            Do not print status messages except for fatal errors
-b, --box-id=id When you run multiple sandboxes in parallel, you have to assign each unique IDs to them by this option.
-M, --meta=file
Output meta-data on the execution of the program to a given file. See below for syntax of the meta-files.

-t, --time=time
Limit run time of the program to time seconds.

-x, --extra-time=time
When a time limit is exceeded, wait for extra time seconds before killing the program. 

-k, --stack=size
Limit process stack to size kilobytes. By default, the whole address space is available for the stack, but it is subject to the --mem limit.

-p, --processes[=max]
Permit the program to create up to max processes and/or threads. Please keep in mind that time and memory limit do not work with multiple processes unless you enable the control group mode. If max is not given, an arbitrary number of processes can be run. By default, only one process is permitted.

--cg-mem=size
Limit total memory usage by the whole control group to size kilobytes. This should be specified with --run.

--cg-timing
Use control groups for timing, so that the --time switch affects the total run time of all processes and threads in the control group. This should be specified with --run. This option is turned on by default, use --no-cg-timing to turn off.
8. META-FILES

-f, --fsize=size
Limit size of files created (or modified) by the program to size kilobytes. In most cases, it is better to restrict overall disk usage by a disk quota (see below). This option can help in cases when quotas are not enabled on the underlying filesystem.

-E, --env=var
Inherit the variable var from the parent.
-E, --env=var=value
Set the variable var to value. When the value is empty, the variable is removed from the environment.

-d, --dir=dir[:options]
Bind the directory /dir to dir inside the sandbox. If there already was a directory rule for in, it is replaced.


实例:
isolate --cg -s -b 544158 -M  /isolate/544158/meta.txt
-t 2 -x 0.5 -w 10.1 -k 64000 -p30 --cg-mem=131072 --no-cg-timing -f 16000
-E HOME=/isolate/544158
-E LANG -E LANGUAGE -E LC_ALL
--dir=/lib32/
--dir=/libx32/
-d /etc:noexec --run
--
./a.out <
/nas/prod/editor/chkpnt/python/cpp9023206133330411412/11.in >
/isolate/544158/stdout.txt 2>
/isolate/544158/stderr.txt

judge创建:
1 创建沙箱,并指定沙箱id为544158
isolate --cg -b 544158 --init

2 手动创建a.out
在/isolate/544158/box路径下创建a.cpp
g++ a.cpp得到a.out

3 执行沙箱指令
isolate --cg -s -b 544158 -M  /isolate/544158/meta.txt -t 2 -x 0.5 -w 10.1 -k 64000 -p30 --cg-mem=101072 --no-cg-timing -f 16000 -E HOME=/ -E LANG -E LANGUAGE -E LC_ALL --dir=/lib32/ --dir=/libx32/ -d /etc:noexec --run -- ./a.out


最终在生产的judge容器中测试发现:
执行上面的指令,就会有oom killed发生,所以本质是docker/k8s的这种告警机制有问题。。。。
查看对应的执行信息,如下:
root@coding-judge-b4579567b-94nkf:/isolate/544158# cat meta.txt 
time:0.027
time-wall:0.050
max-rss:102708
csw-voluntary:9
csw-forced:18
cg-mem:101076
cg-oom-killed:1
exitsig:9
status:SG
message:Caught fatal signal 9

也可以在测试机器执行如下指令进行验证:
isolate --cg  -b 544158 -M  /isolate/544158/meta.txt -t 2 -x 0.5 -w 10.1 -k 64000 -p30 --cg-mem=101072 --no-cg-timing -f 16000 -E HOME=/ -E LANG -E LANGUAGE -E LC_ALL --dir=/lib32/ --dir=/libx32/ -d /etc:noexec --run -- ./a.out
发现打印信息如下:
malloc 10M memory
malloc 20M memory
malloc 30M memory
malloc 40M memory
malloc 50M memory
malloc 60M memory
malloc 70M memory
malloc 80M memory
malloc 90M memory
Caught fatal signal 9

说明:
--cg-mem肯定时生效了,内存使用也控制住了。但是因为开启了cg特性,和docker的底层实现一致了,docker自身存在bug,将子进程的oom信号认成了整个容器的oom,所以k8s出现了oom-killed事件。


解决:
1 docker或k8s的团队修改pod was OOM killed的信息
2 调整限制内存的方式
方式一:增加ulimit -v,如:
ulimit -v 80000; 
isolate --cg -b 544158 -M  /isolate/544158/meta.txt -t 2 -x 0.5 -w 10.1 -k 64000 -p30 --cg-mem=101072 --no-cg-timing -f 16000 -E HOME=/ -E LANG -E LANGUAGE -E LC_ALL --dir=/lib32/ --dir=/libx32/ -d /etc:noexec --run -- ./a.out
说明此种场景ulimit会先生效,所以不会告警;
问题:processUtil无法识别上面的指令,会提示,cannot run program “ulimit”:error=2, no such file or directory.

方式二:不使用cg属性,但是不能保证学生的代码里面没有多线程
isolate -m 128000 -s -b 544158 -M  /isolate/544158/meta.txt -t 2 -x 0.5 -w 10.1 -k 64000 -p30 -f 16000 -E HOME=/ -E LANG -E LANGUAGE -E LC_ALL --dir=/lib32/ --dir=/libx32/ -d /etc:noexec --run -- ./a.out
说明:
删除了: --cg  --no-cg-timing
--cg-mem=101072修改为-m 101072

方式三:
增加-m限制资源,--cg-mem也依旧使用,但是将其值调大一倍。
如下:
root@coding-judge-b4579567b-94nkf:/# isolate --cg -b 544158 -M  /isolate/544158/meta.txt -t 2 -x 0.5 -w 10.1 -k 64000 -p30 --cg-mem=101072 -m 80000 --no-cg-timing -f 16000 -E HOME=/ -E LANG -E LANGUAGE -E LC_ALL --dir=/lib32/ --dir=/libx32/ -d /etc:noexec --run -- ./a.out
malloc error!
Exited with error status 255
说明:此时的错误就不再是Caught fatal signal 9,而是255,k8s就不会再有pod was OOM killed这种告警了。

judge优化,沙箱是否可以重复利用,每个pod创建200个,重复使用?
processUtil为什么需要split,然后执行的时候再拼接?能否取消split,直接执行?

2021.11.28新增----------------
做了上述修改后能解决大部分告警,但是还存在如下两个问题:
1)周末发现还是存在oom告警问题,应该是有的场景未覆盖到,下周还需要进一步花时间定位和解决
2)原本有些场景实际使用内存很大的场景并没与受到限制,所以能正常运行,修改后可能导致部分场景失败,如周末解决的xcode代码报错问题。这种可能需要不断发现和解决。

xcode提示,运行失败:
请求入口:/jr/py  
PyEditorEntryHandler.handleMessage EditorConstants.START_COMMAND
unshare_py_run 最终一直调用到 py_run.sh
原因可能是以前内存没限制住,修改了ulimit -v为ulimit-m后则限制住了,导致执行失败

py run容器内执行如下内容:
timeout -s 9 1800 unshare_py_run.sh /nas/prod/api/python/exercise/ff80808165a4bbb00165c412857e3263-2994730/ff80808165a4bbb00165c412857e3263-2994730.py 3 1280000

原因:报错 .....cannot allocate memory...
将1280000修改为256000后发现不再报错
所以修改nacos中对内存的限制(ssh.python.config.limit-memory)即可

oom问题继续跟进:
阿里云容器服务:集群容器副本OOM
报警时间:2021-11添加到日历-07 19:11:26
集群:c312bcb65b5fb49829bde6317149ec1f6
Namespace:peiyou-xiaohoucode-prod
PodName:coding-editor-channel-67794f85d4-7dthh
NodeName:cn-zhangjiakou.10.158.224.146
报警信息:["pod was OOM killed. node:cn-zhangjiakou.10.158.224.146 pod:coding-editor-channel-67794f85d4-7dthh namespace:peiyou-xiaohoucode-prod uuid:93797f95-152d-4fc0-92cc-3bc4e6959398"]
报警事件数:1
本消息由阿里云用户: 1641920444274528配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值