【发布时间】:2020-10-18 14:25:00
【问题描述】:
有没有一种简单的方法可以从内部完全暂停非特权 docker 容器,同时保留从外部取消暂停/执行它的能力?
【问题讨论】:
有没有一种简单的方法可以从内部完全暂停非特权 docker 容器,同时保留从外部取消暂停/执行它的能力?
【问题讨论】:
在 linux 容器上,答案肯定是,是,因为这两者是等价的:
docker pause [container-id]
kill -SIGSTOP [process(es)-id]
或者,even shorter
kill -SIGSTOP -1
请注意:
PID 是 1,那么您属于边缘情况,因为在 Linux 中,初始化进程 PID 1 确实具有特定含义 and behaviour。这两个也是等价的:
docker unpause [container-id]
kill -SIGCONT [process(es)-id]
或者,even shorter
kill -SIGCONT -1
还要注意,在某些边缘情况下,这不起作用。边缘情况是您的进程旨在捕获这两个信号,SIGSTOP 和 SIGCONT,并忽略它们。
在这些情况下,您将不得不
--init 运行您的容器,这样PID 1 将只是一个由Docker 初始化的包装进程,您无需再暂停它为了暂停容器内运行的进程。
您可以使用
--init标志来指示应该将一个init 进程用作容器中的PID 1。指定一个 init 进程可确保在创建的容器内执行 init 系统的通常职责,例如获取僵尸进程。使用的默认初始化进程是在 Docker 守护进程的系统路径中找到的第一个
docker-init可执行文件。这个docker-init二进制文件包含在默认安装中,由 tini 提供支持。
这对于 Linux 容器绝对是可能的,并且以某种方式在文档中进行了解释,他们指出运行 docker pause [container-id] 只是意味着 Docker 将使用等效机制将 SIGSTOP 信号发送到进程运行在您的容器中。
docker pause命令暂停指定容器中的所有进程。在 Linux 上,这使用 freezer cgroup。传统上,当挂起一个进程时,会使用SIGSTOP信号,这可以通过被挂起的进程观察到。使用 freezer cgroup,该进程不知道也无法捕获它正在暂停并随后恢复。在 Windows 上,只能暂停 Hyper-V 容器。有关详细信息,请参阅freezer cgroup documentation。
来源:https://docs.docker.com/engine/reference/commandline/pause/
下面是一个关于 NGINX Alpine 容器的示例:
### For now, we are on the host machine
$ docker run -p 8080:80 -d nginx:alpine
f444eaf8464e30c18f7f83bb0d1bd07b48d0d99f9d9e588b2bd77659db520524
### Testing if NGINX answers, successful
$ curl -I -m 1 http://localhost:8080/
HTTP/1.1 200 OK
Server: nginx/1.19.0
Date: Sun, 28 Jun 2020 11:49:33 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 May 2020 15:37:18 GMT
Connection: keep-alive
ETag: "5ecd37ae-264"
Accept-Ranges: bytes
### Jumping into the container
$ docker exec -ti f7a2be0e230b9f7937d90954ef03502993857c5081ab20ed9a943a35687fbca4 ash
### This is the container, now, let's see the processes running
/ # ps -o pid,vsz,rss,tty,stat,time,ruser,args
PID VSZ RSS TT STAT TIME RUSER COMMAND
1 6000 4536 ? S 0:00 root nginx: master process nginx -g daemon off;
29 6440 1828 ? S 0:00 nginx nginx: worker process
30 6440 1828 ? S 0:00 nginx nginx: worker process
31 6440 1828 ? S 0:00 nginx nginx: worker process
32 6440 1828 ? S 0:00 nginx nginx: worker process
49 1648 1052 136,0 S 0:00 root ash
55 1576 4 136,0 R 0:00 root ps -o pid,vsz,rss,tty,stat,time,ruser,args
### Now let's send the SIGSTOP signal to the workers of NGINX, as docker pause would do
/ # kill -SIGSTOP 29 30 31 32
### Running ps again just to observer the T (stopped) state of the processes
/ # ps -o pid,vsz,rss,tty,stat,time,ruser,args
PID VSZ RSS TT STAT TIME RUSER COMMAND
1 6000 4536 ? S 0:00 root nginx: master process nginx -g daemon off;
29 6440 1828 ? T 0:00 nginx nginx: worker process
30 6440 1828 ? T 0:00 nginx nginx: worker process
31 6440 1828 ? T 0:00 nginx nginx: worker process
32 6440 1828 ? T 0:00 nginx nginx: worker process
57 1648 1052 136,0 S 0:00 root ash
63 1576 4 136,0 R 0:00 root ps -o pid,vsz,rss,tty,stat,time,ruser,args
/ # exit
### Back on the host to confirm NGINX doesn't answer anymore
$ curl -I -m 1 http://localhost:8080/
curl: (28) Operation timed out after 1000 milliseconds with 0 bytes received
$ docker exec -ti f7a2be0e230b9f7937d90954ef03502993857c5081ab20ed9a943a35687fbca4 ash
### Sending the SIGCONT signal as docker unpause would do
/ # kill -SIGCONT 29 30 31 32
/ # ps -o pid,vsz,rss,tty,stat,time,ruser,args
PID VSZ RSS TT STAT TIME RUSER COMMAND
1 6000 4536 ? S 0:00 root nginx: master process nginx -g daemon off;
29 6440 1828 ? S 0:00 nginx nginx: worker process
30 6440 1828 ? S 0:00 nginx nginx: worker process
31 6440 1828 ? S 0:00 nginx nginx: worker process
32 6440 1828 ? S 0:00 nginx nginx: worker process
57 1648 1052 136,0 S 0:00 root ash
62 1576 4 136,0 R 0:00 root ps -o pid,vsz,rss,tty,stat,time,ruser,args 29 30 31 32
/ # exit
### Back on the host to confirm NGINX is back
$ curl -I http://localhost:8080/
HTTP/1.1 200 OK
Server: nginx/1.19.0
Date: Sun, 28 Jun 2020 11:56:23 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 May 2020 15:37:18 GMT
Connection: keep-alive
ETag: "5ecd37ae-264"
Accept-Ranges: bytes
对于有意义的进程是PID 1 和is protected by the Linux kernel 的情况,您可能想在容器运行时尝试--init 标志,这样Docker 将创建一个能够将信号传递给您的应用程序。
$ docker run -p 8080:80 -d --init nginx:alpine
e61e9158b2aab95007b97aa50bc77fff6b5c15cf3b16aa20a486891724bec6e9
$ docker exec -ti e61e9158b2aab95007b97aa50bc77fff6b5c15cf3b16aa20a486891724bec6e9 ash
/ # ps -o pid,vsz,rss,tty,stat,time,ruser,args
PID VSZ RSS TT STAT TIME RUSER COMMAND
1 1052 4 ? S 0:00 root /sbin/docker-init -- /docker-entrypoint.sh nginx -g daemon off;
7 6000 4320 ? S 0:00 root nginx: master process nginx -g daemon off;
31 6440 1820 ? S 0:00 nginx nginx: worker process
32 6440 1820 ? S 0:00 nginx nginx: worker process
33 6440 1820 ? S 0:00 nginx nginx: worker process
34 6440 1820 ? S 0:00 nginx nginx: worker process
35 1648 4 136,0 S 0:00 root ash
40 1576 4 136,0 R 0:00 root ps -o pid,vsz,rss,tty,stat,time,ruser,args
看看之前用例中的PID 1 的nginx: master process nginx -g daemon off; 现在如何变成PID 7?
这使我们能够kill -SIGSTOP -1 并确保所有有意义的进程都已停止,但我们仍不会被锁定在容器之外。
在研究这个问题时,我发现这篇博文似乎是关于该主题的好读物:https://major.io/2009/06/15/two-great-signals-sigstop-and-sigcont/
还与ps 手册页有关进程状态代码的摘录:
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process: D uninterruptible sleep (usually IO) I Idle kernel thread R running or runnable (on run queue) S interruptible sleep (waiting for an event to complete) T stopped by job control signal t stopped by debugger during the tracing W paging (not valid since the 2.6.xx kernel) X dead (should never be seen) Z defunct ("zombie") process, terminated but not reaped by its parent For BSD formats and when the stat keyword is used, additional characters may be displayed: < high-priority (not nice to other users) N low-priority (nice to other users) L has pages locked into memory (for real-time and custom IO) s is a session leader l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) + is in the foreground process group
来源https://man7.org/linux/man-pages/man1/ps.1.html#PROCESS_STATE_CODES
【讨论】:
sigstop 似乎没有任何作用。
PID 1 是否不是这种边缘情况之一,让我测试一下 cgroup 设置,然后将其添加到答案中。
--init 标志运行容器,那么您将能够将SIGSTOP 信号发送到所有有意义的进程。
docker pause 内部命令对于非特权容器是不可能的。它需要通过挂载套接字来访问 docker 守护进程。
您需要构建自定义解决方案。只是基本的想法:您可以从主机绑定一个文件夹。在此文件夹中,您创建一个用作锁的文件。因此,当您在容器内暂停时,您将创建文件。当文件存在时,您会主动等待/睡眠。一旦主机删除已挂载的路径上的文件,您的代码就会恢复。这是一种相当幼稚的方法,因为您会主动等待,但它应该可以解决问题。
您还可以研究 inotify 以克服激活等待。 https://lwn.net/Articles/604686/
【讨论】:
docker pause,但您可以完全按照docker pause 的方式运行。