关注公众号,回复“1024”获取2TB学习资源!

大家都知道,如果我们进入了容器的终端,此时可以直接通过 exit 命令来退出并停止容器。exit 命令在退出容器的同时也关闭容器,进入docker容器后如果退出容器,容器就会变成 Exited 的状态。或者直接通过 ctrl+P+Q 快捷键来控制容器不停止只退出。

通过 stop 和 kill 命令也能关闭容器。

docker stop 容器id #正常关闭容器
docker kill 容器id #暴力关闭容器
docker stop 与 docker kill 的区别如下
  • docker stop 执行时,首先给容器发送一个TERM信号,让容器做一些退出前必须做的保护性、安全性操作,然后让容器自动停止运行,如果在一段时间内容器没有停止运行,再执行 kill -9 指令,强制终止容器。

  • docker kill 执行时,不论容器是什么状态,在运行什么程序,直接执行 kill -9 指令,强制终止容器。

最近把 Docker 官方的 Docker Reference 文档又读了一遍,发现有些细节深究起来,还是有很多可挖的。针对写 Dockerfile ,大部分时候只要照葫芦画瓢,基本也不会有什么大的问题,但是如果再深入理解一下那就更有意思了。

要说如何优雅的关闭容器,那就不得不提到信号(Signal)的理念,以及 Dockerfile 中 ENTRYPOINT 和 CMD 指令了。在具体说优雅关闭之前,先了解一下信号这个 Linux 中的基础概念。

信号

信号是事件发生时对进程的通知机制,有时也称之为软件中断。

信号有不同的类型,Linux 对标准信号的编号为 1~31,可以通过 kill -l 获取信号名称:

# kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT
4) SIGILL       5) SIGTRAP      6) SIGABRT
7) SIGBUS       8) SIGFPE       9) SIGKILL
10) SIGUSR1    11) SIGSEGV     12) SIGUSR2
13) SIGPIPE    14) SIGALRM     15) SIGTERM
... ...

实际列出的信号超过了 31 个,有些是其它名称的同义词,有些则是定义但未使用的。以下介绍几个常用的信号:

  • 1) SIGHUP 当终端断开(挂机)时,将发送该信号给终端控制进程。SIGHUP 信号还可用于守护进程(比如,init 等)。许多守护进程会在收到 SIGHUP 信号时重新进行初始化并重读配置文件。

  • 2) SIGINT 当用户键入终端中断字符(通常为 Control-C ) 时,终端驱动程序将发送该信号给前台进程组。该信号的默认行为是终止进程。

  • 3) SIGQUIT 当用户在键盘上键入退出字符(通常为Control-\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成用于调试的核心转储文件。进程如果陷入无限循环,或者不再响应时,使用 SIGQUIT 信号就很合适。

  • 9) SIGKILL 此信号为 “必杀(sure kill)” 信号,处理器程序无法将其阻塞、忽略或者捕获,故而 “一击必杀”,总能终止程序。

  • 15) SIGTERM 这是用来终止进程的标准信号,也是 killkillallpkill 命令所发送的默认信号。精心设计的应用程序应当为 SIGTERM 信号设置处理器程序,以便其能够预先清除临时文件和释放其它资源,从而全身而退。因此,总是应该先尝试使用 SIGTERM 信号来终止进程,而把 SIGKILL 作为最后手段,去对付那些不响应 SIGTERM 信号的失控进程。

  • 20) SIGTSTP 这是作业控制的停止信号,当用户在键盘上输入挂起字符(通常为 Control-Z )时,将该信号给前台进程组,使其停止运行。

值得注意的是, Control-D 不会发起信号,它表示 EOF(End-Of-File),关闭标准输入(stdin)管道(比如可以通过 Control-D 退出当前 shell)。如果程序不读取当前输入的话,是不受 Control-D 影响的。

程序可以针对信号捕捉,然后执行相应函数:以上知识大部分都来自 《Linux/UNIX 系统编程手册》,想要了解更多的,可以查看该书上册的 20、21、22 章节。

ENTRYPOINT 、 CMD

可能有人会问,说了半天,那信号和优雅的关闭容器有半毛钱的关系啊?话说,这和钱确实没关系,但是和如何优雅关闭容器却关系密切。

接着说 Dockerfile 中的 ENTRYPOINT 和 CMD 指令,它们的主要功能是指定容器启动时执行的程序。

CMD 有三种格式:

  • CMD ["executable","param1","param2"] (exec 格式, 推荐使用这种格式)

  • CMD ["param1","param2"] (作为 ENTRYPOINT 指令参数)

  • CMD command param1 param2 (shell 格式,默认 /bin/sh -c )ENTRYPOINT 有两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"] (exec 格式,推荐优先使用这种格式)

  • ENTRYPOINT command param1 param2 (shell 格式)

其中,不管你 Dockerfile 用其中哪个指令,两个指令都推荐使用 exec 格式,而不是 shell 格式。原因就是因为使用 shell 格式之后,程序会以 /bin/sh -c 的子命令启动,并且 shell 格式下不会传递任何信号给程序。这也就导致,在 docker stop 容器的时候,以这种格式运行的程序捕捉不到发送的信号,也就谈不上优雅的关闭了。

➜  ~ docker stop --helpUsage:  docker stop [OPTIONS] CONTAINER [CONTAINER...]Stop one or more running containersOptions:--help       Print usage-t, --time int   Seconds to wait for stop before killing it (default 10)

docker stop 停掉容器的时候,默认会发送一个 SIGTERM 的信号,默认 10s 后容器没有停止的话,就 SIGKILL 强制停止容器。通过 -t 选项可以设置等待时间。

➜  ~ docker kill --helpUsage:  docker kill [OPTIONS] CONTAINER [CONTAINER...]Kill one or more running containersOptions:--help            Print usage-s, --signal string   Signal to send to the container (default "KILL")

通过 docker kill-s 选项还可以指定给容器发送的信号。

所以,说了那么多,只要 Dockerfile 中通过 exec 格式执行容器启动命令就相安无事了?那当然是,没有那么简单的了,接下来我们通过实例来看看具体的效果是怎么样的。

实例

通过 Go 写一个简单的信号处理器:

➜  ~ cat signals.go
package mainimport ("fmt""os""os/signal""syscall"
)func main() {sigs := make(chan os.Signal, 1)done := make(chan bool, 1)signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)go func() {sig := <-sigsfmt.Println()fmt.Println(sig)done <- true}()fmt.Println("awaiting signal")<-donefmt.Println("exiting")
}

实例 1

➜  ~ GOOS=linux GOARCH=amd64 go build signals.go
➜  ~ ls
Dockerfile signals    signals.go
➜  ~ cat Dockerfile
FROM busyboxCOPY signals /signalsCMD ["/signals"]    # exec 格式执行
➜  ~ docker build -t signals .

通过 tmux 开启两个面板,一个运行容器,一个执行 docker stop :

➜  ~ docker run -it --rm --name signals signals
awaiting signalterminated
exiting
➜  ~ time docker stop signals
signals
docker stop signals  0.01s user 0.02s system 4% cpu 0.732 total
➜  ~

可以发现,容器停止之前,程序接收到信号并输出相应信息,并且停止总耗时为 0.732 s,达到了优雅的效果。

修改 Dockerfile 中 CMD 执行格式,执行相同操作:

➜  ~ cat Dockerfile
FROM busyboxCOPY signals /signalsCMD /signals        # shell 格式执行
➜  ~ docker build -t signals .
➜  ~ docker run -it --rm --name signals signals
awaiting signal
➜  ~
➜  ~ time docker stop signals
signals
docker stop signals  0.01s user 0.01s system 0% cpu 10.719 total

通过 shell 格式之后,可以发现容器停止之前,程序并未接收到任何信号,并且停止时间为 10.719s,说明该容器是被强制停止的。

结论很明显,为了优雅的退出容器,我们应该采用 exec 这种格式。

实例 2

通过实例 1 我们都会在 Dockerfile 中都会通过 exec 这种格式来执行程序了,那如果执行的程序本身也是一个 shell 脚本呢?

➜  ~ ls
Dockerfile signals    signals.go start.sh
➜  ~ cat Dockerfile
FROM busyboxCOPY signals /signals
COPY start.sh /start.sh     # 引入 shell 脚本启动CMD ["/start.sh"]
➜  ~ cat start.sh
#!/bin/sh/signals
➜  ~

测试依然引用实例 1 中的方法:

➜  ~ docker run -it --rm --name signals signals
awaiting signal
➜  ~
➜  ~ time docker stop signals
signals
docker stop signals  0.01s user 0.02s system 0% cpu 10.765 total
➜  ~

可以发现,即使 Dockerfile 中的 CMD 指令使用的是 exec 格式,容器中的程序依然没有接收到信号,最后被强制关闭。因为 shell 脚本中执行的原因,导致信号依然没有被传递,我们需要针对 shell 脚本做一些改造:

➜  ~ cat start.sh
#!/bin/shexec /signals   # 加入 exec 执行
➜  ~ docker build -t signals .
➜  ~ docker run -it --rm --name signals signals
awaiting signalterminated
exiting
➜  ~ time docker stop signals
signals
docker stop signals  0.02s user 0.02s system 4% cpu 0.744 total
➜  ~

可以看到,加入 exec 命令之后,程序又可以接收到信号正常退出了。当然,如果你 Dockerfile 中的 CMD 是以 shell 格式运行的,即使启动脚本中加入 exec 也是无效的。再者,如果你的程序本身不能针对信号做一些处理,也就谈不上优雅关闭了。

来源:https://blog.opskumu.com/graceful-

shutdown-docker.html

邀你加入技术交流群,2023 我们一起卷!

推荐阅读 点击标题可跳转

太突然!国风知名企业线下门店全部关闭

快播宣告破产!彻底倒闭了

微信新功能冲上热搜,网友们吵翻了

不愧是最牛逼的集群监控系统!强的一批

413 亿收购 H3C!江山易主了

又一款轻量级监控利器!开源了

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下在看,加个星标,这样每次新文章推送才会第一时间出现在你的订阅列表里。点在看支持我们吧!

弃用 Docker kill,事实证明,它更牛逼!相关推荐

  1. java 核心技术pad_弃用 Notepad++,还有 5 款更牛逼的选择!

    原标题:弃用 Notepad++,还有 5 款更牛逼的选择! 作为文本编辑工具,有比 Notepad++ 更好的替代工具: Sublime Text (非开源) Sublime Text 是一个轻量. ...

  2. 请立即弃用 Notepad++,还有 5 款更牛逼的选择!

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 作为文本 ...

  3. 2018年,人工智能 VS 区块链,谁更牛逼?

    IT派 - {技术青年圈} 持续关注互联网.区块链.人工智能领域 区块链,又火了 互联网成了维密的 T 台,大佬们穿着技术的内衣裤,搔首弄姿秀性感,腿毛飘飘在风中丝丝缕缕,油黑闪亮. 2017 年末, ...

  4. 阿里云和华为云谁更牛逼

    阿里云和华为云,目前是中国最牛逼的两个云服务供应商,自从2020年疫情过后,这两个也是被人讨论的最多的云服务了 既然两个共同存在,所谓没有对比就没有伤害,那么阿里云和华为云,在技术上面谁更牛逼一点呢? ...

  5. 小米电视是鸿蒙系统吗,小米高管:华为鸿蒙真的很牛,如果都能实现就更牛逼了!...

    原标题:小米高管:华为鸿蒙真的很牛,如果都能实现就更牛逼了! 8月9日华为发布了鸿蒙系统,10日紧接着又发布了搭载鸿蒙系统的荣耀智慧屏,这是华为第一次推出智能电视,也是开启鸿蒙生态链最重要的一步,荣耀 ...

  6. 放弃 Notepad++,事实证明,还有 5 款更牛逼……

    点击关注公众号,Java干货及时送达 今天跟大家聊一聊,作为文本编辑工具,还有比 Notepad++ 更好的替代工具吗?别说,还真有另外5款(为啥有些粉丝说,要卸载Notepad++ 呢,其实这个No ...

  7. 卸载Notepad++!事实已证明,它更牛逼……

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:lucida lucida.me/blog/subl ...

  8. struts2前台获取setattribute为空指针异常_告诉你,这样设计 Java 异常更优雅,更牛逼!...

    来源:Lrwin lrwinx.github.io/2016/04/28/如何优雅的设计java异常/ 导语 异常处理是程序开发中必不可少操作之一,但如何正确优雅的对异常进行处理确是一门学问,笔者根据 ...

  9. 不用 Notepad++,还有更牛逼的选择!

    来源:oschina.net/news/110987/no-notepad-plus-plus 这两天 Notepad++ 牛逼了,然后引发了大家的关注,具体事件内容请大家自行百度,其实作为文本编辑工 ...

最新文章

  1. mysql连接池_基于Swoole的通用连接池 - 数据库连接池(life)
  2. web服务器获取项目路径问题,读取web项目properties文件路径 解决tomcat服务器找不到properties路径问题...
  3. redis的基本使用笔记一
  4. Shiro相关文章资料
  5. esxi管理端口_网工知识角|一分钟轻松了解华为端口安全机制
  6. SCOM 2012知识分享-9:配置警报解决状态
  7. SqoopFlume、Flume、HDFS之间比较
  8. 阿里大数据中台12年建设经验的精华总结!一次性说清!
  9. 推荐一款Linux服务器连接工具FinalShell
  10. 很多朋友问做自媒体的意义是什么?
  11. Boosting and AdaBoost
  12. 二叉树的存储结构入门(java描述)
  13. python文本自动伪原创_现在有哪些好用的伪原创工具?
  14. 编程十年 (13):毁人不倦1
  15. 内核层读写应用层文件,使用filp_open函数——完美
  16. 【微信小程序开发】缓存Storage的存入与获取
  17. 用汇编语言程序设计实现c=a b,西安交通大学18年3月课程考试《汇编语言程序设计》作业考核试题...
  18. 超实用的Mac快捷键神器:CheatSheet Mac中文免费版
  19. 帝国CMS 7.2-插件包整合
  20. Groovy(Java笨狗)系列---断言

热门文章

  1. 航班信息数据获取(传入起始点、终点以及日期)
  2. pipeline+sonar
  3. 架构师接龙:盛大许式伟VS. 金山张宴
  4. 全息舞台,和曾年轻的自己共舞争艳
  5. reiserfs相关
  6. easyUI datagrid 单元格数据的修改,保存,json 数据的转化
  7. 我用闲言碎语,脱掉了你的外衣。
  8. design短语的用法总结_分词短语用法总结
  9. 元年广角下的财务共享服务中心
  10. 单片机控制发光二极管的显示(1)