【问题标题】:Wait for kubernetes job to complete on either failure/success using command line使用命令行等待 kubernetes 作业在失败/成功时完成
【发布时间】:2019-07-31 02:10:18
【问题描述】:

等待 Kubernetes 作业完成的最佳方式是什么?我注意到很多使用建议:

kubectl wait --for=condition=complete job/myjob

但我认为只有在工作成功的情况下才有效。如果失败,我必须做类似的事情:

kubectl wait --for=condition=failure job/myjob

有没有办法使用等待来等待这两个条件?如果没有,等待工作成功或失败的最佳方法是什么?

【问题讨论】:

    标签: kubernetes wait jobs kubectl kubernetes-jobs


    【解决方案1】:

    将第一个等待条件作为子进程运行并捕获其 PID。如果条件满足,该进程将退出,退出码为0。

    kubectl wait --for=condition=complete job/myjob &
    completion_pid=$!
    

    对失败等待条件执行相同的操作。这里的诀窍是添加&& exit 1,以便子进程在作业失败时返回非零退出代码。

    kubectl wait --for=condition=failed job/myjob && exit 1 &
    failure_pid=$! 
    

    然后使用 Bash 内置 wait -n $PID1 $PID2 等待条件之一成功。该命令将捕获第一个要退出的进程的退出代码:

    wait -n $completion_pid $failure_pid
    

    最后,你可以查看wait -n的实际退出码,看看作业是否失败:

    exit_code=$?
    
    if (( $exit_code == 0 )); then
      echo "Job completed"
    else
      echo "Job failed with exit code ${exit_code}, exiting..."
    fi
    
    exit $exit_code
    

    完整示例:

    # wait for completion as background process - capture PID
    kubectl wait --for=condition=complete job/myjob &
    completion_pid=$!
    
    # wait for failure as background process - capture PID
    kubectl wait --for=condition=failed job/myjob && exit 1 &
    failure_pid=$! 
    
    # capture exit code of the first subprocess to exit
    wait -n $completion_pid $failure_pid
    
    # store exit code in variable
    exit_code=$?
    
    if (( $exit_code == 0 )); then
      echo "Job completed"
    else
      echo "Job failed with exit code ${exit_code}, exiting..."
    fi
    
    exit $exit_code
    

    【讨论】:

    • 您可以使用if wait ...,而不是将退出代码存储在变量中。
    • 我认为--for=condition=failure 应该是--for=condition=failed
    • @JamesMcLaughlin 你是对的,可以在 API 参考 kubectl explain job.status.conditions.type 中看到。我已经更新了代码:)
    • @Exagone313 你是对的 - 在原始脚本中我使用了陷阱,所以我在其他地方使用了退出代码。
    • wait -n 在 MacOS 上不可用 :(
    【解决方案2】:

    你可以利用--timeout=0时的行为。

    在这种情况下,命令行会立即返回结果代码 0 或 1。下面是一个示例:

    retval_complete=1
    retval_failed=1
    while [[ $retval_complete -ne 0 ]] && [[ $retval_failed -ne 0 ]]; do
      sleep 5
      output=$(kubectl wait --for=condition=failed job/job-name --timeout=0 2>&1)
      retval_failed=$?
      output=$(kubectl wait --for=condition=complete job/job-name --timeout=0 2>&1)
      retval_complete=$?
    done
    
    if [ $retval_failed -eq 0 ]; then
        echo "Job failed. Please check logs."
        exit 1
    fi
    

    所以当condition=failedcondition=complete 为真时,执行将退出while 循环(retval_completeretval_failed 将是0)。

    接下来,您只需要检查并按照您想要的条件进行操作。就我而言,我想快速失败并在作业失败时停止执行。

    【讨论】:

      【解决方案3】:

      wait -n 方法对我不起作用,因为我需要它同时在 Linux 和 Mac 上工作。

      我对 Clayton 提供的答案进行了一些改进,因为他的脚本在启用 set -e -E 的情况下无法工作。即使在这种情况下,以下内容也将起作用。

      while true; do
        if kubectl wait --for=condition=complete --timeout=0 job/name 2>/dev/null; then
          job_result=0
          break
        fi
      
        if kubectl wait --for=condition=failed --timeout=0 job/name 2>/dev/null; then
          job_result=1
          break
        fi
      
        sleep 3
      done
      
      if [[ $job_result -eq 1 ]]; then
          echo "Job failed!"
          exit 1
      fi
      
      echo "Job succeeded"
      

      您可能需要添加超时以避免无限循环,具体取决于您的情况。

      【讨论】:

      • 我会问为什么只使用set -e 是不够的,这会识别错误命令。那我就不需要检查失败的情况了?? @马丁梅尔卡
      • 当您调用 kubectl wait --for=condition=failed --timeout=0 job/name 并且 pod 的状态为 not failed 时,该命令将以非零退出代码退出。启用set -e 后,将导致整个脚本终止。这里的逻辑是“当kubectl wait 以非零代码退出时,继续轮询它”。我们只希望脚本在 kubectl wait 以零退出代码退出时退出,因为这意味着 pod 要么完成要么失败。
      • 但 pod 状态通常会出现“错误”,例如 0/1(错误),因此可能在 first 失败的作业集中 -e 会正确退出脚本?
      • 对不起,我没有理解你的意思。 kubectl wait 不会以 pod 状态的退出代码退出。如果 pod 当前 处于完成(成功)状态,kubectl wait --for=condition=complete --timeout=0 job/name 将以 0(成功)退出。 1(错误)否则(即,如果 pod 当前仍在运行/挂起/失败/其他)。同样,如果 pod 当前处于失败状态,kubectl wait --for=condition=failed --timeout=0 job/name 将以 0(成功)退出。之所以这样做,是因为没有kubectl cmd 可以“在 pod 成功或错误时退出”。
      【解决方案4】:

      kubectl wait --for=condition=<condition name 正在等待特定条件,因此目前无法指定多个条件。

      我的解决方法是使用oc get --wait,如果目标资源更新,--wait 将关闭该命令。我将使用oc get --wait 监控作业的status 部分,直到更新statusstatus 部分的更新意味着作业已完成并具有一些状态条件。

      如果作业成功完成,则status.conditions.type 会立即更新为Complete。但如果作业失败,则作业 pod 将自动重新启动,无论 restartPolicyOnFailure 还是 Never。但是如果在第一次更新后不更新为Complete,我们可以认为工作是Failed状态。

      看我的测试证据如下。

      • 测试成功完成的作业 yaml
      # vim 工作.yml api版本:批处理/v1 种类:工作 元数据: 名称:pi 规格: 并行度:1 完成:1 模板: 元数据: 名称:pi 规格: 容器: - 名称:pi 图片:perl 命令:["perl", "-w​​le", "exit 0"] 重启策略:从不
      • 如果成功完成工作,它将显示Complete
      # oc create -f job.yml && oc get job/pi -o=jsonpath='{.status}' -w && oc get job/pi -o=jsonpath='{.status.conditions[*].type}' | grep -i -E '失败|完成' ||回显“失败” job.batch/pi 创建 map[startTime:2019-03-09T12:30:16Z active:1]完成
      • 用于测试的作业 yaml 完成失败
      # vim 工作.yml api版本:批处理/v1 种类:工作 元数据: 名称:pi 规格: 并行度:1 完成:1 模板: 元数据: 名称:pi 规格: 容器: - 名称:pi 图片:perl 命令:["perl", "-w​​le", "exit 1"] 重启策略:从不
      • 如果第一个工作更新不是Complete,它将显示Failed。删除现有作业资源后测试是否。
      # oc 删除作业 pi job.batch "pi" 已删除 # oc create -f job.yml && oc get job/pi -o=jsonpath='{.status}' -w && oc get job/pi -o=jsonpath='{.status.conditions[*].type}' | grep -i -E '失败|完成' ||回显“失败” job.batch/pi 创建 地图[活动:1 开始时间:2019-03-09T12:31:05Z]失败

      希望对你有帮助。 :)

      【讨论】:

      • 我最终只是制作了一个简单的脚本来检查您所显示的状态:until [[ $SECONDS -gt $end ]] || [[ $(kubectl get jobs $job_name -o jsonpath='{.status.conditions[?(@.type=="Failed")].status}') == "True" ]] || [[ $(kubectl get jobs $job_name -o jsonpath='{.status.conditions[?(@.type=="Complete")].status}') == "True" ]]; do
      • 太好了,很抱歉展示了openshift cli 示例。不过可以追上kubernetes cli,太好了!
      • 实际上没有--wait,-w确实代表--watch
      猜你喜欢
      • 1970-01-01
      • 2021-06-16
      • 1970-01-01
      • 2011-10-20
      • 2015-11-17
      • 1970-01-01
      • 2015-06-13
      • 2016-08-31
      • 1970-01-01
      相关资源
      最近更新 更多