我如何排查 Amazon EKS 中的容器组(pod)状态问题?

5 分钟阅读
0

我在 Amazon Elastic Compute Cloud (Amazon EC2) 实例或托管节点组上运行的 Amazon Elastic Kubernetes Service (Amazon EKS) 容器组(pod)卡住了。我想要使我的容器组(pod)处于“Running”或“Terminated”状态。

解决方法

**重要提示:**以下步骤仅适用于在 Amazon EC2 实例上或托管节点组上启动的容器组(pod)。这些步骤不适用于在 AWS Fargate 上启动的容器组(pod)。

了解容器组(pod)的状态

要对 Amazon EKS 中的容器组(pod)状态进行问题排查,请完成以下步骤:

  1. 要获取容器组(pod)的状态,请运行以下命令:

    $ kubectl get pod
  2. 要从容器组(pod)的事件历史记录中获取信息,请运行以下命令:

    $ kubectl describe pod YOUR_POD_NAME
  3. 根据您的容器组(pod)的状态,完成以下部分中的步骤。

容器组(pod)处于 Pending 状态

**注意:**以下步骤中的示例命令位于默认命名空间中。对于其他命名空间,请在命令中附加 -n YOURNAMESPACE。

由于资源不足或者您定义了 hostPort,容器组(pod)可能会停留在 Pending 状态。有关更多信息,请参阅 Kubernetes 网站上的容器组(pod)阶段

如果 Worker 节点上的资源不足,请删除不必要的容器组(pod)。您也可以在 Worker 节点上添加更多资源。当您的集群中没有足够的资源时,请使用 Kubernetes Cluster Autoscaler 来自动扩展您的 Worker 节点组。

CPU 不足示例:

$ kubectl describe pod frontend-cpu
Name:         frontend-cpu
...
Status:       Pending
...
Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  22s (x14 over 13m)  default-scheduler  0/3 nodes are available: 3 Insufficient cpu.

内存不足示例:

$ kubectl describe pod frontend-memory
Name:         frontend-memory
...
Status:       Pending
...
Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  80s (x14 over 15m)  default-scheduler  0/3 nodes are available: 3 Insufficient memory.

如果为容器组(pod)定义了 hostPort,请遵循以下最佳实践:

  • 由于 hostIPhostPortprotocol 组合必须是唯一的,因此仅在必要时才指定 hostPort
  • 如果指定了 hostPort,则安排与 Worker 节点数量相同的容器组(pod)。

**注意:**当您将容器组(pod)绑定到 hostPort 时,可以安排容器组(pod)的位置数量有限。

以下示例显示了处于 Pending 状态的容器组(pod)(即 frontend-port-77f67cff67-2bv7w)的 describe 命令的输出。由于请求的主机端口不适用于集群中的 Worker 节点,因此未安排容器组(pod):

$ kubectl describe pod frontend-port-77f67cff67-2bv7w
Name:           frontend-port-77f67cff67-2bv7w
...
Status:         Pending
IP:
IPs:            <none>
Controlled By:  ReplicaSet/frontend-port-77f67cff67
Containers:
  app:
    Image:      nginx
    Port:       80/TCP
    Host Port:  80/TCP
...
Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  11s (x7 over 6m22s)  default-scheduler  0/3 nodes are available: 3 node(s) didn't have free ports for the requested pod ports.

如果由于节点存在容器组(pod)无法支持的污点而不能安排容器组(pod),则示例输出会类似于以下内容:

$ kubectl describe pod nginx
Name:         nginx
...
Status:       Pending
...
Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  8s (x10 over 9m22s)  default-scheduler  0/3 nodes are available: 3 node(s) had taint {key1: value1}, that the pod didn't tolerate.

要检查节点是否存在污点,请运行以下命令:

$ kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

要保留节点污点,请在 PodSpec 中为容器组(pod)指定容忍度。有关更多信息,请参阅 Kubernetes 网站上的概念。或者,在污点值的末尾添加 -,来删除节点污点:

$ kubectl taint nodes NODE_Name key1=value1:NoSchedule-

如果容器组(pod)仍处于 Pending 状态,则完成其他问题排查部分中的步骤。

容器处于 Waiting 状态

由于 Docker 映像不正确或存储库名称不正确,您的容器可能处于 Waiting 状态。或者,由于映像不存在或您缺少权限,您的容器组(pod)可能处于 Waiting 状态。

要确认映像和存储库名称正确无误,请登录 Docker Hub、Amazon Elastic Container Registry (Amazon ECR) 或其他容器映像存储库。将存储库或者存储库中的映像与容器组(pod)规范中指定的存储库或映像名称进行比较。如果映像不存在或您缺少权限,请完成以下操作:

  1. 验证指定的映像在存储库中是否可用,以及是否配置了正确的权限以允许提取该映像。

  2. 要确认您可以提取映像并且不存在一般的网络和存储库权限问题,请手动提取映像。您必须使用 Docker 从 Amazon EKS Worker 节点提取映像:

    $ docker pull yourImageURI:yourImageTag
  3. 要验证映像是否存在,请检查映像和标签是否都存在于 Docker Hub 或 Amazon ECR 中。

**注意:**如果您使用的是 Amazon ECR,则验证存储库策略是否允许为 NodeInstanceRole 提取映像。或者,验证 AmazonEC2ContainerRegistryReadOnly 角色是否已附加到策略中。
以下示例显示了由于映像提取错误,容器组(pod)处于 Pending 状态,而容器处于 Waiting 状态:

$ kubectl describe po web-test

Name:               web-test
...
Status:             Pending
IP:                 192.168.1.143
Containers:
  web-test:
    Container ID:
    Image:          somerandomnonexistentimage
    Image ID:
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       ErrImagePull
...
Events:
  Type     Reason            Age                 From  Message
  ----     ------            ----                ----                                                 -------
  Normal   Scheduled         66s                 default-scheduler                                    Successfully assigned default/web-test to ip-192-168-6-51.us-east-2.compute.internal
  Normal   Pulling           14s (x3 over 65s)   kubelet, ip-192-168-6-51.us-east-2.compute.internal  Pulling image "somerandomnonexistentimage"
  Warning  Failed            14s (x3 over 55s)   kubelet, ip-192-168-6-51.us-east-2.compute.internal  Failed to pull image "somerandomnonexistentimage": rpc error: code = Unknown desc = Error response from daemon: pull access denied for somerandomnonexistentimage, repository does not exist or may require 'docker login'
  Warning  Failed            14s (x3 over 55s)   kubelet, ip-192-168-6-51.us-east-2.compute.internal  Error: ErrImagePull

如果您的容器仍处于 Waiting 状态,则完成其他问题排查部分中的步骤。

Pod 处于 CrashLoopBackOff 状态

如果收到“Back-Off restarting failed container”输出消息,则您的容器可能在 Kubernetes 启动容器后不久就退出了。

要在当前容器组(pod)的日志中查找错误,请运行以下命令:

$ kubectl logs YOUR_POD_NAME

要在先前崩溃的容器组(pod)的日志中查找错误,请运行以下命令:

$ kubectl logs --previous YOUR-POD_NAME

对于多容器容器组(pod),可在末尾追加容器名称。例如:

$ kubectl logs [-f] [-p] (POD | TYPE/NAME) [-c CONTAINER]

如果存活探针未返回 Successful 状态,则验证是否为应用程序正确配置了存活探针。有关更多信息,请参阅 Kubernetes 网站上的配置探针

以下示例显示了处于 CrashLoopBackOff 状态的容器组(pod),因为应用程序在启动后退出:

$ kubectl describe pod crash-app-b9cf4587-66ftw
Name:         crash-app-b9cf4587-66ftw
...
Containers:
  alpine:
    Container ID:   containerd://a36709d9520db92d7f6d9ee02ab80125a384fee178f003ee0b0fcfec303c2e58
    Image:          alpine
    Image ID:       docker.io/library/alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Tue, 12 Oct 2021 12:26:21 +1100
      Finished:     Tue, 12 Oct 2021 12:26:21 +1100
    Ready:          False
    Restart Count:  4
    ...
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Started    97s (x4 over 2m25s)  kubelet            Started container alpine
  Normal   Pulled     97s                  kubelet            Successfully pulled image "alpine" in 1.872870869s
  Warning  BackOff    69s (x7 over 2m21s)  kubelet            Back-off restarting failed container
  Normal   Pulling    55s (x5 over 2m30s)  kubelet            Pulling image "alpine"
  Normal   Pulled     53s                  kubelet            Successfully pulled image "alpine" in 1.858871422s

以下是容器组(pod)存活探针失败的示例:

$ kubectl describe pod nginx
Name:         nginx
...
Containers:
  nginx:
    Container ID:   containerd://950740197c425fa281c205a527a11867301b8ec7a0f2a12f5f49d8687a0ee911
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:06e4235e95299b1d6d595c5ef4c41a9b12641f6683136c18394b858967cd1506
    Port:           80/TCP
    Host Port:      0/TCP
    State:
          Waiting      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Tue, 12 Oct 2021 13:10:06 +1100
      Finished:     Tue, 12 Oct 2021 13:10:13 +1100
    Ready:          False
    Restart Count:  5
    Liveness:       http-get http://:8080/ delay=3s timeout=1s period=2s #success=1 #failure=3
    ...
Events:
  Type     Reason     Age                    From               Message
  ----     ------     ----                   ----               -------
  Normal   Pulled     2m25s                  kubelet            Successfully pulled image "nginx" in 1.876232575s
  Warning  Unhealthy  2m17s (x9 over 2m41s)  kubelet            Liveness probe failed: Get "http://192.168.79.220:8080/": dial tcp 192.168.79.220:8080: connect: connection refused
  Normal   Killing    2m17s (x3 over 2m37s)  kubelet            Container nginx failed liveness probe, will be restarted
  Normal   Pulling    2m17s (x4 over 2m46s)  kubelet            Pulling image "nginx"

如果容器组(pod)仍处于 CrashLoopBackOff 状态,则完成其他问题排查部分中的步骤。

容器组(pod)处于 Terminating 状态

如果容器组(pod)停留在 Terminating 状态,则检查该容器组(pod)的运行节点和终结器的运行状况。终结器是在容器组(pod)过渡到 Terminated 之前执行终止处理的函数。有关更多信息,请参阅 Kubernetes 网站上的 Finalizers。要检查正在终止的容器组(pod)的终结器,请运行以下命令:

$ kubectl get po nginx -o yaml  

apiVersion: v1  
kind: Pod  
metadata:  
...  
  finalizers:  
  - sample/do-something  
...

在前面的示例中,只有在移除终结器 sample/do-something 之后,容器组(pod)才会过渡到 Terminated 状态。通常,自定义控制器会处理终结器,然后将其移除。随后,容器组(pod)会过渡到 Terminated 状态。

要解决此问题,请检查自定义控制器的容器组(pod)是否正确运行。解决控制器容器组(pod)的任何问题,并让自定义控制器完成终结器过程。然后,容器组(pod)会自动过渡到 Terminated 状态。或者,运行以下命令来直接删除终结器:

$ kubectl edit po nginx

其他问题排查

如果您的容器组(pod)仍然卡滞,请完成以下步骤:

  1. 要确认 Worker 节点存在于集群中并处于 Ready 状态,请运行以下命令:

    $ kubectl get nodes

    如果节点的状态为 NotReady,请参阅如何将我的节点状态从“NotReady”或“Unknown”状态更改为“Ready”状态?如果节点无法加入集群,请参阅如何让我的 Worker 节点加入我的 Amazon EKS 集群?

  2. 要检查 Kubernetes 集群的版本,请运行以下命令:

    $ kubectl version --short
  3. 要检查 Kubernetes Worker 节点的版本,请运行以下命令:

    $ kubectl get node -o custom-columns=NAME:.metadata.name,VERSION:.status.nodeInfo.kubeletVersion
  4. 确认集群的 Kubernetes 服务器版本与 Worker 节点的版本相匹配,其中的版本偏差在可接受的范围内。有关更多信息,请参阅 Kubernetes 网站上的版本偏差策略
    **重要提示:**集群和 Worker 节点的补丁版本可能不同(例如,对于集群,版本为 v1.21.x;对于 Worker 节点,版本为 v1.21.y)。如果集群和 Worker 节点版本不兼容,则使用 eksctl 或 AWS CloudFormation 创建新的节点组。或者,使用兼容的 Kubernetes 版本创建新的托管节点组(例如,Kubernetes:v1.21;平台:eks.1 及更高版本)。然后,删除包含不兼容的 Kubernetes 版本的节点组。

  5. 确认 Kubernetes 控制面板可以与 Worker 节点进行通信。根据 Amazon EKS 安全组要求和注意事项中的必要规则检查防火墙规则。然后,验证节点是否处于 Ready 状态。

AWS 官方
AWS 官方已更新 3 个月前