前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Docker stop或者Docker kill为何不能停止容器

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

作者头像
Criss@陈磊
发布2019-08-02 10:54:11
3.6K0
发布2019-08-02 10:54:11
举报
文章被收录于专栏:测试技术圈测试技术圈

背景

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

代码语言:javascript
复制
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信号的捕获和处理:

代码语言:javascript
复制
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系统也救不了它。只有重启系统才能清除。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 质问 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Docker主要执行流程
    • 一、Docker Stop主要流程
      • 二、Docker Kill主要流程
        • Docker stop中存在的问题
          • Docker kill为何会阻塞
            • 一、容器主/子进程处于D状态
          • 二、问题解释
            • 三、在docker pidnamespace共享特性下容器对信号的响应
            • 总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档