Projet · Phase 6.3

CI/CD (3/5) — Déployer sur le cluster (CD)

Jenkins produit l'image, mais ne la déploie pas. On lui donne une identité limitée sur le cluster (ServiceAccount + RBAC), un kubeconfig dédié, et un stage Deploy qui met à jour la release via Helm.

⏱ ~2 hNiveau avancéPrérequis : CI/CD (2/5)Pratique · TP
L’idée reçue

« Pour déployer, je donne à Jenkins mon kubeconfig admin, c’est plus simple. »

Jamais. Un serveur CI qui détient les pleins pouvoirs sur le cluster est une catastrophe de sécurité en attente. On donne à Jenkins une identité dédiée et limitée : le droit de déployer uniquement dans son namespace, rien d’autre. C’est le principe de moindre privilège, appliqué au CD.

À la fin de ce module, tu sauras…

  • Installer Helm sur la VM Jenkins et comprendre pourquoi il déploie localement
  • Créer une identité cluster limitée (ServiceAccount + Role + RoleBinding)
  • Générer un kubeconfig dédié et l’injecter comme credential
  • Ajouter un stage Deploy qui met à jour la release via Helm

01Donner Helm à Jenkins

Expérience 1

L’outil de déploiement, côté Jenkins

Le stage Deploy utilisera Helm : il doit être présent sur la VM Jenkins (c’est Jenkins qui déploie). Installe-le en binaire autonome :

Manipule
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
Observe
version.BuildInfo{Version:"v3.x.x", ...}
Helm est un binaire autonome : l’installer sur Jenkins ne touche pas au cluster. Il servira à lancer helm upgrade depuis le pipeline.
Comprends deux patterns de CD, et pourquoi celui-ci

Pattern A (celui-ci) : Jenkins exécute Helm localement, en pointant vers l’API du cluster via un kubeconfig. Pattern B : Jenkins se connecte en SSH à un nœud et y lance Helm. A est plus propre — pas de couplage à une machine précise, tout passe par l’API Kubernetes. C’est aussi ce que fait un outil comme Argo CD, en continu : parler à l’API du cluster avec une identité limitée. Le concept (identité + API) survit au changement d’outil.

02Une identité limitée sur le cluster

Expérience 2

ServiceAccount + Role + RoleBinding

Sur le nœud master, crée une identité jenkins-cd qui ne peut agir que dans lab-soria :

Manipule
cat > /tmp/jenkins-cd-rbac.yaml <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-cd
namespace: lab-soria
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: jenkins-cd-role
namespace: lab-soria
rules:
- apiGroups: [""]
  resources: ["configmaps","secrets","services","pods","pods/log","events"]
  verbs: ["get","list","watch","create","update","patch","delete"]
- apiGroups: ["apps"]
  resources: ["deployments","replicasets"]
  verbs: ["get","list","watch","create","update","patch","delete"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","list","watch","create","update","patch","delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jenkins-cd-binding
namespace: lab-soria
subjects:
- kind: ServiceAccount
  name: jenkins-cd
  namespace: lab-soria
roleRef:
kind: Role
name: jenkins-cd-role
apiGroup: rbac.authorization.k8s.io
EOF
kubectl apply -f /tmp/jenkins-cd-rbac.yaml
kubectl -n lab-soria get sa jenkins-cd
Jenkins pourra créer/mettre à jour Deployments, Services, Ingress, ConfigMaps et Secrets — seulement dans lab-soria. Aucun droit ailleurs, aucun droit cluster-admin.
Comprends le modèle RBAC, universel

Trois objets : le ServiceAccount (l’identité), le Role (quelles actions sur quelles ressources, dans un namespace), le RoleBinding (qui relie l’identité au rôle). C’est le modèle RBAC de Kubernetes : « qui peut faire quoi, où ». Les verbes autorisés couvrent exactement ce dont Helm a besoin pour gérer l’app — ni plus. Ce raisonnement « donner juste les droits nécessaires » vaut pour tout outil de CD.

03Fabriquer un kubeconfig dédié

Expérience 3

Un kubeconfig pour cette identité

Manipule
SA=jenkins-cd
NS=lab-soria
TOKEN=$(kubectl -n $NS create token $SA --duration=8760h)
CA=$(kubectl -n kube-system get configmap kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')
SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')

cat > /tmp/kubeconfig-jenkins-cd.yaml <<EOF
apiVersion: v1
kind: Config
clusters:
- name: k3s
cluster:
  server: ${SERVER}
  certificate-authority-data: $(printf "%s" "$CA" | base64 -w0)
contexts:
- name: jenkins-cd@k3s
context:
  cluster: k3s
  namespace: ${NS}
  user: jenkins-cd
current-context: jenkins-cd@k3s
users:
- name: jenkins-cd
user:
  token: ${TOKEN}
EOF
ls -la /tmp/kubeconfig-jenkins-cd.yaml
!
Point crucial — le champ server:. Il doit contenir une adresse d’API joignable depuis la VM Jenkins. Si c’est https://127.0.0.1:6443 ou une IP interne inaccessible, le deploy échouera. Vérifie et, au besoin, remplace par l’IP réseau du master atteignable par Jenkins.
Comprends ce que contient un kubeconfig

Un kubeconfig = est l’API (server), comment vérifier son certificat (certificate-authority-data), et qui tu es (token du ServiceAccount). Avec ces trois éléments, Helm/kubectl peut parler au cluster en tant que jenkins-cd, borné à son namespace.

04Injecter le kubeconfig dans Jenkins

Expérience 4

Un credential de type Secret file

Récupère /tmp/kubeconfig-jenkins-cd.yaml, puis dans Jenkins → Credentials → Add Credentials :

ChampValeur
KindSecret file
IDkubeconfig-soria
Filele fichier kubeconfig généré
Le kubeconfig (qui contient le token) vit désormais dans le coffre Jenkins, jamais dans le dépôt. Le pipeline le montera comme fichier temporaire pendant le deploy.
Comprends pourquoi Secret file, pas texte

Un kubeconfig est un fichier ; le type Secret file le stocke tel quel et le remet au pipeline comme chemin temporaire (via KUBECONFIG). Il n’apparaît jamais en clair dans les logs et n’est présent que le temps du stage.

05Le stage Deploy

Expérience 5

helm upgrade, piloté par Jenkins

Ajoute ce stage après Push dans le Jenkinsfile (on suppose un chart soria-web dans le dépôt) :

Manipule
stage('Deploy (Helm)') {
steps {
  withCredentials([file(credentialsId: 'kubeconfig-soria', variable: 'KUBECONFIG_FILE')]) {
    sh '''
      set -eu
      export KUBECONFIG="$KUBECONFIG_FILE"
      helm upgrade --install soria-web ./soria-web \\
        -n lab-soria \\
        --set image.repository="$IMAGE_REPO" \\
        --set image.tag="$IMAGE_TAG"
      helm -n lab-soria history soria-web | tail -n 5
    '''
  }
}
}

Commit, push, relance le job, et lis le Console Output du stage Deploy :

Observe
Release "soria-web" has been upgraded. Happy Helming!
REVISION  STATUS      DESCRIPTION
2         deployed    Upgrade complete
Jenkins a déployé tout seul : la release passe en révision 2, les Pods roulent vers la nouvelle image. La boucle CI et CD est vivante.
Comprends upgrade –install, et le passage de tag

helm upgrade –install : installe si la release n’existe pas, met à jour sinon (idempotent — parfait pour un pipeline rejoué). Le pipeline passe le même tag calculé au stage Compute Tag ($IMAGE_TAG) : l’image construite est exactement celle déployée. Traçabilité de bout en bout : commit → image → release.

Ce que tu retiens

Le CD relie l’artefact au cluster. Jenkins déploie via Helm local, avec une identité limitée (ServiceAccount + Role + RoleBinding, bornée au namespace) et un kubeconfig dédié stocké en Secret file. Le stage Deploy fait helm upgrade –install avec le tag calculé : commit → image → release, sans jamais de droits admin dans Jenkins.

Mini-projet

De l’image à la release, automatiquement

À faire

  1. Installe Helm sur la VM Jenkins.
  2. Crée l’identité jenkins-cd (SA + Role + RoleBinding) bornée à lab-soria.
  3. Génère le kubeconfig dédié, vérifie le champ server:, injecte-le en Secret file.
  4. Ajoute le stage Deploy, push, relance, prouve la révision Helm qui s’incrémente.
  5. Vérifie kubectl -n lab-soria get pods : nouvelle image en service.
  6. Documente dans BookStack le modèle RBAC (SA/Role/Binding) et le contenu d’un kubeconfig.
Fil rouge — La chaîne complète fonctionne… mais tu cliques encore « Build Now ». Au module suivant, on branche le déclencheur : un webhook GitHub lancera le pipeline à chaque push — et on éprouve la boucle entière, rollback compris.

?Auto-évaluation

1. Pourquoi ne pas donner un kubeconfig admin à Jenkins ?

Moindre privilège : Jenkins ne doit pouvoir déployer que dans son namespace. Un accès admin ferait de Jenkins un point de compromission du cluster entier.

2. À quoi servent le Role et le RoleBinding ?

Le Role définit les actions autorisées sur des ressources dans un namespace ; le RoleBinding relie ce rôle au ServiceAccount de Jenkins.

3. Pourquoi helm upgrade –install et pas juste upgrade ?

Il est idempotent : installe si absent, met à jour sinon. Idéal pour un pipeline rejoué plusieurs fois sans erreur.

Connecte-toi pour enregistrer ta progression.

Suite → CI/CD (4/5) — Automatiser : webhook & boucle complète