--- title: 调试运行中的 Pod content_type: task --- 本页解释如何在节点上调试运行中(或崩溃)的 Pod。 ## {{% heading "prerequisites" %}} * 你的 {{< glossary_tooltip text="Pod" term_id="pod" >}} 应该已经被调度并正在运行中, 如果你的 Pod 还没有运行,请参阅 [应用问题排查](/zh/docs/tasks/debug-application-cluster/debug-application/)。 * 对于一些高级调试步骤,你应该知道 Pod 具体运行在哪个节点上,在该节点上有权限去运行一些命令。 你不需要任何访问权限就可以使用 `kubectl` 去运行一些标准调试步骤。 ## 检查 Pod 的日志 {#examine-pod-logs} 首先,查看受到影响的容器的日志: ```shell kubectl logs ${POD_NAME} ${CONTAINER_NAME} ``` 如果你的容器之前崩溃过,你可以通过下面命令访问之前容器的崩溃日志: ```shell kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME} ``` ## 使用容器 exec 进行调试 {#container-exec} 如果 {{< glossary_tooltip text="容器镜像" term_id="image" >}} 包含调试程序, 比如从 Linux 和 Windows 操作系统基础镜像构建的镜像,你可以使用 `kubectl exec` 命令 在特定的容器中运行一些命令: ```shell kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN} ``` {{< note >}} `-c ${CONTAINER_NAME}` 是可选择的。如果Pod中仅包含一个容器,就可以忽略它。 {{< /note >}} 例如,要查看正在运行的 Cassandra pod中的日志,可以运行: ```shell kubectl exec cassandra -- cat /var/log/cassandra/system.log ``` 你可以在 `kubectl exec` 命令后面加上 `-i` 和 `-t` 来运行一个连接到你的终端的 Shell,比如: ```shell kubectl exec -it cassandra -- sh ``` 若要了解更多内容,可查看[获取正在运行容器的 Shell](/zh/docs/tasks/debug-application-cluster/get-shell-running-container/)。 ## 使用临时调试容器来进行调试 {#ephemeral-container} {{< feature-state state="alpha" for_k8s_version="v1.18" >}} 当由于容器崩溃或容器镜像不包含调试程序(例如[无发行版镜像](https://github.com/GoogleContainerTools/distroless)等) 而导致 `kubectl exec` 无法运行时,{{< glossary_tooltip text="临时容器" term_id="ephemeral-container" >}}对于排除交互式故障很有用。 从 'v1.18' 版本开始,'kubectl' 有一个可以创建用于调试的临时容器的 alpha 命令。 ## 使用临时容器来调试的例子 {#ephemeral-container-example} {{< note >}} 本示例需要你的集群已经开启 `EphemeralContainers` [特性门控](/zh/docs/reference/command-line-tools-reference/feature-gates/), `kubectl` 版本为 v1.18 或者更高。 {{< /note >}} 你可以使用 `kubectl alpha debug` 命令来给正在运行中的 Pod 增加一个临时容器。 首先,像示例一样创建一个 pod: ```shell kubectl run ephemeral-demo --image=k8s.gcr.io/pause:3.1 --restart=Never ``` {{< note >}} 本节示例中使用 `pause` 容器镜像,因为它不包含调试程序,但是这个方法适用于所有容器镜像。 {{< /note >}} 如果你尝试使用 `kubectl exec` 来创建一个 shell,你将会看到一个错误,因为这个容器镜像中没有 shell。 ```shell kubectl exec -it ephemeral-demo -- sh ``` ``` OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown ``` 你可以改为使用 `kubectl alpha debug` 添加调试容器。 如果你指定 `-i` 或者 `--interactive` 参数,`kubectl` 将自动挂接到临时容器的控制台。 ```shell kubectl alpha debug -it ephemeral-demo --image=busybox --target=ephemeral-demo ``` ``` Defaulting debug container name to debugger-8xzrl. If you don't see a command prompt, try pressing enter. / # ``` 此命令添加一个新的 busybox 容器并将其挂接到该容器。`--target` 参数指定另一个容器的进程命名空间。 这是必需的,因为 `kubectl run` 不能在它创建的pod中启用 [共享进程命名空间](/zh/docs/tasks/configure-pod-container/share-process-namespace/)。 {{< note >}} {{< glossary_tooltip text="容器运行时" term_id="container-runtime" >}}必须支持`--target`参数。 如果不支持,则临时容器可能不会启动,或者可能使用隔离的进程命名空间启动。 {{< /note >}} 你可以使用 `kubectl describe` 查看新创建的临时容器的状态: ```shell kubectl describe pod ephemeral-demo ``` ``` ... Ephemeral Containers: debugger-8xzrl: Container ID: docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb Image: busybox Image ID: docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084 Port: Host Port: State: Running Started: Wed, 12 Feb 2020 14:25:42 +0100 Ready: False Restart Count: 0 Environment: Mounts: ... ``` 使用 `kubectl delete` 来移除已经结束掉的 Pod: ```shell kubectl delete pod ephemeral-demo ``` ## 通过 Pod 副本调试 有些时候 Pod 的配置参数使得在某些情况下很难执行故障排查。 例如,在容器镜像中不包含 shell 或者你的应用程序在启动时崩溃的情况下, 就不能通过运行 `kubectl exec` 来排查容器故障。 在这些情况下,你可以使用 `kubectl debug` 来创建 Pod 的副本,通过更改配置帮助调试。 ### 在添加新的容器时创建 Pod 副本 当应用程序正在运行但其表现不符合预期时,你会希望在 Pod 中添加额外的调试工具, 这时添加新容器是很有用的。 例如,应用的容器镜像是建立在 `busybox` 的基础上, 但是你需要 `busybox` 中并不包含的调试工具。 你可以使用 `kubectl run` 模拟这个场景: ```shell kubectl run myapp --image=busybox --restart=Never -- sleep 1d ``` 通过运行以下命令,建立 `myapp` 的一个名为 `myapp-debug` 的副本, 新增了一个用于调试的 Ubuntu 容器, ```shell kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug ``` ``` Defaulting debug container name to debugger-w7xmf. If you don't see a command prompt, try pressing enter. root@myapp-debug:/# ``` {{< note >}} * 如果你没有使用 `--container` 指定新的容器名,`kubectl debug` 会自动生成的。 * 默认情况下,`-i` 标志使 `kubectl debug` 附加到新容器上。 你可以通过指定 `--attach=false` 来防止这种情况。 如果你的会话断开连接,你可以使用 `kubectl attach` 重新连接。 * `--share-processes` 允许在此 Pod 中的其他容器中查看该容器的进程。 参阅[在 Pod 中的容器之间共享进程命名空间](/zh/docs/tasks/configure-pod-container/share-process-namespace/) 获取更多信息。 {{< /note >}} 不要忘了清理调试 Pod: ```shell kubectl delete pod myapp myapp-debug ``` ### 在改变 Pod 命令时创建 Pod 副本 有时更改容器的命令很有用,例如添加调试标志或因为应用崩溃。 为了模拟应用崩溃的场景,使用 `kubectl run` 命令创建一个立即退出的容器: ``` kubectl run --image=busybox myapp -- false ``` 使用 `kubectl describe pod myapp` 命令,你可以看到容器崩溃了: ``` Containers: myapp: Image: busybox ... Args: false State: Waiting Reason: CrashLoopBackOff Last State: Terminated Reason: Error Exit Code: 1 ``` 你可以使用 `kubectl debug` 命令创建该 Pod 的一个副本, 在该副本中命令改变为交互式 shell: ``` kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh ``` ``` If you don't see a command prompt, try pressing enter. / # ``` 现在你有了一个可以执行类似检查文件系统路径或者手动运行容器命令的交互式 shell。 {{< note >}} * 要更改指定容器的命令,你必须用 `--container` 命令指定容器的名字, 否则 `kubectl debug` 将建立一个新的容器运行你指定的命令。 * 默认情况下,标志 `-i` 使 `kubectl debug` 附加到容器。 你可通过指定 `--attach=false` 来防止这种情况。 如果你的断开连接,可以使用 `kubectl attach` 重新连接。 {{< /note >}} 不要忘了清理调试 Pod: ```shell kubectl delete pod myapp myapp-debug ``` ### 在更改容器镜像时创建 Pod 副本 在某些情况下,你可能想从正常生产容器镜像中 把行为异常的 Pod 改变为包含调试版本或者附加应用的镜像。 下面的例子,用 `kubectl run`创建一个 Pod: ``` kubectl run myapp --image=busybox --restart=Never -- sleep 1d ``` 现在可以使用 `kubectl debug` 创建一个副本 并改变容器镜像为 `ubuntu`: ``` kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu ``` `--set-image` 与 `container_name=image` 使用相同的 `kubectl set image` 语法。 `*=ubuntu` 表示把所有容器的镜像改为 `ubuntu`。 ```shell kubectl delete pod myapp myapp-debug ``` ## 在节点上通过 shell 来调试 {#node-shell-session} 如果这些方法都不起作用,你可以找到运行 Pod 的主机并通过 SSH 进入该主机, 但是如果使用 Kubernetes API 中的工具,则通常不需要这样做。 因此,如果你发现自己需要使用 ssh 进入主机,请在GitHub 上提交功能请求, 以描述你的用例以及这些工具不足的原因。