docker容器如何优雅的终止详解
作者:daisy 发布时间:2023-04-22 06:00:41
前言
在Docker大行其道的今天,我们能够非常方便的使用容器打包我们的应用程序,并且将它在我们的服务器上部署并运行起来。但是,谈论到如何停掉运行中的docker容器并正确的终止其中的程序,这就成为一个非常值得讨论的话题了。
事实上,在我们日常的项目当中,这是我们经常需要面对和处理的问题:
场景A:假如我们打包在容器中的程序,提供HTTP方式的服务,负责处理各种HTTP requests并返回结果,我们必然希望在容器被停掉的时候,能够让程序有时间把已经在处理中的请求继续处理完毕,并返回结果给客户端。
场景B:又比如我们打包在容器中的程序,负责写入数据到某个数据文件中,我们希望程序能够在容器被停掉的时候,有时间把内存中缓存的数据持久化到存储设备中,以防数据丢失。
场景C:再比如现在流行的微服务架构中,一般会有服务发现的机制,也即每一个微服务在启动之后,都会主动把自己的地址信息注册到服务发现模块当中,让其他的服务可以知道自己的存在。而在容器被停掉的时候,微服务需要即时从服务发现模块中注销自己,以防止从API Gateway而来的请求被错误的路由到了已经被停止掉的微服务。
如上的各种场景中,都要求打包在容器中的应用程序能够被优雅的终止(也即gracefully shutdown),这种gracefully shutdown的方式,允许程序在容器被停止的时候,有一定时间做一些后续处理操作,这也是我们需要进一步探讨的话题。
docker stop 与 docker kill 的区别
Docker本身提供了两种终止容器运行的方式,即docker stop
与docker kill
。
docker stop
先来说说docker stop
吧,当我们用docker stop
命令来停掉容器的时候,docker默认会允许容器中的应用程序有10秒的时间用以终止运行。所以我们查看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
命令执行的时候,会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的10秒,会继续发送SIGKILL的系统信号强行kill掉进程。在容器中的应用程序,可以选择忽略和不处理SIGTERM信号,不过一旦达到超时时间,程序就会被系统强行kill掉,因为SIGKILL信号是直接发往系统内核的,应用程序没有机会去处理它。在使用docker stop
命令的时候,我们唯一能控制的是超时时间,比如设置为20秒超时:
docker stop --time=20 container_name
docker kill
接着我们来看看docker kill
命令,默认情况下,docker kill
命令不会给容器中的应用程序有任何gracefully shutdown的机会。它会直接发出SIGKILL的系统信号,以强行终止容器中程序的运行。通过查看docker kill
命令的帮助,我们可以看到,除了默认发送SIGKILL信号外,还允许我们发送一些自定义的系统信号:
→ 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中的程序发送SIGINT信号,我们可以这样来实现:
docker kill --signal=SIGINT container_name
与docker stop命令不一样的地方在于,docker kill
没有任何的超时时间设置,它会直接发送SIGKILL信号,以及用户通过signal参数指定的其他信号。
其实不难看出,docker stop命令,更类似于Linux系统中的kill命令,二者都是发送系统信号SIGTERM。而docker kill
命令,更像是Linux系统中的kill -9或者是kill -SIGKILL命令,用来发送SIGKILL信号,强行终止进程。
在程序中接收并处理信号
了解了docker stop
与docker kill
的区别,我们能够知道,docker kill
适合用来强行终止程序并实现快速停止容器。而如果希望程序能够gracefully shutdown的话,docker stop
才是不二之选。这样,我们可以让程序在接收到SIGTERM信号后,有一定的时间处理、保存程序执行现场,优雅的退出程序。
接下来我们可以写一个简单的Go程序来实现信号的接收与处理,程序在启动过后,会一直阻塞并监听系统信号,直到监测到对应的系统信号后,输出控制台并退出执行。
// main.go
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
fmt.Println("Program started...")
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
s := <-ch
if s == syscall.SIGTERM {
fmt.Println("SIGTERM received!")
//Do something...
}
fmt.Println("Exiting...")
}
接下来使用交叉编译的方式来编译程序,让程序可以在Linux下运行:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o graceful
编译好之后,我们还需要打包程序到容器中运行。于是,我们还得有个Dockerfile。在这里,我们选择使用体积小又轻盈的alpine镜像作为基础镜像,打包这个Go程序:
from alpine:latest
MAINTAINER Timothy
ADD graceful /graceful
CMD ["/graceful"]
这里需要避开的一个坑,是Dockerfile中CMD命令的用法。
CMD命令有两种方式:
CMD /graceful
使用 CMD command param1 param2
这种方式,其实是以shell的方式运行程序。最终程序被执行时,类似于/bin/sh -c的方式运行了我们的程序,这样会导致/bin/sh以PID为1的进程运行,而我们的程序只不过是它fork/execs出来的子进程而已。前面我们提到过docker stop的SIGTERM信号只是发送给容器中PID为1的进程,而这样,我们的程序就没法接收和处理到信号了。
CMD [“/graceful”]
使用 CMD [“executable”,”param1”,”param2”]
这种方式启动程序,才是我们想要的,这种方式执行和启动时,我们的程序会被直接启动执行,而不是以shell的方式,这样我们的程序就能以PID=1
的方式开始执行了。
话题转回来,我们开始执行容器构建操作,打包程序:
docker build -t registry.xiaozhou.net/graceful:latest .
打包过后的镜像,才6MB左右:
λ Timothy [workspace/src/graceful] → docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.xiaozhou.net/graceful latest b2210a85ca55 20 hours ago 6.484 MB
启动并运行容器:
λ Timothy [workspace/src/graceful] → docker run -d --name graceful b2210a85
查看容器运行状态:
λ Timothy [workspace/src/graceful] → docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd18eedafd16 b221 "/graceful" 3 seconds ago Up 2 seconds graceful
查看容器输出,能看到程序已经正常启动:
λ Timothy [workspace/src/graceful] → docker logs graceful
Started...
接着我们要使用docker stop
* ,看程序能否响应SIGTERM信号:
λ Timothy [workspace/src/graceful] → docker stop graceful
graceful
最后,查看容器的日志,检验输出:
λ Timothy [workspace/src/graceful] → docker logs graceful
Started...
SIGTERM received!
Exiting...
总结


猜你喜欢
- 下午15时左右,百度再次改版百度指数(index.baidu.com)功能和布局。新布局更加直观,便于查看相关检索词数据与相关新闻内容。此外
- 虚拟服务器:对外它是单一的入口,对内有很多台计算机为它服务.对使用它的人来说,它是一台机器,有单一的入口点.具体的实现技术包括两种: 应用层
- 其实这个也没有什么可以改的,因为以前的也能用,不过局限性太小,现在,在代码里面用一个函数来获取表单所有数据来构造URL,避免一些不必要的麻烦
- 稳定使用多年的UCHOME最近经常出现问题,今天的怪事则是无论普通用户登录还是管理员登录管理,均显示登录成功,而后又自动跳转到重新登录页面。
- 在建设本网站的时候,发现新建了很多的网页,突然发现,每个文件都需要进行修改一样的内容,一个一个打开很是麻烦,所以,总结了一下如何快速修改一个
- Linux服务器误删了/usr/bin和/usr/sbin目录,然后,从别的地方拷贝这两个目录过来,发现可能很多是链接等非普通文件,可拷贝的
- 就是这个表情框。我的解决办法是,首先禁止弹出表情选择框,然后替换默认表情为“题”字。效果如图演示网站http://www.cmbro.com
- 雅虎大刀阔斧的改革还在继续。雅虎对外宣称,将在美国东部时间10月26日关闭在该公司旗下的GeoCities网络托管服务。这也标志着个人主页时
- 进入:控制面板 - 卸载程序 - 打开或关闭Windows功能如果访问任何不存在页面或页面出错时空白
- 10月16日消息昨日下午,微软在北京发布其旗下最新品牌windowsphone,酷6网出现在微软全新手机操作系统windowsmobile6
- 下面我们以大家常用的sina邮箱为例说明如何进行UCenter Home的邮件设置。一、UCenter Home后台设置1、首先要保证您已经
- 老网民们大概都记得,刚开始上网的时候,是不存在验证码(capcha)这么一种东西的。这造成的结果是,垃圾评论和垃圾邮件可以轻松通过任何一个网
- 访问iis时候出现一下错误服务器应用程序不可用 您试图在此 Web 服务器上访问的 Web 应用程序
- 如果您已经安装了IIS,支持了asp和.net,也许你还希望在iis上安装PHP+Zend+Mysql+phpMyAdmin,难道要一个软件
- shell中的curl网络请求的实现curl 是利用URL语法在命令行下工作的文件传输工具,1997年首次发行,支持文件上传和下载,结合sh
- 首次在站长站上写东西,就随便扯点吧,希望各位站长多指教,多交流!为什么我会把许三多与个人网站扯在一起呢?相信看过这部片子的人,应该都记得连长
- 一、系统配置1、关闭sudo密码为了避免每次使用sudo命令时都输入密码,我们可以将密码关闭。操作方法:1、终端输⼊命令 sudo visu
- 今天在检查博客附件的时候发现有以前上传的一个注册表导入脚本reg文件丢了,重新上传的时候居然报错了。仔细看了才发现是之前安装的Waterma
- 关于网站用户粘性的问题,谈及的文章已经很多了。结合自己的经验和教训,总有不吐不快的感觉,因此也就来说说这个问题。请各位同仁斧正。先谈用户。任
- 全新推出的Discuz! 7.2版本从用户注册登陆应用、社区论坛前后管理两方面着眼,增加和改进了30多处与社区门户运营密切的功能与细节,实现