技巧42 清理容器
Docker新手经常抱怨的一点便是,在短时间内,用户可能在系统上残留许多不同状态的容器,而且没有一个标准工具通过命令行管理这些容器。
问题
想要清理系统上的残留容器。
解决方案
设置一个别名来执行清理旧容器的命令。这里最简单的办法是删除所有容器。显然,这是一个有风险的方案,只应在确定这是预期行为的时候使用。下列命令将会删除宿主机上的所有容器。
$ docker ps -a -q | \ ⇽--- 获取所有容器ID的列表,包括正在运行的以及已停止的,然后将它们传给……
xargs --no-run-if-empty docker rm -f ⇽--- ……docker rm -f命令,被传入的任意容器将会被删除,即使它们还处于运行状态
简单介绍一下 xargs
命令,它会获取输入的每一行内容,并将它们全部作为参数传递给后续命令。为了防止报错,我们这里传入了一个额外参数 --no-run-if-empty
,这可以避免在前面的命令完全没有输出的情况下执行该命令。
如果有正在运行的容器想要保留,但是又想删除所有已经退出的容器,那么不妨过滤一下 docker ps
命令返回的条目:
docker ps -a -q --filter status=exited | \ ⇽--- --filter标志会告知docker ps命令想要返回的容器。在这种情况下限制成状态为已经退出的那些容器。也可以选择处于正在运行中或者正在重启状态的容器
xargs --no-run-if-empty docker rm ⇽--- 这次不用再强行删除容器,因为根据给定的过滤参数,它们本身就不应该处于运行状态
事实上,删掉所有已停止的容器是一个很常见的用例,为此Docker专门添加了一条命令: docker container prune
。然而,这条命令仅限于该用例,要进行任何更复杂的操作,仍然需要回过头来参考本技巧里介绍的命令。
作为更高级用例的示范,下列命令将会列出所有返回非零错误码的容器。如果系统上有许多容器,用户想要自动检查并删除那些异常退出的任意容器,就可能需要这样做:
comm -3 \ ⇽--- 执行comm命令来比较两个文件内容的差异。加上-3 参数将不会显示同时出现在两个文件里的行内容(这些容器的退出码都是0),然后输出其他不同的部分
<(docker ps -a -q --filter=status=exited | sort) \ ⇽--- 找出退出的容器 ID,给它们排序,然后以文件形式传给comm
<(docker ps -a -q --filter=exited=0 | sort) | \ ⇽--- 找出退出码为0的容器,给它们排序,然后以文件形式传给comm
xargs --no-run-if-empty docker inspect > error_containers ⇽--- 对非0退出码(comm命令管道的输出)的容器执行docker inspect,并将输出结果保存到error_containers文件中
提示
也许你还没看到过这种用法,bash里的 <(command)
语法被称为进程替换。它允许把一个命令的输出结果作为文件,传给其他命令,这在无法使用管道输出的时候非常有用。
上述示例相对比较复杂,但是它展示了将不同的工具命令组合在一起的威力。它会输出所有已停止的容器的ID,然后挑出那些非0退出码的容器(即那些以异常方式退出的容器)。如果读者还在努力理解这个用法,不妨先单独执行每条命令,然后理解它们的含义,这样有助于了解整个过程。
像这样的命令可以用来在生产环境里采集容器信息。用户可能想要对它做些调整,改为执行一个cron定时任务来清除正常退出的容器。
将单行代码包装成命令
可以给命令设置别名,以便在登录到宿主机后更容易操作。为了达成这一点,需要在~/.bashrc文件里添加如下代码:
alias dockernuke='docker ps -a -q | \
xargs --no-run-if-empty docker rm -f'
然后,在下一次登录时,从命令行执行 dockernuke
,将删除在系统上找到的任何Docker容器。
我们发现这样做节省的时间是相当可观的。但是要小心!这种方式同样也非常容易误删生产环境的容器,我们可以证明。即使足够小心,不去删除正在运行的容器,仍然可能会误删那些没有运行但仍然有用的纯数据容器。
讨论
本书介绍到的许多技巧的最终目的都是创建容器,尤其是在技巧76介绍到的Docker Compose以及有关编排的章节里——毕竟,编排都是关于如何管理多个容器的。用户也许会发现这里讨论到的命令用于清理机器(本地或远程)很有价值,在完成每个技巧后可以获得一个全新的环境。