概述

玩过docker的朋友可能都使用过 docker stop 命令来停止正在运行的容器,有些会使用 docker kill 命令强行关闭容器或者把某个信号传递给容器中的进程。这些操作的本质都是通过从主机向容器发送信号实现主机与容器中程序的交互。

比如我们可以向容器中的应用发送一个重新加载信号,容器中的应用程序在接到信号后执行相应的处理程序完成重新加载配置文件的任务。

下面主要介绍在 docker 容器中捕获信号的基本知识。


信号(linux)

信号是一种进程间通信的形式。一个信号就是内核发送给进程的一个消息,告诉进程发生了某种事件。当一个信号被发送给一个进程后,进程会立即中断当前的执行流并开始执行信号的处理程序。如果没有为这个信号指定处理程序,就执行默认的处理程序。

进程需要为自己感兴趣的信号注册处理程序,比如为了能让程序优雅的退出(接到退出的请求后能够对资源进行清理)一般程序都会处理 SIGTERM 信号。与 SIGTERM 信号不同,SIGKILL 信号会粗暴的结束一个进程。因此我们的应用应该实现这样的目录:捕获并处理 SIGTERM 信号,从而优雅的退出程序。如果我们失败了,用户就只能通过 SIGKILL 信号这一终极手段了。除了 SIGTERM 和 SIGKILL ,还有像 SIGUSR1 这样的专门支持用户自定义行为的信号。

下面的代码简单的说明在 nodejs 中如何为一个信号注册处理程序:


容器中的信号

Docker 的 stop 和 kill 命令都是用来向容器发送信号的。注意,只有容器中的 1 号进程能够收到信号,这一点非常关键!

stop 命令会首先发送 SIGTERM 信号,并等待应用优雅的结束。如果发现应用没有结束(用户可以指定等待的时间),就再发送一个 SIGKILL 信号强行结束程序。

kill 命令默认发送的是 SIGKILL 信号,当然你可以通过 -s 选项指定任何信号。

下面我们通过一个 nodejs 应用演示信号在容器中的工作过程。创建 app.js 文件,内容如下:

这个应用是一个 http 服务器,监听端口 3000,为 SIGINT 和 SIGTERM 信号注册了处理程序。接下来我们将介绍以不同的方式在容器中运行程序时信号的处理情况。


应用程序作为容器中的 1 号进程

创建 Dockerfile 文件,把上面的应用打包到镜像中:

这里要注意ENTRYPOINT指令的写法,这种写法会让 node 在容器中以 1 号进程的身份运行。

接下来创建镜像:

$ docker build --no-cache -t signal-app -f Dockerfile .

然后启动容器运行应用程序:

$ docker run -it --rm -p 3000:3000 --name="my-app" signal-app

此时 node 应用在容器中的进程号为 1:

让程序退出,执行命令:

$ docker container kill --signal="SIGTERM" my-app

此时应用会以我们期望的方式退出:


应用程序不是容器中的 1 号进程

创建一个启动应用程序的脚本文件 app1.sh,内容如下:

然后创建 Dockerfile1 文件,内容如下:

接下来创建镜像:

$ docker build --no-cache -t signal-app1 -f Dockerfile1 .

然后启动容器运行应用程序:

$ docker run -it --rm -p 3000:3000 --name="my-app1" signal-app1

此时 node 应用在容器中的进程号不再是 1:

如果这时候给 my-app1 发送 SIGTERM 信号试试,已经无法退出程序了!在这个场景中,应用程序由 bash 脚本启动,bash 作为容器中的 1 号进程收到了 SIGTERM 信号,但是它没有做出任何的响应动作。

我们可以通过:

退出应用,它们最终都是向容器中的 1 号进程发送了 SIGKILL 信号。


在脚本中捕获信号

创建另外一个启动应用程序的脚本文件 app2.sh,内容如下:

#!/usr/bin/env bashset -xpid=0 # SIGUSR1-handlermy_handler() { echo "my_handler"} # SIGTERM-handlerterm_handler() { if [ $pid -ne 0 ]; then kill -SIGTERM "$pid" wait "$pid" fi exit 143; # 128 + 15 -- SIGTERM}# setup handlers# on callback, kill the last background process, which is `tail -f /dev/null` and execute the specified handlertrap 'kill ${!}; my_handler' SIGUSR1trap 'kill ${!}; term_handler' SIGTERM # run applicationnode app &pid="$!" # wait foreverwhile truedo tail -f /dev/null & wait ${!}done

这个脚本文件在启动应用程序的同时可以捕获发送给它的 SIGTERM 和 SIGUSR1 信号,并为它们添加了处理程序。其中 SIGTERM 信号的处理程序就是向我们的 node 应用程序发送 SIGTERM 信号。

然后创建 Dockerfile2 文件,内容如下:

接下来创建镜像:

$ docker build --no-cache -t signal-app2 -f Dockerfile2 .

然后启动容器运行应用程序:

$ docker run -it --rm -p 3000:3000 --name="my-app2" signal-app2

此时 node 应用在容器中的进程号也不是 1,但是它却可以接收到 SIGTERM 信号并优雅的退出了:


总结

容器中的 1 号进程是非常重要的,如果它不能正确的处理相关的信号,那么应用程序退出的方式几乎总是被强制杀死而不是优雅的退出。究竟谁是 1 号进程则主要由 EntryPoint, CMD, RUN 等指令的写法决定,所以这些指令的使用是很有讲究的。

后面小编会分享更多devops干货,感兴趣的朋友只可以关注下~

sigterm信号_详解如何在 docker 容器中捕获信号相关推荐

  1. jsfor循环终止_详解如何在JS代码中消灭for循环

    Edit: 在我入职第三家公司的第一天,看到代码库里面一堆的 for 循环,内心有些崩溃,于是做了一次技术分享,展示怎样在代码中避免 for 循环.这篇文章是那次分享的总结.本文并不完美,其中递归的部 ...

  2. android 最新sdk版本,详解如何在Android studio中更新sdk版本和build-tools版本

    一.首先看下Android开发用到的sdk目录: build-tools 保存着一些Android平台相关通用工具,比如adb.和aapt.aidl.dx等文件. aapt即Android Asset ...

  3. 如何在Docker容器中挂载主机目录

    本文翻译自:How to mount a host directory in a Docker container I am trying to mount a host directory into ...

  4. 如何在Docker容器中运行GUI程序

    如何在Docker容器中运行GUI程序 各位,今天我们将学习如何在Docker之中运行GUI程序.我们可以轻易地在Docker容器中运行大多数GUI程序且不出错.Docker是一个开源项目,提供了一个 ...

  5. python命令窗口在哪里_详解如何在cmd命令窗口中搭建简单的python开发环境

    详解如何在cmd命令窗口中搭建简单的python开发环境 1.快捷键win+r输入cmd回车调出cmd界面,在命令行输入python回车,显示python命令无法识别 2.登陆python官网http ...

  6. jq的插件 vue中引用_详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件

    本篇文章主要介绍了详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件,具有一定的参考价值,有兴趣的可以了解一下 使用vue-cli构建的vue项目,webpack的配置文 ...

  7. 如何在Docker容器中运行Docker [3种方法]

    在本博客中,我将向您介绍在docker中运行docker所需的三种不同方法. Docker In Docker的用处 dockerIndocker的一个潜在用处是CI管道,在代码成功构建后,您需要在其 ...

  8. docker镜像指定安装源_详解如何修改docker pull镜像源

    Docker Hub Mirror 为全球最大的Docker Registry(Docker Hub)提供在中国的镜像代理服务.Docker Hub Mirror会为中国的用户在国内的服务器上缓存诸多 ...

  9. 案例详解-如何在 Linux 系统中安装和使用 7zip 以及 7zip的脚本编程使用教程(非p7zip,而是官方版本7zip for linux)附deb包下载链接

    如何在 Linux 系统中安装和使用 7zip 李俊才(jcLee95) 的个人博客 邮箱 :291148484@163.com CSDN 主页:https://blog.csdn.net/qq_28 ...

最新文章

  1. 算法提高课-搜索-DFS之连通性模型-AcWing 1112. 迷宫:dfs和bfs两种解法
  2. onRetainNonConfigurationInstance和getLastNonConfigurationInstance
  3. python爬虫网站简单_Python爬虫之简单爬虫框架实现
  4. 删除华为电视鸿蒙系统,华为解决互联网电视痛点:鸿蒙系统首发设备欲屏蔽开机广告...
  5. android标签循环,iOS和Android规范解析——标签导航和分段控件
  6. qscoj:喵哈哈村的狼人杀大战(5)
  7. python语言翻译成汇编语言_计原 || 1计算机语言发展与计算机层次结构
  8. testng 组_TestNG组
  9. 【codeforces 709C】Letters Cyclic Shift
  10. KVM虚拟化教程(超详细)
  11. Intel VT学习笔记(一)—— 基础知识支持检测
  12. JAVA:实现ClosestPair最近对算法(附完整源码)
  13. 赛效:如何在线更改图片格式 图片格式在线转换方法介绍
  14. LoRaWAN协议-物理层(PHY)详解
  15. 支持向量机是怎么画分类平面的?
  16. Robomaster小陀螺
  17. 帝国CMS采集插件哪个好用?帝国CMS采集文章教程
  18. timepicker时间选择控件 时:分:秒
  19. 安装lint-staged 报错:Cannot add lint-staged: only eslint, stylelint, prettier...
  20. Flutter 笔记 | Flutter 核心原理(一)架构和生命周期

热门文章

  1. 十年老鸟倾囊分享撬开企业之门的简历情书
  2. 【数据结构】TopK问题
  3. wide dynamic range - WDR - 宽动态范围 - high dynamic range - HDR - 高动态范围
  4. 加入klamm我就返佣(强力返佣!!)
  5. SpringBoot Web项目Mock测试
  6. AJAX - onreadystatechange 事件
  7. 微信小程序学习笔记(4) -- 页面间的跳转和传值
  8. 25.改进分辨率和按键响应
  9. 软件定义无线电(RFIC系列)
  10. Python读取WAV文件的几种方式整理