本文转载自微信公众号「云原生百宝箱」
这篇内容主要探讨了 Kubernetes 的调试功能,介绍了 kubectl debug 和 kubectl superdebug。它们支持容器挂载并且能够调试一些需要排查问题的 Pod。文章指出了在 Kubernetes 中使用 kubectl exec 命令的限制,并介绍了 kubectl debug 的作用,它能创建一个新的容器来调试运行中的容器,并且能够在同一个 Pod 内共享系统资源。此外,还提到了 ephemeral containers,它们在调试过程中可以临时运行在现有的 Pod 中,支持一些排查操作。最后,文章还提及了一些非 Kubernetes 本地调试容器的方法,包括使用 Docker Engine 或者一些基于 Linux namespaces 的工具。
使用 kubectl exec 执行命令
如果你在 Kubernetes 上运行软件,你有时会想要调试所部署的应用。对于习惯使用虚拟机的人来说,一种简单的调试方法是连接到正在运行的 Pod 并进行分享:
kubectl exec -it podname -c containername -- bash
这通常有效并且非常有用。然而,至少有两个 Kubernetes“最佳实践”限制了 exec 在现实世界中的用处:
• 不以 root 身份运行。容器以尽可能少的权限运行,甚至可以使用随机 UID 运行。
• 最小镜像。镜像尽可能小,极端情况下将二进制文件安装到distroless 镜像中。[1]
当应用这些最佳实践时,使用kubectl exec连接到容器要么是不可能的,要么会让你陷入不适合调试的贫瘠荒地般的环境。
调试容器
调试正在运行的容器的 Kubernetes 原生答案是使用kubectl debug。
debug 命令将一个新容器附加到正在运行的 pod 中。这个新容器可以以不同的用户身份从你选择的任何镜像运行。由于调试容器与其目标容器在同一 Pod 中运行(因此在同一节点上),因此两个容器之间的隔离不需要是绝对的。调试容器可以与同一 Pod 中运行的其他容器共享系统资源。
考虑要检查pod容器postpod中运行的 PostgreSQL 数据库的 CPU 使用情况。Pod 不以 root 身份运行,并且 Postgres 镜像没有类似top或htop安装的工具——换句话说,该kubectl exec命令没什么用处。你可以运行以下命令:
kubectl debug -it \
--container=debug-container \
--image=alpine \
--target=postcont \
postpod
你将以 root 身份登录(这是 Alpine 镜像的默认设置),并且可以轻松安装你最喜欢的交互式进程查看器 htop ( apt add htop)。你与容器postcont共享相同的进程命名空间,并且可以查看甚至杀死在那里运行的所有进程!当你退出该进程时,临时容器也将停止存在。
注意:你可以通过按 CTRL+P 或 CTRL+D 断开与临时容器/bash 会话的连接,而无需退出(终止)它。然后你可以稍后使用 重新连接到它kubectl attach。
注意:kubectl debug提供的功能比此处概述的更多,例如使用修改后的启动命令复制 Pod 或启动可访问节点文件系统的“节点”Pod。
临时容器
上面的命令kubectl debug通过创建一个称为临时容器[2]东西来工作。这些容器应该在现有Pod 中临时运行,以支持故障排除等操作。
“普通”容器和临时容器之间的区别很小。没有什么能真正阻止临时容器长期运行。我认为,通过查看 Kubernetes 在诞生之初所做的基础架构选择,可以最好地理解拥有临时容器的原因:
• Pod 应该是一次性且可更换的,并且支持这一点,
• Pod 规范是不可变的。
当 Kubernetes 主要用于部署无状态工作负载时(当 Pod 本身可以被认为是短暂的)时,这非常有意义。在这个 Kubernetes无所不能的新世界中,它可能会受到限制。Pod 规范保持不变,但 Kubernetes 将临时容器建模为Pod 的子资源。与“普通”容器不同,临时容器不是 Pod规范的一部分,即使它们是 pod 的一部分。这种微妙的区别让每个人都高兴🥳!
临时容器仍然相对较新;
它们自 Kubernetes v1.25(2022 年 8 月)处于Stable状态,自 v1.23(2021 年 12 月)是Beta状态,自 v1.22(2021 年 8 月)是Alpha状态。
挂载卷
内置命令kubectl debug非常有用。它允许你将临时容器添加到正在运行的 Pod,可以选择与正在运行的容器共享其进程命名空间。但是,如果你希望用于kubectl debug检查或修改正在运行的容器的文件系统的任何部分,那么你就不走运了 - 调试 Pod 的文件系统与你连接到的容器的文件系统是脱节的。
幸运的是,我们可以做得更好。这个想法很简单:
• 检索正在运行的目标容器的规格。
• 将临时容器修补到 Pod 中。将其配置为与目标容器共享相同的进程命名空间,并另外包含相同的卷挂载。
没有用于创建临时容器的 kubectl 命令,因此我们需要向 K8s API 发送 PATCH 请求来创建它。该kubectl proxy命令允许访问 K8s API。
这个过程并不完全是用户友好的,因此将过程包装到脚本或 kubectl 插件中是有意义的。你可以在此处找到此类脚本的示例实现:
https://github.com/JonMerlevede/kubectl-superdebug
kubectl debug 的扩展将目标容器的卷规格附加到临时调试 pod 中……
kubectl superdebug
请注意,此方法和脚本可以轻松扩展以从目标容器复制环境变量规范。
如果将此脚本另存为kubectl-superdebug并使其在你的路径上可用,则可以kubectl superdebug从任何地方运行它,如下所示:
kubectl superdebug \
--container=debug-container \
--image=alpine \
--target=postcont \
postpod
你可能还想扩展此脚本以将目标容器的其他方面复制到调试容器中,例如对环境变量的引用。
这完成了调试运行容器的 Kubernetes 原生方法的概述,应该可以满足大多数人的需求。
非 Kubernetes 原生方法
Kubernetes 不提供以 root 身份连接到正在运行的容器(除非主进程以 root 身份运行)或从另一个容器访问容器的根文件系统的方法。这并不意味着这些事情不可能做到。毕竟,Kubernetes 只是一个位于容器化引擎之上的容器编排器。如果由于某种原因确实有必要的话,通常可以通过删除抽象层来做任何你想做的事情。只要确保你必须...
如果你使用 Docker 引擎并且可以直接从节点或通过节点上运行的特权容器访问你的引擎,那么你可以docker exec --user作为你选择的用户运行和执行进程。
诸如kubectl ssh和 之类的插件kubectl exec-user实现了这种方法。不幸的是, containerd[3]和CRI-O[4]等现代引擎不再提供--user标志功能——这意味着这些插件无法在现代 Kubernetes 安装上运行。
然而,即使是这些现代引擎通常也只是与 Linux 命名空间交互。你可以通过输入适当的 Linux 命名空间集在任何你想要的“容器”中运行命令。kpexec[5]工具实现了这种方法。它在与目标容器相同的节点上启动一个特权 Pod,然后确定要定位的 (Linux) 命名空间,在这些 (Linux) 命名空间中执行命令,最后将其输出流式传输到你的终端。作为一个额外的好处,它可以在目标容器的文件系统之上覆盖一组可用于调试的工具。
与 kubectl exec 不同,kpexec 可以运行具有不同 uid/gid 甚至不同功能的命令作为容器的主进程。它与containerd和cri-o兼容。kpexec 采用某种重量级且脆弱的方法,可能与你的集群的安全配置不兼容。如果 kubectl (super)debug 无法满足你的需求,则值得考虑。
请注意,kpexec 使用nsenter命令直接执行到命名空间中。它与无处不在的容器运行时 runc 兼容,但与Kata Containers[6]等运行时不兼容。
在这篇文章中,我们研究了两种调试运行容器的 Kubernetes 原生方法:kubectl exec和kubectl debug。我们研究了kubectl debug工作原理,并提出了kubectl superdebug一种变体,kubectl debug它启动一个与目标容器共享相同卷和相同进程命名空间的临时容器。最后,我们回顾了一些非 Kubernetes 原生的容器调试方法。
引用链接
[1] distroless 镜像中。: https://github.com/GoogleContainerTools/distroless
[2] 临时容器: https://kubernetes.io/docs/concepts/workloads/pods/ephemeral-containers/
[3] containerd: https://containerd.io/
[4] CRI-O: https://cri-o.io/
[5] kpexec: https://github.com/ssup2/kpexec[6] Kata Containers: https://katacontainers.io/