专栏首页测试技术圈Docker stop或者Docker kill为何不能停止容器

Docker stop或者Docker kill为何不能停止容器

背景

我们内部压力(cpu 80%,内存90%)通过stress (做页面压力测试)在容器内部做测试中,发现某几个时候通过

docker stop $containerid

docker cli退出后,短暂时间内docker ps查看到容器依然在运行状态。但是很快docker ps查看容器或者ps查看容器主进程pid就可以确认容器推出了。我们需要解释一下Docker stop发生了什么

Docker主要执行流程

一、Docker Stop主要流程

1.Docker 通过containerd向容器主进程发送SIGTERM信号后等待一段时间后,如果从containerd收到了容器退出消息那么容器退出成功。

2、在上一步中,如果等待超时,那么Docker将使用Docker kill 方式试图终止容器

二、Docker Kill主要流程

1.Docker引擎通过containerd使用SIGKILL发向容器主进程,等待一段时间后,如果从containerd收到容器退出消息,那么容器Kill成功

2.在上一步中如果等待超时,Docker引擎将跳过Containerd自己亲自动手通过kill系统调用向容器主进程发送SIGKILL信号。如果此时kill系统调用返回主进程不存在,那么Docker kill成功。否则引擎将一直死等到containerd通过引擎,容器退出。

Docker stop中存在的问题

在上文中我们看到Docker stop首先间接向容器主进程发送sigterm信号试图通知容器主进程优雅退出。但是容器主进程如果没有显示处理sigterm信号的话,那么容器主进程对此过程会不会有任何反应,此信号被忽略了 这里和常规认识不同,在常规想法中任何进程的默认sigterm处理应该是退出。但是namespace中pid==1的进程,sigterm默认动作是忽略。也即是容器首进程如果不处理sigterm,那么此信号默认会被忽略,这就是很多时候Docker Stop不能立即优雅关闭容器的原因——因为容器主进程根本没有处理SIGTERM

特别指出linux上全局范围内pid=1的进程,不能被sigterm、sigkill、sigint终止 进程组首进程退出后,子进程收到sighub

在bash shell里可以通过trap命令捕获发往shell的信号,如果docker的主进程是shell进程的话,可以通过trap命令实现SIGTERM信号的捕获和处理:

term_func(){      echo “receiving SIGTERM”      kill -s SIGTERM  $1}pid=trap “ term_func $pid” TERMYour command & pid = $!wait

wait 命令的意思是等待所有子进程退出。放在这里是因为,trap命令只能等前台运行的命令结束后才能处理信号,但是wait命令会在收到信号后立即退出,所以将命令后台化以后加wait,可以保证脚本对信号的即时响应。关于shell里通过trap命令处理信号的详细使用方式见《shell trap信号处理》《Sending and Trapping Signals》

Docker kill为何会阻塞

一、容器主/子进程处于D状态

进程D状态表示进程处于不可中断睡眠状态,一般都是在等待IO资源。当然有些时候如果系统IO出现问题,那么将有大量的进程处于D状态。在这种状态,信号是无法将进程唤醒;只有等待进程自己从D状态中返回。而且在常规内核中,如果某个进程一直处于D状态,那么理论上除了重启系统那么没有什么方法或手段将它从D中接回。

从上面解释Docker kill第二步中可以看到一旦容器中主进程或者子进程处于D状态,那么Docker将等待,一直等到所有容器主进程和其子进程都退出后才返回,那么此时Docker kill就卡住了。

二、问题解释

当出现问题时刻,宿主机上发现大量的stress进程(实际是容器的进程)处于D状态,而系统响应变慢。问题可以这样解释:

1.Docker kill通过containerd间接向容器主进程发送SIGKill信号以后,由于系统响应慢,容器内部子进程(stress)处于D状态,那么在超时时间内containerd没有上报容器退出。Docker kill走到了直接发送Sigkill阶段

2.在此阶段前,容器内部主进程退出了,所以系统调用kill 发送SIGKILL很快就返回进程不存在了。引擎认为自己把容器杀死了,Docker kill成功返回了。

3.在一定时间后容器子进程从D状态中恢复,它们退出了,containerd上报容器退出,引擎清理资源,此时Docker ps看到容器才是退出状态

三、在docker pidnamespace共享特性下容器对信号的响应

在k8s的pod下常见的场景,pause容器和其他容器共享pid namespace(pause容器pidnamespace共享给相同pod下其他容器使用)。pause容器退出后,其他容器也会退出(pause容器如果收到SIGTERM并退出了,那么其他容器也会退出);直接给其他容器发送SIGTERM信号,pause容器不会收到SIGTERM。

总结

  • 容器主进程最好需要自己处理SIGTERM信号,因为这是你优雅退出的机会。如果你不处理,那么在Docker stop里你会收到Kill,你未保存的数据就会直接丢失掉。
  • Docker stop和Docker kill返回并不意味着容器真正退出成功了,必须通过docker ps查看。
  • 对于通过restful与docker 引擎链接的客户端,需要在docker stop和kill restful请求链接上加上超时。对于docker cli用户,需要有另外的机制监控Docker stop或Docker kill命令超时卡死
  • 处于D状态一致卡死的进程,内核无法杀死,docker系统也救不了它。只有重启系统才能清除。

本文分享自微信公众号 - V社 北京社(SoftwareTesters)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Docker入门

    上一节中,我们了解到了Docker 的一些基本知识点,它的一些核心概念,Docker的使用安装等。此篇文章我们对 Docker 进行入门讲解

    Criss@陈磊
  • 10个小技巧提高 Kubernetes 容器效率

    近年来,容器以及 Kubernetes 成为开发者以及企业用户重点关注的技术趋势,本文总结了构建和管理容器的十个重要技巧来优化 IT 成本并提高效率。

    Criss@陈磊
  • DockerFile 详解

    Dockerfile由一行行命令语句组成,并且支持用“#”开头作为注释,一般的,Dockerfile分为四部分:基础镜像信息,维护者信息,镜像操作指令和容器启动...

    Criss@陈磊
  • 微服务架构之「 容器技术 」

    现在一聊到容器技术,大家就默认是指 Docker 了。但事实上,在 Docker 出现之前,PaaS社区早就有容器技术了,以 Cloud Foundry、Ope...

    奎哥
  • 下一代容器架构已出,Docker何去何处?看看这里的6问6答!!

    我猜很多人一看这个标题已经感觉很懵逼了,什么?下一代容器都出来了,我还没学Docker呢!!!

    云爬虫技术研究笔记
  • 微服务架构之「 容器技术 」

    因为传统的PaaS技术虽然也可以一键将本地应用部署到云上,并且也是采用隔离环境(容器)的形式去部署,但是其兼容性非常的不好。因为其主要原理就是将本地应用程序和启...

    黄泽杰
  • Docker操作实践(1):容器的本质是什么?容器从何而来?

    容器本质上是一种进程隔离的技术。容器为进程提供了一个隔离的环境,容器内的进程无法访问容器外的进程。

    嘉为科技
  • 3分钟快速理解什么是Docker容器

    在容器技术没有出来之前,开发运维在进行交付的时候经常出现,开发环境执行正常,生产环境部署运行出现问题,很多原因就是因为环境导致。还有部署环境太过于复杂,交付成本...

    Java团长
  • Docker入门

    上一节中,我们了解到了Docker 的一些基本知识点,它的一些核心概念,Docker的使用安装等。此篇文章我们对 Docker 进行入门讲解

    cxuan
  • Docker入门

    上面我们使用了 docker info 指令,该命令会返回所有容器和镜像的数量、Docker 使用的执行驱动和存储驱动,以及Docker 的基本配置

    JAVA葵花宝典

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动