正确使用‘trap指令’实现Docker优雅退出

一般应用(比如mariadb)都会有一个退出命令,用户使用类似systemctl stop ****.service方法,停止其服务时,systemd会调用其配置文件注册的退出命令,该命令执行清理资源、退出集群、输出必要日志等操作后才杀死自己的进程;在系统shutdown的时候也会有类似的流程,最大程度的保证应用正常退出,下面我们称之为“进程优雅退出”。

将应用Docker化后,一个突出的问题是,如何让进程优雅的退出,而不是强行杀死进程。Docker stop和Docker kill分别实现了优雅退出和强行退出两个操作:

Docker stop:向容器内1号进程,发送SIGTERM信号,在10S之后(可通过参数指定)再发送SIGKILL信号。

Docker kill:直接发送SIGKILL信号。

显然Docker已经考虑到应用优雅退出的问题,但在实际使用中,会遇到下面2个困难:

1.  只有1号进程才收到SIGTERM信号,但Docker中有很多1号进程为monitor或者为初始化脚本的进程,并不是工作进程,且1号进程不能处理SIGTERM信号,以mariadb为例,容器内的进程关系如下:

1 /bin/sh /usr/bin/mysqld_safe --wsrep-cluster-address=gcomm://
2 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql/ ...

其中2号进程为1号进程的子进程,虽然2号进程可以处理SIGTERM信号但其收不到该信号,而1号进程虽然能收到SIGTERM但并不能处理此信号。

2. 即使1号进程能处理SIGTERM信号,但若其有子进程为外部命令(非build in命令),且子进程为前台阻塞状态,那么1号进程在直到子进程退出前仍然不能收到SIGTERM信号。如下bash是不会处理SIGTERM信号的:

#!/bin/bash
trap ‘exit 0‘ SIGTERM
sleep 10000

关于这一点需要了解进程处理信号的限制:只有当进程阻塞在内建命令时才可以响应SIG信号,否则会一直等待子进程退出后再处理,如上面的bash,要等到10000秒之后才能处理SIGTERM。关于内建命令和外部命令,描述如下:

内部命令实际上是shell程序的一部分,shell不需要创建子进程,比如:exit,history,cd,echo,wait,trap等,linux系统加载运行时shell就被加载并驻留在系统内存中,一般不会fork子进程。
外部命令是linux系统中的实用程序部分,需要时才将其调用内存。一般会fork出子进程。
用type命令可以分辨内部命令与外部命令。

综上所述,对于多进程Docker,我建议在容器中使用自定义bash脚本作为容器入口,脚本中使用后台方式执行具体应用的命令,然后使用内建wait阻塞,并通过trap指令监听SIGTERM,执行应用退出操作,下面以容器化mariadb为例,描述其脚本的大概实现:

#!/bin/bash
trap ‘mysqladmin -uroot -p123456 shutdown‘ SIGTERM
mysqld_safe --wsrep-cluster-address=gcomm://10.158.113.207,10.158.113.80,10.158.113.79 &
wait $!

以上述脚本为入口的maraidb容器内进程关系如下:

mysql        1  0.0  0.0  11628  1352 ?        Ss+  07:59   0:00 /bin/bash /usr/bin/test.sh
mysql        9  0.0  0.0  11764  1636 ?        S+   07:59   0:00 /bin/sh /usr/bin/mysqld_safe --wsrep-cluster-address=gcomm://10.158.113.207,10.158.113.80,10
mysql      188  1.0  3.7 1087368 300168 ?      Sl+  07:59   0:16  \_ /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/p

当执行docker stop ***的时候,该容器会自动调用mysqladmin shutdown优雅退出。

以上为个人原创,欢迎转发,并保留出处。

个人能力有限,错误之处请留言指出。

时间: 2024-08-05 19:36:04

正确使用‘trap指令’实现Docker优雅退出的相关文章

#每日Linux小练习#09 trap指令

在有些情况下,我们不希望自己的shell脚本在运行时刻被中断,比如说我们写得shell脚本设为某一用户的默认shell,使这一用户进入系统后只能作某一项工作,如数据库备份, 我们可不希望用户使用ctrl+C之类便进入到shell状态,做我们不希望做的事情.这便用到了信号处理. trap命令用来指定shell需要捕捉哪些Linux信号,以及如何处理这些信号.格式如下: trap commands signals 不同的signal之间用空格隔开,commands表示如何处理signals. ech

最优雅退出 Android 应用程序的 6 种方式

一.容器式 建立一个全局容器,把所有的Activity存储起来,退出时循环遍历finish所有Activity import java.util.ArrayList;    import java.util.List; import android.app.Activity;    import android.os.Bundle; public class BaseActivity extends Activity {        @Override        protected voi

Docker实现退出container后保持继续运行的解决办法

现象: 运行一个image,例如ubuntu14.04: 1 docker run -it --rm ubuntu:14.04 bash 退出时: 执行Ctrl+D或者执行exit 查看线程: 1 docker ps 发现为空,说明没有正在运行的容器. 解决方法: 运行一个image: docker run -dit ubuntu:14.04 查看线程: 发现有一个线程,打开它: docker attach [CONTAINER ID或NAMES] 退出时如果想继续运行:按顺序按[ctrl+p]

Dokcer入门及其Docker file的制作指令

Docker借鉴了kvm中应用镜像的方式,使得Docker的出现方便了容器的实现和使用,从此docker就占据了容器的市场. docker:引擎:创建.运行容器,采用C/S架构 客户端:docker 服务端:dockerd,dockerd负责接收docker客户端的请求,客户端发送指令,dockerd通过镜像仓库,把镜像拖到本地,执行运行容器 容器是基于镜像启动的,如果本地没有镜像,dockerd会去远程拉取镜像. IT行业的部署异构化程度越来越大.不同的开发平台,不同的运行平台,各个服务不同的

Docker的使用初探(一):常用指令说明

目录 Docker的使用初探(一):常用指令说明 为什么要用Docker Docker的安装与简单使用 国内镜像加速 常用指令 Docker的使用初探(一):常用指令说明 前几个星期实践的了,再不记录一下真的就忘干净了 Docker即容器技术,具体的介绍已经有很多,不打算赘述了,说一些优点 为什么要用Docker 对我个人来说,最大的优点就是在一台电脑上可以部署不同的环境而不用担心它们产生冲突,最常见的冲突就是端口占用,使用Docker技术可以很方便地规避这一问题,而且便于管理,我不用在本地处理

Docker基本命令与使用 —— Dockerfile指令与构建(三)

一.Dockerfile指令上 1.指令格式 # Comment 注释, 以#开头 INSTRUCTION argument 以大写的指令+参数 #First Dockerfile 注释 FROM ubuntu:14.04 MAINTAINER dormancypress "[email protected]" RUN apt-get update RUN apt-get install -y nginx EXPOSE 80 From MAINTAINER RUN EXPOSE FRO

26. 后台程序如何优雅的退出

一. 前言 项目初期我们可以使用kill -9 pid的方法来杀死后台服务进程,然后重新部署. 但是随着时间发展,这种简单粗暴的方法会有一些问题: 如何在退出时清理一些资源? 如果某个请求执行操作到一半,直接被退出了,就会造成脏数据. 如何给客户端正确的反馈? 二. Java虚拟机退出钩子 虚拟机允许在退出的时候执行钩子程序,那么我们可以在这个方法里面作一些释放资源的操作,如下: System.out.println("add hook..."); Thread shutdownThr

从nsq中学习如何优雅的退出go 网络程序

退出运行中的程序,可以粗暴的kill -9 $PID,但这样会破坏业务的完整性,有可能一个正在在执行的逻辑半途而费,从而产生不正常的垃圾数据. 本文总结在go语言中,如何能优雅的退出网络应用,涉及的知识包括:signal,channel,WaitGroup等. 从这里:https://gobyexample.com/channel-synchronization 可以简单了解到,在go中如何使用channel实现goroutines同步. 在nsq中,也使用了相同的机制,不过封装更复杂了些.我们

docker基础——关于安装、常用指令以及镜像制作初体验

为什么使用docker docker就是一个轻量级的虚拟机,他解决的是服务迁移部署的时候环境配置问题.比如常见的web服务依赖于jdk.Tomcat.数据库等工具,迁移项目就需要在新的机器重新配置这些,不光麻烦,而且可能配错. 如果能够将整个服务连同他依赖的外部环境一同打包就好了,docker就是这么干的.他将配置好的软件打包成image,在新的机器里面启动这个image即可 当然我所理解的只是一小部分,docker还有提供弹性云服务.组建微服务架构等方面的应用 最后,相对于传统虚拟机,dock