在bash中捕获来自“docker stop”的信号

25

我有一个入口点脚本在一个Docker容器中,大致如下:

#!/bin/bash

echo starting up

function shut_down() {
    echo shutting down

    pid=$(ps -e | grep myapp | awk '{print $1}')
    kill -SIGTERM $pid  
    exit
}

trap "shut_down" SIGKILL SIGTERM SIGHUP SIGINT EXIT

/opt/myapp

我无法弄清如何捕获通过运行docker stop停止容器时发送的信号。交互式运行时,ctrl+c会按预期触发它,但docker stop命令只会等待10秒超时,然后退出而从未进入shut_down函数。

我该如何在bash中捕获由docker stop发送的信号来进行一些清理?

4个回答

21

非常感谢!这个方法太棒了,我一整天都在苦苦寻找答案! - ollien
这本应该是答案!但链接现在已经失效了。 - Xavi Montero
1
@XaviMontero 我刚刚在我的新博客上重新发布了它,并编辑了链接。我会进行重定向,以使旧链接也能正常工作。 - Alen Komljen
这个是否应该与xargs一起使用? - nilfalse

10

请看this,可能会有启发 :-)

更新:

@nick-humrich,这是我复制粘贴的版本(原作者为https://github.com/lgierth)。

#!/bin/bash

function ensure_started_container {
  exists=`docker ps -q | grep $1`
  if [ "$?" = "0" ] ; then
    echo "[docker-exec] skipping docker start, already started"
  else
    output=`docker start "$1"`
    echo "[docker start] $output"
  fi
  running=1
}

function setup_signals {
  cid="$1"; shift
  handler="$1"; shift
  for sig; do
    trap "$handler '$cid' '$sig'" "$sig"
  done
}

function handle_signal {
  echo "[docker-exec] received $2"
  case "$2" in
    SIGINT)
      output=`docker stop -t 5 "$1"`
      echo "[docker stop] $output"
      running=0
      ;;
    SIGTERM)
      output=`docker stop -t 5 "$1"`
      echo "[docker stop] $output"
      running=0
      ;;
    SIGHUP)
      output=`docker restart -t 5 "$1"`
      echo "[docker restart] $output"

      # restart logging
      docker attach "$1" &
      kill "$logger_pid" 2> /dev/null
      logger_pid="$!"
      ;;
  esac
}

running=0

setup_signals "$1" "handle_signal" SIGINT SIGTERM SIGHUP

ensure_started_container "$1"

docker attach "$1" &
logger_pid="$!"

while true; do
  if [ "$running" = "1" ]; then
    sleep 1
  else
    break
  fi
done

exit_code=`docker wait "$1"`
exit "$exit_code"

12
具体而言,/opt/myapp 在前台运行,阻止了 trap 命令的执行。解决方案是将该行更新为 /opt/webapp & ; wait - ben schwartz
1
DV的原因:仅包含链接的回答被认为是劣质回答。如果链接失效会发生什么? - Nick Humrich
@Nick-Humrich 真的吗?! - Luca G. Soave
请参考以下链接:http://meta.stackexchange.com/questions/92505/should-i-flag-answers-which-contain-only-a-link-as-not-an-answer 和 http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers - Nick Humrich
3
@Nick-Humrich 是的,这是我几乎停止使用stackoverflow服务的原因之一,虽然我已经是超过5年的会员了,但有太多限制、权限和无用的专断管理(等等 :-))。 - Luca G. Soave
显示剩余2条评论

5

从 Docker 1.9 开始,您可以在 Dockerfile 中使用 STOPSIGNAL 指令:

STOPSIGNAL signal

The STOPSIGNAL sets the system call signal that will be sent to the container to exit. This signal can be a valid unsigned number that matches a position in the kernel’s syscall table, for instance 9, or a signal name in the format SIGNAME, for instance SIGKILL.

来源:https://docs.docker.com/engine/reference/builder/#stopsignal

这篇文章介绍了在使用Docker构建容器时,如何设置停止信号。当Docker收到停止信号时,它会尝试优雅地关机,以便容器中正在运行的进程有时间完成并退出。


6
请注意,你可以使用各种信号,但Signal 9可能是最糟糕的。它被称为KILL,因为它会在不给目标进程清理的机会的情况下终止它。例如,在配置不良的数据库服务器上使用它可能会带来灾难性后果。 - Doctor Eval

1
在我的情况下,仅仅设置陷阱是不起作用的。我必须将进程设置为后台以捕获信号,否则信号会发送到启动的进程本身。
启动脚本的原始最后一行是:
sudo -H -u "$uid" sh -c "myserviced"

我用以下代码替换了这一行:
shutdown_myservice() {
  set +e
  echo "[SHUTDOWN] Shutting down myservice"
  sudo -u "$uid" myservicecmd -c Shutdown
  echo "[SHUTDOWN] sent. $?"
  wait $dpid  # make sure the process terminated and not just started stopping
  echo "[SHUTDOWN] down. $?"
  return 0
}

trap "shutdown_myservice" HUP INT QUIT TERM USR1

# here, start the process in BACKGROUND
sudo -H -u "$uid" sh -c "myserviced" &
dpid=$!
wait $dpid

这样,"焦点"不再是守护进程,而是启动脚本。似乎只有"焦点"进程(前台进程)才能接收到信号。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接