31

TLDR: I want to be able to run job simultaneously on multiple nodes in Jenkins pipeline. [ for example - build application x on nodes dev, test & staging nodes based on aws ]

I have a large group of nodes with the same label. I would like to be able to run a job in Jenkins that executes on all of the nodes with the same label as well as doing so simultaneously.

I saw a suggestion to use the matrix configuration option in Jenkins, but I can only think of one axis (the label group). When I try and run the job, it seems like it only executes once instead of 300 times (1 for each of the nodes in that label group).

What should my other axis be? Or...is there some plugin to do this? I had tried the NodeLabel Parameter Plugin, and choosing "run on all available online nodes", but it does not seem to run the jobs simultaneously.

avivamg
  • 12,197
  • 3
  • 67
  • 61
user2406467
  • 1,017
  • 6
  • 18
  • 22
  • Is there another way to add nodes to a job without editing the resources.xml file? We will eventually be scaling up to thousands of nodes, and having to add them manually to the XML file is slightly tedious, especially since the nodes may be changing. Other than that, Rundeck looks like it has a lot of the features I want. – user2406467 Jun 26 '13 at 16:50
  • @MarkO'Connor - I think Rundeck can accomplish what we're doing, then :) We will eventually be using EC2. If you want to add your replies as an answer, I should be able to accept it. Thanks! – user2406467 Jun 28 '13 at 19:05

10 Answers10

18
  1. Install
  2. For the job you want to run, enable Execute concurrent builds if necessary
  3. Create another job besides the job you want to run on all slaves and configure it
    • Build > Add build step > Trigger/call builds on other projects
      • Add ParameterFactories > All Nodes for Label Factory > Label: the label of the nodes
pevik
  • 4,523
  • 3
  • 33
  • 44
thSoft
  • 21,755
  • 5
  • 88
  • 103
13

The matrix build will work; use "Slaves" as the axis and expand the "Individual nodes" list to select all of your nodes.

Note that you will need to update the selection every time you add or remove a slave.

For a more maintainable solution, you could use the Job DSL plugin to set up a seed job that has the template for the build, then loops over each slave and creates a new job with the build label set to the name of the slave.

gareth_bowles
  • 20,760
  • 5
  • 52
  • 82
  • 1
    Having to select the individual nodes would be very inconvenient, seeing as how we'd like to scale up to several thousand at some point. I looked at the Job DSL plugin and was able to generate one job, but I'm not quite seeing how to loop over each slave. Also..it seems like once the jobs are created, I still have to manually execute them. – user2406467 Jun 24 '13 at 23:54
  • @^^^ here's a nice intro to Job DSL that shoud demystify it a bit: https://www.youtube.com/watch?v=Gyccyj6lA8k – dyodji Jan 27 '14 at 23:51
  • You can make one job to trigger the others. Use the [downstream](https://jenkinsci.github.io/job-dsl-plugin/#method/javaposse.jobdsl.dsl.helpers.publisher.PublisherContext.downstream) command. – Captain Man Jan 18 '19 at 17:30
13

There is two plugins that you need: Paramitrized Trigger Plugin to be able to trigger other jobs as build step of your main job, and NodeLabel Plugin (read the BuildParameterFactory section for descrition of what you need) to specify the label.

Sergey Irisov
  • 468
  • 4
  • 7
11

Taking a few of the above answers and adjusting them for 2.0 series.

You can now launch all a job on all nodes.

// The script triggers PayloadJob on every node.
// It uses Node and Label Parameter plugin to pass the job name to the payload job.
// The code will require approval of several Jenkins classes in the Script Security mode
def branches = [:]
def names = nodeNames()
for (int i=0; i<names.size(); ++i) {
  def nodeName = names[i];
  // Into each branch we put the pipeline code we want to execute
  branches["node_" + nodeName] = {
    node(nodeName) {
      echo "Triggering on " + nodeName
      build job: 'PayloadJob', parameters: [
              new org.jvnet.jenkins.plugins.nodelabelparameter.NodeParameterValue
                  ("TARGET_NODE", "description", nodeName)
          ]
    }
  }
}

// Now we trigger all branches
parallel branches

// This method collects a list of Node names from the current Jenkins instance
@NonCPS
def nodeNames() {
  return jenkins.model.Jenkins.instance.nodes.collect { node -> node.name }
}

Taken from the code https://jenkins.io/doc/pipeline/examples/#trigger-job-on-all-nodes

drubin
  • 1,012
  • 7
  • 10
10

The best and easiest way to accomplish this is using Elastic Axis plugin.
1. Install the pulgin.
2. Create a Multi Configuration job.(Install if not present)
3. In the job configuration you can find new axis added as Elastic axis. Add the label as shown below to get the job run on multiple slaves. enter image description here

Amol Manthalkar
  • 1,890
  • 2
  • 16
  • 16
  • 1
    This doesn't work. Putting a single label in the axis will build the job on a single node with that label. – jwg Nov 05 '15 at 18:07
  • 1
    Yes, it will build the job on that single node. Usually we have labels that are generic i.e. multiple nodes use same label name as per their use. (we don't use unique label names for all nodes, as node name is already unique and serves that purpose). Also you can provide more than one label name in the text area provided. This is the only solution I have found. Please help me if you have any better solution to this. – Amol Manthalkar Nov 16 '15 at 05:20
  • the point of labels is to be able to group them together, when scaling, just make sure there is a lable ie 'script-1' and it will add to axis – jonypony3 Feb 08 '17 at 21:10
9
Mark O'Connor
  • 76,015
  • 10
  • 139
  • 185
8

I was looking for a way to run docker system prune on all nodes (with label docker). I ended with a pretty simple scripted pipeline, which AFAIK will only need the pipeline plugin to work:

#!/usr/bin/env groovy

def nodes = [:]

nodesByLabel('docker').each {
  nodes[it] = { ->
    node(it) {
      stage("docker-prune@${it}") {
        sh('docker system prune -af --filter "until=1440h"')
      }
    }
  }
}

parallel nodes

Note: Requires Pipeline Utility Steps

What this does, it is looking for all nodes with label docker, then iterates over it and creates an associative array nodes with one step per found node (to be precise, what this is doing is cleaning all old docker stuff older then 60 days). parallel nodes starts to execute in parallel (on all found nodes simultaneously).

Hope that this will help someone.

marverix
  • 7,184
  • 6
  • 38
  • 50
7

Got it - No need for any special plugin!

I've created a parent job that triggers/call another build , And when I'm calling him I pass him the Label that I wan't the child job to run on.

So basically the parent job Only triggers the job I need , and the child job will run as many times as the number of slaves in that Label (In my case 4 times).

enter image description here

Shahar Hamuzim Rajuan
  • 5,610
  • 9
  • 53
  • 91
  • What happens when a node of the selected label takes too long to start one of the triggered builds? Won't it run on another node of the same label, which has already run a triggered build? – ldnunes Sep 08 '15 at 16:59
  • 1
    @ldnunes No,The build won't finish until it will run on all nodes in this label (connected slaves only of course) – Shahar Hamuzim Rajuan Jan 12 '16 at 14:54
  • 7
    This requires the 'parameterized trigger + nodelabel parameter plugin' for anyone unable to find these parameters in their Jenkins (ie me). – medwards Feb 12 '16 at 09:28
1

Enable This project is parameterized, add a parameter of type Label, enter an arbitrary name for the label and select a default value such as a label covering a number of nodes or a conjuction (&&) of such labels. Enable Run on all nodes matching the label, keep Run regardless of result, keep Node eligibility at All nodes.

enter image description here

General Grievance
  • 4,555
  • 31
  • 31
  • 45
eel ghEEz
  • 1,186
  • 11
  • 24
0

Solution: You can succinctly parallel the same build across multiple Jenkins nodes

This can be useful for building the same project on different environments ( for example: build node applications on test ,dev and staging environments )

Example:

pipeline {
agent { docker {  image 'node:14-alpine' } }
stages {
    stage('build') {
        steps {
            parallelTasks
        }
      }
   }
}


def parallelTasks() {
  def labels = ['test', 'dev', 'staging'] // labels for Jenkins node types we will build on
  def builders = [:]
  for (x in labels) {
      def label = x
      
      builders[label] = {
          node(label) {
              sh """#!/bin/bash -le
                    echo "build app on ${label} node"
                    cd /home/app
                    npm run build
              """
          }
      }
  }
  parallel builders


}
avivamg
  • 12,197
  • 3
  • 67
  • 61