如何在 Docker 容器内修复 Ctrl+C?
简介
本文的实际问题陈述是,开发人员在运行或创建 Docker 容器时无法使用中断信号。这不是 Docker 中的错误,而是 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
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
以交互模式运行容器。应该传递相同的sleep命令,然后按下键盘上的快捷键。
$docker run -i busybox sleep 10
命令在容器内执行。
方法 3
现在使用 -t 标记添加 tty 选项,并检查是否有任何变化。
$docker run -t busybox sleep 10
相同的结果,命令将完全执行。
方法 4
使用交互式和 tty
$docker run -it busybox sleep 10
方法 5
包含分离标记。
$docker run -itd busybox sleep 10
Docker 容器将在后台运行 10 秒。如果您在运行命令后立即按下 Ctrl+C,它将不会响应中断。
问题的解决方法
在了解解决方案之前,我们必须知道为什么中断信号在上述方法中不起作用。我们传递给容器的命令 sleep 获取一个进程 ID 作为PID 1。而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 不仅仅是关于学习容器化,还关于以最有效的方式使用它来实现我们的目标。