写点什么

【服务故障问题排查心得】「内存诊断系列」Docker 容器经常被 kill 掉,k8s 中该节点的 pod 也被驱赶,怎么分析?

作者:洛神灬殇
  • 2022-12-15
    江苏
  • 本文字数:3283 字

    阅读完需:约 11 分钟

【服务故障问题排查心得】「内存诊断系列」Docker容器经常被kill掉,k8s中该节点的pod也被驱赶,怎么分析?

背景介绍

最近的 docker 容器经常被 kill 掉,k8s 中该节点的 pod 也被驱赶。


我有一个在主机中运行的 Docker 容器(也有在同一主机中运行的其他容器)。该 Docker 容器中的应用程序将会计算数据和流式处理,这可能会消耗大量内存。


该容器会不时退出。我怀疑这是由于内存不足,但不是很确定。我需要找到根本原因的方法。那么有什么方法可以知道这个集装箱的死亡发生了什么?

容器层级判断检测

提到 docker logs container_id 以查看状态的详细信息,例如:


    "State": {        "Status": "exited",        "Running": false,        "Paused": false,        "Restarting": false,        "OOMKilled": false,        "Dead": false,        "Pid": 0,        "ExitCode": 2,        "Error": "",        "StartedAt": "2016-06-28T21:26:53.477229071Z",        "FinishedAt": "2016-06-28T21:26:53.478066987Z"    }
复制代码


重要的一行是“ OOMKilled”,如果您超出了容器的内存限制,并且 Docker 杀死了您的应用程序,则该行将为 true。您可能还需要查找退出代码,以查看其是否标识出您的应用退出的原因。


  • Docker 内部,这仅表示 docker 本身是否会杀死您的进程,并要求您在容器上设置内存限制。

  • Docker 外部,如果主机本身内存不足,Linux 内核可以销毁进程。发生这种情况时,Linux 通常会在/ var / log 中写入日志。使用 Windows 和 Mac 上的 Docker Desktop,您可以在 docker 设置中调整分配给嵌入式 Linux VM 的内存。

  • 可以通过阅读日志来了解容器内的进程是否被 OOM 杀死。OOMkill 是由内核启动的,因此每次发生时,都会在中包含很多行/var/log/kern.log,例如:


python invoked oom-killer: gfp_mask=0x14000c0(GFP_KERNEL), nodemask=(null), order=0, oom_score_adj=995oom_kill_process+0x22e/0x450Memory cgroup out of memory: Kill process 31204 (python) score 1994 or sacrifice childKilled process 31204 (python) total-vm:7350860kB, anon-rss:4182920kB, file-rss:2356kB, shmem-rss:0kB
复制代码

Linux 操作系统的进程服务发生被 killed 的原因是什么

在 Linux 中,经常会遇到一些重要的进程无缘无故就被 killed,而大多数的经验之谈就是系统资源不足或内存不足所导致的。


当 Linux 系统资源不足时,Linux 内核可以决定终止一个或多个进程,内存不足时会在系统的物理内存耗尽时触发 OOM killed,可以利用“dmesg | tail -N”命令来查看 killed 的近 N 行日志。


常规的宕机监控之类

在服务宕机或者重启之前我们的常规操作就是采用 ps 指令判定服务的增长趋势以及展示真实使用的资源的大小的前几位排名。


  • Linux 下显示系统进程的命令 ps,最常用的有 ps -ef 和 ps aux。这两个到底有什么区别呢?


ps -ef指令代表着'SystemV 风格',而ps aux代表着’BSD 风格‘。



由上图所示,可以分析出对应的数据结构模型。


USER      //用户名%CPU      //进程占用的CPU百分比%MEM      //占用内存的百分比VSZ       //该进程使用的虚拟內存量(KB)RSS       //该进程占用的固定內存量(KB)resident set sizeSTAT      //进程的状态START     //该进程被触发启动时间TIME      //该进程实际使用CPU运行的时间
复制代码


其中 CPU 算是第 3 个位置、内存 MEM 算是第 4 个位置,虚拟内存 VSZ 是第 5 个位置,记住这个后面我们会使用这个方式进行排序。

查看当前系统内 CPU 占用最多的前 10 个进程(栏位属于第 3 个)

ps auxw | sort -rn -k3 | head -10
复制代码


ps auxw 指令(BSD 风格)

  • u:以用户为主的格式来显示程序状况

  • x:显示所有程序,不以终端机来区分

  • w:采用宽阔的格式来显示程序状况

sort 排序指令

sort -rn -k5
复制代码


-n 是按照数字大小排序(-n 这代表着排除 n 行的操作处理),-r 是以相反顺序,-k 是指定需要排序的栏位


ps auxw | head -1
复制代码

内存消耗最多的前 10 个进程(栏位属于第 4 个)

ps auxw | head -1;ps auxw|sort -rn -k4|head -10
复制代码

虚拟内存使用最多的前 10 个进程(栏位属于第 5 个)

ps auxw|head -1;ps auxw|sort -rn -k5|head -10
复制代码

去掉 x 参数的结果

ps auw | head -1; ps auw|sort -rn -k4 | head -10
复制代码
stat 取值含义


D      //无法中断的休眠状态(通常 IO 的进程);R      //正在运行可中在队列中可过行的;S      //处于休眠状态;T      //停止或被追踪;W      //进入内存交换 (从内核2.6开始无效);X      //死掉的进程 (基本很少见);Z      //僵尸进程;<      //优先级高的进程N      //优先级较低的进程L      //有些页被锁进内存;s      //进程的领导者(在它之下有子进程);l      //多线程,克隆线程(使用 CLONE_THREAD, 类似 NPTL pthreads);+      //位于后台的进程组;
复制代码

dmesg 的命令分析

有几个工具/脚本/命令 可以更轻松地从该虚拟设备读取数据,其中最常见的是 dmesg 和 journalctl。


输入 dmesg 指令进行 egrep 正则表达式匹配 killed 的进程信息,将输出对应的进程信息。


dmesg | egrep -i -B100 'killed process'
复制代码



dmesg | grep -i -B100 'killed process'
复制代码


以上的指令就可以输出最近 killed 的信息,其中-B100,表示 'killed process’之前的 100 行内容,与 head 的指令非常的相似。



如果我们看到了 oom-kill 的字样之后,就可以判断它是被内存不足所导致的 kill,oom-kill 之后,就是描述那个被 killed 的程序的 pid 和 uid。


Out of memory: Killed process 1138439 (python3) total-vm:8117956kB, anon-rss:5649844kB,内存不够
复制代码
total_vm 和 rss 的指标值


  • total_vm:总共使用的虚拟内存 Virtual memory use (in 4 kB pages),8117956/1024(得到 MB)/1024(得到 GB)=7.741GB

  • rss:常驻内存使用 Resident memory use (in 4 kB pages) 5649844/1024/1024=5.388GB


案例 1:查看到 pod 被驱赶的原因


[3899860.525793] Out of memory: Kill process 64058 (nvidia-device-p) score 999 or sacrifice child[3899860.526961] Killed process 64058 (nvidia-device-p) total-vm:126548kB, anon-rss:2080kB, file-rss:0kB, shmem-rss:0kB
复制代码


案例 2:查看到 docker 容器被 kill 的原因


[3899859.737598] Out of memory: Kill process 27562 (jupyter-noteboo) score 1000 or sacrifice child[3899859.738640] Killed process 27562 (jupyter-noteboo) total-vm:215864kB, anon-rss:45928kB, file-rss:0kB, shmem-rss:0kB
复制代码

journalctl 命令 – 查看指定的日志信息

当内存不足时,内核会将相关信息记录到内核日志缓冲区中,该缓冲区可通过 /dev/kmsg 获得。除了上面的 dmesg 之外,还有一个 journalctl。


语法格式: journalctl [参数]

常用参数:

查看 Killed 日志

使用 sudo dmesg | tail -7 命令(任意目录下,不需要进入 log 目录,这应该是最简单的一种)而 journalctl 命令来自于英文词组“journal control”的缩写,其功能是用于查看指定的日志信息。

journalctl 指令介绍

在 RHEL7/CentOS7 及以后版本的 Linux 系统中,Systemd 服务统一管理了所有服务的启动日志,带来的好处就是可以只用 journalctl 一个命令,查看到全部的日志信息了。


查看所有日志(默认情况下 ,只保存本次启动的日志)


journalctl
复制代码


查看内核日志(不显示应用日志)


journalctl -k
复制代码


查看系统本次启动的日志


journalctl -bjournalctl -b -0
复制代码


查看上一次启动的日志(需更改设置)


journalctl -b -1
复制代码


查看指定时间的日志


journalctl --since=“2021-09-16 14:22:02”
journalctl --since “30 min ago”
journalctl --since yesterday
journalctl --since “2021-01-01” --until “2021-09-16 13:40”
journalctl --since 07:30 --until “2 hour ago”
复制代码


显示尾部的最新 10 行日志


journalctl -n
复制代码


显示尾部指定行数的日志


journalctl -n 15
复制代码


实时滚动显示最新日志


journalctl -f
复制代码


查看指定服务的日志


journalctl /usr/lib/systemd/systemd
复制代码


比如查看 docker 服务的日志


systemctl status docker
复制代码


查看某个 Unit 的日志


journalctl -u nginx.servicejournalctl -u nginx.service --since today
复制代码


实时滚动显示某个 Unit 的最新日志


journalctl -u nginx.service -f
复制代码


合并显示多个 Unit 的日志


$ journalctl -u nginx.service -u php-fpm.service --since today
复制代码


发布于: 2022-12-15阅读数: 23
用户头像

洛神灬殇

关注

🏆 InfoQ写作平台-签约作者 🏆 2020-03-25 加入

【个人简介】酷爱计算机科学、醉心编程技术、喜爱健身运动、热衷悬疑推理的“极客达人” 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、微服务/分布式体系和算法设计等

评论

发布
暂无评论
【服务故障问题排查心得】「内存诊断系列」Docker容器经常被kill掉,k8s中该节点的pod也被驱赶,怎么分析?_Docker_洛神灬殇_InfoQ写作社区