原创不易,未经允许,请勿转载。
在Linux系统运行一个程序时,如果需要关闭它,一般有两种方式。如果该程序是以非守护进程的方式运行的话,我们可以通过CTRL+C
发送SIGINT信号,默认进程会结束。如果是守护进程方式运行的话,可以通过kill命令发送停止信号,例如kill -2 ${pid}
。-2
信号表示中断当前进程,跟CTRL+C
等效。
总之要关闭一个进程,我们需要向它发送一个信号,进程在收到对应信号后会做出对应的相应动作,例如终止进程。
使用过docker的同学应该知道,当我们执行docker stop xxx
命令时,会停止对应的容器。那么docker在停止容器时,是怎么关闭容器中运行的进程呢?
根据不同业务场景的需要,有些程序我们需要再关闭前进行清理缓存,保存数据等操作。要进行这些操作的前提就是我们程序可以处理系统信号,然后做出相应操作。但有些信号的默认行为是无法被更该的,例如kill -9 ${pid}
。使用-9
信号会强制杀死进程,不会给业务程序任何反应的时间。
那么在docker中,当停止容器时,docker内部会发送什么信号给进程,又有多少时间供进程处理善后操作呢?
我们可以写一个代码验证一下,以go代码为例
初始化go mod
go mod init docker-stop-demo
新建一个main.go文件,代码内容如下。
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
// 创建一个接受信号的通道
sigs := make(chan os.Signal, 1)
// 这里不指定具体的信号类型的话,表示监听所有信号
signal.Notify(sigs)
// 程序会阻塞在此处,直到接收到信号
sig := <-sigs
fmt.Println("接收到信号:", sig)
i := 0
// 每个一秒打印一次,最后通过查看日志判断持续了多久
for {
fmt.Println(i)
i++
time.Sleep(time.Second)
}
}
编写Dockerfile用于构建镜像
FROM golang:1.19-alpine AS builder
LABEL stage=gobuilder
WORKDIR /app
ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
COPY . .
RUN go build -ldflags="-s -w" -o docker-stop-demo ./main.go
CMD ["./docker-stop-demo"]
构建镜像
docker build -t docker-stop-demo:0.0.1 .
构建成功之后,运行docker 容器
docker run -id --name docker-stop-demo docker-stop-demo:0.0.1
接着执行docker stop
命令,关闭该容器。执行完命令后,我们可以观察到卡了一会后,命令才执行结束。
docker stop docker-stop-demo
我们查看下容器打印了什么日志出来。可以看到接收到一个信号为15的terminated信号。后面紧跟着十个数字,说明docker容器给了十秒的缓存时间,让我们的程序完成最后的事项。
docker logs docker-stop-demo
接收到信号: terminated
0
1
2
3
4
5
6
7
8
9
经过上面的实验我们可以得出,当执行docker stop
命令后,docker内部会向我们程序发送信号15,即中断信号,并给了十秒的缓冲时间。
博客主页:http://xiaojujiang.cn/