调试Kubernetes工作负载的最简单方法 - Martin


对于每个使用Kubernetes的开发人员和DevOps工程师来说, 调试容器化的工作负载和Pod都是日常工作。通常很简单,kubectl logs或者kubectl describe pod足以找到某些问题的根源,但是有些问题很难找到。
在这些情况下,您可能会尝试使用,kubectl exec但甚至可能还不够用,因为某些容器(例如Distroless)甚至不包含您可以通过SSH进入的外壳。那么,如果以上所有方法都失败了,我们还剩下什么呢?...
 
可能会有更好的方法...
有时您需要抓住更大的锤子,或者只是使用更合适的工具来完成手头的任务。
如果要在Kubernetes上调试工作负载,则该工具将是kubectl debug,这是不久前添加的新命令(v1.18),可用于调试正在运行的Pod。
它将名为EphemeralContainer的特殊类型的容器注入有问题的Pod中,从而使您可以四处寻找问题并进行故障排除。对于前面描述的情况或在交互调试更可取或更有效的任何其他情况下,这可能非常有用。
因此,kubectl debug看起来像要走的路,但是要使用它,我们将需要短暂的临时Ephemeral容器,那么这些到底是什么?
临时容器是类似于正常容器的Pod子资源。与常规容器不同,临时容器不是用于架构应用,而是用于检查它们。
我们不是在Pod的创建时定义它们,而是使用特殊的API将它们注入到运行Pod中以运行故障排除命令并检查Pod的环境。
除了这些差异之外,临时容器还缺少基本容器的某些字段,例如ports或resources。
但是,为什么我们需要它们?我们不能只使用基本容器吗?好吧,您不能将容器添加到Pod中,因为它们应该是一次性的(或换句话说,可以随时删除并重新创建),这可能使得很难对难以重现需要检查Pod的bug进行故障排除。这就是将临时容器添加到API的原因:它们使您可以将容器添加到现有的容器中,从而更容易检查正在运行的容器。
考虑到临时容器是Pod规范的一部分,而Pod规范是Kubernetes的核心,那么您(可能)还没有听说过它呢?这些几乎都是未知功能的原因是,临时容器处于Alpha早期阶段,这意味着默认情况下未启用它们。此阶段的资源和功能可能会发生重大变化,或者在以后的Kubernetes版本中会被完全删除。因此,使用它们,你必须在kubelet使用Feature Gates明确激活它们。
 
配置Feature Gates
我们已经确定要尝试kubectl debug,那么如何启用临时容器功能?好吧,这取决于您的集群设置。例如,如果您kubeadm用于启动创建集群,则可以使用以下ClusterConfiguration启用临时容器:

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.20.2
apiServer:
  extraArgs:
    feature-gates: EphemeralContainers=true

但是,在以下示例中,出于简化和测试目的,我们将使用KinD(Docker中的Kubernetes)集群,这也允许我们指定要启用的功能门。因此,要创建我们的游乐场集群:

# File: config.yaml
# Run:  kind create cluster --config ./config.yaml --name kind --image=kindest/node:v1.20.2
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
featureGates:
  EphemeralContainers: true
nodes:
- role: control-plane

在集群运行的情况下,我们应验证其确实有效。查看是否已应用此配置的最简单方法是检查Pod API,该API现在应在常规容器旁边包括ephemeralContainers部分:

~ $ kubectl explain pod.spec.ephemeralContainers
KIND:     Pod
VERSION:  v1

RESOURCE: ephemeralContainers <[]Object>

DESCRIPTION:
     List of ephemeral containers run in this pod....
...

这确认我们已经拥有了,因此可以开始使用kubectl debug。因此,让我们从一个简单的示例开始:

~ $ kubectl run some-app --image=k8s.gcr.io/pause:3.1 --restart=Never
~ $ kubectl debug -it some-app --image=busybox --target=some-app
Defaulting debug container name to debugger-tfqvh.
If you don't see a command prompt, try pressing enter.
/ #

# From other terminal...
~ $ kubectl describe pod some-app
...
Containers:
  some-app:
    Container ID:   containerd://60cc537eee843cb38a1ba295baaa172db8344eea59de4d75311400436d4a5083
    Image:          k8s.gcr.io/pause:3.1
    Image ID:       k8s.gcr.io/pause@sha256:f78411e19d84a252e53bff71a4407a5686c46983a2c2eeed83929b888179acea
...
Ephemeral Containers:
  debugger-tfqvh:
    Container ID:   containerd://12efbbf2e46bb523ae0546b2369801b51a61e1367dda839ce0e02f0e5c1a49d6
    Image:          busybox
    Image ID:       docker.io/library/busybox@sha256:ce2360d5189a033012fbad1635e037be86f23b65cfd676b436d0931af390a2ac
    Port:           >none<
    Host Port:      >none<
    State:          Running
      Started:      Mon, 15 Mar 2021 20:33:51 +0100
    Ready:          False
    Restart Count:  0
    Environment:    >none<
    Mounts:         >none<

我们首先启动一个Pod,Pod名为 some-app,以便我们可以“debug”某些东西。然后kubectl debug,我们针对此Pod运行,将其指定busybox为临时容器的镜像Image,以及作为原始容器的目标。此外,我们还包含-it参数,以便我们立即附加到容器并获得Shell会话。
在上面的代码段中,您还可以看到,如果在运行Pod之后kubectl debug我们对其进行了描述,则其描述将包括“临时容器”部分,其中包含我们之前指定为命令选项的值。
更详细点击标题。