首页 > 解决方案 > 詹金斯计划的备份管道 - 确保所有作业在运行前停止

问题描述

我有一个通宵运行的 Jenkins 管道来进行备份。我需要找到一种方法,仅在所有其他作业完成后才运行管道,并确保同时不会触发其他作业。这应该类似于这个插件https://plugins.jenkins.io/exclusive-execution/正在做的事情。但是声明性管道不支持“独占执行”。

我附上我的 Jenkinsfile 以防万一有人需要看一下:

def BACKUP_DATETIME = new Date().format('yyyy_MM_dd_HH_mm_ss', TimeZone.getTimeZone('Canada/Eastern'))

pipeline {
    agent { label 'master' }
    options {
        //Build options
        disableResume()
        disableConcurrentBuilds()
        buildDiscarder(
          logRotator (
                       artifactDaysToKeepStr: '10',
                       artifactNumToKeepStr: '1',
                       daysToKeepStr: '30',
                       numToKeepStr: '30'
                     )
        )
    }
    triggers { cron('TZ=Canada/Eastern\n0 3 * * *') }
    environment {
        GIT_SSH_KEY_ID = 'gitlab_builduser'
        BACKUP_PREFIX_NAME = 'jenkins-back-'
        BACKUP_REPO_NAME = "jenkins_backups"
        BACKUP_REPO_URL = "git@XXX:XX/${env.BACKUP_REPO_NAME}.git"
        NUMBER_OF_BACKUPS = '7'
        GOOGLE_CHAT_TOKEN = 'XXXXXF'
        JENKINS_VERSION = ''
    }
    stages {
        stage('Init') {
            steps {
                script {
                    env.JENKINS_VERSION = sh(script: "java -jar /usr/share/jenkins/jenkins.war --version", returnStdout: true).trim()
                }
                dir("/tmp") {
                  withCredentials([sshUserPrivateKey(credentialsId: "${env.GIT_SSH_KEY_ID}", keyFileVariable: 'SSH_KEY', usernameVariable: 'SSH_USER')]) {
                    withEnv(["GIT_SSH_COMMAND=ssh -i ${SSH_KEY}"]) {
                        sh '''
                          if [ ! -d ${BACKUP_REPO_NAME} ]
                          then
                            git clone ${BACKUP_REPO_URL}
                          else
                            cd ${BACKUP_REPO_NAME}
                            git fetch origin
                            git reset --hard origin/master
                          fi
                        '''
                    }
                  }
                }
            }
        }
        stage('Backup') {
            steps {
                dir ('jenkins_backup') {
                  sh """
                    chmod +x ./jenkins-backup.sh
                    ./jenkins-backup.sh ${JENKINS_HOME} /tmp/${env.BACKUP_REPO_NAME}/${BACKUP_PREFIX_NAME}_${BACKUP_DATETIME}.tar.gz
                  """
                }
            }
        }
        stage('Upload') {
            steps {
                // TO_DO: SAVE THE BACKUP file
                dir("/tmp/${env.BACKUP_REPO_NAME}") {
                  withCredentials([sshUserPrivateKey(credentialsId: "${env.GIT_SSH_KEY_ID}", keyFileVariable: 'SSH_KEY', usernameVariable: 'SSH_USER')]) {
                    withEnv(["GIT_SSH_COMMAND=ssh -i ${SSH_KEY}"]) {
                        sh '''
                          git config user.email "${SSH_USER}@email.com"
                          git config user.name "${SSH_USER}"
                          # backup only keep the lastest X backup-files
                          ls -tp ${BACKUP_PREFIX_NAME}_*.tar.gz | grep -v '/$' | tail -n +$(expr ${NUMBER_OF_BACKUPS} + 1) | xargs -I {} rm -- {}
                          # Push to git
                          git add --all
                          git status
                          git commit --amend -CHEAD --allow-empty
                          git push --force-with-lease origin master
                          env
                        '''
                    }
                  }
                }

            }
        }
    }
    post {
        failure {
          hangoutsNotify message: "${JOB_BASE_NAME} faild!",token: "${env.GOOGLE_CHAT_TOKEN}",threadByJob: true
        }
    }
}

标签: jenkinsjenkins-pipelinejenkins-groovy

解决方案


我最终编写了一个 groovy 脚本来满足我的需要。该脚本将列出除备份作业之外的所有当前正在运行和排队的作业,如果其中一个列表不为空,则放置一个计时器。当所有作业都处于空闲状态时,备份将继续运行。

我将在此处附上我的脚本:

詹金斯文件:

def BACKUP_DATETIME = new Date().format('yyyy_MM_dd_HH_mm_ss', TimeZone.getTimeZone('Canada/Eastern'))

pipeline {
    agent { label 'master' }
    options {
        //Build options
        disableResume()
        disableConcurrentBuilds()
        buildDiscarder(
          logRotator (
                       artifactDaysToKeepStr: '10',
                       artifactNumToKeepStr: '1',
                       daysToKeepStr: '30',
                       numToKeepStr: '30'
                     )
        )
    }
    triggers { cron('TZ=Canada/Eastern\n0 3 * * *') }
    environment {
        GIT_SSH_KEY_ID = 'gitlab_builduser'
        BACKUP_PREFIX_NAME = 'jenkins-backup'
        BACKUP_REPO_NAME = "jenkins_backups"
        BACKUP_REPO_URL = "git@XXXX/${env.BACKUP_REPO_NAME}.git"
        NUMBER_OF_BACKUPS = '7'
        GOOGLE_CHAT_TOKEN = 'XXXX'
        JENKINS_VERSION = ''
    }
    stages {
        stage('Init') {
            steps {
                script {
                    env.JENKINS_VERSION = sh(script: "java -jar /usr/share/jenkins/jenkins.war --version", returnStdout: true).trim()
                    def safeBackupModule = load("${WORKSPACE}/jenkins_backup/safe_backup_run.groovy")
                    safeBackupModule.jenkins_safe_backup("${env.JOB_NAME}")

                }
                dir("/tmp") {
                  withCredentials([sshUserPrivateKey(credentialsId: "${env.GIT_SSH_KEY_ID}", keyFileVariable: 'SSH_KEY', usernameVariable: 'SSH_USER')]) {
                    withEnv(["GIT_SSH_COMMAND=ssh -i ${SSH_KEY}"]) {
                        sh '''
                          if [ ! -d ${BACKUP_REPO_NAME} ]
                          then
                            git clone ${BACKUP_REPO_URL}
                          else
                            cd ${BACKUP_REPO_NAME}
                            git fetch origin
                            git reset --hard origin/master
                          fi
                        '''
                    }
                  }
                }
            }
        }
        stage('Backup') {
            steps {
                dir ('jenkins_backup') {
                  sh """
                    chmod +x ./jenkins-backup.sh
                    ./jenkins-backup.sh ${JENKINS_HOME} /tmp/${env.BACKUP_REPO_NAME}/${BACKUP_PREFIX_NAME}_${BACKUP_DATETIME}.tar.gz
                  """
                }
            }
        }
        stage('Upload') {
            steps {
                // TO_DO: SAVE THE BACKUP file
                dir("/tmp/${env.BACKUP_REPO_NAME}") {
                  withCredentials([sshUserPrivateKey(credentialsId: "${env.GIT_SSH_KEY_ID}", keyFileVariable: 'SSH_KEY', usernameVariable: 'SSH_USER')]) {
                    withEnv(["GIT_SSH_COMMAND=ssh -i ${SSH_KEY}"]) {
                        sh '''
                          git config user.email "${SSH_USER}@XXX.com"
                          git config user.name "${SSH_USER}"
                          # backup only keep the lastest X backup-files
                          ls -tp ${BACKUP_PREFIX_NAME}_*.tar.gz | grep -v '/$' | tail -n +$(expr ${NUMBER_OF_BACKUPS} + 1) | xargs -I {} rm -- {}
                          # Push to git
                          git add --all
                          git status
                          git commit --amend -CHEAD --allow-empty
                          git push --force-with-lease origin master
                          env
                        '''
                    }
                  }
                }

            }
        }
    }
    post {
        failure {
          hangoutsNotify message: "${JOB_BASE_NAME} faild!",token: "${env.GOOGLE_CHAT_TOKEN}",threadByJob: true
        }
    }
}

Groovy 脚本“safe_backup_run.groovy”:

import hudson.model.RestartListener
import jenkins.model.*;

//backup method
def jenkins_safe_backup(job_name) {
    //set the
    if(!binding.hasVariable('timeout_seconds')) {
        timeout_seconds = 60
    }

    if(timeout_seconds in String) {
        timeout_seconds = Integer.decode(timeout_seconds)
    }

    //type check user defined parameters/bindings
    if(!(timeout_seconds in Integer)) {
        throw new Exception('PARAMETER ERROR: timeout_seconds must be an integer.')
    }
    print("Jenkins safe backup initiated.")
    while(true) {
        //get the list of all the currenty running jobs
        runningBuilds = Jenkins.instance.getView('All').getBuilds().findAll() { it.getResult().equals(null) }
        //remove the 'Jenkins_Backup' job from the list
        runningBuilds.removeAll { it.toString().toLowerCase().startsWith(job_name.toLowerCase()) }
        //get the list of queued jobs
        def queuedBuilds = []
        Jenkins.instance.queue.items.findAll { queuedBuilds.add(it.task.name) }
        //remove the 'Jenkins_Backup' job from the list
        queuedBuilds.removeAll { it.toString().toLowerCase().startsWith(job_name.toLowerCase()) }

        //If no job is running except 'Jenkins_Backup' break
        if((runningBuilds.isEmpty()) && (queuedBuilds.isEmpty())) {
            break
        }
        print("Jenkins jobs/queue are not idle.  Waiting ${timeout_seconds} seconds before next backup attempt.")
        print("RUNNING jobs: " + runningBuilds)
        print("QUEUED jobs: " + queuedBuilds)
        sleep(timeout_seconds)
    }
}

return this

推荐阅读