【问题标题】:How to fix ctrl+c inside a docker container如何在 docker 容器中修复 ctrl+c
【发布时间】:2017-04-27 03:07:13
【问题描述】:

如果我连接到 docker 容器

$> docker exec -it my_container zsh

在里面我想杀死我从ctrl+c开始的东西我注意到它需要很长时间才能完成。我搜索了一下,似乎ctrl+c 的工作方式与您的预期有些不同。我的问题,如何在容器内修复 ctrl+c

【问题讨论】:

    标签: shell docker kill-process


    【解决方案1】:

    问题在于 Ctrl-C 向容器内的顶级进程发送了一个信号,但该进程不一定会像您预期的那样做出反应。顶级进程在容器内的 ID 为 1,这意味着它不会获得进程通常拥有的默认信号处理程序。如果顶级进程是一个shell,那么它可以通过自己的处理程序接收信号,但不会将其转发给在shell中执行的命令。详细说明here。在这两种情况下,docker 容器的行为就好像它只是忽略了 Ctrl-C。

    docker 0.6.5开始,你可以在docker run命令中添加-t,这将附加一个pseudo-TTY。然后你可以输入Control-C 来从容器中分离而不终止它。

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

    测试 1:

    $ ID=$(sudo docker run -t -d ubuntu /usr/bin/top -b)
    $ sudo docker attach $ID
    Control-P Control-Q
    $ 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,则容器仍将运行。

    使用 docker-entrypoint.sh bash 脚本包装程序,该脚本会阻塞 容器进程并且能够捕获 ctrl-c。这个 bash 示例 可能有帮助: https://rimuhosting.com/knowledgebase/linux/misc/trapping-ctrl-c-in-bash

    #!/bin/bash
    
    # trap ctrl-c and call ctrl_c()
    trap ctrl_c INT
    
    function ctrl_c() {
            echo "** Trapped CTRL-C"
    }
    
    for i in `seq 1 5`; do
        sleep 1
        echo -n "."
    done
    

    【讨论】:

    • 我的印象(如果我错了,请纠正我)你描述的ctrl+c 是为了退出容器,这不是我想要的。我想停止我手动启动的程序
    • 请详细说明脚本如何阻止容器进程,以及在 Ctrl-C 陷阱中应该做什么。
    • OT:我认为使用 CTRL+C 停止容器是一个糟糕的设计……我总是不小心停止容器,只是因为习惯以这种方式退出 shell 进程。
    • 谢谢!现在我真的知道为什么总是添加-it 是个好主意。顺便说一句,它也很容易记住。只需docker run -it ...! :-D
    【解决方案2】:

    使用 Ctrl+\ 代替 Ctrl+C
    它会终止进程,而不是礼貌地要求它关闭。(阅读更多 here。)

    【讨论】:

    • 它对我不起作用。当正在运行的 docker 容器没有响应 Ctrl+C 时,它也会忽略 Ctrl+\
    【解决方案3】:

    在某些情况下,当我使用 ctrl-C 终止容器内的进程时,容器也会终止。

    此外,我见过在容器中运行的进程离开僵尸进程的情况。

    我发现使用“--init”开关启动容器时,这两个问题都得到了解决。这似乎使我的容器以更“正常的、预期的类 UNIX 方式”运行。

    如果您想了解有关“--init”开关的作用的更多信息,请在包含“docker run”信息的 Docker 网页上阅读它。该网页上的信息显示“在容器内运行一个 init 来转发信号并获取进程”。

    【讨论】:

      【解决方案4】:

      当我尝试在 docker 容器中运行 mdbook(Rust 可执行文件)时,我遇到了类似的问题。 mdbook 启动简单的网络服务器,我想通过 Ctrl+C 停止它,但它不起作用。

      $ docker -ti --rm -p 4321:4321 my-docker-image mdbook serve --hostname 0.0.0.0 --port 4321
      2019-08-16 14:00:11 [INFO] (mdbook::book): Book building has started
      2019-08-16 14:00:11 [INFO] (mdbook::book): Running the html backend
      2019-08-16 14:00:11 [INFO] (mdbook::cmd::serve): Serving on: http://0.0.0.0:4321
      2019-08-16 14:00:11 [INFO] (ws): Listening for new connections on 0.0.0.0:3001.
      2019-08-16 14:00:11 [INFO] (mdbook::cmd::watch): Listening for changes...
      ^C^C
      

      受到@NID 回答的启发,我通过通用 bash 脚本 docker-entrypoint.sh 封装了 mdbook 可执行文件,这可以解决问题(无需显式捕获 INT 信号)。

      $ docker -ti --rm -p 4321:4321 my-docker-image docker-entrypoint.sh mdbook serve --hostname 0.0.0.0 --port 4321
      2019-08-16 14:00:11 [INFO] (mdbook::book): Book building has started
      2019-08-16 14:00:11 [INFO] (mdbook::book): Running the html backend
      2019-08-16 14:00:11 [INFO] (mdbook::cmd::serve): Serving on: http://0.0.0.0:4321
      2019-08-16 14:00:11 [INFO] (ws): Listening for new connections on 0.0.0.0:3001.
      2019-08-16 14:00:11 [INFO] (mdbook::cmd::watch): Listening for changes...
      ^C $
      

      docker-entrypoint.sh的内容很简单:

      #!/bin/bash
      
      $@
      

      【讨论】:

      • 这很好,谢谢。我看到的唯一缺点是你必须写下整个命令
      • 打开你喜欢的编辑器,写上上面提到的两行并保存为docker-entrypoint.sh :-) 然后通过你的Dockerfile中的ADD命令将此文件添加到图像中。
      【解决方案5】:

      如果你使用Docker Compose,可以添加init参数将信号转发到容器:

      version: "2.4"
      services:
        web:
          image: alpine:latest
          init: true
      

      要使其正常工作,您需要在 docker exec 命令中添加选项 -ti

      【讨论】:

        【解决方案6】:

        浪费了大约 2 个小时。

        新命令——(工作正常)

        sudo docker stop 
        sudo docker rm   
        sudo docker run -t
        

        旧命令 --(不再工作)

        sudo docker stop
        sudo docker rm   
        sudo docker run
        Ctrl + C
        sudo docker start
        

        希望对某人有所帮助。

        【讨论】:

          【解决方案7】:

          我尝试了@Remy Orange 的--init 解决方案,它对我有用。经过一番搜索,包括i)How to use --init parameter in docker run,ii)What is advantage of Tini?和iii)init,我写了下面的详细解决方案:

          1. 在 Ubuntu 上安装 tini
          • 通过发射:
          $ sudo apt update && sudo apt install tini
          
          • 或者,如果 tini 在您的发行版中不可用或太旧,请检查 Dockerfile 以在 here 添加 tini。
          1. 使用 --init 运行 Docker 容器:
          docker run -ti --init --rm YOUR_DOCKER_CONTAINER_EXMAPLE bash
          
          1. 然后你进入你的 docker 容器,你可以运行一些进程或实验。例如,运行 Python 代码,然后您可以启动 Ctrl + C 以取消此 Python 代码,就像您可以在 Ubuntu 上执行的操作一样(即 docker 容器外部的常规终端)。

          2. 查看我的截图:

          • 启动Ctrl + C (i.e., ^C)以取消python进程:

          • 它停止,按预期显示KeyboardInterrupt

          【讨论】:

          • 我使用的是 Ubuntu 16.04。
          • 或者您也可以 1) 运行“docker exec -it SOME_CONTAINER_ID ps -aus”来查找容器中的 SOME_PID;然后2)运行“docker exec -it SOME_CONTAINER_ID kill -9 SOME_PID”来杀死docker容器内的进程。
          【解决方案8】:

          对于仍然有这个问题的人,Ctrl+d 为我工作。 Ctrl+cCtrl+z 都不起作用。

          【讨论】:

          • Ctrl+Z 在 docker run 后可以正常工作,谢谢!
          猜你喜欢
          • 2019-06-03
          • 1970-01-01
          • 2022-12-20
          • 2018-07-18
          • 1970-01-01
          • 2019-07-23
          • 1970-01-01
          • 2020-06-29
          • 1970-01-01
          相关资源
          最近更新 更多