查找并移除影响集群性能的隐藏 pod

在某些情况下,pod 会被关闭,但会继续在后台运行。 从 OpenShift® Console 中看不到 pod,运行 oc get pods 命令时也不会返回 pod。 但是,pod 会消耗集群资源,从而影响集群的性能。 清理隐藏的 pod 可以释放资源,提高集群性能。

故障现象

考虑到当前的工作量,群集使用的资源超出了您的预期。 例如:
  • 挂载硬盘的响应速度比平时慢。
  • 群集使用的 CPU 超过当前请求和当前已知 pod 所定义的限制。
  • 群集使用的内存超出了当前请求和当前已知 pod 所定义的限制。

原因

当关闭或删除 pod 时,Red Hat® OpenShift Container Platform 通常会完成一系列清理步骤,其中包括从容器运行时界面中删除 pod。 但是,如果 pod 被强行删除或杀死,则可能无法进行清理。 如果不进行清理,pod 将继续运行并消耗群集资源。

环境

任何 Red Hat OpenShift Container Platform 集群都可能出现此问题。

诊断问题

群集管理员 群集管理员可以使用以下任一方法识别隐藏的 pod:

手动检查每个节点是否有隐藏的 pod
  1. 运行以下命令可获得正在使用且Red Hat OpenShift Container Platform已知的 pod 的完整列表:
    oc get pods -owide

    此命令将返回每个已知 pod 运行所在节点的信息。

  2. 运行以下命令,以后续步骤所需的格式获取节点名称:
    oc get nodes -oname
  3. 群集中的每个节点
    1. NODE_NAME 环境变量设置为节点名称:
      export NODE_NAME=<node-name>
    2. 以调试模式访问节点,并获取容器运行时接口已知的 pod 列表。
      oc debug -q ${NODE_NAME} -- chroot /host /bin/bash -c "crictl pods --namespace <namespace> -o yaml"
      重要: 在运行命令前,请替换 <namespace>

      对群集上要调查的每个命名空间重复此步骤。

  4. oc get pods -owide 命令返回的 pod 列表与在每个节点上运行的 crictl pods 命令返回的 pod 列表进行比较。
    • 如果没有不同 pod 数量的节点,则无需采取其他措施。
    • 如果有不同 pod 数量的节点,请继续下一步。
  5. 记录 crictl pods 命令返回的 pod 数量与 oc get pods -owide 命令返回的 pod 数量不同的节点和命名空间列表。 然后,继续 解决问题
运行脚本检查隐藏的 pod

您可以从群集节点或可以连接到群集的工作站运行脚本。 工作站必须有 OpenShift 命令行界面(oc CLI)。

最佳实践:如果为安装设置了环境变量,就可以完全按照编写的内容运行本任务中的许多命令。 有关说明,请参阅 设置安装环境变量

确保在运行本任务中的命令之前,为环境变量提供源代码。

  1. 在群集节点或可连接群集的工作站上保存以下脚本。

    将脚本保存为 hidden-pods.sh

    #!/usr/bin/env bash
    set -o pipefail -o noclobber -o nounset
    
    login() {
      token=$1
      server=$2
      namespace=$3
      if [[ "${token}" != *"token"* ]] || [[ "${server}" != *"server"* ]]; then
        usage
      fi
      oc login "${token}" "${server}"
      oc project "${namespace}"
    }
    
    usage() {
      echo "Usage: hidden-pods.sh [-h|--help] --token=xxxx --server=xxxx <namespace>"
      echo "Example: ./hidden-pods.sh --token=xxxxx --server=xxxxx zen"
      exit 1
    }
    
    print_warn() {
      echo "############# WARNING ###############"
      echo "Mismatch of replicas for ${1}"
      echo "Found ${2}, expected ${3}"
      echo "#####################################"
    }
    
    check_replicas() {
      namespace=$1
      rs=$(oc get rs --no-headers | awk '{print $1}' | wc -l)
      echo "There are ${rs} replicasets"
      replicas=$(oc get rs --no-headers | awk '$3 = $4 {print $1}' | wc -l)
      echo "There are ${replicas} replicasets with correct replica count"
    
      for deploy in $(oc get rs --no-headers | awk '{print $1}'); do
        echo "ReplicaSet: ${deploy}"
        requiredReplicas=$(oc get rs ${deploy} --no-headers | awk '{print $3}')
        echo "Required replicas: ${requiredReplicas}"
        reportedReplicas=$(oc get rs ${deploy} --no-headers | awk '{print $4}')
        echo "Reported replicas: ${reportedReplicas}"
        if [ "${requiredReplicas}" -ne "${reportedReplicas}" ]; then
          print_warn ${deploy} ${reportedReplicas} ${requiredReplicas}
        fi
        actualReplicas=0
        for node in ${nodeList}; do
          podDetected=$(grep "${deploy}" ${node})
          numPodsDetected=$(grep -o "${deploy}" ${node} | wc -l)
          if [ ${numPodsDetected} -gt 0 ]; then 
            echo "Node: ${node}"
            echo ${podDetected}
            echo "Number of pods: ${numPodsDetected}"
            ((actualReplicas = ${actualReplicas} + ${numPodsDetected}))
          fi 
        done
        if [ "${actualReplicas}" -ne "${requiredReplicas}" ]; then
          print_warn ${deploy} ${actualReplicas} ${requiredReplicas}
        fi
        echo "Actual replicas: ${actualReplicas}"
        echo
      done
    }
    
    check_sts() {
      namespace=$1
      statefulsets=$(oc get sts --no-headers | awk '{sub(/\//," ")} $2 = $3 {print $1}' | wc -l)
      echo "There are ${statefulsets} statefulsets with correct replica count"
    
      for sts in $(oc get sts --no-headers | awk '{print $1}'); do
        echo "StatefulSet: ${sts}"
        requiredReplicas=$(oc get sts ${sts} --no-headers | awk '{sub(/\//," ")} {print $2}')
        echo "Required replicas: ${requiredReplicas}"
        reportedReplicas=$(oc get sts ${sts} --no-headers | awk '{sub(/\//," ")} {print $3}')
        echo "Reported replicas: ${reportedReplicas}"
        if [ "${requiredReplicas}" -ne "${reportedReplicas}" ]; then
          print_warn ${sts} ${reportedReplicas} ${requiredReplicas}
        fi
        actualReplicas=0
        for node in ${nodeList}; do
          podDetected=$(grep "${sts}" ${node})
          numPodsDetected=$(grep -o "${sts}" ${node} | wc -l)
          if [ ${numPodsDetected} -gt 0 ]; then 
            echo "Node: ${node}"
            echo ${podDetected}
            echo "Number of pods: ${numPodsDetected}"
            ((actualReplicas = ${actualReplicas} + ${numPodsDetected}))
          fi 
        done
        if [ "${actualReplicas}" -ne "${requiredReplicas}" ]; then
          print_warn ${sts} ${actualReplicas} ${requiredReplicas}
        fi
        echo "Actual replicas: ${actualReplicas}"
        echo
      done
    }
    
    main() {
      creds=("$@")
    
      if [ "${#creds[@]}" -ne 3 ] || [ "${creds[0]}" = "-h" ] || [ "${creds[0]}" = "--help" ]; then
        usage
      fi
    
      login "${creds[0]}" "${creds[1]}" "${creds[2]}"
    
      numberNodes=$(oc get nodes -oname | wc -l)
      nodeList=$(oc get nodes -oname);
      mkdir node
      echo "Scanning ${numberNodes} nodes"
      for node in ${nodeList}; do
        oc debug -q ${node} -- chroot /host /bin/bash -c "crictl pods --namespace ${creds[2]} -o yaml" | grep "io.kubernetes.pod.name:" 1> ${node} 2>/dev/null
      done
    
      check_replicas "${creds[2]}"
      check_sts "${creds[2]}"
    
    #  rm -rf node
    }
    
    main "$@"
  2. 对于要调查的群集上的每个命名空间:
    1. NAMESPACE 环境变量设置为命名空间的名称
    2. 运行以下脚本:
      ./hidden-pods.sh ${SERVER_ARGUMENTS} ${LOGIN_ARGUMENTS} ${NAMESPACE}
    3. 记录脚本返回警告的节点。 例如:
      Node: node/<node-name>
      Number of pods:        1
      ############# WARNING ###############
      Mismatch of replicas for zen-minio
      Found 4, expected 3
      #####################################
      Actual replicas: 4

解决问题

本节中的步骤旨在解决发现的 pod 数量高于预期 pod 数量的警告。 例如:
Node: node/<node-name>
Number of pods:        1
############# WARNING ###############
Mismatch of replicas for zen-minio
Found 4, expected 3
#####################################
Actual replicas: 4
不过,当找到的 pod 数量少于预期数量时,脚本可能会返回警告:例如
Node: node/<node-name>
Number of pods:        1
############# WARNING ###############
Mismatch of replicas for zen-minio
Found 2, expected 3
#####################################
Actual replicas: 2

如果您看到这些警告,完成本节中的步骤并不能解决这些问题。 您必须分别调查这些问题。

要删除隐藏的 pod,请使用您在完成 诊断问题时编制的节点列表。 对于列表中的每个节点
  1. NODE_NAME 环境变量设置为节点名称:
    export NODE_NAME=<node-name>
  2. 以调试模式访问节点,并运行以下命令重启容器运行时接口:
    oc debug node/${NODE_NAME} -- chroot /host /bin/bash -c 'systemctl restart crio'

当容器运行时界面重新启动时,它只包含活动的已知 pod。