Agents et Builds Distribués
Architecture Master/Agent
Pourquoi des agents ?
- Scalabilité : Exécuter plusieurs builds en parallèle
- Isolation : Environnements séparés
- Hétérogénéité : Linux, Windows, macOS
- Sécurité : Le controller ne devrait pas exécuter de builds
Types d'agents
Agent permanent (SSH)
Agent installé sur une machine dédiée.
Agent JNLP
Agent qui se connecte au controller.
Agent Docker
Container éphémère pour un build.
Agent Kubernetes
Pod éphémère pour un build.
Configuration d'un agent SSH
Prérequis sur l'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 dans Jenkins
- Manage Jenkins > Manage Nodes and Clouds > New Node
- Configurer :
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: {}
Agent JNLP (Inbound)
Sur le controller
- Créer le node dans Jenkins
- Copier la commande de connexion
Sur l'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
Avec 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:
Agent Docker Cloud
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"
Utilisation dans un pipeline
pipeline {
agent {
docker {
image 'node:18'
label 'docker-agent'
args '-v /tmp:/tmp'
}
}
stages {
stage('Build') {
steps {
sh 'npm ci && npm run build'
}
}
}
}
Agent Kubernetes
Configuration du Cloud
# 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"
Pod Template inline
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 et sélection d'agents
Définir des labels
Agent 1: linux docker java
Agent 2: linux nodejs
Agent 3: windows dotnet
Agent 4: macos xcode
Utiliser les 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'
}
}
}
}
Expressions de labels
// ET logique
agent { label 'linux && docker' }
// OU logique
agent { label 'linux || macos' }
// Négation
agent { label 'linux && !slow' }
// Combinaison
agent { label '(linux || macos) && docker' }
Ressources et capacité
Limiter les executors
# jenkins.yaml
jenkins:
numExecutors: 0 # Pas de builds sur le controller
nodes:
- permanent:
name: "agent-1"
numExecutors: 4 # 4 builds parallèles max
Ressources par 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 des agents
Vérifier l'état
# 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
Métriques
// Plugin Prometheus Metrics
// Expose /prometheus endpoint
// Métriques disponibles:
// jenkins_node_count
// jenkins_executor_count
// jenkins_executor_in_use
// jenkins_queue_size
Résumé
| Type | Avantage | Inconvénient |
|---|---|---|
| SSH | Simple | Maintenance manuelle |
| JNLP | Traverse firewall | Configuration agent |
| Docker | Isolation | Docker requis |
| Kubernetes | Scalable | Complexité |