0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何优雅地关闭容器

马哥Linux运维 来源:opskumu.com 2023-02-01 09:21 次阅读

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

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

1 信号

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

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

#kill-l
1)SIGHUP2)SIGINT3)SIGQUIT
4)SIGILL5)SIGTRAP6)SIGABRT
7)SIGBUS8)SIGFPE9)SIGKILL
10)SIGUSR111)SIGSEGV12)SIGUSR2
13)SIGPIPE14)SIGALRM15)SIGTERM
......

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

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

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

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

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

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

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

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

程序可以针对信号捕捉,然后执行相应函数:

7234d2c0-a1b2-11ed-bfe3-dac502259ad0.png

以上知识大部分都来自 《Linux/UNIX 系统编程手册》,想要了解更多的,可以查看该书上册的 20、21、22 章节。

2 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 容器的时候,以这种格式运行的程序捕捉不到发送的信号,也就谈不上优雅的关闭了。

➜~dockerstop--help

Usage:dockerstop[OPTIONS]CONTAINER[CONTAINER...]

Stoponeormorerunningcontainers

Options:
--helpPrintusage
-t,--timeintSecondstowaitforstopbeforekillingit(default10)

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

➜~dockerkill--help

Usage:dockerkill[OPTIONS]CONTAINER[CONTAINER...]

Killoneormorerunningcontainers

Options:
--helpPrintusage
-s,--signalstringSignaltosendtothecontainer(default"KILL")

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

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

3 实例

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

➜~catsignals.go
packagemain

import(
"fmt"
"os"
"os/signal"
"syscall"
)

funcmain(){
sigs:=make(chanos.Signal,1)
done:=make(chanbool,1)

signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)

gofunc(){
sig:=<-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

3.1 实例 1

➜~GOOS=linuxGOARCH=amd64gobuildsignals.go
➜~ls
Dockerfilesignalssignals.go
➜~catDockerfile
FROMbusybox

COPYsignals/signals

CMD["/signals"]#exec格式执行
➜~dockerbuild-tsignals.

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

➜~dockerrun-it--rm--namesignalssignals
awaitingsignal

terminated
exiting
➜~timedockerstopsignals
signals
dockerstopsignals0.01suser0.02ssystem4%cpu0.732total
➜~

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

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

➜~catDockerfile
FROMbusybox

COPYsignals/signals

CMD/signals#shell格式执行
➜~dockerbuild-tsignals.
➜~dockerrun-it--rm--namesignalssignals
awaitingsignal
➜~
➜~timedockerstopsignals
signals
dockerstopsignals0.01suser0.01ssystem0%cpu10.719total

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

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

3.2 实例 2

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

➜~ls
Dockerfilesignalssignals.gostart.sh
➜~catDockerfile
FROMbusybox

COPYsignals/signals
COPYstart.sh/start.sh#引入shell脚本启动

CMD["/start.sh"]
➜~catstart.sh
#!/bin/sh

/signals
➜~

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

➜~dockerrun-it--rm--namesignalssignals
awaitingsignal
➜~
➜~timedockerstopsignals
signals
dockerstopsignals0.01suser0.02ssystem0%cpu10.765total
➜~

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

➜~catstart.sh
#!/bin/sh

exec/signals#加入exec执行
➜~dockerbuild-tsignals.
➜~dockerrun-it--rm--namesignalssignals
awaitingsignal

terminated
exiting
➜~timedockerstopsignals
signals
dockerstopsignals0.02suser0.02ssystem4%cpu0.744total
➜~

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

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10986

    浏览量

    206713
  • 信号
    +关注

    关注

    11

    文章

    2634

    浏览量

    75370
  • 容器
    +关注

    关注

    0

    文章

    481

    浏览量

    21876
  • 命令
    +关注

    关注

    5

    文章

    638

    浏览量

    21848
  • Docker
    +关注

    关注

    0

    文章

    437

    浏览量

    11602

原文标题:如何优雅地关闭容器

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何让你的Python代码竟优雅又地道

    pythonic的代码简练,明确,优雅,绝大部分时候执行效率高。阅读pythonic的代码能体会到“代码是写给人看的,只是顺便让机器能运行”畅快。这篇文章是网友Jeff Paine整理的他在2013年...
    的头像 发表于 02-03 12:35 4401次阅读

    200.200 尚硅谷 SparkStreaming 优雅关闭 恢复数据

    数据库
    充八万
    发布于 :2023年07月18日 09:25:47

    神舟优雅U10R上网本 参考价格:2499元

    推荐产品:神舟优雅U10R上网本  参考价格:2499元神舟 优雅U10R神舟 优雅U10R仅重1.15kg 轻盈生活随处可得   优雅U10R采用10.1寸超轻薄靓丽LED
    发表于 07-01 22:30

    热门低价3G上网本推荐 神舟优雅U20Y 参考售价:2399元

    获得了补贴,含上网自费套餐的3G上网本价格上并不比没有3G功能的上网本高。因此在有补贴的情况,今年购买还是比较划算的。下面我们就一起看看具体的情况。  神舟优雅U20Y  参考售价:2399元(不含
    发表于 07-02 09:03

    Spark Streaming的offset管理和关闭策略

    Spark Streaming优雅关闭策略优化
    发表于 04-22 14:59

    如何优雅地完成倒计时定时器自适应显示呢

    如何实现倒计时的基本功能呢?如何优雅地完成倒计时定时器自适应显示呢?
    发表于 10-27 07:15

    磁通电容器的资料分享

    更多的丝印艺术。再次感谢 pcb 方式,一块板已完全组装,我编写了代码让灯闪烁以模仿通量电容器动画。该按钮目前没有做任何事情,但我打算用它来打开和关闭 LED,或者启用其他类型的动画。 LED 在日光下非常明亮,并具有温暖的黄色/橙色。PCB
    发表于 07-13 07:50

    利用golang优雅的实现单实例分享

    1、利用golang优雅的实现单实例平时编写代码过程中,经常会遇到对于全局角度只需运行一次的代码,比如全局初始化操作,设计模式中的单例模式。针对单例模式,java中又出现了饿汉模式、懒汉模式,再配
    发表于 10-17 16:46

    经验分享|BI数据可视化报表布局——容器

    设置。2、放大容器点击右侧的【专有】,将看到一个【允许放大容器】的开关。这是一个什么功能?关闭该开关,表示可以放大容器以及容器内的图表;开启
    发表于 03-15 17:10

    如何通过ESP模块打开/关闭树莓派GPIO电路?

    如何通过ESP模块打开/关闭树莓派GPIO电路?树莓派作为优雅的电源开/关功能。您只需关闭 Pi Pin 5 (BCM 3) GPIO 到 GND。只要您在 Pi OS 中完成了一些软件配置,这就
    发表于 05-15 06:13

    单片机优雅的开发Clion环境搭建

    单片机优雅的开发Clion环境搭建
    发表于 11-13 12:21 17次下载
    单片机<b class='flag-5'>优雅</b>的开发Clion环境搭建

    如何用责任链默认优雅地进行参数校验

    那么有什么更好的参数校验的方式呢?本文就推荐一种通过责任链设计模式来优雅地实现参数的校验功能,我们通过一个用户注册的例子来讲明白如何实现。
    的头像 发表于 04-06 15:00 314次阅读

    Tokio 模块的优雅停机机制

    在进行高并发、网络编程时,优雅停机是一个非常重要的问题。在 Rust 语言中,Tokio 是一个非常流行的异步编程框架,它提供了一些优雅停机的机制,本文将围绕 Tokio 模块的优雅停机进行详细
    的头像 发表于 09-19 15:26 296次阅读

    linux关闭docker的命令

    在 Linux 系统中,关闭 Docker 的操作可以通过以下多种方式进行。本文将详细讲解每一种方式,并提供示例代码和命令,以帮助读者更好地理解和实践。 使用 docker 命令 最常用的方法
    的头像 发表于 11-23 09:39 1138次阅读

    优雅停机是什么?SpringBoot+Nacos+k8s实现优雅停机

    优雅停机是什么?网上说的优雅下线、无损下线,都是一个意思。
    的头像 发表于 02-20 10:00 496次阅读
    <b class='flag-5'>优雅</b>停机是什么?SpringBoot+Nacos+k8s实现<b class='flag-5'>优雅</b>停机