Skip to content

Jenkins ​

Jenkins est le robot qui teste, construit et déploie. Il tourne sur tm-tooling et s'affiche sur ci.time.jostud.com.

Particularité de cette installation : rien n'est configuré à la main dans l'interface de Jenkins. Utilisateurs, droits, identifiants, jobs : tout vient de fichiers versionnés. Si la machine disparaît, un ansible-playbook recrée un Jenkins identique.

L'image : jenkins/Dockerfile ​

dockerfile
FROM jenkins/jenkins:lts
USER root
RUN apt-get update && apt-get install -y docker.io python3-pip curl && rm -rf /var/lib/apt/lists/*
COPY jenkins/Plugins/plugins.txt /Plugins/plugins.txt
COPY jenkins/job_dsl.groovy /job_dsl.groovy
RUN jenkins-plugin-cli --plugin-file /Plugins/plugins.txt
RUN mkdir -p /var/jenkins_home/casc_configs && chown -R jenkins:jenkins /var/jenkins_home/casc_configs
USER jenkins

Les plugins sont installés au build de l'image, pas au démarrage : Jenkins démarre avec tout ce qu'il faut.

Les plugins : Plugins/plugins.txt ​

PluginRôle
configuration-as-codeLire casc.yml
job-dslCréer les jobs depuis job_dsl.groovy
workflow-aggregatorLes pipelines
git, git-client, githubCloner le dépôt
credentials, credentials-bindingRanger et utiliser les secrets
ssh-agent, ssh-slavesSe connecter en SSH pour déployer
docker-plugin, docker-workflow, docker-commonsUtiliser Docker dans les pipelines
role-strategy, matrix-authLes droits des utilisateurs
script-security, structsDépendances des autres plugins
ws-cleanupNettoyer l'espace de travail
pipeline-stage-view, blueoceanVoir les étapes des pipelines dans l'interface

Le conteneur : jenkins/docker-compose.yml ​

yaml
services:
  jenkins:
    build:
      context: ..
      dockerfile: jenkins/Dockerfile
    restart: unless-stopped
    user: "root"
    environment:
      - JAVA_OPTS=-Djenkins.install.runSetupWizard=false
      - CASC_JENKINS_CONFIG=/var/jenkins_home/casc_configs/casc.yml
    env_file:
      - .env
    container_name: jenkins
    ports:
      - "${JENKINS_BIND_IP}:8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - ./casc.yml:/var/jenkins_home/casc_configs/casc.yml:ro
    secrets:
      - deploy_key

secrets:
  deploy_key:
    file: ./secrets/deploy_key
ÉlémentExplication
runSetupWizard=falsePas d'assistant d'installation : la configuration vient de casc.yml
env_file: .envLes variables écrites par Ansible (mot de passe admin, jetons, IP de prod...)
${JENKINS_BIND_IP}:8080:8080Jenkins n'écoute que sur l'IP privée 10.132.0.3
jenkins_homeUn volume : l'historique des builds survit aux redémarrages
/var/run/docker.sockJenkins pilote le Docker de la machine pour construire les images
user: "root"Nécessaire pour utiliser docker.sock
secrets: deploy_keyLa clé SSH privée est montée dans /run/secrets/deploy_key, jamais dans une variable d'environnement

La configuration : jenkins/casc.yml ​

Le plugin Configuration as Code (CasC) lit ce fichier au démarrage. Les ${...} sont remplacés par les variables d'environnement du .env.

Le système ​

yaml
jenkins:
  systemMessage: "Welcome to Time Manager Jenkins."
  numExecutors: 4
  globalNodeProperties:
    - envVars:
        env:
          - key: "GHCR_OWNER"
            value: "${GHCR_USERNAME}"
          - key: "VITE_API_URL"
            value: "${VITE_API_URL}"
          - key: "PROD_HOST"
            value: "${PROD_HOST}"
  • 4 builds peuvent tourner en même temps.
  • Trois variables sont disponibles dans tous les pipelines : env.GHCR_OWNER, env.VITE_API_URL, env.PROD_HOST.

Les utilisateurs et les droits ​

yaml
  securityRealm:
    local:
      allowsSignup: false
      users:
        - id: "admin"
          password: "${JENKINS_ADMIN_PASSWORD}"
  authorizationStrategy:
    roleBased:
      roles:
        global:
          - name: "admin"
            permissions: ["Overall/Administer"]
            entries:
              - user: "admin"

Un seul compte, admin. Personne ne peut créer de compte (allowsSignup: false). Un visiteur non connecté ne voit rien.

Les identifiants ​

IDTypeContenuUtilisé pour
github-tokenUtilisateur + mot de passeCompte et jeton GitHubCloner le dépôt
ghcrUtilisateur + mot de passeCompte et jeton GitHubPousser les images sur ghcr.io
vm-ssh-keyClé SSHUtilisateur deploy, clé lue dans /run/secrets/deploy_keySe connecter à tm-prod pour déployer

Les pipelines les utilisent par leur ID, sans jamais voir leur valeur en clair dans les journaux (Jenkins masque les secrets).

Les jobs ​

yaml
jobs:
  - file: job_dsl.groovy

CasC exécute job_dsl.groovy, qui crée les jobs.

Les jobs : jenkins/job_dsl.groovy ​

groovy
folder('TimeManager') {
    description('Time Manager build and deployment jobs.')
}

pipelineJob('TimeManager/frontend') {
    triggers {
        scm('H/2 * * * *')
    }
    definition {
        cpsScm {
            scm {
                git {
                    remote {
                        url('https://github.com/Epitech2026-2027/I-DEV-700-INT-7-1-timemanager-21.git')
                        credentials('github-token')
                    }
                    branches('*/main')
                    extensions {
                        pathRestriction {
                            includedRegions('timemanager-web/.*\ntimemanager-devops/jenkins/pipelines/frontend.groovy')
                        }
                    }
                }
            }
            scriptPath('timemanager-devops/jenkins/pipelines/frontend.groovy')
        }
    }
}

Le job TimeManager/backend est identique, avec timemanager-api/ et backend.groovy. Le job TimeManager/docs aussi, avec timemanager-docs/, docs/database.mmd et docs.groovy.

ÉlémentSens
folder('TimeManager')Un dossier qui regroupe les jobs
scm('H/2 * * * *')Toutes les 2 minutes, regarder s'il y a de nouveaux commits. H étale les vérifications pour ne pas tout lancer à la même seconde.
branches('*/main')Seulement la branche main
pathRestrictionNe lancer le job que si un fichier de ces chemins a changé. Un commit qui ne touche que le mobile ne relance rien.
scriptPathLe pipeline à exécuter est dans le dépôt, à ce chemin

Le pipeline vit dans le dépôt

Le contenu du pipeline n'est pas dans Jenkins mais dans pipelines/*.groovy, versionné avec le code. Modifier ce fichier dans main relance d'ailleurs le pipeline (il est dans includedRegions).

Pourquoi interroger GitHub plutôt qu'un webhook ​

Le dépôt appartient à une organisation de l'école dans laquelle l'équipe n'a pas les droits d'administration. Les webhooks (GitHub qui prévient Jenkins à chaque commit) et les clés de déploiement ne sont donc pas disponibles. Jenkins interroge le dépôt à la place :

  • toutes les deux minutes, chaque job regarde le dernier commit de main ;
  • le filtre pathRestriction ne déclenche un job que si son code a changé : timemanager-web/ pour le frontend, timemanager-api/ pour le backend, plus le fichier de pipeline de chaque job.

L'interrogation n'a besoin que d'un accès en lecture, et GitHub n'a jamais besoin de joindre Jenkins.

Se connecter ​

ci.time.jostud.com, compte admin, mot de passe jenkins_admin_password du vault group_vars/tooling.yml :

bash
cd timemanager-devops/ansible
ansible-vault view group_vars/tooling.yml

Sans connexion, Jenkins répond 403 et n'affiche rien. Les liens directs vers chaque pipeline et ce qu'on y trouve sont dans Pipelines CI/CD.

Par un tunnel SSH ​

Si le domaine ne répond pas, on peut joindre Jenkins directement par un tunnel SSH vers tm-tooling :

bash
ssh -N -L 8080:10.132.0.3:8080 eliashounnou@34.156.3.169

puis ouvrir http://localhost:8080. Le tunnel reste ouvert tant que la commande tourne.