Moby: 无法使用 Ctrl-C 或 Ctrl-\ 杀死或分离 docker 容器

创建于 2013-11-24  ·  71评论  ·  资料来源: moby/moby

你好,

在 Ubuntu 13.04 上运行 docker 0.6.7。

我构建了一个容器,其 CMD 执行 uwsgi 进程(也尝试过其他可执行文件)。 我在没有分离的情况下运行,并且 sigproxy 是 True。

按 Ctrl-C 似乎对正在运行的容器没有影响,离开或杀死它的唯一方法是使用docker killdocker stop ...

这是一个已知的问题?

areapi

最有用的评论

从 docker 0.6.5 开始,您可以将-tdocker run命令,这将附加一个伪 TTY。 然后您可以键入Control-C以从容器中分离而不终止它。

如果您使用-t-i那么Control-C将终止容器。 当-i-t一起使用时,您必须使用Control-P Control-Q来分离而不终止。

测试 1:

$ ID=$(sudo docker run -t -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

容器仍被列出。

测试 2:

$ ID=$(sudo docker run -t -i -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

容器不存在(它已被终止)。 如果您在第二个示例中键入Control-P Control-Q而不是Control-C ,容器仍将运行。

修复 Hello World 守护程序示例文档的拉取请求在这里:

https://github.com/dotcloud/docker/pull/2845

我不知道你在哪里看到了在这个例子之外对Control-C的推荐。 如果您在其他地方看到此参考资料,能否请您提交一个新的拉取请求来修复您参考的文档?

您可能还会发现此邮件列表主题很有帮助。

所有71条评论

从 docker 0.6.5 开始,您可以将-tdocker run命令,这将附加一个伪 TTY。 然后您可以键入Control-C以从容器中分离而不终止它。

如果您使用-t-i那么Control-C将终止容器。 当-i-t一起使用时,您必须使用Control-P Control-Q来分离而不终止。

测试 1:

$ ID=$(sudo docker run -t -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

容器仍被列出。

测试 2:

$ ID=$(sudo docker run -t -i -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps

容器不存在(它已被终止)。 如果您在第二个示例中键入Control-P Control-Q而不是Control-C ,容器仍将运行。

修复 Hello World 守护程序示例文档的拉取请求在这里:

https://github.com/dotcloud/docker/pull/2845

我不知道你在哪里看到了在这个例子之外对Control-C的推荐。 如果您在其他地方看到此参考资料,能否请您提交一个新的拉取请求来修复您参考的文档?

您可能还会发现此邮件列表主题很有帮助。

@lhazlewood好吧,-t -d 可以让您在

$ ID=$(docker run -d ubuntu bash -c "while true; do echo foo; sleep 5; done")
$ docker attach $ID

在这种情况下 ctrl-C 什么也不做。 我希望它会终止“docker attach”进程(而不是容器)。

如果守护进程不是用 -t 启动的,我认为 docker attach 应该默认为 -sig-proxy=false

据我所知,您需要-t-i才能让 Ctrl-C 按预期工作......

@vmalloc这取决于您的期望。 我希望能够使用 ctrl-c 与“docker attach”分离,而不是杀死守护进程。 如果不手动指定 -sig-proxy=false,目前这是不可能的。 如果您不小心这样做,唯一的出路是从另一个终端终止 docker attach 进程。

啊,抱歉上面有点糊涂了。 如果您运行“docker run -d -t”并且docker attach,那么ctrl-c _does_ detach,而不是杀死守护进程。 问题是,如果你忘记了 -t,然后使用 docker attach 你最终会得到一些你无法分离的东西,除非从不同的终端杀死它。

@alexlarsson是的,我上面展示的两个测试用例显示了带和不带-i会发生什么。

是的,我遇到了同样的问题。 一种简单的重现方法是docker run busybox sleep 60然后按 CTRL-C 即可。 在 60 秒结束之前,该命令不会终止或分离。 我希望 CTRL-C 向 sleep 命令发送一个 SIGTERM,这应该会停止 docker 实例。

另见#2855

要让 ctrl+c 停止容器,您必须使用 -it
要从容器中分离,您应该使用 ctrl+pq

关闭。

为什么这被关闭了? 你能解释一下原因吗?

我问是因为很明显社区的大多数人不想要当前的默认行为。

另请注意,在 VirtualBox 中运行 Ubuntu 的 Mac OS X 上,ctrl+pq _不起作用_。

@lhazlewood因为问题不在于 ctrl+pq,它是关于使用 ctrl-c 退出附加,这是不支持的,因为“附加”实际上是将您附加到正在运行的进程中,应该是预期的行为。
如果你只想要 stdout+stderr 流,你应该使用docker logs不附加。
我认为这是一个培训问题,而不是 Docker 问题。

如果我们想讨论更改 ctrl-pq,并且我知道我已经看到其他问题,那很好。
我不确定我们现在可以改变它。
我们讨论过为它引入一个可配置的命令,但它是在服务器端实现的,并不理想,在客户端实现它很困难。

我以最友好的语气写下这一切,我生性简洁,对此感到抱歉。

我对这一切感到非常困惑。

在 shell 提示符下的默认行为是 ^C 终止正在运行的前台进程。 如果不存在这样的进程,它就什么也不做,除了可能显示一个新的提示。 ^D 另一方面,在不停止机器的情况下退出 shell。 这就是我所期望的,因为这就是其他人所做的。

那么,为什么 Docker 的做法不同呢? 为什么我会读到这个奇怪的讨论? 我错过了什么吗?

@rolkar因为在这种情况下有两个前台进程。
您在容器和 docker 客户端中附加的进程。

我个人认为这应该与 SSH 的行为方式大致相同,但转义序列不同。
仅供参考 SSH 的转义序列是<enter>~.

还是不明白。 为什么 Ctrl-C 不发送 SIGINT?

@Vanuan - 因为该进程在

是的,我想通了。 Docker 实际上会发送 SIGINT,但内核会忽略它,因为进程 ID 是 1。而且由于该进程没有自己的 SIGINT 处理程序,所以什么也不会发生。

内核不会忽略它,进程会。

@cpuguy83
所以你是说如果 pid 不是 1,SIGINT 被发送到父进程?

@Vanuan信号始终发送到您指定的进程,但某些进程在 pid 1 时不会加载信号处理程序,因此信号会被忽略。
杀死它的唯一方法是kill -9 ,在这种情况下内核终止进程而不是进程终止自身。

似乎不是真的:

// main.c
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

void catch_interrupt(int sig) {
  printf("Interrupting...\n");
}

int main() {
  signal(SIGINT, catch_interrupt);
  sleep(10);
  return 0;
}
$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev gcc main.c -o main
$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev ./main
^CInterrupting...

注释掉signal相同设置:

$ docker run -it --rm -v `pwd`:/src -w /src iron/gcc:dev ./main
^C^C^C^C^C
# nothing happens

如您所见,信号处理程序已加载。 即使 PID 仍然是“1”,它也会响应 Ctrl-C 并终止(虽然我们没有要求它终止,我们只是处理了它)。 但是当没有注册信号处理程序时,它不会响应 Ctrl-C 并且没有任何反应。

@Vanuan是的,这正是我所说的。
信号仍然发送到程序,程序只是不响应它。

我提到了这部分:

某些进程在 pid 1 时不加载信号处理程序

是不是还有别的意思? 信号处理程序总是在注册时加载。

但是,如果 pid = 1 AND 信号处理程序未注册,则似乎有一些默认信号处理程序不会加载。 这是你的意思吗?

@Vanuan正是。

我仍然对此感到困惑。 示例运行:

docker run -i $IMAGE
ping google.com
PING google.com (216.58.217.46) 56(84) bytes of data.
64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms
^C64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms
^C^C^C^C^C^C^C^C64 bytes from 216.58.217.46: icmp_seq=1 ttl=61 time=31.5 ms

因此 ctrl+c 不会将 SIGINT 发送到 docker 进程或 ping 进程。 docker stop我唯一的选择吗? (ctrl+p、ctrl+c 也不起作用。我在 OSX 上)

@johshoff这取决于图像如何启动ping
它是否在/bin/sh内运行? 如果是这样,信号被发送到/bin/sh ,它在作为 pid 1 运行时忽略信号。

谢谢, @cpuguy83 ,这使它更清楚。 ping确实在/bin/sh下运行。

它是否在 /bin/sh 中运行? 如果是这样,信号将发送到 /bin/sh,它在作为 pid 1 运行时忽略信号。

不,/bin/sh 不会忽略 Ctrl-C。 Ping 也没有忽略它。 这里的问题是缺少-t标志:

docker run -i $IMAGE

应该是

docker run -it $IMAGE

例如:

docker run --rm -it alpine sh -c "ping -c 4 google.com"

对比

docker run --rm -i alpine sh -c "ping -c 4 google.com"

对比

docker run --rm -ti alpine ping -c 4 google.com

对比

docker run --rm -i alpine ping -c 4 google.com

我同意@cpuguy83 的观点,因为我在这里看不到/bin/sh的事实。 对此非常简单的解决方案是使用

sh -c "exec ping google.com"

用 ping 进程替换 shell 进程
代替

sh -c "ping google.com"

当使用sh -c "exec ping google.com" ,信号 (Ctrl-C) 被发送到ping而不是sh 。 并且 ping 以 pid 1 运行时不会忽略此信号

@jarl-dk

并且 ping 以 pid 1 运行时不会忽略此信号

如果没有,为什么不直接使用 ping 呢?

您可以告诉 node.js 处理信号:

// For Docker
process.on('SIGINT', function() { console.log('Caught Ctrl+C...'); process.exit(); }); // Ctrl+C
process.on('SIGTERM', function() { console.log('Caught kill...'); process.exit(); }); // docker stop

我已经用 ping 命令启动了容器,当我连接容器时,即使我按下 ctrl+c,Ping 进程仍然继续运行

如何停止 ping In 容器?

使用的步骤:

docker run --name centos-linux -d centos /bin/sh -c "while true; do ping 8.8.8.8; done"
docker 附加 centos-linux

很抱歉,但我不明白为什么有时无法从附加的容器中分离。 我知道,如果以 pid=1 运行,某些进程可能会忽略 SIGINT,但在这种情况下,必须有一种方法可以从同一个终端会话中杀死或分离附加的容器。

例如,当使用 eboraas/apache 映像时,该映像通过在 Dockerfile 中以 exec 形式使用 CMD 命令在前台执行 apache:
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
当我从这个图像启动容器docker run -p 80:80 -d eboraas/apache ,重视它与docker attach或者如果我不小心错过了-d标志我从容器要么分离无变化或从同一个终端会话停止/终止容器。
要停止容器,我必须使用另一个终端会话来执行docker stop 。 即使我使用-i标志启动容器,ctrl+c、ctrl+d 和 ctrl+pq 也无效。

我在 Mac OS X 下使用 Docker for Mac。

把它放到你的 alpine 3.4 Dockerfile 中:

RUN apk add tini
ENTRYPOINT ["/sbin/tini", "--"]

你很高兴去。

1 年后,我们仍然不能 Ctrl+C 一个 docker 容器

@sebdelvalle你可以。 只是不是所有的 docker 容器都响应 Ctrl+C。 如果您创建了在 docker 容器中运行的软件,则您有责任正确处理 Ctrl+C。 如果您不按 Ctrl+C 关闭软件,则没有人会为您关闭。

你看,当你在没有 docker 的情况下运行你的软件时,你有一个终端和初始化系统,它可以处理 Ctrl+C。 当您在 docker 中运行它时,您没有任何终端。 您的软件成为系统的入口点。 这就像直接从重启启动你的软件。

我的 node.js 应用程序也有同样的问题......我用

/**
 * Does what it says :-)
 */
function endProcess(reason) {
        // eslint-disable-next-line no-console
        console.log(`Quitting... Reason: ${reason}`);
        process.exit();
}

/**
 * Adds hooks for Docker CTRL-C and Stop
 */
function dockerConfig() {
        [
                'SIGINT',
                'SIGTERM'
        ].forEach((signal) => {
                process.on(signal, () => {
                        endProcess(signal);
                });
        });
}

module.exports = dockerConfig;

哪个命令停止泊坞窗?
ctrl + C 不要停止

@romenigld docker在进程停止时停止

exec 为我工作。

例如,运行 uwsgi,ctrl+c 不起作用:

*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 1)
spawned uWSGI worker 1 (pid: 6, cores: 1)
spawned uWSGI worker 2 (pid: 7, cores: 1)
spawned uWSGI worker 3 (pid: 8, cores: 1)
spawned uWSGI worker 4 (pid: 9, cores: 1)
spawned uWSGI worker 5 (pid: 10, cores: 1)
^C^C^C^C^C^C^C^C^C

但是然后运行 ​​exec uwsgi,ctrl+c 被显式接收:

*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI master process (pid: 1)
spawned uWSGI worker 1 (pid: 6, cores: 1)
spawned uWSGI worker 2 (pid: 7, cores: 1)
spawned uWSGI worker 3 (pid: 8, cores: 1)
spawned uWSGI worker 4 (pid: 9, cores: 1)
spawned uWSGI worker 5 (pid: 10, cores: 1)
^CSIGINT/SIGQUIT received...killing workers...

@montanaflynn它能够从终端打开另一个选项卡并输入: docker stop <container_id>

另一种方式,如果你使用像@alvarow这样的节点,你可以使用npm startpackage.json的规则,npm 将在ctrl-c上关闭:

像这样:

{
  "name": "prom-koa-example",
  "version": "1.0.0",
  "description": "Expose prometheus metrics in koa",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
  },
  "dependencies": {
    "koa": "^1.2.0",
    "koa-router": "^5.4.0",
    "prom-client": "^9.1.1"
  }
}

我使用了一个 shell 脚本,因为我的命令docker-php-entrypoint apache2-foreground没有在 Control+C 上退出。

docker-php-entrypoint apache2-foreground &
apache_pid="$!"

kill_apache() {
  kill "$apache_pid"
}
#trap 'kill_apache' INT
wait "$apache_pid"

这里, docker-php-entrypoint apache2-foreground只是一个不响应 Control+C 的命令示例。 如果您有其他命令没有响应,您可以尝试将其替换为您的命令,然后查看脚本是否响应 Control+C。

您正在使用apache2-foreground启动 apache,然后使用&对其进行守护?

这是我对它的理解。

在编写 Dockerfile 时,有两个与启动容器进程相关的命令: ENTRYPOINTCMD

ENTRYPOINT定义了要启动的可执行文件,而CMD定义了要传递给可执行文件的参数。 默认情况下, ENTRYPOINT等于/bin/sh

因此,当 Dockerfile 没有ENTRYPOINT而只有CMD "node src/index.js" ,容器中实际运行的进程类似于: /bin/sh -c "node src/index.js"

现在由于某种原因我不完全理解,当sh收到一个 SIGINT 时,无论是通过docker stop还是ctrl-c中的docker run -it ... ,它都不会将此信号转发到它启动的进程(在这种情况下node src/index.js )。

因此,定义 Dockerfile 以运行 nodejs 容器的正确方法是:

FROM node:8

# some stuff...

ENTRYPOINT ["node"]
CMD ["src/index.js"]

其中src/index.js具有 SIGINT 的信号处理程序。
现在, docker stop <container>完美运行。

这可以很容易地复制到其他类型的过程中,所以我希望它有所帮助 :smiley:

现在由于某种原因我不完全理解,当 sh 收到 SIGINT 时,无论是通过 docker stop 还是 ctrl-c 在 docker run -it ...,它都不会将此信号转发到它启动的进程

正确的; 以pid 1运行的进程通常与以不同 pid 运行的同一进程不同。 ( pid 1是“特殊的”,因为它是计算机的(在这种情况下:容器的)主进程)。 当以 pid 1 运行时, sh不会转发信号,因此它的子进程不会被杀死。

直接通过ENTRYPOINT / CMD指定进程是一种选择; 另一个选项是exec要在入口点脚本末尾运行的进程。

比如官方的httpd镜像使用httpd-foreground包装脚本来执行一些初始化任务,但是最后一步是exec容器的主进程( httpd ); https://github.com/docker-library/httpd/blob/53654452889ae3af537eef2dbb981ccac6fb907f/2.2/httpd-foreground#L1 -L7

使用exec使 shell 本身退出,并运行您指定的进程(从而使其成为容器内的pid 1

引用我上面的建议:

RUN apk add tini
ENTRYPOINT ["/sbin/tini", "--"]

现在您不在乎是否在 CMD 中使用sh

如果您不需要sh ,请不要运行它; 使用exec可以避免在容器内运行一个额外的进程,这可能会有所帮助。

(另外,使用docker run --init会自动在你的容器中注入tini ,无需添加)

凉爽的! 是否有相应的 compose 选项?
看起来没有: https :
所以对于 swarm 来说,如果你使用 docker-compose 你仍然需要tini

可以默认启用 init 选项(通过设置守护程序选项); 然后所有容器,包括那些作为服务的一部分启动的容器都得到--init 。 正在进行 PR https://github.com/docker/cli/pull/479

无法在我的 Ubuntu 17.04 操作系统中使用 CTRL+P、CTRL+Q 分离。 啊啊啊

1 年后,我们仍然不能 Ctrl+C 一个 docker 容器

现在2年了🎂
在 MacOSX 上仍然出现错误。

@felipekm没有帮助:您遇到了什么问题? 你读过这个线程吗?

是的,我有@thaJeztah ,我只是想杀死一个由docker run -ti <image_id>初始化的进程。

@felipekm在另一个终端类型docker ps然后docker stop <container_id>

如果您希望Ctrl+C工作,您需要更改您的 docker 容器以响应SIGINT

更新:这里已经回答了https://github.com/moby/moby/issues/37200

真实世界用例:运行 Docker 容器并在 CI 中进行测试:

docker run -ti my-image my-test-script

CI 不支持 TTY 所以我得到:

the input device is not a TTY
stdin: is not a tty
ERROR: Job failed: exit status 1

当我没有 TTY 时,如何将 SIGTERM 传播到容器入口点?

我想要的进程树:

  bash
    docker run -ti my-image my-test-script

CI runner 将向 bash 脚本发送一个 SIGTERM,该脚本将传播到 docker run,但由于没有 TTY 支持,它无法使用 -ti 运行。

这适用于我的终端。

@aalexgabi这是我在 CI (jenkins) 上使用的:

# Fix docker signal proxy issue without tty
function docker() {
    case "$1" in
        run)
            shift
            if [ -t 1 ]; then # have tty
                command docker run --init -it "$@"
            else
                id=`command docker run -d --init "$@"`
                trap "command docker kill $id" INT TERM
                command docker wait $id
            fi
            ;;
        *)
            command docker "$@"
    esac
}

# export to sub-shell
export -f docker

@felipekm尝试在启动容器时添加--init 。 这会将Tini作为 PID 1 启动,Tini 将确保您的应用程序获得 SIGINT(而不是被 /bin/sh 拦截)。

更多细节在上面的线程中。

总之:

  1. 如果您运行的程序不处理它, docker run -it是不够的。 如果它不是您的程序,则特别有问题……例如,另一个程序以有限的方式解释您的程序时就是这种情况。
  2. 您可以将ENTRYPOINT ["/sbin/tini", "--"]设为入口点...而不是您的应用程序。
  3. 使用docker run -it --init它将解决您所有的问题。

@guiambros非常有用,它在我的别名中。

也适用于群模式:

相应的撰写更改最近已合并,因此在下一个版本中,您将能够执行以下操作:

version: '3.7'
services:
  myservice:
    image: myimage
    init: true

最后,您不必为了添加tini而修改图像,以便在不响应 SIGTERM 时不会杀死您的容器(退出代码 137)。 特别适用于数据库以防止数据丢失。

最后,您不必为了添加 tini 而修改图像,以便在不响应 SIGTERM 时不会杀死您的容器(退出代码 137)。 特别适用于数据库以防止数据丢失。

只需将其添加为附加信息(其中一些已在之前的评论中提到);

虽然使用--init (或在您的图像中添加tini )可以快速帮助处理无法正确处理信号的图像,但请确保您没有“掩盖”实际问题与图像。

Tini 提供以下功能;

  1. 收获过程; 一旦父进程终止,任何产生子进程的(Linux)进程也负责_收割_这些进程。 这意味着 tini _should not_ 被需要,除非容器的主二进制文件违反了这个规则(不幸的是,那里有一些例子)。
  2. 它以 PID-1 启动,这意味着容器的主进程现在变为 PID-2。 PID-1 是“特殊的”; 杀死 PID-1 意味着杀死“机器”(或在 Docker 的上下文中;杀死容器)。 因此,如果某些进程作为 PID-1 执行,则它们的行为会有所不同; 他们会忽略(某些)信号,以防止被杀死(因为这会杀死机器,这通常是不可取的,除非您想关闭或重新启动)。

如果您知道在容器内运行的进程是一个“坏演员”,并且不处理收割,那么使用--init绝对是让您前进的好选择。 但是请注意,您实际上遇到了错误; 请务必向您在容器中运行的软件的维护者/发布者报告问题:也许他们不知道这种情况,并且可以修复它。

如果您因为2. --init而使用

例如; 以下CMD将启动一个 shell( /bin/sh/bin/bash ,具体取决于图像),其中启动mysqld

CMD /usr/sbin/mysqld

正因为如此, /bin/sh (不是mysqld )成为了容器的主进程(PID-1),任何发送到容器的信号都会由/bin/sh (和没有转发到mysqld )。 使用--init将“解决”这种情况( /bin/sh现在作为 PID-2 运行,而mysqld作为 PID-3 运行),但仍然在容器。

要使容器的进程在_无_shell 的情况下运行,请使用 JSON ("exec" form) 。 比如下面是mysql官方镜像CMD

CMD ["mysqld"]

但是如果你需要在启动容器时(在运行容器的主进程之前)运行一些命令,比如设置权限,或者在容器第一次运行时做一些设置呢?

这些步骤可以在entrypoint-script 中完成。 入口点脚本可以运行一个 shell,执行初始化(例如,参见官方 WordPress 入口点脚本中exec切换到容器的主进程。

使用exec ,当前进程会被另一个进程替换,因此在入口点脚本末尾使用exec时,容器的主进程将作为 PID-1 运行,而不是运行壳内; 这是WordPress 入口点脚本

exec "$@"

Dockerfile 同时使用ENTRYPOINTCMD ,在这种情况下, CMD用作ENTRYPOINT

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]

exec "$@"将因此默认为exec apache2-foreground ,但将替换为用于启动容器的命令(例如docker run mysql echo "hello" ,将运行入口点脚本,然后exec echo "hello" )

TL; 博士; --init选项可以安全使用,并且可能对您的情况有所帮助,但请确保您没有缩小图像中的实际问题:微笑:

更新:这里已经回答了https://github.com/moby/moby/issues/37200

@rayfoss @thaJeztah当您没有 TTY 时, --init-ti再次不是解决方案,请参阅 #37200

我希望docker run myScriptImage与运行./myScript完全相同,只是它在隔离和受控的环境中运行。 我希望 Ctrl-C 可以像“任何其他脚本”一样开箱即用。 如果在任何 bash 脚本中我可以用等效的docker run myEquivalentCommand替换任何系统命令并使其工作方式与我在本地启动命令(stdin、stdout、stderr、退出代码、输入)相同,那将是理想的缓冲、输出缓冲、信号处理等)。

更新:这里已经回答了https://github.com/moby/moby/issues/37200

@tsl0922谢谢,但这就是我试图避免做的。 我觉得我可能正在执行一个名为wrap-docker-run-as-if-is-a-regular-program的命令,它将使用 bash 陷阱处理所有这些,而不是在所有 ci 管道中复制粘贴该代码(大约有 30 个。)

@aalexgabi

--init 和 -ti 当您没有 TTY 时不是解决方案,请参阅 #37200

TTY 与能够发送或捕获信号无关。 请阅读我在您提到的那个问题线程中的消息。

一个问题:想知道官方图片使用的解决方案是什么......比如说nginx。 Nginx 容器可以在不处于分离模式时使用 SIGINT 终止。

@bidiu首先,它使用 exec 形式:

CMD ["nginx", "-g", "守护进程关闭;"]

其次,nginx 自己处理信号。
最后,它使用
STOPSIGNAL SIGTERM 以便当您以交互模式向 docker 发送 SIGINT 或运行 docker stop 容器时,它会发送 SIGTERM,该 SIGTERM 将由 nginx 处理以终止自身

你可以通过查看它的 Dockerfile 自己弄清楚:
https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/Dockerfile

@Vanuan
啊哈,之前不知道STOPSIGNAL指令,也不熟悉“exec form”以外的模式。 谢谢你的澄清😊

所以有shell形式和exec形式。 Shell 形式的用法如下:

CMD nginx

执行形式如下:

CMD ["nginx"]

在第一种情况下,shell 处理您的信号,在第二种情况下,nginx 进程

对于在无法使用 SIGINT 停止node进程后来到这里的人们,我发现另一个问题对于理解这种行为也非常有帮助。

截至目前,使用 Windows 10,您需要在 git bash 中像这样运行

winpty docker run -it i<id>

此页面是否有帮助?
0 / 5 - 0 等级