Docker 退出信号
1、概述
不论是什么类型的应用,都会希望在服务停止前能够收到停止通知,有一定的时间做退出前的释放资源、关闭连接、不再接收外部请求等工作。比如你的应用正在处理HTTP请求,你希望在停止前能完成所有未完成的请求;如果你的应用正在写入文件,你也许希望在停止容器前能够正确的刷新数据到磁盘并关闭文件。
2、Linux常见信号
要了解应用优雅停止方法,我们先回顾一下与容器相关的Linux常见信号。
信号是一种进程间通信的形式。一个信号就是内核发送给进程的一个消息,告诉进程发生了某种事件。进程需要为自己感兴趣的信号注册处理程序,举例:
- 为了能让程序优雅的退出(接到退出的信号后,对资源进行清理),一般程序都会处理SIGTERM信号。与SIGTERM信号不同,SIGKILL信号会粗暴的结束一个进程;
- 许多守护进程会通过处理SIGHUP信号实现热加载配置文件。
使用 kill -l 命令会显示Linux支持的信号列表。其中编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。
下面对常用的信号进行说明:
- SIGHUP(1)
当用户终端连接结束时,系统会像所有运行中的进程发出这个信号;通常在热加载配置文件时候也会使用该信号。wget命令就注册了SIGHUP(1)信号,这样就算你退出了Linux登录,wget也能继续下载文件。同样的,如Docker/Nginx/LVS等服务也会注册SIGHUP(1)信号,实现服务的热加载配置文件功能。
- SIGINT(2)
程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。
- SIGQUIT(3)
和SIGINT类似,但由QUIT字符(通常是Ctrl+反斜杠)来控制。Nginx就是通过注册这个信号来实现优雅停止服务的。
- SIGKILL(9)
立刻结束程序。该信号不能被阻塞、处理和忽略,不能在程序中被获取到。
- SIGTERM(15)
程序结束(Terminate)信号,又叫请求退出信号,与SIGKILL不同的是该信号可以被阻塞和处理,我们可以通过在程序中注册该信号来实现服务的优雅停止。使用kill命令缺省会发出这个信号。
- SIGCHLD(17)
子进程结束时,一般会向父进程发送这个信号。Nginx是个多进程程序,master进程和worker进程通信就使用的这个信号。
3、Docker容器常见退出码
Exit Code 1
- 程序错误,或者Dockerfile中引用不存在的文件,如 entrypoint中引用了错误的包
- 程序错误可以很简单,例如“除以0”,也可以很复杂,比如空引用或者其他程序 crash
Exit Code 137
- 表明容器收到了SIGKILL信号,进程被杀掉,对应kill -9
- 引发SIGKILL的是docker kill。这可以由用户或由docker守护程序来发起,手动执行:docker kill
- 137 比较常见,如果 pod 中的limit 资源设置较小,会运行内存不足导致OOMKilled,此时state 中的”OOMKilled”值为true,你可以在系统的 dmesg 中看到 oom 日志
Exit Code 139
- 表明容器收到了SIGSEGV信号,无效的内存引用,对应kill -11
- 一般是代码有问题,或者 docker 的基础镜像有问题
Exit Code 143
- 表明容器收到了SIGTERM信号,终端关闭,对应kill -15
- 一般对应docker stop 命令
- 有时docker stop也会导致Exit Code 137。发生在与代码无法处理SIGTERM的情况下,docker进程等待十秒钟然后发出SIGKILL强制退出。
不常用的一些 Exit Code
- Exit Code 126: 权限问题或命令不可执行
- Exit Code 127: Shell脚本中可能出现错字且字符无法识别的情况
- Exit Code 1 或 255:因为很多程序员写异常退出时习惯用 exit(1) 或 exit(-1),-1 会根据转换规则转成 255。这个一般是自定义 code,要看具体逻辑
退出状态码的区间
- 必须在 0-255 之间,0 表示正常退出
- 外界将程序中断退出,状态码在 129-255
- 程序自身异常退出,状态码一般在 1-128
- 假如写代码指定的退出状态码时不在 0-255 之间,例如: exit(-1),这时会自动做一个转换,最终呈现的状态码还是会在 0-255 之间。我们把状态码记为 code,当指定的退出时状态码为负数,那么转换公式如下:256 – (|code| % 256)
对于被信号终止的进程,操作系统会将信号编号加上128,作为进程的退出状态码。这是因为在Unix系统中,信号编号的范围通常是1到31,而进程退出状态码的范围是0到255,为了区分正常的退出状态码和信号终止导致的退出状态码,就将信号编号加上128。
具体来说:
- 如果一个进程接收到了信号编号为9(SIGKILL),那么它的退出状态码就是 128 + 9 = 137。
- 如果一个进程接收到了信号编号为15(SIGTERM),那么它的退出状态码就是 128 + 15 = 143。
这种设计保证了在获取进程退出状态码时,可以区分出是正常退出(0到127范围内)还是信号终止(128到255范围内)。在Docker等容器环境中,这种区分特别重要,因为容器的退出状态码可以帮助用户或管理程序了解容器是如何结束的,从而采取相应的处理措施。
Loading...