如何在 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 1sleep命令的原因。

为了解决此问题,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 命令的PID1,但在第二种情况下,PID 1docker-init,这就是我们能够在第二种情况下终止ps命令的原因。

结论

有时一些小问题会阻碍您的整个项目。在这里,我们讨论了各种中断信号以及如何在特殊情况下将它们连接到 Docker 容器。Docker 不仅仅是关于学习容器化,还关于以最有效的方式使用它来实现我们的目标。

更新于: 2023 年 1 月 11 日

3K+ 次查看

启动您的职业生涯

通过完成课程获得认证

开始
广告