如何在Docker容器内修复Ctrl+C
简介
这篇文章的真正问题声明是,开发人员在运行或创建Docker容器时无法使用中断信号。这不是Docker中的一个bug,而是一个Linux特性。完整的问题和重新创建问题的方法与解决方案一起被解释。
Linux中的流程
目前正在运行或执行的程序是一个进程。这个进程可以是前台的,也可以是后台的。为了查看这些进程,我们有一些终端命令,如ps和top。ps显示活动进程,top显示进程的实时更新。
$ps
输出结果给你提供了进程ID、链接的tty、进程的时间以及实际运行的命令或程序。
输出
PID TTY TIME CMD
2584 pts/0 00:00:00 bash
4146 pts/0 00:00:00 ps
要查看所有运行在你系统中的进程。
$ps –e
输出
PID TTY TIME CMD
1 ? 00:00:02 systemd
2 ? 00:00:00 kthreadd
3 ? 00:00:00 rcu_gp
4 ? 00:00:00 rcu_par_gp
5 ? 00:00:00 netns
.
.
.
.
3650 ? 00:00:01 kworker/1:0-cgroup_destroy
3694 ? 00:00:00 kworker/u4:1-events_unbound
4138 ? 00:00:00 kworker/0:1-cgroup_destroy
4197 ? 00:00:00 kworker/0:2-events
4198 pts/0 00:00:00 ps
在这里,我们有四千多个程序在运行。
Linux终端上的信号
信号或我们可以说是中断。这些信号是通过键盘快捷键发送到终端的。例如,为了粘贴,我们使用Ctrl+V。这将发送一个中断,使自己优先于其他正在进行的进程,并粘贴文本或图像。同样地,我们也有键盘快捷键来向Linux终端发送各种中断信号。这些信号在下面提到。
- Tab
-
Ctrl+C
-
Ctrl+D
-
Ctrl+U
-
Ctrl+A
Tab
这个标签使用起来很有效率。它有助于命令的自动完成。它能识别命令的模式,并在按下时为你完成该命令。例如,停止容器4b534b34h34b34,$ docker stop 4b5,然后按tab键,它将获取完整的容器ID。
Ctrl+C
这个快捷方式将终止/中止/杀死当前执行的进程或命令。当命令被卡在一个循环中或你忘记了要添加到命令中的东西时,这很有用。只要用这个快捷方式终止命令,然后用编辑好的命令重新运行。
Ctrl+D
这个中断将杀死当前的用户终端并回溯到最后使用的用户终端。
Ctrl+U
这将删除完整的一行,并将光标移到该行的开头。
Ctrl+A
这将使光标回到命令的开头,而不删除命令。
重现问题的方法
执行下面给出的方法,在你的机器上重现这个问题。问题是,Docker容器对中断信号Ctrl+C没有反应。
方法1
在没有任何模式的情况下运行容器,并给出一个可以保持进程几秒钟的命令。
$docker run busybox sleep 10
输出
无论你按多少次Ctrl+C,你都没有得到任何回应。这个快捷方式应该是终止了在容器内运行的sleep 10命令。该命令得到完全执行。
方法2
在交互式模式下运行容器。应通过相同的睡眠命令并按下键盘上的快捷键。
$docker run -i busybox sleep 10
命令在容器内被执行。
方法3
现在使用-t标志添加tty选项,并检查是否有变化。
$docker run -t busybox sleep 10
同样的结果,命令被完全执行。
方法4
同时使用交互式和tty
$docker run -it busybox sleep 10
方法5
包括detach标志。
$docker run -itd busybox sleep 10
docker容器将在后台运行10秒。如果你在运行命令后刚按下Ctrl+C,它将不会对中断作出反应。
问题的解决方案
在解决这个问题之前,我们必须知道中断信号在上述方法中不起作用的原因。我们传递给容器的sleep命令得到了一个PID 1的进程ID。而PID 1对其他进程有一些特殊的权力。这就是中断不能终止PID 1或sleep命令的原因。
为了解决这个问题,docker提供了一个名为 --init 的选项。这将克服我们所有关于不同中断信号的问题。
$docker run --init busybox sleep 10
现在,Docker容器将响应Ctrl+C的中断,在没有完全执行的情况下终止命令sleep 10。
上述解决方案的证明
不用–init | 用–init |
---|---|
$docker run busybox ps | $docker run –init busybox ps |
PID USER TIME COMMAND 1 root 0:00 ps | PID USER TIME COMMAND 1 root 0:00 /sbin/docker-init — ps 7 root 0:00 ps |
在第一种情况下,ps命令的PID是1,但在第二种情况下,PID 1是docker-init,这就是为什么我们能够在第二种情况下终止ps命令。
结论
有时小问题会卡住你的整个项目。这里我们讨论了各种中断信号,以及在特殊情况下如何将它们连接到docker容器。Docker不仅是学习容器化,而且是以最有效的方式使用它来实现我们的目标。