Reputation: 3700
I'm new to Jenkins pipeline and trying to understand how can I throttle an "entire" pipeline, which basically means that the following will take place:
1) I will be able to run the same pipeline maximum number of concurrent runs, say MAX_CONCURRENT_RUNS
= 2
2) Each run (essentially build) can have its own parameters, with the following "extra requirement", that two (or more) different builds CAN have (if required) the same parameters sent to it.
3) In the case where at a particular point in time there are already MAX_CONCURRENT_RUNS
builds (runs) of the pipeline, then the MAX_CONCURRENT_RUNS
+ 1 run will "hold" itself until the first currently running build will terminate and only then will start to execute.
I have looked in this SO question and also this SO question, but they both not "exactly" applicable to my situation (requirements).
I'm using Jenkins server version 2.176.1
Upvotes: 0
Views: 4461
Reputation: 3700
After some research I did mainly in these two links: The throttle plugin official GitHub page and JENKINS-45140 issue where some of the comments were very useful, I have composed this solution:
1) First thing is install the required plugin, that can be found in the Manage Jenkins --> Manage Plugins "search tab" by typing throttle-concurrents (the official plugin page can be found here).
2) A "simple" throttle category needs to be added to the global configuration of the "throttle builder plugin" within Jenkins' global configuration. This can be done by going to Manage Jenkins --> Configure system. There under the "Throttle Concurrent Builds" section the "new" category needs to be added. In the below example, I have set the name of the category to: simpleThrottleCatagory and the following parameters:
This way, the pipeline that would be able to run several builds at the same time, with some "upper limit" on how many builds, which is essentially the MAX_CONCURRENT_RUNS
(in this case 2).
3) In this example I will keep the pipeline "itself" implementation "as simple as possible" in order to focus on the "throttling" considerations and not the "common pipeline stuff".
3.1) The "simple concurrent pipeline" will simply receive two parameters from the user:
Number of seconds to sleep:NumSecToSleep
.
Some sample choice parameter named BocaOrRiver
with two possible values: boca
or river
.
3.2) The entire pipeline implementation in this case is as follows (note that some extra "approvals" needs to take place so that Calendar.getInstance().getTime().format('YYYY/MM/dd-hh:mm:ss',TimeZone.getTimeZone('CST'))
function will work. In case you are unable to perform these changes, replace the two lines with this function call with any other implementation that will get the current time stamp):
// Do NOT place within the pipeline block
properties([ [ $class: 'ThrottleJobProperty',
categories: ['simpleThrottleCatagory'],
limitOneJobWithMatchingParams: false,
maxConcurrentPerNode: 2,
maxConcurrentTotal: 2,
paramsToUseForLimit: '',
throttleEnabled: true,
throttleOption: 'category' ] ])
pipeline
{
agent any parameters
{
string(name: "NumSecToSleep", description: "Number of second to sleep in the Sleep stage")
choice(name: "BocaOrRiver", choices: "boca\nriver", description: "Which Team in Buenos Aires do you prefer?")
}
stages
{
stage("First stage")
{
steps
{
echo "WORKSPACE is:${WORKSPACE}"
echo "Build number is:${env.BUILD_NUMBER}"
}
}
stage("Sleep stage")
{
steps
{
script
{
def time = params.NumSecToSleep echo "Sleeping for ${params.NumSecToSleep} seconds"
def timeStamp = Calendar.getInstance().getTime().format('YYYY/MM/dd-hh:mm:ss',TimeZone.getTimeZone('CST'))
println("Before sleeping current time is:" + timeStamp)
sleep time.toInteger() // seconds
timeStamp = Calendar.getInstance().getTime().format('YYYY/MM/dd-hh:mm:ss',TimeZone.getTimeZone('CST'))
println("After sleeping current time is:" + timeStamp)
echo "Done sleeping for ${params.NumSecToSleep} seconds"
}
}
}
}
}
3.3) NOTES:
3.3.1) The code within the actual pipeline block is essentially straight forward: Simply display some "build specific" parameters just to be sure that each build of the job gets its specific user defined parameters and it sleeps for some number of seconds also so that two (in this case) or more builds can be run indeed concurrently and it would be able to see "for our own eyes" (at run time) that the two jobs run together (in parallel).
3.3.2) The more interesting part of the pipeline is the properties
block (at the top):
3.3.2.1) Note that it needs to be defined OUTSIDE of the pipeline
block section.
3.3.2.2) I think that most of the settings defined within this properties
block are very "self explanatory" YET the two that should be mentioned are:
4) Basic illustration:
In the figure below there is a screen shot of a situation where three builds where started one after the other, with "enough" time to sleep in each one of them so that the first two (build 17 & 18 pointed in points 2 & 3 respectively) won't "finish too soon", meaning, so that indeed the "third" build (build 19) will "have to wait" for an available executor (pointed in point 4):
5) Here I have described a very simple and minimal yet (IMMO) representative implementation along with "global configuration" of an "entire" concurrent pipeline. Off course this topic can be discussed MUCH further, for example, it is also possible to throttle only single step within a pipeline.
Upvotes: 7