在升级到 GKE 版本 1.35 之前配置 exec 探测超时


本页面介绍了如何在将 Google Kubernetes Engine (GKE) 集群升级到 1.35 版及更高版本之前,通过为活性探测、就绪性探测和启动探测中的命令设置超时时间来准备这些探测。

关于 exec 探测的超时

从 GKE 版本 1.35 开始,Kubernetes 会强制执行活跃性探测、就绪性探测和启动探测的 exec 字段中命令的超时。探测规范中的 timeoutSeconds 字段定义了 Kubernetes 等待探测完成任何操作的时间。如果省略此字段,则默认值为 1,这意味着任何操作都有一秒钟的时间来完成。

在 1.35 之前的 GKE 版本中,Kubernetes 会忽略 exec 探测命令中 timeoutSeconds 字段的值。例如,假设某个活性探测器具有以下属性:

  • timeoutSeconds 字段中的值为 5
  • exec.command 字段中需要 10 秒才能完成的命令。

在 1.35 之前的版本中,Kubernetes 会忽略此 5 秒超时,并错误地将探测报告为成功。在 1.35 版及更高版本中,Kubernetes 会在 5 秒后正确地使探测失败。

Kubernetes 忽略 exec 探测超时时间的这种行为可能会导致探测无限期运行,这可能会隐藏应用存在的问题,也可能会导致不可预测的行为。在 GKE 版本 1.35 及更高版本中,Kubernetes 会正确强制执行命令超时,从而实现与开源 Kubernetes 一致且可预测的探测行为。

强制执行 exec 探测超时带来的影响

这是 GKE 1.35 及更高版本中的一项重大变更,对于在 GKE 上运行的工作负载的稳定性和可靠性而言是必要的。将集群升级到 1.35 及更高版本后,如果工作负载具有以下属性之一的 exec 探测,您可能会发现工作负载出现意外行为:

  • 省略 timeoutSeconds 字段:在 1.35 及更高版本中,这些探测器有一秒的时间来成功完成命令。如果该命令在一秒内未成功完成,探测器将正确报告失败。
  • 指定较短的超时时间:在 1.35 及更高版本中,如果探测的超时时间短于命令完成时间,则会正确报告失败。

在 GKE 1.34 版及更早版本中,如果 exec 探测满足以下任一条件,Kubernetes 会报告错误。不过,这些执行探测中的命令仍可运行完成,因为探测错误不是探测失败

如果您未指定更准确的超时时长,并且命令的完成时间超过了现有的超时时长,则探测将在 1.35 及更高版本中报告失败。如果探测失败,系统会根据探测类型采取以下行为:

  • 活跃性探测:如果活跃性探测因命令超时而失败,Kubernetes 会假定应用失败并重启容器。如果探测反复失败,您的 Pod 可能会陷入崩溃循环,并显示 CrashLoopBackOff Pod 状态。
  • 就绪性探测:如果就绪性探测因命令超时而失败,Kubernetes 会更新 Ready Pod 条件并将其状态设为 False。在探测成功之前,Kubernetes 不会向 Pod 发送任何流量。如果支持某项服务的所有 Pod 的 Ready 条件都处于 False 状态,您可能会注意到该服务出现中断。
  • 启动探测:如果启动探测失败,Kubernetes 会假定应用启动失败并重启容器。如果探测反复失败,您的 Pod 可能会卡在崩溃循环中,并显示 CrashLoopBackOff Pod 状态。

已暂停自动升级

GKE 在检测到集群中的工作负载可能会受到此更改的影响时,会暂停自动升级到 1.35 版。如果版本 1.35 是控制平面和节点的自动升级目标版本,并且满足以下任一条件,GKE 会恢复自动升级:

  • 您已使用超时值更新工作负载探测,并且 GKE 在 7 天内未检测到潜在问题。
  • 版本 1.34 在您的发布渠道中达到支持终止日期

确定受影响的集群或工作负载

以下部分将介绍如何识别可能受此变更影响的集群或工作负载。

使用命令行检查 Kubernetes 事件

在 GKE 1.34 及更早版本中,您可以手动检查集群中的 Kubernetes 事件,以查找完成时间长于现有超时时限的 exec 探测。Kubernetes 会为这些探测添加一条包含 command timed out 消息的事件。此方法有助于识别因超时值过短而已经出现问题的工作负载。

如需查找受影响的工作负载,请执行以下操作之一:

使用脚本查找多个集群中的工作负载

以下 bash 脚本会遍历 kubeconfig 文件中的所有集群,以查找受影响的工作负载。此脚本会检查所有现有且可访问的 Kubernetes 上下文中的执行探测超时错误,并将检查结果写入名为 affected_workloads_report.txt 的文本文件。如需运行此脚本,请按以下步骤操作:

  1. 将以下脚本保存为 execprobe-timeouts.sh

    #!/bin/bash
    
    # This script checks for exec probe timeouts across all existing and reachable
    # Kubernetes contexts and writes the findings to a text file, with one
    # row for each affected workload, including its cluster name.
    
    # --- Configuration ---
    OUTPUT_FILE="affected_workloads_report.txt"
    # -------------------
    
    # Check if kubectl and jq are installed
    if ! command -v kubectl &> /dev/null || ! command -v jq &> /dev/null; then
        echo "Error: kubectl and jq are required to run this script." >&2
        exit 1
    fi
    
    echo "Fetching all contexts from your kubeconfig..."
    
    # Initialize the report file with a formatted header
    printf "%-40s | %s\n" "Cluster Context" "Impacted Workload" > "$OUTPUT_FILE"
    
    # Get all context names from the kubeconfig file
    CONTEXTS=$(kubectl config get-contexts -o name)
    
    if [[ -z "$CONTEXTS" ]]; then
      echo "No Kubernetes contexts found in your kubeconfig file."
      exit 0
    fi
    
    echo "Verifying each context and checking for probe timeouts..."
    echo "=================================================="
    
    # Loop through each context
    for CONTEXT in $CONTEXTS; do
      echo "--- Checking context: $CONTEXT ---"
    
      # Check if the cluster is reachable by running a lightweight command
      if kubectl --context="$CONTEXT" get ns --request-timeout=1s > /dev/null 2>&1; then
        echo "Context '$CONTEXT' is reachable. Checking for timeouts..."
    
        # Find timeout events based on the logic from the documentation
        AFFECTED_WORKLOADS_LIST=$(kubectl --context="$CONTEXT" get events --all-namespaces -o json | jq -r '.items[] | select((.involvedObject.namespace | type == "string") and (.involvedObject.namespace | endswith("-system") | not) and (.message | test("^(Liveness|Readiness|Startup) probe errored(.*): command timed out(.*)|^ * probe errored and resulted in .* state: command timed out.*"))) | .involvedObject.kind + "/" + .involvedObject.name' | uniq)
    
        if [[ -n "$AFFECTED_WORKLOADS_LIST" ]]; then
          echo "Found potentially affected workloads in context '$CONTEXT'."
    
          # Loop through each affected workload and write a new row to the report
          # pairing the context with the workload.
          while IFS= read -r WORKLOAD; do
            printf "%-40s | %s\n" "$CONTEXT" "$WORKLOAD" >> "$OUTPUT_FILE"
          done <<< "$AFFECTED_WORKLOADS_LIST"
        else
          echo "No workloads with exec probe timeouts found in context '$CONTEXT'."
        fi
      else
        echo "Context '$CONTEXT' is not reachable or the cluster does not exist. Skipping."
      fi
      echo "--------------------------------------------------"
    done
    
    echo "=================================================="
    echo "Script finished."
    echo "A detailed report of affected workloads has been saved to: $OUTPUT_FILE"
    
  2. 运行脚本:

    bash execprobe-timeouts.sh
    
  3. 读取 affected_workloads_report.txt 文件的内容:

    cat affected_workloads_report.txt
    

    输出类似于以下内容:

    Cluster Context                   | Impacted Workload
    -----------------------------------------|----------------------------
    gke_my-project_us-central1-c_cluster-1   | Pod/liveness1-exec
    gke_my-project_us-central1-c_cluster-1   | Deployment/another-buggy-app
    gke_my-project_us-east1-b_cluster-2      | Pod/startup-probe-test
    

使用命令行查找特定集群中的工作负载

如需确定特定集群中受影响的工作负载,您可以使用 kubectl 工具检查是否存在执行探测超时错误。对于运行 1.34 版或更低版本的每个 GKE 集群,请按以下步骤操作:

  1. 连接到该集群:

    gcloud container clusters get-credentials CLUSTER_NAME \
        --location=LOCATION
    

    替换以下内容:

    • CLUSTER_NAME:集群的名称。
    • LOCATION:集群控制平面的位置,例如 us-central1
  2. 检查是否有事件表明 exec 探测出现超时错误:

    kubectl get events --all-namespaces -o json |
        jq -r '.items[] | select((.involvedObject.namespace | type == "string") and (.involvedObject.namespace | endswith("-system") | not) and (.message | test("^(Liveness|Readiness|Startup) probe errored(.*): command timed out(.*)|^ * probe errored and resulted in .* state: command timed out.*"))) | "\(.involvedObject.kind)/\(.involvedObject.name)        Namespace: \(.involvedObject.namespace)"'
    

    此命令会忽略许多系统命名空间中的工作负载。如果存在受影响的工作负载,则输出类似于以下内容:

    Pod/liveness1-exec      Namespace: default
    
  3. 针对运行 GKE 1.35 之前版本的每个集群重复执行上述步骤。

在 Cloud Logging 中查找受影响的集群和工作负载

  1. 在 Google Cloud 控制台中,前往 Logs Explorer 页面。

    转到 Logs Explorer

  2. 如需打开查询编辑器,请点击显示查询切换开关。

  3. 请运行以下查询:

    jsonPayload.message=~" probe errored and resulted in .* state: command timed out" OR jsonPayload.message=~" probe errored : command timed out"
    

    输出是因命令完成时间超过配置的超时时间段而导致的探测错误列表。

在升级到 1.35 之前更新受影响的工作负载

确定受影响的工作负载后,您必须更新受影响的探测器。

  1. 查看每个受影响的 Pod 的活跃性、就绪性和启动探测,并确定合适的 timeoutSeconds 值。此值应足够长,以便命令在正常情况下成功执行。如需了解详情,请参阅配置活跃性、就绪性和启动探测
  2. 打开受影响工作负载的清单文件,然后添加或修改活跃性、就绪性或启动探测的 timeoutSeconds 字段。例如,以下活跃度探测在 timeoutSeconds 字段中的值为 10

    spec:
      containers:
      - name: my-container
        image: my-image
        livenessProbe:
          exec:
            command:
            - cat
            - /tmp/healthy
          initialDelaySeconds: 5
          periodSeconds: 5
          timeoutSeconds: 10
    
  3. 将更新后的清单应用到您的集群。

  4. 按照使用命令行检查 Kubernetes 事件中的步骤检查更新后的探测器是否存在错误。

更新并测试所有受影响的工作负载后,您可以将集群升级到 GKE 1.35 版本。