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

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

3天内不再提示

信号的理念以及Dockerfile中ENTRYPOINT和CMD指令

马哥Linux运维 来源:CSDN技术社区 作者:CSDN技术社区 2022-06-12 16:42 次阅读
最近把 Docker 官方的 Docker Reference 文档又读了一遍,发现有些细节深究起来,还是有很多可挖的。针对写 Dockerfile ,大部分时候只要照葫芦画瓢,基本也不会有什么大的问题,但是如果再深入理解一下那就更有意思了。要说如何优雅的关闭容器,那就不得不提到信号(Signal)的理念,以及 Dockerfile 中 ENTRYPOINT 和 CMD 指令了。在具体说优雅关闭之前,先了解一下信号这个 Linux 中的基础概念。

1 信号

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

信号有不同的类型,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 影响的。

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

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

➜  ~ docker stop --help
Usage:  docker stop [OPTIONS] CONTAINER [CONTAINER...]
Stop one or more running containers
Options:      --help       Print usage  -t, --time int   Seconds to wait for stop before killing it (default 10)
docker stop停掉容器的时候,默认会发送一个SIGTERM的信号,默认 10s 后容器没有停止的话,就SIGKILL强制停止容器。通过-t选项可以设置等待时间。
➜  ~ docker kill --help
Usage:  docker kill [OPTIONS] CONTAINER [CONTAINER...]
Kill one or more running containers
Options:      --help            Print usage  -s, --signal string   Signal to send to the container (default "KILL")

	

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

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

3 实例

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

➜  ~ cat signals.gopackage main
import (    "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 := <-sigs        fmt.Println()        fmt.Println(sig)        done <- true    }()
    fmt.Println("awaiting signal")    <-done    fmt.Println("exiting")}

3.1 实例 1

➜  ~ GOOS=linux GOARCH=amd64 go build signals.go➜  ~ lsDockerfile signals    signals.go➜  ~ cat DockerfileFROM busybox
COPY signals /signals
CMD ["/signals"]    # exec 格式执行➜  ~ docker build -t signals .
通过 tmux 开启两个面板,一个运行容器,一个执行 docker stop :
➜  ~ docker run -it --rm --name signals signalsawaiting signal
terminatedexiting
➜  ~ time docker stop signalssignalsdocker stop signals  0.01s user 0.02s system 4% cpu 0.732 total➜  ~

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

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

➜  ~ cat DockerfileFROM busybox
COPY signals /signals
CMD /signals        # shell 格式执行➜  ~ docker build -t signals .
➜  ~ docker run -it --rm --name signals signalsawaiting signal➜  ~
➜  ~ time docker stop signalssignalsdocker stop signals  0.01s user 0.01s system 0% cpu 10.719 total

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

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

3.2 实例 2

通过实例 1 我们都会在 Dockerfile 中都会通过 exec 这种格式来执行程序了,那如果执行的程序本身也是一个 shell 脚本呢?
➜  ~ lsDockerfile signals    signals.go start.sh➜  ~ cat DockerfileFROM busybox
COPY signals /signalsCOPY start.sh /start.sh     # 引入 shell 脚本启动
CMD ["/start.sh"]➜  ~ cat start.sh#!/bin/sh
/signals➜  ~

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

➜  ~ docker run -it --rm --name signals signalsawaiting signal➜  ~
➜  ~ time docker stop signalssignalsdocker stop signals  0.01s user 0.02s system 0% cpu 10.765 total➜  ~
可以发现,即使 Dockerfile 中的 CMD 指令使用的是 exec 格式,容器中的程序依然没有接收到信号,最后被强制关闭。因为 shell 脚本中执行的原因,导致信号依然没有被传递,我们需要针对 shell 脚本做一些改造:
➜  ~ cat start.sh#!/bin/sh
exec /signals   # 加入 exec 执行➜  ~ docker build -t signals .
➜  ~ docker run -it --rm --name signals signalsawaiting signal
terminatedexiting
➜  ~ time docker stop signalssignalsdocker stop signals  0.02s user 0.02s system 4% cpu 0.744 total➜  ~

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

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

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

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

    关注

    87

    文章

    11031

    浏览量

    207275
  • 信号
    +关注

    关注

    11

    文章

    2687

    浏览量

    75780
  • 容器
    +关注

    关注

    0

    文章

    483

    浏览量

    21928
  • Docker
    +关注

    关注

    0

    文章

    443

    浏览量

    11672

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

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

收藏 人收藏

    评论

    相关推荐

    11.Dockerfile配置之CMDENTRYPOINT

    前端编程语言
    电子学习
    发布于 :2023年02月01日 21:52:51

    用LV连续使用cmd发送接收指令,可做顺序自动发送指令

    可以用lv2015 连续在电脑端通过cmd收发指令
    发表于 12-28 16:38

    Dockerfile的最佳实践

    ”微服务一条龙“最佳指南-“最佳实践”篇:Dockerfile
    发表于 07-11 16:22

    Dockerfile使用规则

    Dockerfile编写规范
    发表于 08-12 14:30

    CMD文件定义怎么理解?

    ,256k L2 RAM, 以及通过EMIFA及DDR2/mDDR扩展的RAM或FLASH,这些空间可供用户使用;CMD文件即将这些空间进行划分。疑问如下:1. 上述CMD的定义,EntryP
    发表于 10-22 11:57

    一文详解DockerFile基础知识

    # 当构建一个被继承Dockerfile 这个时候运行ONBUILD指定,触发指令COPY # 将文件拷贝到镜像ENV # 构建的时候设置环境变量构建自己的Centos镜像Docker hub
    发表于 09-15 15:54

    CMD55、CMD55、CMD55、CMD55综合测试仪新到

    CMD55、CMD55、CMD55、CMD55综合测试仪新到9台深圳市理想微仪器通信有限公司 联系人:萧小姐135-1060-9722 联系电话:0755-83761992/837
    发表于 08-05 15:42 733次阅读

    系统之上的进程级别虚拟化技术——Docker

    的正常代码的区别。而且后者在一个 Dockerfile 文件中只能有一个存在。CMD/ENTRYPOINT 的区别除了在写法上有区别之外,还有在docker run命令后增加 CMD
    的头像 发表于 05-02 14:55 4892次阅读
    系统之上的进程级别虚拟化技术——Docker

    全面详解Dockerfile文件

    Docker 可以通过读取 Dockerfile 中的指令自动构建镜像。Dockerfile 是一个文本文档,其中包含了用户创建镜像的所有命令和说明。 一、 变量 变量
    的头像 发表于 09-22 15:38 1742次阅读

    镜像构建Dockerfile的介绍

    Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
    的头像 发表于 09-06 09:36 901次阅读

    Dockerfile的最佳实践

    随着应用的容器化、上云后,将伴随着 Docker 镜像的构建,构建 Docker 镜像成为了最基本的一步,其中 Dockerfile 便是用来构建镜像的一种文本文件,镜像的优劣全靠
    的头像 发表于 01-20 10:59 752次阅读
    <b class='flag-5'>Dockerfile</b>的最佳实践

    Docker入门指南之什么是Dockerfile

    Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像 * 对于开发人员:可以为开发团队提供一个完全一致的开发环境 * 对于测试人员:可以直接拿开
    的头像 发表于 02-06 15:25 518次阅读
    Docker入门指南之什么是<b class='flag-5'>Dockerfile</b>

    使用匿名管道技术获取CMD命令的执行结果

    远程 CMD 是指恶意程序接收到控制端发送的 CMD 指令后,在本地执行 CMD 命令,并将执行结果回传至控制端。本文将演示使用匿名管道技术获取 C
    的头像 发表于 04-03 18:04 2809次阅读

    Dockerfile定义Docker镜像的构建过程

    了解Dockerfile Dockerfile 是一个文本文件,用于定义 Docker 镜像的构建过程。它以指令的形式描述了如何构建镜像,从基础镜像开始逐步添加配置、文件和依赖,最终形成我们所需
    的头像 发表于 09-30 10:22 2255次阅读

    如何使用dockerfile创建镜像

    如何使用Dockerfile创建镜像,包括Dockerfile的语法和常用指令以及具体操作步骤。 编写Dockerfile
    的头像 发表于 11-23 09:52 516次阅读