Objectifs
- Configurer une instance Jenkins entièrement dockerisée comprenant un Controller (Master) et un Agent (Slave).
- Router l’ensemble via notre proxy inverse Traefik mis en place précédemment.
Introduction
Avant de passer au cœur de la configuration, introduisons quelques concepts clés.
Les pipelines CI/CD modernes font face à un défi majeur : créer un environnement de build cohérent, propre et reproductible. Les méthodes traditionnelles mènent souvent à des conflits de dépendances et au fameux problème “ça marche sur ma machine”.
Docker apporte la solution idéale en isolant les applications et leurs dépendances dans des conteneurs. En combinant Jenkins, le serveur d’automatisation de référence, avec Docker, nous créons un système CI/CD puissant et flexible.
Cette intégration peut se faire de deux manières principales :
-
Jenkins à l’intérieur d’un conteneur Docker qui possède son propre démon Docker (Docker-in-Docker ou DinD).
-
Jenkins à l’intérieur d’un conteneur Docker qui utilise le démon Docker de l’hôte (Docker-out-of-Docker ou DoD).
Voici une comparaison rapide entre DinD et DoD dans le contexte de Jenkins :
| Caractéristique | Docker-in-Docker (DinD) | Docker-out-of-Docker (DoD) |
|---|---|---|
| Fonctionnement | Le conteneur Jenkins exécute son propre démon Docker imbriqué. | Le conteneur Jenkins utilise le démon Docker de l’hôte. |
| Sécurité | ⚠️ Expertise requise ! Nécessite le drapeau --privileged. |
✅ Direct. Pas de mode privilégié nécessaire. |
| Performance | 🔻 Moyenne. Surcharge due aux démons multiples ; pas de cache partagé. | ✅ Excellente. Démon unique et cache d’images partagé. |
| Isolation | ✅ Totale. Chaque build est isolé de l’hôte et des autres jobs. | 🔻 Limitée. Tous les conteneurs sont “frères” sur le même hôte. |
Important !
Cet article couvrira exclusivement l’approche DoD. De plus, pour garder les choses simples, j’utiliserai le même hôte pour le Controller et l’Agent.
Code source
Vous trouverez ici le code source pour une installation Jenkins complète :
- Controller (master) : https://github.com/a-naitslimane/anaitslimane.article.jenkins-traefik-docker.jenkins-controller
- Agent (slave) : https://github.com/a-naitslimane/anaitslimane.article.jenkins-traefik-docker.jenkins-agent
Configuration
Prérequis :
- Docker et Docker Compose installés sur votre machine.
- Un proxy inverse Traefik dockerisé tournant localement. Si ce n’est pas le cas, veuillez suivre mon article précédent Traefik reverse proxy .
Important !
Les configurations suivantes sont étroitement liées aux paramètres de mon article précédent. Pensez à les ajuster avec vos propres valeurs, en particulier le nom du réseau Docker externe.
Mise en place des conteneurs
-
Clonez le code source vers votre répertoire de travail.
-
Définissez les variables d’environnement
- Créez une copie de .env.example nommée .env.
Vous pouvez y définir vos propres valeurs, tant qu’elles restent cohérentes avec la suite de la configuration.
- Créez une copie de .env.example nommée .env.
-
Liez votre DOMAIN_NAME au localhost :
-
Ajoutez votre nom de domaine local au fichier
hosts:C:\Windows\System32\drivers\etc\hosts(sur Windows) ou/etc/hosts(sur Linux).127.0.0.1 ci-cd-ctrl.my-local-domain.com
-
-
Lancez le service via Docker Compose :
- Dans votre terminal, cd vers votre répertoire de travail puis tapez simplement :
docker compose up
- Dans votre terminal, cd vers votre répertoire de travail puis tapez simplement :
Test du Controller
-
Vérifiez que le conteneur du controller est actif et sain. Récupérez le mot de passe généré par défaut.
-
Dans votre navigateur, ouvrez l’adresse configurée dans DOMAIN_NAME (ex: ci-cd-ctrl.my-local-domain.com). Saisissez le mot de passe récupéré à l’étape précédente.
-
Choisissez votre méthode préférée pour l’installation des plugins. Notez que vous pourrez gérer les plugins plus tard.
-
Complétez votre profil administrateur.
-
Configurez l’URL Jenkins avec la valeur de votre DOMAIN_NAME (ex: ci-cd-ctrl.my-local-domain.com).
Quid de l’Agent (Node) ?
Il existe deux méthodes pour lier l’agent au controller via le Launch method. La première demande à l’agent de se connecter au controller, ce qui nécessite l’ouverture et la configuration d’un port entrant sur le controller. Veuillez consulter la documentation jenkins/ssh-agent pour ce cas.
Pour cet article, j’ai délibérément choisi la seconde option ! Celle-ci fonctionne à l’inverse : c’est le controller qui se connecte à l’agent via SSH. Pour y parvenir, nous devons créer ce que l’on appelle dans Jenkins des credentials (identifiants). Consultez la doc Jenkins pour en savoir plus sur la gestion des identifiants .
Parmi les options disponibles, j’ai choisi “SSH Username with private key”. Cela nécessite la création d’une paire de clés SSH à utiliser entre le controller et l’agent.
Maintenant que le contexte est posé, passons à la pratique :
Préparation de la configuration SSH
-
Connectez-vous à la console du conteneur controller (bash est disponible dans l’image) :
docker exec -it ci-cd-jenkins-ctrl bash -
Générez la clé (j’ai choisi l’algorithme ssh-ed25519) avec la commande suivante. Laissez la “passphrase” vide pour éviter toute complication inutile pour le moment.
ssh-keygen -t ed25519 -C "jenkins"Cela générera la paire de clés (privée/publique). Le répertoire par défaut de sauvegarde devrait être ’/var/jenkins_home/.ssh’.
Ajout des identifiants
- Allez sur le tableau de bord du controller.
- Cliquez sur Administrer Jenkins > Credentials.
- Vous pouvez créer un domaine spécifique ou, pour faire simple, choisir le domaine par défaut Global credentials (unrestricted).
- Cliquez sur Add Credentials.
- Sélectionnez l’option SSH Username with private key.
- Entrez jenkins dans le champ Username.
- Pour la Private Key, sélectionnez Enter directly.
- Cliquez sur Add, puis copiez-collez l’intégralité de la clé privée générée précédemment pour l’utilisateur jenkins.
Ajout de l’Agent (Node)
- Retournez sur le tableau de bord.
- Dans le menu de gauche, cliquez sur le lien Build executor Status.
- Cliquez sur New Node, donnez-lui un nom, sélectionnez Permanent Agent et validez.
- Remplissez les champs requis, notamment :
- “Launch method” : Launch agent via SSH.
- “Host” : le nom du conteneur agent ci-cd-jenkins-ssh-agent.
- “Remote root directory” : /home/jenkins/agent.
- Sélectionnez les credentials créés précédemment.
- Pour “Host Key Verification Strategy”, j’ai choisi Manually trusted key Verification Strategy.
Lancement du conteneur Docker de l’agent
- Clonez le code source .
- Définissez les variables d’environnement (copie de .env.example vers .env).
- Configurez notamment la valeur SSH_PUBKEY avec la clé publique générée précédemment depuis l’interface du controller.
## env ##1PORT_JENKINS_AGENT_SSH=22 2COMPOSE_PROJECT_NAME=ci-cd-jenkins-ssh-agent 3DOCKER_GROUP_ID=0 4SSH_PUBKEY=<votre_clé_publique_ici> - Lancez l’agent :
docker compose up
Test de l’agent
- Allez sur le tableau de bord du controller.
- Sélectionnez votre agent.
- Cliquez sur le bouton Launch agent et voilà ! Votre agent est connecté.
Pièges courants
Basique
- Clé publique erronée : Une erreur de frappe de la clé publique empêchera toute connexion. Il est crucial de respecter scrupuleusement le format attendu par le conteneur agent.
- Solution : Vérifiez que la clé commence bien par son type (ex:
ssh-ed25519) et qu’elle ne contient pas de retours à la ligne parasites dans votre fichier.env.
- Solution : Vérifiez que la clé commence bien par son type (ex:
- Format de la clé privée : Lors de la création des identifiants (Credentials) dans Jenkins, l’ajout d’espaces superflus ou de sauts de ligne incorrects invalidera la clé.
- Solution : Copiez l’intégralité du bloc, y compris les balises
-----BEGIN...et...END-----, sans modification manuelle du contenu.
- Solution : Copiez l’intégralité du bloc, y compris les balises
- Configuration du Port SSH : Lors de la déclaration du Node sur le dashboard Jenkins, la connexion échouera si le port ne correspond pas à celui exposé par le conteneur de l’agent.
- Solution : Assurez-vous que le champ “Port” dans Jenkins correspond exactement à la valeur
PORT_JENKINS_AGENT_SSHdéfinie dans votre configuration Docker (par défaut 22).
- Solution : Assurez-vous que le champ “Port” dans Jenkins correspond exactement à la valeur
Avancé
- Permissions du Socket Docker (Conflit de GID) : Dans une configuration DoD, l’agent doit pouvoir communiquer avec
/var/run/docker.socksur l’hôte. Si l’ID de groupe (GID) du groupe “docker” de l’hôte ne correspond pas auDOCKER_GROUP_IDde votre.env, vous ferez face à des erreurs “Permission Denied”.- Solution : Lancez
stat -c '%g' /var/run/docker.socksur votre hôte et mettez à jour votre.envavec cette valeur numérique.
- Solution : Lancez
- Ports SSH non standards : Si vous avez modifié le
PORT_JENKINS_AGENT_SSHdans le.envde l’agent pour une valeur autre que22, Jenkins ne pourra pas se connecter.- Solution : Vérifiez que le champ “Port” dans la configuration du Node Jenkins correspond exactement au port SSH exposé par votre conteneur.
- Conteneurs de Build “Zombies” : Comme les conteneurs de build sont des “frères” sur l’hôte, un crash de l’agent peut laisser des conteneurs orphelins en cours d’exécution.
- Solution : Auditez périodiquement votre hôte avec
docker pspour vous assurer qu’aucun processus de build ne consomme de ressources inutilement après l’échec d’un pipeline.
- Solution : Auditez périodiquement votre hôte avec
- Connectivité réseau Traefik : Le Controller Jenkins doit pouvoir atteindre le conteneur Agent. S’ils ne sont pas sur le même réseau Docker, la connexion SSH échouera (Time out).
- Solution : Vérifiez que les deux services sont connectés au même réseau docker externe défini dans vos fichiers Compose.
En résumé
En routant votre Controller et votre Agent Jenkins via Traefik, vous avez dépassé le stade du simple lab pour mettre en place un environnement CI/CD professionnel avec proxy inverse. L’approche DoD garantit la rapidité de vos builds grâce au partage du cache d’images de l’hôte, tandis que la configuration de l’Agent via SSH maintient une architecture découplée et sécurisée.
Note finale:
- Évolutivité : Vous pouvez désormais passer à l’échelle horizontalement en ajoutant d’autres agents via le même modèle SSH sur différents hôtes.
- Maintenance : Puisque les conteneurs de build sont des “frères” sur votre hôte, pensez à exécuter un
docker image pruneoccasionnellement pour garder votre environnement propre.
Vous disposez maintenant d’une stack d’automatisation robuste et dockerisée, prête à propulser vos pipelines les plus complexes. Bonne automatisation !