Skip to main content

Agents and Distributed Builds


Master/Agent architecture

Why agents?

  • Scalability: Run multiple builds in parallel
  • Isolation: Separate environments
  • Heterogeneity: Linux, Windows, macOS
  • Security: The controller should not run builds

Agent types

Permanent agent (SSH)

An agent installed on a dedicated machine.

JNLP agent

An agent that connects to the controller.

Docker agent

An ephemeral container for a build.

Kubernetes agent

An ephemeral Pod for a build.


Configuring an SSH agent

Prerequisites on the agent

# 1. Installer Java
sudo apt install openjdk-17-jdk -y

# 2. Créer l'utilisateur jenkins
sudo useradd -m -d /home/jenkins -s /bin/bash jenkins

# 3. Créer le répertoire de travail
sudo mkdir -p /home/jenkins/agent
sudo chown jenkins:jenkins /home/jenkins/agent

# 4. Configurer SSH
sudo mkdir -p /home/jenkins/.ssh
# Ajouter la clé publique du controller
echo "ssh-rsa AAAA..." | sudo tee /home/jenkins/.ssh/authorized_keys
sudo chown -R jenkins:jenkins /home/jenkins/.ssh
sudo chmod 700 /home/jenkins/.ssh
sudo chmod 600 /home/jenkins/.ssh/authorized_keys

Configuration in Jenkins

  1. Manage Jenkins > Manage Nodes and Clouds > New Node
  2. Configure:
Node name: linux-agent-1
Type: Permanent Agent

# Configuration
Remote root directory: /home/jenkins/agent
Labels: linux docker maven
Usage: Use this node as much as possible
Launch method: Launch agents via SSH
Host: 192.168.1.100
Credentials: jenkins-ssh-key
Host Key Verification Strategy: Known hosts file
Availability: Keep this agent online as much as possible

Via JCasC

# jenkins.yaml
jenkins:
nodes:
- permanent:
name: "linux-agent-1"
remoteFS: "/home/jenkins/agent"
labelString: "linux docker maven"
numExecutors: 4
mode: NORMAL
launcher:
ssh:
host: "192.168.1.100"
port: 22
credentialsId: "jenkins-ssh-key"
sshHostKeyVerificationStrategy:
knownHostsFileKeyVerificationStrategy:
{}
retentionStrategy:
always: {}

JNLP Agent (Inbound)

On the controller

  1. Create the node in Jenkins
  2. Copy the connection command

On the agent

# Télécharger l'agent
curl -O http://jenkins:8080/jnlpJars/agent.jar

# Lancer l'agent
java -jar agent.jar \
-jnlpUrl http://jenkins:8080/computer/agent-1/jenkins-agent.jnlp \
-secret <SECRET> \
-workDir /home/jenkins/agent

With Docker

docker run -d \
--name jenkins-agent \
-e JENKINS_URL=http://jenkins:8080 \
-e JENKINS_AGENT_NAME=docker-agent-1 \
-e JENKINS_SECRET=<SECRET> \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/inbound-agent

Docker Compose

# docker-compose.yml
version: '3.8'

services:
jenkins-agent:
image: jenkins/inbound-agent
restart: unless-stopped
environment:
- JENKINS_URL=http://jenkins:8080
- JENKINS_AGENT_NAME=docker-agent
- JENKINS_SECRET=${JENKINS_AGENT_SECRET}
- JENKINS_AGENT_WORKDIR=/home/jenkins/agent
volumes:
- agent-workspace:/home/jenkins/agent
- /var/run/docker.sock:/var/run/docker.sock

volumes:
agent-workspace:

Docker Cloud Agent

Configuration

# jenkins.yaml
jenkins:
clouds:
- docker:
name: "docker-cloud"
dockerApi:
dockerHost:
uri: "unix:///var/run/docker.sock"
templates:
- labelString: "docker-agent"
dockerTemplateBase:
image: "jenkins/agent:latest"
mounts:
- type: bind
source: /var/run/docker.sock
destination: /var/run/docker.sock
remoteFs: "/home/jenkins/agent"
connector:
attach:
user: "jenkins"
instanceCapStr: "10"

Usage in a pipeline

pipeline {
agent {
docker {
image 'node:18'
label 'docker-agent'
args '-v /tmp:/tmp'
}
}
stages {
stage('Build') {
steps {
sh 'npm ci && npm run build'
}
}
}
}

Kubernetes Agent

Cloud configuration

# jenkins.yaml
jenkins:
clouds:
- kubernetes:
name: "kubernetes"
serverUrl: "" # Utilise le service account
namespace: "jenkins"
jenkinsUrl: "http://jenkins.jenkins.svc.cluster.local:8080"
jenkinsTunnel: "jenkins-agent.jenkins.svc.cluster.local:50000"
containerCap: 100
podRetention: "never"
templates:
- name: "jnlp"
label: "kubernetes-agent"
serviceAccount: "jenkins-agent"
containers:
- name: "jnlp"
image: "jenkins/inbound-agent:latest"
workingDir: "/home/jenkins/agent"
resourceRequestCpu: "200m"
resourceRequestMemory: "256Mi"
resourceLimitCpu: "500m"
resourceLimitMemory: "512Mi"
volumes:
- hostPathVolume:
hostPath: "/var/run/docker.sock"
mountPath: "/var/run/docker.sock"

Inline Pod Template

pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
jenkins: agent
spec:
serviceAccountName: jenkins-agent
containers:
- name: jnlp
image: jenkins/inbound-agent:latest
resources:
requests:
memory: "256Mi"
cpu: "200m"
- name: maven
image: maven:3.8-openjdk-17
command: ['sleep', 'infinity']
volumeMounts:
- name: m2-cache
mountPath: /root/.m2/repository
- name: docker
image: docker:24-dind
securityContext:
privileged: true
volumeMounts:
- name: docker-socket
mountPath: /var/run/docker.sock
- name: kubectl
image: bitnami/kubectl:latest
command: ['sleep', 'infinity']
volumes:
- name: m2-cache
persistentVolumeClaim:
claimName: maven-cache
- name: docker-socket
hostPath:
path: /var/run/docker.sock
'''
}
}

stages {
stage('Build') {
steps {
container('maven') {
sh 'mvn clean package -DskipTests'
}
}
}

stage('Test') {
steps {
container('maven') {
sh 'mvn test'
}
}
}

stage('Docker Build') {
steps {
container('docker') {
sh 'docker build -t my-app:${BUILD_NUMBER} .'
}
}
}

stage('Deploy') {
steps {
container('kubectl') {
sh 'kubectl apply -f k8s/'
}
}
}
}
}

Labels and agent selection

Defining labels

Agent 1: linux docker java
Agent 2: linux nodejs
Agent 3: windows dotnet
Agent 4: macos xcode

Using labels

pipeline {
agent none

stages {
stage('Build Java') {
agent { label 'linux && java' }
steps {
sh 'mvn package'
}
}

stage('Build Node') {
agent { label 'linux && nodejs' }
steps {
sh 'npm run build'
}
}

stage('Build .NET') {
agent { label 'windows && dotnet' }
steps {
bat 'dotnet build'
}
}

stage('Build iOS') {
agent { label 'macos && xcode' }
steps {
sh 'xcodebuild'
}
}
}
}

Label expressions

// ET logique
agent { label 'linux && docker' }

// OU logique
agent { label 'linux || macos' }

// Négation
agent { label 'linux && !slow' }

// Combinaison
agent { label '(linux || macos) && docker' }

Resources and capacity

Limiting executors

# jenkins.yaml
jenkins:
numExecutors: 0 # Pas de builds sur le controller

nodes:
- permanent:
name: "agent-1"
numExecutors: 4 # 4 builds parallèles max

Resources per container (K8s)

agent {
kubernetes {
yaml '''
spec:
containers:
- name: builder
image: maven:3.8
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
'''
}
}

Monitoring agents

Check the status

# Via API
curl -u admin:token http://jenkins:8080/computer/api/json?pretty=true

# Via CLI
java -jar jenkins-cli.jar -s http://jenkins:8080/ list-nodes

Metrics

// Plugin Prometheus Metrics
// Expose /prometheus endpoint

// Métriques disponibles:
// jenkins_node_count
// jenkins_executor_count
// jenkins_executor_in_use
// jenkins_queue_size

Summary

TypeAdvantageDrawback
SSHSimpleManual maintenance
JNLPTraverses firewallAgent configuration
DockerIsolationDocker required
KubernetesScalableComplexity

← Plugins | Security →