Сценарий jenkinsfile параллельной стадии

Я пытаюсь написать сценарий Jenkinsfile, используя отличный DSL, который будет иметь параллельные шаги в наборе этапов.

Вот мой файл jenkinsfile:

node {   
stage('Build') {
    sh 'echo "Build stage"'
}

stage('API Integration Tests') {
    parallel Database1APIIntegrationTest: {
        try {
            sh 'echo "Build Database1APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }               

    }, Database2APIIntegrationTest: {
        try {
            sh 'echo "Build Database2APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }

    }, Database3APIIntegrationTest: {
        try {
            sh 'echo "Build Database3APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }
    }
}

stage('System Tests') {
    parallel Database1APIIntegrationTest: {
        try {
            sh 'echo "Build Database1APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }               

    }, Database2APIIntegrationTest: {
        try {
            sh 'echo "Build Database2APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }

    }, Database3APIIntegrationTest: {
        try {
            sh 'echo "Build Database3APIIntegrationTest parallel stage"'
        }
        finally {
            sh 'echo "Finished this stage"'
        }
    }
}
}

Я хочу иметь 3 этапа: сборка; Интеграционные тесты и системные тесты. На двух этапах тестирования я хочу, чтобы 3 набора тестов выполнялись параллельно, каждый на своей базе данных.

У меня есть 3 доступных исполнителя. Один на мастере, 2 агента и я хочу, чтобы каждый параллельный шаг выполнялся на любом доступном исполнителе.

Я заметил, что после запуска конвейера я вижу только три этапа, каждый из которых выделен зеленым цветом. Я не хочу просматривать журналы для этой стадии, чтобы определить, были ли какие-либо параллельные шаги в этой стадии успешными / нестабильными / неудачными.

Я хочу видеть 3 шага на моих этапах тестирования - помеченных зеленым, желтым или красным (Успешно, нестабильно или неудачно).

Я рассмотрел возможность расширения тестов на их собственные этапы, но понял, что параллельные этапы не поддерживаются (кто-нибудь знает, будет ли это когда-либо поддерживаться?), Поэтому я не могу сделать это, поскольку конвейер может занять слишком много времени для завершения,

Любое понимание будет высоко ценится, спасибо

8 ответов

В скриптовом конвейере Jenkins параллель (...) берет карту, описывающую каждый этап, который будет построен. Поэтому вы можете программно построить этапы сборки заранее, шаблон, который позволяет гибкое последовательное / параллельное переключение.
Я использовал код, похожий на этот, где prepareBuildStages возвращает список карт, каждый элемент списка выполняется последовательно, в то время как карта описывает параллельные этапы в этой точке.

// main script block
// could use eg. params.parallel build parameter to choose parallel/serial 
def runParallel = true
def buildStages

node('master') {
  stage('Initialise') {
    // Set up List<Map<String,Closure>> describing the builds
    buildStages = prepareBuildStages()
    println("Initialised pipeline.")
  }

  for (builds in buildStages) {
    if (runParallel) {
      parallel(builds)
    } else {
      // run serially (nb. Map is unordered! )
      for (build in builds.values()) {
        build.call()
      }
    }
  }

  stage('Finish') {
      println('Build complete.')
  }
}

// Create List of build stages to suit
def prepareBuildStages() {
  def buildList = []

  for (i=1; i<5; i++) {
    def buildStages = [:]
    for (name in [ 'one', 'two', 'three' ] ) {
      def n = "${name} ${i}"
      buildStages.put(n, prepareOneBuildStage(n))
    }
    buildList.add(buildStages)
  }
  return buildList
}

def prepareOneBuildStage(String name) {
  return {
    stage("Build stage:${name}") {
      println("Building ${name}")
      sh(script:'sleep 5', returnStatus:true)
    }
  }
}

Результирующий конвейер выглядит как: Параллельный трубопровод Jenkins Blue Ocean

Существуют определенные ограничения на то, что может быть вложено в параллельный блок, подробные сведения см. В документации конвейера. К сожалению, большая часть ссылок кажется смещенной в сторону декларативного конвейера, несмотря на то, что она довольно менее гибкая, чем сценарий (IMHO). Страница примеров конвейера была наиболее полезной.

Вот простой пример без циклов или функций, основанный на сообщении @Ed Randall:

       node('docker') {
    stage('unit test') {
        parallel([
            hello: {
                echo "hello"
            },
            world: {
                echo "world"
            }
        ])
    }

    stage('build') {
        def stages = [:]

        stages["mac"] = {
            echo "build for mac"
        }
        stages["linux"] = {
            echo "build for linux"
        }

        parallel(stages)
    }
}

... что дает следующее:

Обратите внимание, что значения карты не обязательно должны быть этапами. Вы можете указать шаги напрямую.

Вот пример из их документов:

Параллельное исполнение

Пример в приведенном выше разделе выполняет тесты на двух разных платформах в линейной серии. На практике, если выполнение контрольной проверки занимает 30 минут, этап "Тест" теперь занимает 60 минут!

К счастью, Pipeline имеет встроенную функциональность для параллельного выполнения частей Scripted Pipeline, реализованную в метко названном параллельном шаге.

Рефакторинг приведенного выше примера для использования параллельного шага:

// Jenkinsfile (Scripted Pipeline)


stage('Build') {
    /* .. snip .. */
}

stage('Test') {
    parallel linux: {
        node('linux') {
            checkout scm
            try {
                unstash 'app'
                sh 'make check'
            }
            finally {
                junit '**/target/*.xml'
            }
        }
    },
    windows: {
        node('windows') {
            /* .. snip .. */
        }
    }
}

Чтобы упростить ответ @Ed Randall здесь. Помните, что это сценарий Jenkinsfile (не декларативный)

stage("Some Stage") {
    // Stuff ...
}


stage("Parallel Work Stage") {

    // Prealocate dict/map of branchstages
    def branchedStages = [:]

    // Loop through all parallel branched stage names
    for (STAGE_NAME in ["Branch_1", "Branch_2", "Branch_3"]) {

        // Define and add to stages dict/map of parallel branch stages
        branchedStages["${STAGE_NAME}"] = {
            stage("Parallel Branch Stage: ${STAGE_NAME}") {
                // Parallel stage work here
                sh "sleep 10"
            }
        }

    }

    // Execute the stages in parallel
    parallel branchedStages
}


stage("Some Other Stage") {
    // Other stuff ...
}

Обратите внимание на фигурные скобки. Это приведет к следующему результату (с плагином BlueOcean Jenkins):

Сценарий Jenkinsfile Result Link

Я также пробовал подобные шаги, чтобы выполнить параллельные этапы и отобразить их все в виде этапа. Вы должны написать этап внутри параллельного шага, как показано в следующем блоке кода.

// Jenkinsfile (Scripted Pipeline)

stage('Build') {
    /* .. Your code/scripts .. */
}

stage('Test') {
    parallel 'linux': {
        stage('Linux') {
            /* .. Your code/scripts .. */
        }
    }, 'windows': {
        stage('Windows') {
            /* .. Your code/scripts .. */
        }
    }
}

Приведенный выше пример с FOR неверен, поскольку переменная STAGE_NAME будет перезаписываться каждый раз, у меня была та же проблема, что и у Wei Huang.

Нашел решение здесь:

https://www.convalesco.org/notes/2020/05/26/parallel-stages-in-jenkins-scripted-pipelines.html

      def branchedStages = [:]
def STAGE_NAMES =  ["Branch_1", "Branch_2", "Branch_3"]
STAGE_NAMES.each { STAGE_NAME ->
 // Define and add to stages dict/map of parallel branch stages
    branchedStages["${STAGE_NAME}"] = {
        stage("Parallel Branch Stage: ${STAGE_NAME}") {
        // Parallel stage work here
            sh "sleep 10"
        }
    }
  }
parallel branchedStages

Я использовал, как показано ниже, где три этапа параллельны.

      def testCases() {
  stage('Test Cases') {
    def stages = [:]    // declaring empty list
      stages['Unit Testing'] = {
      sh "echo Unit Testing completed"
      }
      stages['Integration Testing'] = {
        sh "echo Integration Testing completed"
      }
      stages['Function Testing'] = {
        sh "echo Function Testing completed"
      }
    parallel(stages) // declaring parallel stages
  }
}   

Я использовал stage{} в параллельных блоках по несколько раз. Затем каждый этап отображается в виде этапа. Родительский этап, который содержит parallel не включает в себя время для всех параллельных этапов, но каждый параллельный этап отображается в виде этапа.

В синем океане параллельные этапы появляются отдельно вместо показа этапов. Если есть родительский этап, он отображается как родитель параллельных этапов.

Если у вас нет такого опыта, возможно, стоит обновить плагин.

Другие вопросы по тегам