Wie verhindere ich, dass zwei Pipeline-Jenkins-Jobs desselben Typs parallel auf demselben Knoten ausgeführt werden?

72

Ich möchte nicht zulassen, dass zwei Jobs desselben Typs (dasselbe Repository) parallel auf demselben Knoten ausgeführt werden.

Wie kann ich das mit groovy in Jenkinsfile machen?

Sorin
quelle

Antworten:

66

Die Antwort unter https://stackoverflow.com/a/43963315/6839445 ist veraltet.

Die aktuelle Methode zum Deaktivieren gleichzeitiger Builds besteht darin, folgende Optionen festzulegen:

options { disableConcurrentBuilds() }

Eine ausführliche Beschreibung finden Sie hier: https://jenkins.io/doc/book/pipeline/syntax/#options

M0nt3c1t0
quelle
9
Ich versuche dies und es deaktiviert gleichzeitige Builds global. OP möchte gleichzeitige Builds pro Knoten deaktivieren, aber immer noch zulassen, dass mehr als ein Build gleichzeitig ausgeführt wird, nur einer pro verfügbarem Knoten.
Quantic
Dies funktioniert für unseren speziellen Zweck, obwohl es mich stört, dass wir dies in der Pipeline konfigurieren müssen und dass es nicht einfach global irgendwie gemacht werden kann.
Trejkaz
42

Sie haben die Eigenschaft disableConcurrentBuilds erhalten:

properties properties: [
  ...
  disableConcurrentBuilds(),
  ...
]

Dann würde der Job warten, bis der ältere fertig ist

hypery2k
quelle
3
Ich denke, das ist die vernünftigste Antwort
Dieter Hubau
Diese Lösung ist leider veraltet. Optionen sollten wie in einer anderen Antwort beschrieben verwendet werden.
Thomas Sundberg
3
was genau meinst du damit Der Snippet-Generator zeigt diese Option weiterhin für geskriptete Pipelines an. Haben Sie einen Link zu der genannten Abschreibung?
Hypery2k
32

Eine andere Möglichkeit ist die Verwendung des Plugins für abschließbare Ressourcen: https://wiki.jenkins-ci.org/display/JENKINS/Lockable+Resources+Plugin

Sie können Sperren (Mutexe) definieren, wie Sie möchten, und Variablen in die Namen einfügen. Um beispielsweise zu verhindern, dass mehrere Jobs gleichzeitig einen Compiler auf einem Build-Knoten verwenden:

stage('Build') {
    lock(resource: "compiler_${env.NODE_NAME}", inversePrecedence: true) {
      milestone 1
      sh "fastlane build_release"
    }
}

Wenn Sie also verhindern möchten, dass mehr als ein Job desselben Zweigs gleichzeitig pro Knoten ausgeführt wird, können Sie Folgendes tun

stage('Build') {
    lock(resource: "lock_${env.NODE_NAME}_${env.BRANCH_NAME}", inversePrecedence: true) {
      milestone 1
      sh "fastlane build_release"
    }
}

Von: https://www.quernus.co.uk/2016/10/19/lockable-resources-jenkins-pipeline-builds/

Matt Hamilton
quelle
Bei Verwendung des Codes wurde folgende Fehlermeldung angezeigt: >> Unbekannter Bühnenabschnitt "Sperre". Ab Version 0.5 müssen sich die Schritte in einer Stufe in einem 'Schritte'-Block befinden. << Ich habe den Abschnitt "Sperren" in einen Abschnitt "Schritte" verschoben, dann hat es funktioniert.
Erik Nellessen
Was bedeutet es inversePrecedence: truebeim Erstellen eines lock?
Yash
24

Beispiel für die Verwendung des Optionsblocks in der deklarativen Pipeline-Syntax:

pipeline {

  options { 
    disableConcurrentBuilds() 
  }

...
}
Bryji
quelle
15

Ich denke, es gibt mehr als nur einen Ansatz für dieses Problem.

Pipeline

  • Verwenden Sie die neueste Version des Lockable Resources Plugins und dessen lockSchritt, wie in einer anderen Antwort vorgeschlagen.
  • Wenn Sie dasselbe Projekt erstellen:
    • Deaktivieren Sie Execute concurrent builds if necessary.
  • Wenn Sie verschiedene Projekte erstellen:
    • Stellen Sie anders nodeoder labelfür jedes Projekt ein.

Jenkins

  • Begrenzen Sie die Anzahl der Executoren des Knotens auf 1?

Plugins

luka5z
quelle
@sorin Kannst du etwas genauer sagen, was du hier erreichen willst?
luka5z
1
Die Begrenzung der Anzahl der Knotenausführer ist nicht möglich, da sie für andere Jobs benötigt werden. Ich möchte nur verhindern, dass dasselbe Repository parallel erstellt wird.
Sorin
1
Irgendeine Idee, wie man Execute concurrent builds if necessaryvon einem Jenkinsfile?
Somatik
2
Das "Throttle Concurrent Builds Plugin" bietet jetzt Pipeline-Unterstützung. Bitte sehen Sie meine Antwort unten.
Mig82
+1, um die Anzahl der Executoren des Knotens auf 1 zu begrenzen - das ist eine der besten Möglichkeiten, eine Jenkins-Installation zu optimieren.
Mkobit
12

Das " Throttle Concurrent Builds Plugin " unterstützt seitdem die Pipeline throttle-concurrents-2.0. Jetzt können Sie so etwas tun:

Feuern Sie die Pipeline unten zweimal ab, unmittelbar nacheinander, und Sie werden sehen. Sie können dies manuell tun, indem Sie auf "Jetzt erstellen " doppelklicken oder es von einem parallelSchritt in einem anderen Job aus aufrufen .

stage('pre'){
    echo "I can run in parallel"
    sleep(time: 10, unit:'SECONDS')
}
throttle(['my-throttle-category']) {
    
    // Because only the node block is really throttled.
    echo "I can also run in parallel" 
    
    node('some-node-label') {
        
        echo "I can only run alone"
        
        stage('work') {
            
            echo "I also can only run alone"
            sleep(time: 10, unit:'SECONDS')
            
        }
    }
}
stage('post') {
    echo "I can run in parallel again"
    // Let's wait enough for the next execution to catch
    // up, just to illustrate.
    sleep(time: 20, unit:'SECONDS')
}

In der Pipeline-Ansicht können Sie Folgendes erkennen:

Geben Sie hier die Bildbeschreibung ein

Bitte beachten Sie jedoch, dass dies nur für nodeBlöcke innerhalb des throttleBlocks funktioniert . Ich habe andere Pipelines, in denen ich zuerst einen Knoten zuweise, dann einige Arbeiten erledige, die nicht gedrosselt werden müssen, und dann einige, die dies tun.

node('some-node-label') {

    //do some concurrent work

    //This WILL NOT work.
    throttle(['my-throttle-category']) {
        //do some non-concurrent work
    }
}

In diesem Fall throttlelöst der Schritt das Problem nicht, da der throttleSchritt derjenige innerhalb des nodeSchritts ist und nicht umgekehrt. In diesem Fall ist der Sperrschritt besser für die Aufgabe geeignet

Mig82
quelle
Ich erhalte die Meldung "Eine oder mehrere angegebene Kategorien existieren nicht". Wo geben Sie die Kategorien an?
Giovanni G
Ich verstehe nicht, was du meinst.
Mig82
8

Installieren Sie das Jenkins Lockable Resources Plugin .

Wickeln Sie in Ihrem Pipeline-Skript das Teil in den Sperrblock und geben Sie dieser abschließbaren Ressource einen Namen.

lock("test-server"){
    // your steps here
}

Verwenden Sie den Namen der Ressource, die Sie sperren. Nach meiner Erfahrung ist es normalerweise ein Testserver oder eine Testdatenbank.

raitisd
quelle
2
Nur zum Hinzufügen - Ich habe die Sperrfunktion gefunden, aber nicht gedacht, dass sie mein Problem löst. Sie können jedoch Variablennamen verwenden, um die Sperrressource zu definieren. Mit env.NODE_NAME wurde mein Problem behoben, bei dem ich eine Stufe sperren musste, um nicht gleichzeitig auf demselben Knoten ausgeführt zu werden (sie kann gleichzeitig auf verschiedenen Knoten ausgeführt werden)
Ed Mackenzie
Wenn es innerhalb des Schlosses eine Ausnahme oder ein Zeitlimit gibt, wird das Schloss anscheinend nie freigegeben und der andere gleichzeitige Build wird weiter ausgeführt ... Bis zum Zeitlimit ... :( Nicht sicher, aber das beobachte ich
Marcello de Sales
5

Wenn Sie wie mein Team sind, möchten Sie benutzerfreundlich parametrisierte Jenkins-Jobs haben, die Pipeline-Skripte schrittweise auslösen, anstatt all diese deklarative / groovige Suppe beizubehalten. Leider bedeutet dies, dass jeder Pipeline-Build 2+ Executor-Slots belegt (einer für das Pipeline-Skript und andere für die ausgelösten Jobs), sodass die Gefahr eines Deadlocks sehr real wird.

Ich habe überall nach Lösungen für dieses Dilemma gesucht und disableConcurrentBuilds()nur verhindert , dass derselbe Job (Zweig) zweimal ausgeführt wird. Es werden keine Pipeline-Builds für verschiedene Zweige in die Warteschlange gestellt und gewartet, anstatt wertvolle Executor-Slots zu belegen.

Eine hackige (aber überraschend elegante) Lösung für uns bestand darin, die Executoren des Masterknotens auf 1 zu beschränken und die Pipeline-Skripte dazu zu bringen, sie (und nur sie) zu verwenden, und dann einen lokalen Slave-Agenten an Jenkins anzuschließen, um sich um alle zu kümmern andere Beschäftigungen.

JohannSig
quelle
3

Eine der Optionen ist die Verwendung der Jenkins REST-API. Ich habe nach anderen Optionen gesucht, aber es scheint, dass dies nur eine mit Pipelines-Funktionalität ist.

Sie sollten ein Skript schreiben, das Jenkins nach Informationen zu aktuellen Jobs abfragt und prüfen, ob Jobs des gleichen Typs ausgeführt werden. Verwenden Sie dazu die Jenkins REST-API. Die Dokumentation finden Sie in der rechten unteren Ecke Ihrer Jenkins-Seite. Beispielskript:

#!/usr/bin/env bash

# this script waits for integration test build finish
# usage: ./wait-for-tests.sh <jenkins_user_id> <jenkins_user_token_id>
jenkins_user=$1
jenkins_token=$2
build_number=$3

job_name="integration-tests"
branch="develop"

previous_build_number=build_number
let previous_build_number-=1
previous_job_status=$(curl -s http://${jenkins_user}:${jenkins_token}@jenkins.mycompany.com/job/mycompany/job/${job_name}/branch/${branch}/${previous_build_number}/api/json | jq -r '.result')

while [ "$previous_job_status" == "null" ];
do
    previous_job_status=$(curl -s http://${jenkins_user}:${jenkins_token}@jenkins.mycompany.com/job/mycompany/job/${job_name}/branch/${branch}/${previous_build_number}/api/json | jq -r '.result')
    echo "Waiting for tests completion"
    sleep 10
done

echo "Seems that tests are finished."

Ich habe hier Bash verwendet, aber Sie können jede Sprache verwenden. Rufen Sie dann einfach dieses Skript in Ihrer Jenkins-Datei auf:

sh "./wait-for-tests.sh ${env.REMOTE_USER} ${env.REMOTE_TOKEN} ${env.BUILD_NUMBER}"

Es wird also bis zum Abschluss des Jobs warten (nicht zu verwechseln mit Erwähnungen von Integrationstests, es ist nur der Jobname).

Beachten Sie auch, dass dieses Skript in seltenen Fällen zu einem Deadlock führen kann, wenn beide Jobs aufeinander warten. Daher möchten Sie hier möglicherweise einige Richtlinien für maximale Wiederholungsversuche implementieren, anstatt unendlich lange zu warten.

Vladimir Rozhkov
quelle
1

Bis das Plugin "Throttle Concurrent Builds" Pipeline-Unterstützung bietet, besteht eine Lösung darin, einen Executor des Masters mit einem für Ihren Job erforderlichen Label effektiv auszuführen.

Erstellen Sie dazu in Jenkins einen neuen Knoten, z. B. einen SSH-Knoten, der eine Verbindung zu localhost herstellt. Sie können auch die Befehlsoption verwenden, um Slave.jar / swarm.jar abhängig von Ihrem Setup auszuführen. Geben Sie dem Knoten einen Executor und ein Label wie "resource-foo" und geben Sie Ihrem Job auch dieses Label. Jetzt kann jeweils nur ein Job des Labels "resource-foo" ausgeführt werden, da nur ein Executor mit dieser Beschriftung vorhanden ist. Wenn Sie festlegen, dass der Knoten so oft wie möglich verwendet wird (Standard) und die Anzahl der Master-Executoren um eins reduzieren, sollte er sich genau wie gewünscht verhalten, ohne die Gesamtzahl der Executoren zu ändern.

mrooney
quelle