1

调试Docker容器可能是一个非常具有挑战性的过程。在这里,我将分享一些调试容器的基本技术,主要是Docker的技术,但是这些技术也适用于许多其他类型的Linux容器引擎。我将在此处详细介绍的方法适用于基于Linux的系统。

撰写此博客文章的灵感来自于externalsecret-operator 的开发团队在实施1password后端期间遇到的一些近期问题,该库提供了与1password API进行通信的机制。

提供一些背景信息:externalsecret-operator是一个很棒的Kubernetes operator,它使secret管理的挑战性降低,并帮助您将来自第三方凭证存储的秘密直接注入到Kubernetes集群中。 (如果您还没有听说过,那么绝对值得尝试一下。我们的团队正在努力开发它,并且每周都会添加新功能。)

但是回到调试。在以下情况下,如果我们在主机上运行operator,那么一切都会按预期进行,并且可以登录到1password后端:

docker-debugging-1.png

但是在容器中运行时,我们会看到以下错误消息:

docker-debugging-2.png

看起来没有正常运行,但是我们应该如何以及在哪里开始调试?

从代码分析开始

最好的起点总是源代码,并且因为externalsecret-operator是开源的,所以我们可以很容易地看到问题出在哪里。我建议使用Sourcegraph工具,该工具可直接在浏览器中工作并且可以进行代码分析。使用Sourcegraph识别代码中有问题的部分的示例:

source.jpg

这种快速的代码分析显示出一个好消息和一个坏消息:幸运的是,因为我们的代码不是问题的根源。但也很不幸,因为1password客户端二进制文件(op)用于直接从1password秘密存储区中获取凭证-不幸的是,这两个都不是开源的。

这才是真正的乐趣开始的地方。

调试在主机系统上有效但在容器中不起作用的那些东西的线索是了解什么是容器。容器是一种在同一主机上运行时将进程彼此隔离的机制,这意味着其管理员可以直接从主机系统访问任何容器。

有了这些知识,让我们看看1password二进制文件是否损坏。首先,我们必须验证我们的1password二进制文件不适用于容器的理论是否有效。让我们手动使用它,使用伪造的登录数据:

docker-debugging-3.png

答对了!我们的理论得到证实。 1password二进制文件不适用于该容器;恐慌而不是优雅地退出。现在我们有了这些信息,我们应该确认问题是二进制本身还是容器环境造成的。最好的方法是重新使用与容器镜像中完全相同的二进制文件。开始吧!

假设我们对正在运行容器的主机具有root访问权,那么我们可以使用docker top <container id>来查看主机上有问题的容器的PID(进程标识符)是什么:
docker-debugging-4.png

我们确定了容器PID,因此现在可以在Linux系统进程表中找到此进程。进入process目录后,我们应该查看根文件夹,因为它是包含正在运行的容器的文件系统的根文件夹:
docker-debugging-5.png

检查operator

我们可以访问容器中的所有二进制文件,并在主机名称空间中执行它们。

因此,让我们看看operator的问题是否来自使用错误的1password二进制文件:
docker-debugging-6.png

1password op二进制文件的行为正确:由于我们提供了虚假数据,因此无法登录1password域,但最终并没有收到紧急消息。因此,问题不在1password二进制文件之内。

我们可以看到主机名称空间中的执行错误消息提到了有关无效请求的内容。我们可能会怀疑容器中的网络是否存在问题,以及操作二进制的panic,如果它无法到达某些外部服务时,这是另一个需要测试的理论。

如前所述,1password二进制文件不是开源的,因此我们无法知道它在哪里尝试连接。我们可以尝试使用Wireshark或tcpdump等网络嗅探器捕获流量。但…

docker-debugging-7.png

…容器镜像不包含任何这些二进制文件。我们可以将它们安装在Docker镜像中。但是,由于我们具有对主机的root访问权限,因此可以通过使用nsenter来更轻松地进行操作。

nsenter工具允许我们输入特定的进程名称空间-因此,例如,我们可以输入容器的网络名称空间,并且仍然可以访问我们的宿主工具。让我们看看实际情况:
docker-debugging-8.png

我们位于容器网络名称空间中,可以从主机系统访问所有工具。

在另一个shell中,我们可以使用容器中的op二进制文件再次登录1password。但是,即使已连接到互联网,也没有生成任何数据包:

docker-debugging-9.png

重新排查1Password Binary

因此,在这一点上,我们可以假设op二进制panic甚至在建立与外部服务的连接之前就已出现。

现在,我们可以尝试使用GDB(GNU项目调试器)调试op二进制文件,以查看执行失败的时间。但是1password可能不包含调试符号,因此GBD无法为我们提供有关执行的任何信息。通过检查操作二进制文件中是否存在调试符号来进行确认:

docker-debugging-10.png

如所假定的,op二进制根本没有调试符号。如果它具有这些符号,那么在几行中将显示除零以外的数字,如在运算符中一样。使用GDB没有任何意义。

另外,GDB不支持跨命名空间调试,因此不可能从主机命名空间中对其进行调试。我们将必须在Docker映像中安装GDB或从源代码编译GDB

我们无法使用GDB,但可以在此过程中“strace”或跟踪系统调用和信号:查看执行二进制文件中op二进制文件调用了哪些系统调用。为此,我们运行另一个具有相同镜像的容器,但修改后的入口点将直接指向op二进制文件,并使用先前使用的参数:
docker-debugging-11.png

在另一个Shell窗口中,我们将附加到该容器的PID并输入密码:
docker-debugging-12.png

我们发现它存在一些严重的问题。二进制文件尝试在仅允许超级用户的文件系统的根目录/中创建.op目录:

mkdirat(AT_FDCWD,“ /.op”,0700)= -1 EACCES(权限被拒绝)

让我们检查一下如果以root用户身份运行此容器会发生什么情况:

run-as-root copy.png

Binary并没有panic,而strace表明op实际上试图在超级用户主目录而不是/中创建.op二进制文件。

strace-as-root.png

这使我们得出结论,即默认用户可能在Docker镜像中配置错误。让我们检查一下:

docker-debugging-13.png

答对了! / etc / passwd中缺少默认用户,系统无法识别该默认用户,因此op binary无法确定其主目录,并尝试在当前工作目录中创建目录。这是不允许的。因此,它会panic。

我们永远不会忘记Docker容器只是进程,大多数Linux标准调试工具都可以在它们上使用,并消除猜测驱动的调试。

我想再次强调,这些技术主要适用于Linux系统。但是对于那些在Mac上运行容器的人,我建议您研究其他技术,例如使用所有调试工具运行sidecar容器。无论使用什么系统,祝您调试侦探工作顺利!


iyacontrol
1.4k 声望2.7k 粉丝

专注kubernetes,devops,aiops,service mesh。