Jenkins sur VM, c’est des agents statiques qui tournent 24/7, du scaling manuel, et des machines qui dorment 80% du temps. Jenkins sur Kubernetes, c’est un pod par build, créé à la demande, détruit après. Zéro gaspillage, scaling automatique, isolation totale.
Ce cours couvre le triptyque de l’industrialisation : déploiement Helm, pod templates multi-conteneurs, et scaling dynamique des agents.
Déploiement avec Helm
Helm simplifie le déploiement de Jenkins à un fichier values.yaml et trois commandes. Le chart officiel gère le controller, les plugins, l’ingress, le storage et le monitoring.
# Installer Jenkins sur K8s
helm repo add jenkins https://charts.jenkins.io && helm repo update
kubectl create namespace jenkins
kubectl create secret generic jenkins-admin \
--namespace jenkins \
--from-literal=jenkins-admin-user=admin \
--from-literal=jenkins-admin-password='S3cureP@ss!'
helm install jenkins jenkins/jenkins \
--namespace jenkins --values jenkins-values.yaml --wait
Le fichier jenkins-values.yaml est le cœur de ta config. Les sections critiques — controller sans executor, plugins pré-installés, persistence sur un volume rapide :
controller:
image: { repository: jenkins/jenkins, tag: "lts-jdk17" }
resources:
requests: { cpu: "500m", memory: "2Gi" }
limits: { cpu: "2000m", memory: "4Gi" }
numExecutors: 0 # Jamais de builds sur le controller !
installPlugins:
- kubernetes:latest
- workflow-aggregator:latest
- git:latest
- configuration-as-code:latest
- docker-workflow:latest
- credentials-binding:latest
ingress:
enabled: true
ingressClassName: nginx
hostName: jenkins.example.com
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
persistence:
enabled: true
storageClass: "gp3"
size: "50Gi"
⚠️ numExecutors: 0 est crucial. Le controller orchestre, les pods agents exécutent. Un build lourd sur le controller peut crasher toute l’instance — tous les jobs en file d’attente sont perdus.
💡 JCasC intégré : le chart supporte Configuration as Code via controller.JCasC.configScripts. Ta config Jenkins entière — clouds, credentials, global tools — est versionnée dans Git avec le chart Helm.
Pod Templates multi-conteneurs
Un pod template définit la structure du pod créé pour chaque build. Chaque conteneur a un rôle spécifique : JNLP pour la connexion au controller, Node pour le build, Docker pour les images, kubectl pour le déploiement.
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
serviceAccountName: jenkins-agent
containers:
- name: node
image: node:20-alpine
command: ['sleep', 'infinity']
resources:
requests: { memory: "1Gi", cpu: "500m" }
volumeMounts:
- { name: npm-cache, mountPath: /root/.npm }
- name: kubectl
image: bitnami/kubectl:1.28
command: ['sleep', 'infinity']
volumes:
- name: npm-cache
persistentVolumeClaim:
claimName: jenkins-npm-cache
'''
defaultContainer 'node'
}
}
stages {
stage('Build') {
steps { sh 'npm ci && npm run build && npm test' }
}
stage('Deploy') {
steps {
container('kubectl') {
sh 'kubectl set image deploy/app app=reg/app:${BUILD_NUMBER} -n prod'
}
}
}
}
}
Shared Library — factoriser les templates
Pour éviter de copier le YAML dans chaque pipeline, encapsule les templates dans une shared library. L’équipe n’a plus besoin de comprendre Kubernetes — elle appelle une fonction et c’est parti :
// vars/k8sAgent.groovy — dans ta shared library
def call(Map config = [:], Closure body) {
def containers = [
containerTemplate(name: 'jnlp', image: 'jenkins/inbound-agent:latest-jdk17')
]
if (config.node) containers << containerTemplate(
name: 'node', image: "node:${config.nodeVersion ?: '20'}-alpine",
command: 'sleep', args: 'infinity')
if (config.kubectl) containers << containerTemplate(
name: 'kubectl', image: 'bitnami/kubectl:1.28',
command: 'sleep', args: 'infinity')
podTemplate(containers: containers, serviceAccount: 'jenkins-agent') {
node(POD_LABEL) { body() }
}
}
// Jenkinsfile — simple et propre
@Library('ci-lib') _
k8sAgent(node: true, kubectl: true) {
stage('Build') { container('node') { sh 'npm ci && npm run build' } }
stage('Deploy') { container('kubectl') { sh 'kubectl apply -f k8s/' } }
}
🔥 Ce pattern est un game-changer. La complexité K8s est cachée dans la library. Les développeurs écrivent des pipelines de 5 lignes au lieu de 50 lignes de YAML. Et si tu changes de version d’image ou de stratégie de cache, tu modifies un seul fichier pour 200+ pipelines.
Scaling et autoscaling
Le cycle de vie d’un agent K8s est simple : build déclenché → pod créé → JNLP se connecte → steps exécutés → pod détruit. Le scaling se configure à deux niveaux.
Niveau Jenkins
Le plugin Kubernetes expose des paramètres critiques dans le JCasC :
jenkins:
clouds:
- kubernetes:
name: "kubernetes"
containerCap: 50 # Max 50 pods agents simultanés
retentionTimeout: 5 # Garde le pod 5 min après le build
waitForPodSec: 600 # Timeout de démarrage du pod
podRetention: "onFailure" # Conserve les pods en échec (debug)
Niveau cluster
Quand Jenkins lance 30 pods d’un coup, le cluster doit suivre. Deux options principales : le Cluster Autoscaler classique (AWS/GCP/Azure) qui scale les node groups, et Karpenter (AWS) qui provisionne des instances spot automatiquement — plus rapide et moins cher.
🎯 Configuration recommandée : containerCap: 50 pour limiter la facture, podRetention: onFailure pour debugger les builds cassés, instances spot pour diviser le coût par 3.
💡 Cache de dépendances : utilise un PersistentVolumeClaim partagé (ReadWriteMany via EFS ou NFS) pour le cache npm/Maven. Sans ça, chaque pod re-télécharge les dépendances — c’est 5 minutes perdues par build sur un projet lourd.
RBAC et sécurité
Jenkins a besoin de permissions Kubernetes précises. Applique le principe du moindre privilège avec des ServiceAccounts dédiés et des Roles scoped au namespace.
apiVersion: v1
kind: ServiceAccount
metadata: { name: jenkins-agent, namespace: jenkins }
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: jenkins-agent-role, namespace: jenkins }
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "pods/exec"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: jenkins-agent-binding, namespace: jenkins }
subjects:
- { kind: ServiceAccount, name: jenkins-agent, namespace: jenkins }
roleRef:
kind: Role
name: jenkins-agent-role
apiGroup: rbac.authorization.k8s.io
⚠️ Erreur fatale : donner un ClusterRoleBinding avec cluster-admin à Jenkins. Ça marche, mais un Jenkinsfile malveillant pourrait supprimer n’importe quelle ressource du cluster. Utilise un Role par namespace, jamais un ClusterRole global.
⚠️ Network Policies : isole le namespace Jenkins. Les pods agents ne doivent communiquer qu’avec le controller Jenkins, le registry Docker et les API endpoints nécessaires. Tout le reste doit être bloqué par défaut.
Pièges courants et monitoring
Les erreurs classiques lors de la migration Jenkins vers Kubernetes :
⚠️ Pods agents qui timeout — le waitForPodSec par défaut (600s) peut être insuffisant si l’image est lourde ou si le cluster doit scaler un nœud. Monitore le temps de démarrage et ajuste.
⚠️ Mémoire JVM du controller — les OutOfMemoryError sont le crash le plus courant. Configure les limites K8s 20% au-dessus du heap JVM max. Si le heap est à 2G, mets limits.memory: 2.5Gi.
🔥 Monitoring Prometheus : active les métriques avec le plugin prometheus et surveille ces compteurs :
jenkins_queue_size_value— builds en attente (si > 10, tu manques d’agents)jenkins_executor_in_use_value— agents occupés vs disponiblesvm_memory_heap_usage— mémoire JVM (alerte à 80%)
Le dashboard Grafana #9964 affiche tout ça. Si la queue grossit pendant plus de 5 minutes, c’est que ton autoscaler ne suit pas ou que containerCap est trop bas.
Résumé
Jenkins sur Kubernetes se résume à trois piliers :
Helm pour un déploiement reproductible. Values versionnées dans Git, JCasC intégré, plugins pré-installés. Un helm upgrade et c’est déployé en 3 minutes.
Pod Templates pour des agents sur mesure. Multi-conteneurs avec container('name'), shared libraries pour l’abstraction, PVC pour le cache. Les devs écrivent des pipelines simples, la complexité K8s est cachée.
Scaling automatique à deux niveaux. containerCap côté Jenkins pour limiter la charge, Cluster Autoscaler ou Karpenter côté K8s pour provisionner les nœuds. Avec des instances spot, la facture CI/CD baisse de 60-70% par rapport aux VM statiques.
🖥️ Pratique sur ton propre serveur
Pour suivre Jenkins en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Contenu réservé aux abonnés
Ce chapitre fait partie de la formation complète. Abonne-toi pour débloquer tous les contenus.
Débloquer pour 29 CHF/moisLe chapitre 1 de chaque formation est gratuit.
Série pas encore débloquée
Termine la série prérequise d'abord pour accéder à ce contenu.
Aller à la série prérequiseSérie : Jenkins
6 / 8- Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
- Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
- Jenkins #3 — Pipeline Avancé : Scripté, Shared Libraries et Parallel Stages
- Jenkins #4 — Plugins Essentiels : Docker, K8s, Git, SonarQube et Slack
- Jenkins #5 — Jenkins et Docker : Build d'Images, DinD et Kaniko
- 6 Jenkins #6 — Jenkins sur Kubernetes : Helm, Pod Templates et Scaling
- 7 Jenkins #7 — Sécurité : RBAC, Credentials, Audit Trail et Hardening
- 8 Jenkins #8 — Production : HA, Backup, Monitoring et Migration
Sur cette page
Articles liés
Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
Découvre Jenkins de A à Z : pourquoi c'est le standard CI/CD, l'architecture master/agent, l'installation sur Ubuntu et ton premier job freestyle.
Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
Apprends à écrire ton premier Jenkinsfile, maîtrise le pipeline déclaratif avec stages, steps et post, et découvre Blue Ocean UI.
Jenkins #3 — Pipeline Avancé : Scripté, Shared Libraries et Parallel Stages
Maîtrise les pipelines Jenkins avancés : scripté vs déclaratif, shared libraries, stages parallèles, input/approval et patterns de production.