调试Docker容器的5个简单技巧
有时候,Docker容器是一个黑盒子。无论你是建立底层镜像还是使用公共镜像,一个跳动的容器都是令人沮丧的。由于容器的执行方式以及它们处理日志的方式,弄清楚发生了什么事可能很困难。
在这篇文章中,我们将探讨一些基本的命令和参数,你可以用来对特别挑剔的容器进行故障诊断。如果容器无法启动,间歇性地爆炸,或者你只是想更多地了解图像的细节,这些简单的选项是真正的游戏改变者。
1.日志记录和时间戳
第一个也是最简单的例子是使用Docker已经提供的日志工具。大多数人已经知道如何查看一个容器内的日志。
docker logs <container_id>
但是,如果这个特定的容器已经运行了很长时间,并且有一个德克萨斯那么大的日志,怎么办?在这样的情况下,你可以简单地添加额外的--tail 参数。
docker logs --tail 10 <container_id>
使用--tail 选项,你可以只看到日志中最后的n 行。输入你想看的行数可以让你直接跳到最相关和最近的信息。
如果你在容器内的日志输出默认不包含时间戳,你也可以添加这些。Docker允许你将-t 标志传递给log ,这将在每一行前加上一个时间戳。
docker logs -t <container_id>
这些选项也可以结合起来,形成一个精确的故障排除工具。现在你可以准确地知道事情发生的时间,而不需要改变容器内的任何东西。
2.以root身份执行命令
如果你使用的是以默认root用户身份运行的镜像,那么这就不是一个问题。当你不以root身份运行而使用非特权用户时,这是一个伟大的故障排除工具。
如果你运行。
docker exec -it <container_id> /bin/sh
这将总是以底层镜像中定义的用户身份运行。如果这个用户没有root权限,那么试图进入一个正在运行的容器来排除故障可能会很困难(尤其是当你需要安装任何东西的时候)。
如果你想以根用户的身份进入容器,你所要做的就是通过以下内容来代替。
docker exec -u 0 -it <container_id> /bin/sh
这将告诉Docker使用ID为0 的用户。这就是root。现在,当你进入容器时,你就可以用全权限进行调试了。
3.将容器作为一个镜像提交
这是一个经常被忽视的Docker的功能。你实际上可以从一个现有的容器创建一个新的镜像。这意味着,如果你一直在摆弄一个容器,并做了一些改变来修复一些错误,你可以立即从它那里旋转出新的容器。你甚至不需要去重建Docker文件。
下面的命令将从现有的容器中提交一个新的镜像。
docker commit <container_name> <new_image>
这将创建一个新的镜像,无论你指定什么名字,你都可以立即用它来启动新的容器。
commit 命令的另一个额外好处是,在提交过程中,你实际上可以将Dockerfile的语法传递给它。如果你想提交一个现有的容器,但要改变其中的一个环境变量,你可以使用--change 标志来传递这个信息。
docker commit --change="ENV foo=bar" <container_name> <new_image>
你可以向commit 命令传递多个不同的changes ,以便在命令行上创建令人印象深刻的细化镜像。
4.匹配镜像的哈希值
如果你正在排除一个已经存在了一段时间的容器的故障,你可能不知道它是用哪个特定版本的镜像构建的。如果你使用像Docker Hub或Elastic Container Registry这样的容器注册表,你可以很容易地得到镜像的哈希值,以便与你的容器进行比较。
抓取一个容器的所有元数据的一个快速方法是使用inspect 命令。这很好,但它给了你大量的信息。如果你所追求的只是图像的哈希值,你可以使用像这样的小格式化魔法来得到它。
docker inspect --format "{{ .Image }}" <container_id>
这将输出容器正在运行的镜像的sha256哈希值。这个哈希值可以与你的注册表中的哈希值进行比较,以确定它是什么时候建立的。
现在你可以绝对确定哪个版本在哪里运行。
5.跳过构建缓存
如果你真的很难理解为什么构建会失败、有问题,或者只是不包括你所做的一些改动,那么可能是时候放弃缓存了。虽然Docker应该识别层的变化,并根据需要进行重建,但有时你需要从头开始的安心感。
如果你想在不利用任何现有构建缓存的情况下构建镜像,你可以运行以下命令。
docker build --tag <tag> --no-cache .
这将忽略缓存中的任何先前构建的项目,并强制一切从零开始构建。如果你正在对一个图像进行多次迭代,并且想确保你对某些层进行了非常细微的改变,那么这就很方便了。