笔者的同事提了一个需求:
为了验证我们管理工具处理 docker hang 的能力,我们是否可以模拟 Docker hang 的场景?
这是一个灵魂问题,我们需要回顾为什么 Docker 会 hang 住,以及如何注入故障。如果去修改 Docker/Containerd/runc 的代码来实现 Docker hang,这个实际上偏离了我们真实的意图。
我首先想到了通过不响应优雅退出或者优雅退出慢来模拟,上一个启动脚本:
#!/bin/bash# Custom signal handler functioncustom_handler() { echo "Received SIGTERM. Doing custom actions..." sleep 600 # 模拟一个较长的清理操作}# Trap SIGTERM and call custom_handler functiontrap 'custom_handler' SIGTERM# Keep the container runningtail -f /dev/null
当我们使用 docker stop 的时候,它就会捕捉到 SIGTERM 信号,然后 sleep 10 分钟。但是结果没有用,最后被 docker kill 了,我 strace 了一下 docker stop 的流程,大致如下:
strace docker stop ff83f852500execve("/usr/bin/docker", ["docker", "stop", "ff83f852500"], 0x7ffc66a61130 /* 37 vars */) = 0...epoll_pwait(3, [], 128, 0, NULL, 64) = 0futex(0xc000680150, FUTEX_WAKE_PRIVATE, 1) = 1--- SIGURG {si_signo=SIGURG, si_code=SI_TKILL, si_pid=2832418, si_uid=0} ---rt_sigreturn({mask=[]}) = 94567673443813--- SIGURG {si_signo=SIGURG, si_code=SI_TKILL, si_pid=2832418, si_uid=0} ---rt_sigreturn({mask=[]}) = 94567673443813sched_yield() = 0futex(0x5602435c1738, FUTEX_WAIT_PRIVATE, 2, NULL) = -1 EAGAIN (资源暂时不可用)futex(0x5602435c1738, FUTEX_WAKE_PRIVATE, 1) = 0futex(0x5602435c1858, FUTEX_WAKE_PRIVATE, 1) = 1futex(0x5602435c1738, FUTEX_WAKE_PRIVATE, 1) = 1futex(0xc000100550, FUTEX_WAKE_PRIVATE, 1) = 1futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0epoll_pwait(3, [], 128, 0, NULL, 0) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0xc000680550, FUTEX_WAKE_PRIVATE, 1) = 1futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0futex(0x5602435c2110, FUTEX_WAIT_PRIVATE, 0, NULL) = 0ff83f852500+++ exited with 0 +++
总之被 kill 了。
然后我就和同事讨论了一下,颜正辉新司机就提出了:
忘了这茬了,其实很多 docker hang 住就是因为很多进程处于 D 状态,无法响应 Singnal 了。之前笔者还整理了一篇文章:
容器技术回顾 - 从一个“D”状态容器进程回顾 cgroup freezer 子系统
闲话少说,让我们开干:
模拟 D 状态
让容器进程进入到 D 状态:
#随便运行一个容器1. docker run -it -d test:v1.0 -name delayacf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79 #获取 pid2. docker inspect --format {{.State.Pid}} acf6e0a5f23284272 #获取 cgroup 路径3. cat /proc/3284272/cgroup11:blkio:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d7910:cpuset:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d799:perf_event:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d798:freezer:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d797:pids:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d796:cpuacct,cpu:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d795:net_prio,net_cls:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d794:hugetlb:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d793:memory:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d792:devices:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d791:name=systemd:/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79 # 拼接 /sys/fs/cgroup/freezer/ + /docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d794.cat /sys/fs/cgroup/freezer/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79 # 查看freezer.stat5. cat /sys/fs/cgroup/freezer/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79/freezer.stateTHAWED # 把它变成 D 状态进程6. echo "FROZEN" > /sys/fs/cgroup/freezer/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79/freezer.state #查看状态ps -o pid,ppid,stat,comm -p 3284272 PID PPID STAT COMMAND3284272 3284252 Ds+ delay.sh cat /sys/fs/cgroup/freezer/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79/freezer.stateFROZEN #验证docker stop acf6e0a5fError response from daemon: cannot stop container: acf6e0a5f: tried to kill container, but did not receive an exit event
如果系统上有多个包含 D 状态进程的容器,systemctl stop docker 其实很容易就 hang 住了。
消除 D 状态
恢复 D 状态的容器,请使用如下命令:
echo "THAWED" > /sys/fs/cgroup/freezer/docker/acf6e0a5f2a9f299b6e44ba73ed138e9fc112157c4d1bf12f7dd9db99d0c1d79/freezer.state
你学会了吗?