停止与删除容器

用docker stop停止名为container1的容器:

  1. [root@localhost ~]# docker stop container1

也可以用docker kill来杀死容器达到停止容器的目的:

  1. [root@localhost ~]# docker kill container1

当容器停止之后,可以使用docker rm删除容器:

  1. [root@localhost ~]# docker rm container1

当然,使用docker rm -f 强制删除容器也是可以的:

  1. [root@localhost ~]# docker rm -f container1

注意事项

  • 禁止使用docker rm -f XXX 删除容器。如果使用强制删除,docker rm会忽略过程中的错误,可能导致容器相关元数据残留。如果使用普通删除,如果删除过程出错,则会删除失败,不会导致元数据残留。
  • 避免使用docker kill命令。docker kill命令发送相关信号给容器内业务进程,依赖于容器内业务进程对信号的处理策略,可能导致业务进程的信号处理行为与指令的预期不符合的情况。
  • docker stop处于restarting状态的容器可能容器不会马上停止。如果一个容器使用了重启规则,当容器处于restarting状态时,docker stop这个容器时有很低的概率会立即返回,容器仍然会在重启规则的作用下再次启动。
  • 不能用docker restart重启加了—rm参数的容器。加了—rm参数的容器在退出时,容器会主动删除,如果重启一个加了—rm的参数的容器, 可能会导致一些异常情况,比如启动容器时,同时加了—rm与-ti参数,对容器执行restart操作,可能会概率性卡住无法退出。

docker stop/restart 指定t参数且t<0时,请确保自己容器的应用会处理stop信号

Stop的原理:(Restart会调用Stop流程)

  1. Stop会首先给容器发送Stop 信号(15)
  2. 然后等待一定的时间(这个时间就是用户输入的 t)
  3. 过了一定时间,如果容器还活着,那么就发送kill信号(9)强杀

输入参数t(单位s)的含义:

  • t<0 : 表示死等,不管多久都等待程序优雅退出,既然用户这么输入了,表示对自己的应用比较放心,认为自己的程序有合理的stop信号的处理机制
  • t=0 : 表示不等,立即发送kill -9 到容器
  • t>0 : 表示等一定的时间,如果容器还未退出,就发送kill -9 到容器

所以如果用户使用t<0 (比如t=-1),请确保自己容器的应用会正确处理signal 15,如果容器忽略了该信号,会导致docker stop一直卡住。

如果容器处于Dead状态,可能底层文件系统处于busy状态,需要手动删除

Docker在执行容器删除时,先停止容器的相关进程,之后将容器状态更改为Dead,最后执行容器rootfs的删除操作。当文件系统或者device mapper处于忙碌状态时,最后一步rootfs的删除会失败。docker ps -a查看会发现容器处于Dead状态。Dead状态的容器不能再次启动,需要等待文件系统不繁忙时,手动再次执行docker rm进行删除。

共享pid namespace容器,子容器处于pause状态会使得父容器stop卡住,并影响docker run命令执行

使用—pid参数创建共享pid namespace的父子容器,在执行docker stop父容器时,如果子容器中有进程无法退出(比如处于D状态、pause状态),会产生父容器docker stop命令等待的情况,需要手动恢复这些进程,才能正常执行命令。

遇到该问题的时候,请对pause状态的容器使用docker inspect 命令查询 PidMode对应的父容器是否为需要docker stop的容器。如果是该容器,请使用docker unpause将子容器解除pause状态,指令即可继续执行。

一般来说,导致该类问题的可能原因是容器对应的pid namespace由于进程残留导致无法被销毁。如果上述方法无法解决问题,可以通过借助linux工具,获取容器内残留进程,确定pid namespace中进程无法退出的原因,解决后容器就可以退出:

  • 获取容器pid namespace id

    1. docker inspect --format={{.State.Pid}} CONTAINERID | awk '{print "/proc/"$1"/ns/pid"}' |xargs readlink
  • 获取该namespace下的线程

    1. ls -l /proc/*/task/*/ns/pid |grep -F PIDNAMESPACE_ID |awk '{print $9}' |awk -F \/ '{print $5}'