刚刚登录到我的服务器之后,看到 motd 提示有一个僵尸进程。本来处理僵尸进程很简单,杀掉它的父进程就行了。但是紧接着我发现这个进程是属于一个 Docker 容器的,因为我想要更优雅地处理掉它,就顺藤摸瓜找到了对应的容器并将其重启了。这里就记录下我的排查过程以供参考。
因为僵尸进程在 ps
中的状态是 Z
,所以我首先用 ps aux | grep 'Z'
找到这个僵尸进程的 PID。此外因为僵尸进程无法被直接杀死,只能杀掉其父进程来将其连根拔起,所以我还需要用 pstree
命令找到它的父进程 PID。
1 2 3 4 5 6 7 8 9 $ ps aux | grep 'Z' USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 630024 0.0 0.0 0 0 ? Z 00:55 0:00 [wget] <defunct> boris19+ 1180864 0.0 0.0 9696 2332 pts/0 S+ 23:52 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox Z $ pstree -p -s 630024 systemd(1)───containerd-shim(2642178)───node(2642199)───wget(630024) $ ps aux | grep 2642199 boris19+ 1181119 0.0 0.0 9696 2288 pts/0 S+ 23:53 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox 2642199 root 2642199 0.0 0.8 21690116 35956 ? Ssl Sep07 3:21 node /app/dist/index.js
这里可以看到,僵尸 wget
的父进程是 node
,它的 PID 是 2642199
,而 node
的父进程是 containerd-shim
,也就是说这是一个 Docker 容器里的进程。我不确定这时候直接杀掉 node
能不能解决问题。但是老话说,来都来了,那不如继续挖下去。所以我开始尝试去找这个 node
是哪个容器运行的。
在这我稍微走了点弯路。一开始我是想通过搜索 index.js
关键词来找到容器,所以执行了下 docker ps | grep index.js
,但是显然这行不通,不然也不会有这篇文了。在稍微网上冲浪之后,我学到了一个新命令 systemd-cgls
,它可以递归展示出 cgroup 的内容。因为 Docker 用到的技术之一就是 cgroup,那么想必这就是突破口。在执行了它之后,它打出来了一大片东西,就像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Control group /: -.slice ├─user.slice │ └─user-1000.slice │ ├─user@1000.service │ │ └─init.scope │ │ ├─1180199 /lib/systemd/systemd --user │ │ └─1180200 (sd-pam) │ └─session-35890.scope │ ├─1180196 sshd: boris1993 [priv] │ ├─1180374 sshd: boris1993@pts/0 │ ├─1180376 -zsh │ ├─1182022 systemd-cgls │ └─1182023 pager ├─init.scope │ └─1 /sbin/init └─system.slice ├─irqbalance.service │ └─941 /usr/sbin/irqbalance --foreground ├─docker-27434de50f56b4e096bf5f38bafc76f5c74622c758be1a3f2b9531a3549f4550.scope │ └─3204915 /jellyfin/jellyfin ├─docker-adf03dfa49427d2d651cd9101c3033adb00396ed45c390dd6bfda0b9b73eeae3.scope │ └─3775 /watchtower ├─open-vm-tools.service │ └─812 /usr/bin/vmtoolsd ├─containerd.service │ ├─ 1017 /usr/bin/containerd │ ├─ 2197 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 0e752935d348c3a41d66fe539a06268ee29c6975d1d8f0da41bd0c52b5adf337> │ ├─ 2479 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 6f5e80ca8977314f3d3a9a47a93509438040a6908bde34cc578cdd3e8263c01a> │ ├─ 2497 /usr/bin/containerd-shim-runc-v2 -namespace moby -id db3750e57842656c285e9641c4391df926721c94722bfb323789967c3a76d23f> │ ├─ 2886 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 46520cd2506055c50ef361f50381a65b5077ee7b7d2607e26290d70f8b7292ba> │ ├─ 3449 /usr/bin/containerd-shim-runc-v2 -namespace moby -id d45b7c4e806f1c0e681f317b548f795ff09eab53bc4d1a6bde05aa1fbb5064ee> │ ├─ 3451 /usr/bin/containerd-shim-runc-v2 -namespace moby -id adf03dfa49427d2d651cd9101c3033adb00396ed45c390dd6bfda0b9b73eeae3> │ ├─ 3554 /usr/bin/containerd-shim-runc-v2 -namespace moby -id fd2c2b6dfd77b23c0cdd59d50b33bbf70ab8046bf27ffeb595fbeadadf0a4a99> │ ├─ 3837 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 6195cb19666ad7c58ce26a115ba4f2ea3b32185acb0c33ff80cbade860693fd0> │ ├─ 3840 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 559a99dc9580a0ddea8501df71bbce3affb90cf3a91152b8274ae508c20e0f32> │ ├─ 3901 /usr/bin/containerd-shim-runc-v2 -namespace moby -id b11d850487921e336178dd9267b0f281eea279d0135d1d5b853e63f945246baf> │ ├─ 3964 /usr/bin/containerd-shim-runc-v2 -namespace moby -id f3f9400aeab6331d55166b1c80cd3e27dc2083664a6126cccf279340ab738364> │ ├─ 5852 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 722497f0a71df4060151468e590b5a69a49332ba379b17c9504077b18bbd1dfc>
好在它的输出就像 less
一样,可以上下卷动,也可以用 ed
命令(就像 vi
一样),那么自然而然,我可以用 /2642199
来找到那一行。
1 2 ├─docker-ce8f490423d791ed5f6d1aa2b705d797fc3fd2ddc34816d47c228f6fb9a20c63.scope │ └─2642199 node /app/dist/index.js
好,现在我们知道了容器的 ID 是 ce8f490423d7
。虽然现在就可以重启它了,但是我的好奇心不允许我这么做,我想知道是哪个容器。所以
1 2 $ docker ps | grep ce8f490423d ce8f490423d7 boris1993/qinglong-bot:latest "docker-entrypoint.s…" 12 days ago Up 12 days (healthy) 0.0.0.0:3001->3000/tcp, :::3001->3000/tcp qinglong-bot
啊好吧,竟然是我那个青龙的机器人。这里面我只用了 wget
来做容器的 liveness check,我不理解这么简单的操作怎么就能整出一个僵尸进程来。算了,以后有空再分析吧。重启容器,杀掉僵尸,睡觉。
1 2 $ docker restart ce8f490423d ce8f490423d
这时候再用 ps aux | grep 'Z'
就看不到有僵尸进程,也就说明处置成功了。