在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言 在Docker大行其道的今天,我们能够非常方便的使用容器打包我们的应用程序,并且将它在我们的服务器上部署并运行起来。但是,谈论到如何停掉运行中的docker容器并正确的终止其中的程序,这就成为一个非常值得讨论的话题了。 事实上,在我们日常的项目当中,这是我们经常需要面对和处理的问题: 场景A:假如我们打包在容器中的程序,提供HTTP方式的服务,负责处理各种HTTP requests并返回结果,我们必然希望在容器被停掉的时候,能够让程序有时间把已经在处理中的请求继续处理完毕,并返回结果给客户端。 场景B:又比如我们打包在容器中的程序,负责写入数据到某个数据文件中,我们希望程序能够在容器被停掉的时候,有时间把内存中缓存的数据持久化到存储设备中,以防数据丢失。 场景C:再比如现在流行的微服务架构中,一般会有服务发现的机制,也即每一个微服务在启动之后,都会主动把自己的地址信息注册到服务发现模块当中,让其他的服务可以知道自己的存在。而在容器被停掉的时候,微服务需要即时从服务发现模块中注销自己,以防止从API Gateway而来的请求被错误的路由到了已经被停止掉的微服务。 如上的各种场景中,都要求打包在容器中的应用程序能够被优雅的终止(也即gracefully shutdown),这种gracefully shutdown的方式,允许程序在容器被停止的时候,有一定时间做一些后续处理操作,这也是我们需要进一步探讨的话题。 docker stop 与 docker kill 的区别 Docker本身提供了两种终止容器运行的方式,即 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 --time=20 container_name docker kill 接着我们来看看 → 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 stop命令,更类似于Linux系统中的kill命令,二者都是发送系统信号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 [“/graceful”] 使用 话题转回来,我们开始执行容器构建操作,打包程序: 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... 接着我们要使用 λ Timothy [workspace/src/graceful] → docker stop graceful graceful 最后,查看容器的日志,检验输出: λ Timothy [workspace/src/graceful] → docker logs graceful Started... SIGTERM received! Exiting... 总结 以上就是这篇文章的全部内容了,用docker kill命令,可以简单粗暴的终止docker容器中运行的程序,但是想要优雅的终止掉的话,我们需要使用docker stop命令,并且在程序中多花一些功夫来处理系统信号,这样能保证程序不被粗暴的终止掉,从而实现gracefully shutdown。希望本文的内容对大家的学习或者工作能有所帮助,如果有疑问大家可以留言交流。 |
请发表评论