利用Jenkins预约执行部署任务

2026-06-29 07:41:55 0点赞 0收藏 0评论

工作中需要使用Jenkins在没有插件的前提下实现预约执行任务,思考了两种思路并记录。

前提:

工作环境安装有Jenkins,但是无法安装预约功能的插件。使用简单的循环定时进行发布显然不是个好的选择。

思路:

需求:

需要预约(打开任务后)可以执行部署任务,但是只会在约定时间执行一次,执行完成后关闭任务。需要能支持多部署任务的执行。

思路及代码:

由于需求中需要执行、关闭其他任务,在我所知的范围内,最简单的是使用Jenkins的api。 然后是预约定时问题,考虑了两种逻辑。 逻辑1、 每分钟执行,执行后判断时间是否匹配,不匹配则不执行部署任务。

代码如下:

#!/bin/bash #set -euo pipefail set -uo pipefail # ================== 配置区 ================== #=====设置配置 JENKINS_URL="http://192.168.1.22:8086" # 替换为你的 Jenkins 地址 USERNAME="username" # 你的 Jenkins 用户名 API_TOKEN="117da895212c8e1582a4b5eb5ed6213cee" # 替换为你的 API Token THIS_JOB_NAME="定时发布系统" # 当前调度任务名 #=====定时配置 TARGET_DATETIME="2026-02-05 07:40" # 目标触发时间(注意格式) # 按执行顺序列出任务名(支持中文,依次按顺序执行) JOBS=( "代码同步_backend_prd" "部署_webfrontend_prd" "部署并启动_backend_prd" ) MAX_WAIT_MINUTES=60 #超时时间,单位分钟 # ========================================== # ===== 清理函数 ===== cleanup() { # 禁用调度器 if [[ "$SHOULD_DISABLE_SCHEDULER" == "true" ]]; then echo "[$(date)] Disabling scheduler task..." curl -s -f -X POST "${JENKINS_URL}/job/${THIS_JOB_NAME}/disable" --user "${USERNAME}:${API_TOKEN}" --output /dev/null && echo "Disabled." || echo "Warn: disable failed." else echo "[$(date)] Skip cleanup (not triggered)." fi } # ======================== # ===== 注册 trap ===== # 在 EXIT 时总是执行 cleanup trap cleanup EXIT # ========================= # ===== 全局标志 ===== SHOULD_DISABLE_SCHEDULER=false # ========================= CURRENT=$(date '+%Y-%m-%d %H:%M') BUILD_NUMBER=${BUILD_NUMBER:-0} # Jenkins 注入的环境变量 # ===== 清理上一次的“跳过”构建,避免过多任务记录 ===== if [[ -f ".last_skip_build" ]]; then LAST_SKIP=$(cat .last_skip_build) if [[ -n "$LAST_SKIP" ]]; then echo "[$(date)] Cleaning up skipped build #$LAST_SKIP" curl -s -f -X POST "${JENKINS_URL}/job/${THIS_JOB_NAME}/${LAST_SKIP}/doDelete" --user "${USERNAME}:${API_TOKEN}" --output /dev/null && echo "Deleted." || echo "Cleanup failed." fi rm -f .last_skip_build fi # ================================ echo "[$(date)] Current: $CURRENT, Target: $TARGET_DATETIME" # 转为时间戳(兼容 Linux) target_ts=$(date -d "$TARGET_DATETIME" +%s 2>/dev/null) if [[ $? -ne 0 ]]; then echo "[$(date)] ERROR: Failed to parse TARGET_DATETIME" exit 1 fi current_ts=$(date +%s) deadline_ts=$(( target_ts + 60 )) #多一分钟窗口时间 if (( current_ts < target_ts )); then # 还没到时间:跳过,不禁用 echo "[$(date)] Skip: before target time. Waiting..." echo "$BUILD_NUMBER" > .last_skip_build exit 0 elif (( current_ts > deadline_ts )); then # 已超时:跳过 + 禁用 echo "[$(date)] ERROR: Missed target time . Disabling scheduler." SHOULD_DISABLE_SCHEDULER=true # 触发 cleanup 中的 disable echo "$BUILD_NUMBER" > .last_skip_build exit 0 else # 在窗口期内:继续执行 echo "[$(date)] Within grace period. Proceeding..." fi # ========================== echo "Start sequential execution..." # ===== 进入目标时间后,标记为需要禁用 ===== SHOULD_DISABLE_SCHEDULER=true # ============================================== trigger_and_wait() { local job_name="$1" echo "Triggering: $job_name" # 安全获取 nextBuildNumber local next_build_raw next_build next_build_raw=$(curl -s --user "${USERNAME}:${API_TOKEN}" "${JENKINS_URL}/job/${job_name}/api/json?tree=nextBuildNumber" 2>/dev/null || echo "{}") next_build=$(echo "$next_build_raw" | grep -o '"nextBuildNumber":[[:space:]]*[0-9]+' | sed 's/[^0-9]*//g' || echo "") if [[ -z "$next_build" ]]; then echo "ERROR: Cannot get build number for $job_name" exit 1 fi echo "Next build: $next_build" # 触发构建 if ! curl -s -f -X POST "${JENKINS_URL}/job/${job_name}/build" --user "${USERNAME}:${API_TOKEN}" --output /dev/null; then echo "ERROR: Failed to trigger $job_name" exit 1 fi # 等待完成 local check_count=0 max_checks=$(( MAX_WAIT_MINUTES * 6 )) while true; do sleep 10 ((check_count++)) echo "Waiting... (check #$check_count)" [[ $check_count -gt $max_checks ]] && { echo "TIMEOUT"; exit 1; } local result_raw result result_raw=$(curl -s --user "${USERNAME}:${API_TOKEN}" "${JENKINS_URL}/job/${job_name}/${next_build}/api/json?tree=result" 2>/dev/null || echo '{"result":null}') result=$(echo "$result_raw" | grep -o '"result":"[A-Z]*"' | cut -d'"' -f4 || echo "") if [[ -z "$result" ]]; then continue elif [[ "$result" == "SUCCESS" ]]; then echo "✓ Success" return 0 else echo "✗ Failed: $result" exit 1 fi done } for job in "${JOBS[@]}"; do trigger_and_wait "$job" done echo "DONE."


优势:

  1. 可以指定具体年份

  2. 时间设置符合直觉

  3. 错误点击执行不会导致执行关联任务

劣势:

  1. 逻辑较为复杂,不简洁

  2. 每分钟执行一次,有性能占用

  3. 会有非常多的任务记录(后添加自动删除任务记录的逻辑)


逻辑2、 使用定时任务功能,预约时间后,任务会执行一次,然后禁用自己。

#!/bin/bash #set -euo pipefail set -uo pipefail # =====================================================按执行顺序列出任务名(支持中文,依次按顺序执行)=============================== JOBS=( "代码同步_backend_prd" "部署_webfrontend_prd" "部署并启动_backend_prd" ) # ================== 配置区 ================== JENKINS_URL="http://192.168.1.22:8086" # 替换为你的 Jenkins 地址 USERNAME="username" # 你的 Jenkins 用户名 API_TOKEN="117da895212c8e1582a4b5eb5ed6213cee" # 替换为你的 API Token THIS_JOB_NAME="定时发布系统" # 当前调度任务名 MAX_WAIT_MINUTES=60 #超时时间,单位分钟 # ========================================== # ===== 关键:立即禁用自己,确保只运行一次 ===== echo "[$(date)] Disabling scheduler task to ensure one-time execution..." if curl -s -f -X POST "${JENKINS_URL}//job/${THIS_JOB_NAME}/disable" --user "${USERNAME}:${API_TOKEN}" --output /dev/null; then echo "[$(date)] Scheduler disabled successfully." else echo "[$(date)] [WARN] Failed to disable scheduler (may run again next year)." fi # =================================================== # ===== 执行任务函数===== trigger_and_wait() { local job_name="$1" echo "[$(date)] Triggering: $job_name" # 获取下一次构建号 local next_build_raw next_build next_build_raw=$(curl -s --user "${USERNAME}:${API_TOKEN}" "${JENKINS_URL}/job/${job_name}/api/json?tree=nextBuildNumber" 2>/dev/null || echo "{}") next_build=$(echo "$next_build_raw" | grep -o '"nextBuildNumber":[[:space:]]*[0-9]+' | sed 's/[^0-9]*//g' || echo "") if [[ -z "$next_build" ]]; then echo "[$(date)] ERROR: Cannot get build number for $job_name" exit 1 fi echo "[$(date)] Next build: $next_build" # 触发构建 if ! curl -s -f -X POST "${JENKINS_URL}/job/${job_name}/build" --user "${USERNAME}:${API_TOKEN}" --output /dev/null; then echo "[$(date)] ERROR: Failed to trigger $job_name" exit 1 fi # 等待完成(带超时) local check_count=0 max_checks=$(( MAX_WAIT_MINUTES * 6 )) while true; do sleep 10 ((check_count++)) echo "[$(date)] Waiting... (check #$check_count)" if (( check_count > max_checks )); then echo "[$(date)] TIMEOUT: Job did not complete in ${MAX_WAIT_MINUTES} minutes" exit 1 fi local result_raw result result_raw=$(curl -s --user "${USERNAME}:${API_TOKEN}" "${JENKINS_URL}/job/${job_name}/${next_build}/api/json?tree=result" 2>/dev/null || echo '{"result":null}') result=$(echo "$result_raw" | grep -o '"result":"[A-Z]*"' | cut -d'"' -f4 || echo "") if [[ -z "$result" ]]; then continue elif [[ "$result" == "SUCCESS" ]]; then echo "[$(date)] ✓ Success: $job_name" return 0 else echo "[$(date)] ✗ Failed: $job_name with result $result" exit 1 fi done } # =================================================== # ===== 按顺序执行所有任务 ===== echo "[$(date)] Starting sequential execution of ${#JOBS[@]} jobs..." for job in "${JOBS[@]}"; do trigger_and_wait "$job" done echo "[$(date)] DONE. All jobs completed successfully."


优势:

  1. 代码更简单、高效

劣势:

  1. 不在设定时间点击执行任务会导致关联任务被执行

  2. 依赖Cron 表达式,个人不喜欢(主观)

利用Jenkins预约执行部署任务
展开 收起
0评论

当前文章无评论,是时候发表评论了
提示信息

取消
确认
评论举报

相关文章推荐

更多精彩文章
更多精彩文章
最新文章 热门文章
0
扫一下,分享更方便,购买更轻松