Kubernetes Pod 异常调试工具 kubectl-debug

97次阅读

共计 4373 个字符,预计需要花费 11 分钟才能阅读完成。

前言

通常,简单的kubectl logskubectl describe pod就足以找到某些问题的罪魁祸首,但有些问题很难找到。在这些情况下,可能会尝试使用kubectl exec,但这可能还不够,因为一些容器(如Distroless)甚至不包含可以 SSH 进入的 shell。如果以上都失败了,还能怎么办呢?

在 Kubernetes 上调试工作问题时,合适的工具应该是kubectl debug,它是不久前添加的一个新命令 (v1.18),允许您调试正在运行的 pod。它将名为 EphemeralContainer 的特殊类型的容器注入到有问题的 Pod 中,允许查看并进行故障排除。

使用临时调试容器进行调试

临时容器是 Pod 中的一个子资源,类似于普通容器。不过,与常规容器不同的是,临时容器并非用于构建应用程序,而是用于检查应用程序。我们不会在 Pod 创建时定义它们,而是使用特殊的 API 将它们注入到运行 Pod 中,以运行故障排除命令和检查 Pod 的环境。除了这些不同之外,临时容器还缺少一些基本容器的字段,如 port 或 resource。

临时容器背后的主要思想是,K8S 将具有选定自定义镜像的新容器添加到现有 Pod 中,而不需要重新启动该 Pod。这个新的容器可以共享目标容器的资源,包括:

  • Linux 网络命名空间
  • Linux 进程命名空间
  • 访问共享卷
  • 访问 K8S 节点

开区临时容器特性

1. master 节点配置 APIServer 组件
# cat /etc/kubernetes/manifests/kube-apiserver.yaml
- --feature-gates=EphemeralContainers=true
...

2. master 节点配置 controller-manager
# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
spec:
  containers:
  - command:
    - --feature-gates=EphemeralContainers=true            # 增加
...

3. master 节点配置 kube-scheduler
# vim /etc/kubernetes/manifests/kube-scheduler.yaml
spec:
  containers:
  - command:
    - --feature-gates=EphemeralContainers=true            # 增加
# 重启服务 
# systemctl restart kubelet.service

4. 所有 node 节点配置 kubelet 参数
添加 --feature-gates=EphemeralContainers=true
# cat /var/lib/kubelet/kubeadm-flags.env KUBELET_KUBEADM_ARGS="--network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.6 --feature-gates=EphemeralContainers=true"
# 重启 node kubelet 服务
# systemctl daemon-reload
# systemctl restart kubelet

创建简单工作负载

假设需要调试Nginx,我们创建一个单副本Nginx部署,通过运行以下命令来完成。

[root@vm-236-165-centos ~]# kubectl create deployment nginx --image=nginx
[root@vm-236-165-centos ~]# k get po
NAME                     READY   STATUS    RESTARTS   AGE
nginx-75b694f74b-mxt59   1/1     Running   0          2s

调试无法exec的容器

[root@vm-236-165-centos ~]# kubectl run ephemeral-demo --image=registry.aliyuncs.com/google_containers/pause:3.2 --restart=Never
[root@vm-236-165-centos ~]# kubectl exec -it ephemeral-demo -- sh
Defaulted container "ephemeral-demo" out of: ephemeral-demo, debugger-nbl9t (ephem)
OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "sh": executable file not found in $PATH: unknown
command terminated with exit code 126
[root@vm-236-165-centos ~]# kubectl debug -it ephemeral-demo --image=busybox --target=ephemeral-demo --image-pull-policy=Always

此命令添加一个新的 busybox 容器并将其挂接到该容器。--target 参数指定另一个容器的进程命名空间。 这个指定进程命名空间的操作是必需的,因为 kubectl run 不能在它创建的 Pod 中启用共享进程命名空间

说明: 容器运行时必须支持 --target 参数。 如果不支持,则临时容器可能不会启动,或者可能使用隔离的进程命名空间启动, 以便 ps 不显示其他容器内的进程。

Kubernetes Pod 异常调试工具 kubectl-debug

此时再去看 Pod 的信息会发现已经被添加了一个类型为 ephemeralContainers 的容器

Kubernetes Pod 异常调试工具 kubectl-debug

诊断网络故障

诊断网络故障需要共享网络命名空间。当将临时容器附加到运行中的 Pod 时,这是默认的Linux命名空间。

接下来我们创建第一个临时容器,使用nicolaka/netshoot作为新的临时容器的镜像,该镜像包含许多故障排除工具,如tcpdumpstrace

[root@vm-236-165-centos /data]# kubectl debug -it nginx-6799fc88d8-rmqn6 --image=nicolaka/netshoot --image-pull-policy=Always

Kubernetes Pod 异常调试工具 kubectl-debug

现在我们从临时容器中抓取Nginx容器的网络数据包。

从临时容器运行 tcpdump,并试着从节点向这个 Pod 发送一些请求

Kubernetes Pod 异常调试工具 kubectl-debug

现在观察临时容器终端,会发现TCP报文会被打印出来

通过 Pod 副本调试

分析 Crash Pod

一种常见的情况是,应用程序在容器开始时不断崩溃(CrashLoopBackOff),使得调试变得困难,因为没有足够的时间将 shell 会话放入容器并运行一些故障排除命令。在这种情况下,解决方案是创建带有不同入口点/命令的容器,例如一个循环休眠的进程,这将立即阻止应用程序崩溃,并允许我们执行调试

~ $ kubectl get pods
NAME                READY   STATUS             RESTARTS   AGE
crashing-app        0/1     CrashLoopBackOff   1          8s

~ $ kubectl debug crashing-app -it --copy-to=crashing-app-debug --container=crashing-app -- sh
If you don't see a command prompt, try pressing enter.
# id
uid=0(root) gid=0(root) groups=0(root)
#
...
# From another terminal
~ $ kubectl get pods
NAME                READY   STATUS             RESTARTS   AGE
crashing-app        0/1     CrashLoopBackOff   3          2m7s
crashing-app-debug  1/1     Running            0          16s

分析进程

[root@vm-236-165-centos ~]# kubectl debug -it nginx-6799fc88d8-rmqn6 --image=nicolaka/netshoot --copy-to=debug-pod --share-processes

说明:

  • 如果你没有使用 --container 指定新的容器名,kubectl debug 会自动生成的。
  • 默认情况下,-i 标志使 kubectl debug 附加到新容器上。 你可以通过指定 --attach=false 来防止这种情况。 如果你的会话断开连接,你可以使用 kubectl attach 重新连接。
  • --share-processes 允许在此 Pod 中的其他容器中查看该容器的进程。 参阅在 Pod 中的容器之间共享进程命名空间 获取更多信息。

这种方式会新启动一个容器,其中包含可以访问所有进程的额外临时容器。然后列出正在运行的进程,可以看到应用程序容器的进程有PID 7

[root@vm-236-165-centos ~]# kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
debug-pod                2/2     Running   0          34s
nginx-6799fc88d8-rmqn6   1/1     Running   0          7m15s

Kubernetes Pod 异常调试工具 kubectl-debug

Kubernetes Pod 异常调试工具 kubectl-debug

改变命令

有时更改容器的命令很有用,例如添加调试标志或因为应用崩溃。

为了模拟应用崩溃的场景,使用 kubectl run 命令创建一个立即退出的容器:

kubectl run --image=busybox:1.28 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。

说明:

  • 要更改指定容器的命令,你必须用 --container 命令指定容器的名字, 否则 kubectl debug 将建立一个新的容器运行你指定的命令。
  • 默认情况下,标志 -i 使 kubectl debug 附加到容器。 你可通过指定 --attach=false 来防止这种情况。 如果你的断开连接,可以使用 kubectl attach 重新连接。
正文完
 
mervinwang
版权声明:本站原创文章,由 mervinwang 2023-08-03发表,共计4373字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
文章搜索