Aller au contenu principal

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

  1. Manage Jenkins > Manage Nodes and Clouds > New Node
  2. 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

  1. Créer le node dans Jenkins
  2. 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é

TypeAvantageInconvénient
SSHSimpleMaintenance manuelle
JNLPTraverse firewallConfiguration agent
DockerIsolationDocker requis
KubernetesScalableComplexité

← Plugins | Sécurité →