Декларативное конвейерное задание Jenkins - как распределить параллельные шаги между подчиненными? - PullRequest
0 голосов
/ 12 июня 2018

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

Мне нужно сначала получить список доступных узлов, а затем параллельно запустить меньшие тестыпо одному на каждый узел;если узлов недостаточно, один из меньших тестов должен дождаться завершения работы узла.

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

Моя текущая конфигурация для меньшего теста:

    stage('Integration Tests') {
        when {
            expression {params.TESTS_INTEGRATION}
        }
        parallel {
            stage('Test1') {
                agent {node {label 'my_builder'}}
                steps {
                    script {
                        def shell_script = getShellScript("Test1")
                        sh "${shell_script}"
                    }
                }
            }

Я могу получить список доступных рабов из метки, подобной этой:

pipeline {
stages {
    // ... other stages here ...
    stage('NodeList'){
        steps {
            script {
                def nodes =  getNodeNames('my_builder')
                free_nodes = []
                for (def element = 0; element < nodes.size(); element++) {
                    usenode = nodes[element]
                    try { 
                      // Give it 5 seconds to run the nodetest function
                        timeout(time: 5, unit: 'SECONDS') { 
                            node(usenode) { 
                                nodetest() 
                                free_nodes += usenode
                            } 
                        }
                    } catch(err) { 

                    }
                }
                println free_nodes
            }
        }
    }

Где

def getNodeNames (String label) {
    def lgroup = Jenkins.instance.getLabel(label)
    def nodes = lgroup.getNodes()
    def result = []
    if (nodes.size() > 0) {
        for (def element = 0; element < nodes.size(); element++) {
            result += nodes[element].getNodeName()
        }
    }
    return result
}

def nodetest() { 
  sh('echo alive on \$(hostname)') 
}

Как программно получить имя узла из массива free_nodes и указать сцене использовать его?

1 Ответ

0 голосов
/ 13 июня 2018

Я понял это, так что для людей из будущего:

Оказывается, вы можете запустить сценариев конвейер внутри декларативного конвейера, например:

pipeline {
    stage('SomeStage') {
        steps {
            script {
                // ... your scripted pipeline here
            }
        }
    }

Сценарий может делать все, что угодно, в том числе ... запуск конвейера!

Вот сценарий:

script {
    def builders = [:]
    def nodes =  getNodeNames('my_label')
    // let's find the free nodes
    String[] free_nodes = []
    for (def element = 0; element < nodes.size(); element++) {
        usenode = nodes[element]
        try { 
          // Give it 5 seconds to run the nodetest function
            timeout(time: 5, unit: 'SECONDS') { 
                node(usenode) { 
                    nodetest() 
                    free_nodes += usenode
                } 
            }
        } catch(err) { 
            // do nothing
        }
    }
    println free_nodes

    def tests = params.TESTS_LIST.split(',')

    for(int i = 0; i < tests.length; i++) {
        // select the test to run
        def the_test = tests[i]
        // select on which node to run it
        def the_node = free_nodes[i % free_nodes.length]

        // here comes the scripted pipeline: prepare steps
        builders[the_test] = {
            // run on the selected node
            node(the_node) {
                // lock the resource with the name of the node so two tests can't run there at the same time
                lock(the_node) {
                    // name the stage
                    stage(the_test) {
                        println "Running on ${NODE_NAME}"
                        def shell_script = getShellScript("${the_test}")
                        sh "${shell_script}"
                    }
                }
            }
        }
    }
    // run the steps in parallel
    parallel builders
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...