>

Jenkins: Déploiement sous Kubernetes

Introduction

Logo de Jenkins

Cliquez sur l'image pour l'agrandir.

Jenkins fait partie des applicatifs fondateurs du CI/CD. Certains le considèrent d’ailleurs comme un dinosaure sur le marché du DevOps, prônant son remplacement par des outils plus modernes et plus optimisés.

Pourtant, Jenkins continue d’être très largement adopté et utilisé. Sa force réside justement dans son ancienneté. Au fil des années, il s’est enrichi de très nombreux plugins, lui permettant une intégration dans de nombreux écosystèmes.

Des générations d’administrateurs ont pu se former et monter en compétence sur le produit, permettant d’avoir aujourd’hui une grande communauté d’experts.

L’objectif de ce tutoriel est de faire une introduction à l’installation de Jenkins sous Kubernetes. Je n’ai pas vocation à détailler l’usage de Jenkins. Je n’ai pas le niveau suffisant pour ça, et Jenkins est tellement puissant en termes de faisabilité qu’il existe énormément de manières de faire et d’usages possibles.

Je souhaite simplement proposer quelques pistes pour déployer Jenkins sous K8S, avec comme objectif final d’inscrire cet article dans une série de tutoriels visant à illustrer un cas d’utilisation complet de CI/CD.

Contrairement à ArgoCD que j’ai pu présenter ici, focalisé sur le déploiement continu (CD) via une logique GitOPS, Jenkins peut être utilisé à la fois pour le CI (Intégration Continu) et du CD.

Rappel sur les fondamentaux

D’ailleurs, j’en profite pour faire un rappel sur cette distinction trop souvent oublié entre:

  • CI, qui signifie « Continuous Integration », englobe l’ensemble des tâches de compilation, de vérification du code, de création d’images...Ces processus sont effectués de manière automatisée et en continu.
  • CD, quant à lui, fait référence au « Continuous Delivery », qui inclut tous les aspects liés à l'installation, le déploiement et les mises à niveau des applications, jusqu'aux environnements de production. Tout cela également de manière automatisée et ininterrompue.

L’objectif ultime est de construire la fameuse chaine CI/CD, induisant une automatisation complète et continue de la création du code jusqu’à la livraison de l’application associée… toujours jusqu’en production !

Le marketing a souvent tendance à tout mélanger. Il n’est pas rare d’avoir, pour une application, que du CI ou que du CD, voir une automatisation en environnement de DEV uniquement… Sans jamais aller réellement jusqu’à l’industrialisation complète.

Certains outils sont d’ailleurs parfois orientés uniquement CI, ou CD. Jenkins fut d’abord pensé comme un outil de CI. Grâce à son extensibilité via des plugins, il s’est ensuite étendu au CD, ce qui en fait aujourd’hui une plateforme capable de gérer toute une chaîne de CI/CD.

Le revers de la médaille, c’est que Jenkins peut parfois faire les choses d’une manière plus complexe et/ou moins performante pour traiter un pan spécifique de la chaine CI/CD que des outils spécialisés.

Historique

Enfin, pour la petite histoire, Jenkins est dérivé d’Hudson, un logiciel développé par Kohsuke Kawaguchi, ingénieur chez Sun Microsystems en 2004. Suite au rachat de Sun par Oracle en 2010, des tensions sont apparues autour de Hudson provoquant une scission de la communauté en janvier 2011 menant à la création de Jenkins.

Depuis, Hudson est abandonnée… Jenkins est toujours là !.

Jenkins est aujourd’hui un projet maintenu par La Jenkins community, sous l’égide de la Jenkins project, hébergée par la CD Foundation (Continuous Delivery Foundation).

La CDF est à la CI/CD ce que la CNCF est au cloud-native.

Elle structure l’écosystème DevOps open source, en promouvant des projets clés comme Jenkins et Tekton, et en rassemblant une communauté mondiale autour de la livraison logicielle.

Jenkins X

Avant de démarrer, il convient de faire le point sur Jenkins X. Bien qu’avec un nom très proche, Jenkin X est différent de Jenkins.

Logo de Jenkins X

Cliquez sur l'image pour l'agrandir.

Jenkins X date de 2018 et n’a pas la même philosophie que Jenkins. Il a comme fondamental le souhait d’être un outil d’automatisation CI/CD natif pour Kubernetes en exploitant des pipelines prêts à l’emploi basé sur Tekton. Tekton est un framework open source de pipelines conçu pour k8s.

Voici un tableau comparant les deux produits:

Caractéristique Jenkins Jenkins X
Architecture Monolithique extensible Basé sur Kubernetes et Tekton
CI/CD CI centré, CD en plugin CI/CD complet avec GitOps intégré
Déploiement VM, Docker, etc. Kubernetes natif
Pipelines Jenkinsfile (Groovy) YAML + Tekton
Utilisation Généraliste Cloud-native, microservices, GitOps

Dans les faits, l’adoption de Jenkins X est plutôt modeste. Il a souvent été préféré de conserver Jenkins et de l’étendre avec d’autres outils plus nativement compatibles avec Kubernetes, comme ArgoCD, pour traiter certaines parties de la chaine.

Le projet Jenkins X est beaucoup moins actif aujourd’hui. C’est pourquoi je préfère me tourner vers Jenkins et « l’adapter » pour le rendre exploitable sur K8S… d’ailleurs la communauté propose déjà beaucoup de choses pour cela, comme nous allons le voir.

Création des images

Jenkins fonctionne sur le principe de contrôleur et d’agents. Le contrôleur servant de centre d'administration et de point d’entrée pour les configurations, les agents servant à exécuter les jobs contenus dans les pipelines. Je ne vais pas rentrer dans le détail, considérant que vous êtes déjà familier avec l’outil.

Disposant d’une autorité de certification interne (Certificate Authority (CA)) sous la forme de la solution Microsoft, j’exploite des certificats validés uniquement en interne de mon lab.

Jenkins devant être amené à communiquer avec d’autres composants de mon réseau, il se doit de reconnaitre cette CA. C’est d’ailleurs un cas courant en entreprise.

À la base, les images containers de Jenkins ne reconnaissent que les CA publics. Si je n’anticipe par ce point, j’aurais des erreurs dès lors que le contrôleur ou les agents chercheront à communiquer en SSL avec d’autres outils présents dans mon lab.

Il est très courant d’avoir à personnaliser les images de base de Jenkins. L’ajout du support de CA custom est une cause possible, mais d’autres besoins peuvent apparaître. Par exemple, dans mon cas, l’ajout du binaire kubectl pour les agents.

D’autres manières de faire sont possibles, mais c’est le choix que j’ai fait et que vous pourriez retrouver dans certaines structures.

Image du controleur

On va donc commencer par construire l’image du contrôleur Jenkins.

Voici le dokerfile Dockerfile.server utilisé:

  
FROM jenkins/jenkins:2.492.2-lts

USER root

# Copie du CA
COPY ca-coolcorp.crt /usr/local/share/ca-certificates/ca-coolcorp.crt

# Ajout du CA au système et au keystore Java
RUN apt-get update && apt-get install -y ca-certificates openjdk-17-jre-headless \
    #partie systeme
    && update-ca-certificates \
    #partie keystore
    && keytool -importcert -noprompt \
       -trustcacerts \
       -alias my-ca \
       -file /usr/local/share/ca-certificates/ca-coolcorp.crt \
       -keystore "$JAVA_HOME/lib/security/cacerts" \
       -storepass changeit

USER jenkins

  

À l’heure de la rédaction de l’article, je suis parti sur une base 2.492.2-lts.

Les modifications opérées vont être le rajout du certificat public de ma CA (ca-coolcorp.crt ), à la fois au sous-système Linux du conteneur (Debian), et au keystore de Jenkins.

En effet, Jenkins est développé en Java. Il n’utilise pas forcément la reconnaissance des certificats de confiance de l’OS sous-jacent. Il faut donc ajouter le CA dans un keystore java.

Il ne reste plus qu’à compiler l’image et à l’envoyer vers ma registry (gitlab).

docker build -t registry.gitlab.com/apps.coolcorp.priv/jenkins-custom:2.492.2-lts-v1 -f Dockerfile.server . docker push registry.gitlab.com/apps.coolcorp.priv/jenkins-custom:2.492.2-lts-v1
Création de l'image pour le controleur

Cliquez sur l'image pour l'agrandir.

Envoi de l'image de du controleur dans la registry

Cliquez sur l'image pour l'agrandir.

Image de l'agent

On peut maintenant passer à l’agent, via le docker file Dockerfile.agent:

FROM jenkins/inbound-agent:3301.v4363ddcca_4e7-2

USER root

# Copie du CA
COPY ca-coolcorp.crt /usr/local/share/ca-certificates/ca-coolcorp.crt

# Ajout du CA au système et au keystore Java
RUN apt-get update && apt-get install -y ca-certificates openjdk-17-jre-headless \
    #partie systeme
    && update-ca-certificates \
    #partie keystore
    && keytool -importcert -noprompt \
       -trustcacerts \
       -alias my-ca \
       -file /usr/local/share/ca-certificates/ca-coolcorp.crt \
       -keystore "$JAVA_HOME/lib/security/cacerts" \
       -storepass changeit
#ajout du binaire kubectl
RUN curl -LO "https://dl.k8s.io/release/v1.32.0/bin/linux/amd64/kubectl" && \
    install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && \
    rm kubectl       

USER jenkins

Le principe est le même que pour le serveur, on n’y ajoute une étape supplémentaire via l’ajout du binaire kubectl (ici dans la version 1.32).

C’est effectivement l’agent qui sera chargé de passer des commandes kubectl, il faut donc lui intégrer le binaire. D’autres approches sont possibles. Celle-ci présente l’inconvénient de nécessiter de nouvelles images à chaque nouvelle version de kubectl, et d’avoir une dépendance entre l’agent utilisé et la version du cluster sur lequel il opère, mais elle reste simple et rapide à mettre en œuvre.

On compile l’image et on l’envoie dans la registry:

docker build -t registry.gitlab.com/apps.coolcorp.priv/jenkins-custom:agent-2.492.2-lts-v1 -f Dockerfile.agent . docker push registry.gitlab.com/apps.coolcorp.priv/jenkins-custom:agent-2.492.2-lts-v1
Création de l'image pour l'agent

Cliquez sur l'image pour l'agrandir.

Envoi de l'image de l'agent dans la registry

Cliquez sur l'image pour l'agrandir.

On dispose maintenant de deux images « personnalisées ». Il n’est pas rare que d’autres ajustements soient à réaliser en entreprise. Ce n’est pas forcément bloquant, même pour un déploiement assisté de Jenkins via Helm, comme nous allons le voir par la suite.

Déploiement de jenkins

Afin d’exécuter Jenkins, on va préférer lui dédier un namespace. Celui-ci respecte ma nomenclature, et peut se créer très facilement via la commande:

kubectl create ns prd-jenkins-lan
Création du namespace

Cliquez sur l'image pour l'agrandir.

Comme à mon habitude et pour chaque déploiement d’une application sous Kubernetes, voici un schéma cible qui résume les objets qu’il va falloir manipuler:

Schéma d'architecture

Cliquez sur l'image pour l'agrandir.

Le storage

Avant d’entamer le déploiement de Jenkins, on va gérer le besoin de stockage persistent. Comme j’ai opté pour helm pour installer Jenkins, je pourrais m’appuyer sur ce dernier pour provisionner le stockage dynamiquement. Mais c’est une chose que je préfère gérer en amont, pour une meilleure maitrise.

Jenkins peut se contenter d’un simple partage Network File System (NFS) pour héberger ses données et sa configuration. C’est d’ailleurs pour moi une grande force du produit: simple à sauvegarder, simple à restaurer...Vous représenter le volume NFS et Jenkins retrouve automatiquement ses petits.

Dans mon cas, je vais simplement provisionner un dossier sur mon NAS prévu pour être accessible à mon cluster Kubernetes.

Création du dossier sur NFS

Cliquez sur l'image pour l'agrandir.

On peut ensuite générer un PersistentVolume (pv) basé sur le CSI (Container Storage Interface) NFS. Pour ceux qui ne seraient pas familiers avec le principe de stockage sous k8S, je vous invite à lire cet article issu de mon cookbook K8S ainsi que cet article sur le déploiement du CSI NFS.

Voici le contenu du fichier 01-pv-jenkins-default.yml:

---
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: nfs.csi.k8s.io
  name: pv-jenkins-default
  labels:
      environment: prd
      network: lan
      application: jenkins
      tier: default
spec:
  capacity:
    storage: 30Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: nfs.csi.k8s.io
    volumeHandle: /Volume1/nfsshare/rubikub.coolcorp.priv/namespaces/prd-jenkins-lan/default
    volumeAttributes:
      server: 192.168.10.152
      share: /Volume1/nfsshare/rubikub.coolcorp.priv/namespaces/prd-jenkins-lan/default    
    

Rien de nouveau sous le soleil, je fais pointer le volume directement sur mon export NFS /Volume1/nfsshare/rubikub.coolcorp.priv/namespaces/prd-jenkins-lan/default.

Voici le contenu du PersistentVolumeClaim associé à travers le fichier 02-pvc-jenkins-default.yml:

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-jenkins-default
  namespace: prd-jenkins-lan
  labels:
      environment: prd
      network: lan
      application: jenkins
      tier: default
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 30Gi
  volumeName: pv-jenkins-default
  storageClassName: ""

Le pvc fait référence explicitement au pv sans passer par une StorageClass (sc).

Je place les deux dans un dossier storage et je peux les appliquer directement via la commande:

kubectl apply -f storage/
Application des objets pv et pvc

Cliquez sur l'image pour l'agrandir.

On peut contrôler l’état du pvc:

kubectl get pvc -n prd-jenkins-lan
Controle du pvc

Cliquez sur l'image pour l'agrandir.

Pour l’instant il n’est pas encore monté, puisqu’aucune application ne le sollicite.

Usage de Helm

Je pars du principe que vous avez les bases sur Helm et Kubernetes, si ce n’est pas le cas, n’hésitez pas à parcourir toutes les étapes de mon cookbook sur K8S. On va cependant faire un petit rappel rapide sur Helm

Helm est un gestionnaire de paquets pour Kubernetes simplifiant le déploiement d'applications. Il évite d'avoir à manipuler tous les yamls qui pourraient y être rattachés et préconfigure l'application pour un usage rapide.

Helm se présente sous la forme d'un simple binaire à déployer sur son cluster ou sur son environnement de travail destiné à piloter Kubernetes (où se trouve les élements d'accès (fichier config) au cluster).

Un paquet Helm est référencé sour le terme de Chart. C'est en réalité un dossier qui va contenir tout les manifests nécessaires à l'application ainsi qu'un fichier values.yaml contenant la configuration de l'application et les variables à passer aux différents objets Kubernetes.

Dans un usage par défault, tout cela est "masqué" à l'utilisateur qui peut se contenter de lancer l'installation de l'application souhaité avec une simple commande Helm. Ces charts sont disponibles dans des repos, à l'image de ce que l'on peut trouver sur les distributions Linux..

On démarre donc par l'ajout des ces repos .

helm repo add jenkins https://charts.jenkins.io helm repo update
Ajout du repo helm

Cliquez sur l'image pour l'agrandir.

Si j’apprécie beaucoup Helm, il me frustre également. Initialement, si on se contente de déployer les applications packagées, on ne sait pas comment elles seront configurées ni quels objets seront déclarés et avec quelle configuration.

Il est donc conseillé de toujours jeter un œil sur les « values » par défaut. Elles peuvent être directement accessibles sur le github associé repo du projet ou peuvent être extraites avec la commande helm show values nom_repo

Personnellement j’ai pris l’habitude d’exporter ces valeurs dans un fichier default-value.yml au sein d’un sous-dossier helm:

helm show values Jenkins/jenkins > helm/default-value.yml
Extraction des valeurs par défault

Cliquez sur l'image pour l'agrandir.

Valeurs par défault du chart

Cliquez sur l'image pour l'agrandir.

Je peux ainsi m’inspirer de la configuration proposée pour construire mon propre fichier custom-values.yml.

Voici d’ailleurs son contenu:

controller:
  namespaceOverride: prd-jenkins-lan
#Utilisation de l'image custom
  imagePullSecretName: sec-jenkins-registry
  image:
    registry: registry.gitlab.com
    repository: apps.coolcorp.priv/jenkins-custom
    tag: 2.492.2-lts-v1
    pullPolicy: Always
#Précision sur l'URL de jenkins 
  jenkinsUrlProtocol: https
  jenkinsUrl: https://jenkins.coolcorp.priv
#On ne créé par un ingress automatiquement
  ingress:
    enabled: false

  service:
    type: ClusterIP
    port: 8080

  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2000m"
      memory: "4Gi"
 
#Récupération du pvc existent
persistence:
  enabled: true
  existingClaim: pvc-jenkins-default

#Configuration de l'agent
agent:
  enabled: true
  #Usage d'un compte de service dédié
  useDefaultServiceAccount: false
  serviceAccount: sa-jenkins-agent
  #url du controleur
  jenkinsUrl: https://jenkins.coolcorp.priv
  #image de l'agent à utiliser 
  imagePullSecretName: sec-jenkins-registry
  image:
    repository: registry.gitlab.com/apps.coolcorp.priv/jenkins-custom
    tag: agent-2.492.2-lts-v2
  

Comme vous pouvez le constater, il est nettement plus petit, car je n’y fais figurer que les options que je souhaite modifier.

Globalement, je vais préciser les images à utiliser. J’indique celles que j’ai « customisé » en début d’article.

Je viens ajouter également la référence à l’url ou sera accessible le contrôleur (jenkins.coolcorp.priv).

Bien sûr, je déclare le pvc récemment créé,pvc-jenkins-default afin qu’on l’utilise sans que Helm n'ait a créer d'autres objets de stockage.

Point important également, au niveau de la section agent, je précise un compte de service à utiliser: sa-jenkins-agent. Ce passage sera détaillé dans une partie dédiée, mais il faut bien comprendre que l’agent va devoir opérer des actions sur le cluster. Il faut donc qu’il le fasse sous l’identité d’un compte autorisé à manipuler les objets sur le cluster.

Enfin, comme on le verra ensuite, je ne souhaite pas que soit créer automatiquement un objet de type ingress. On s’en chargera nous-mêmes.

Avant de lancer l’installation, il y a un prérequis. Étant donné que j’utilise des images personnalisées issues d’une registry privée, il me faut créer le secret contenant le token d’accès à cette registry.

Ce secret, que je vais nommer sec-jenkins-registry, est d’ailleurs renseigné dans le fichier custom-values.yml dans le champs imagePullSecretName.

Voici le contenu du fichier 01-sec-jenkins-registry.yml (avec de fausse valeur bien sûr… 😊):

apiVersion: v1
data:
  .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5naXRsYWIuY29tIjp7
kind: Secret
metadata:
  name: sec-jenkins-registry
  namespace: prd-jenkins-lan
type: kubernetes.io/dockerconfigjson

On l’applique dans la foulée:

Kubectl apply -f 01-sec-jenkins-registry.yml
Création du secret pour la registry

Cliquez sur l'image pour l'agrandir.

Le déploiement de Jenkins peut enfin se faire en précisant mon fichier de valeurs personnalisées:

helm install Jenkins Jenkins/jenkins -n prd-jenkins-lan -f helm/custom-values.yml
Lancement de l'installation avec helm

Cliquez sur l'image pour l'agrandir.

Notez bien la sortie de helm, qui précise d’utiliser cette instruction: kubectl exec -namespace prd-jenkins-lan—it svc/Jenkins -c Jenkins -- /bin/cat /run/secrets/additional/chart-admin-password && echo pour obtenir le mot de passe du compte admin par défaut.

Récupérez-le et stockez-le en lieu sûr.

Récupération du mot de passe admin

Cliquez sur l'image pour l'agrandir.

La finalisation de l’installation prend quelques minutes, on peut vérifier le succès de l’opération avec la commande:

kubectl get pod -n prd-jenkins-lan
Controle de l'installation

Cliquez sur l'image pour l'agrandir.

(Pour ceux qui ne l'auraient pas remarqué, au vue du nom du pod, on se rend compte que Helm a déployé Jenkins sous forme de StatefulSets)

Notre pod Jenkins est bien là… Encore faut-il le rendre accessible.

Exposition via Ingress

C’est le moment de parler ingress. Pour rappel cet objet permet d’exposer son application à l’extérieur du cluster K8S. Si vous êtes un peu perdu, n’hésitez pas à parcourir cet article.

Mon instance de Jenkins sera publiée sur l’URL https://jenkins.coolcorp.priv. Soit un site HTTPS avec un certificat.

Il me faut donc créer ce certificat en amont et l’enregistrer dans un secret pour qu’il puisse être utilisé par l’ingress.

Il est possible d’automatiser totalement la création et le mappage du certificat, grâce à l’usage de Cert Manager. J’ai d’ailleurs un article dédié si le sujet vous intéresse.

Ici, je vais rester sur une méthode à l’ancienne. Je vais d’abord générer ma clef et ma demande de certificat associée (fichier csr).

openssl req -new -nodes -sha256 -keyout jenkins.coolcorp.priv.key -out jenkins.coolcorp.priv.csr -newkey rsa:4096 -subj "/C=FR/ST=Ile-de-France/L=Paris/O=COOLCORP/OU=Infrastructure/CN=jenkins.coolcorp.priv" -reqexts SAN -config <(printf "[req]\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\n[SAN]\nsubjectAltName=DNS:jenkins.coolcorp.priv")>
Création du csr et de la clef privée

Cliquez sur l'image pour l'agrandir.

Ensuite, je soumets mon CSR à mon autorité de certification (ici sous Windows): certreq -attrib "CertificateTemplate:TPL-SRV-WEB-DEFAULT" -submit .\jenkins.coolcorp.priv.csr

Récupération du certificat

Cliquez sur l'image pour l'agrandir.

Pour enfin récupérer mon certificat.

J’ai donc, en bout de course, un certificat et ma clef. Les deux vont me permettre de créer le secret sec-jenkins-cert.

kubectl create secret tls sec-jenkins-cert --cert=jenkins.coolcorp.priv.cer --key=jenkins.coolcorp.priv.key -n prd-jenkins-lan
Création du secret pour héberger le certificat

Cliquez sur l'image pour l'agrandir.

Je ne rentre pas dans le détail, chacun a souvent sa propre mécanique de gestion des certificats en interne. Sachez que vous pouvez soit générer le secret en utilisant la clé et le certificat, soit déléguer cette tâche à l’outil Cert-Manager. Pour ceux qui comme moi ont une CA Windows, vous pouvez meme suivre ce tutoriel pour tirer partie de l'automatisation via le protocole ACME

On peut passer à l’ingress lui-même, détaillé dans ce fichier 02-ing-jenkins-default.yml:

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ing-jenkins-default
  namespace: prd-jenkins-lan
  labels:
    environment: prd
    network: lan
    application: jenkins
    tier: default
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
    ingressClassName: traefik-lan
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  ingressClassName: traefik-lan
  tls:
  - hosts:
    - jenkins.coolcorp.priv
    secretName: sec-jenkins-cert
  rules:
  - host: jenkins.coolcorp.priv
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jenkins
            port:
              number: 8080

On y retrouve le secret contenant le certificat et la référence à l’ingress class « traefik-lan » a utiliser.

Là aussi, je ne donne pas davantage d’explication. L’usage de traefik vous est décrit ici.

Concernant le nom du service jenkins et le port 8080 vers lesquels rediriger la requête, il s'agit du service créer par helm directement. On peut le retrouver via la commande:

kubectl get service -n prd-jenkins-lan

On applique l’ingress:

kubectl apply -f 02-ing-jenkins-default.yml
Création de l'ingress

Cliquez sur l'image pour l'agrandir.

On peut vérifier l’accès à l’interface de Jenkins via l’URL souhaitée (attention à bien avoir pris soin de faire son enregistrement DNS) et en utilisant le mot de passe du compte admin récupéré précédemment.

Accès à l'interface

Cliquez sur l'image pour l'agrandir.

On dispose maintenant d’une instance Jenkins déployée sur le cluster Kubernetes.

Droits du compte de service

Avant d’aller plus loin, on va revenir sur le compte de service sa-jenkins-agent utilisé par l’agent.

Celui-ci n’existe pour l’instant pas dans le cluster (on a spécifié dans le fichier value de helm qu’on ne souhaitait pas utiliser le compte de service par défaut.).

Il va falloir le créer via le fichier 01-sa-jenkins-agent.yml:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sa-jenkins-agent
  namespace: prd-jenkins-lan

On va dédier un rôle à ce compte. Cela va permettre de définir précisément ce que va pouvoir faire Jenkins sur ce cluster.

Ce rôle est défini dans 02-clusterrole-jenkins-agent.yml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: clusterrole-jenkins-agent
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

Pour le besoin de ce tutoriel, on est sur une base simple. On n’autorise que la manipulation des pods.

Il reste à faire l’association de ce rôle au compte via l’objet clusterrolebinding décrite dans le yaml 03-clusterrolebinding-jenkins-agent.yaml:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: clusterrolebinding-jenkins-agent
subjects:
- kind: ServiceAccount
  name: sa-jenkins-agent
  namespace: prd-jenkins-lan
roleRef:
  kind: ClusterRole
  name: clusterrole-jenkins-agent
  apiGroup: rbac.authorization.k8s.io
  

Je place ces trois fichiers dans un dossier right, et je peux les appliquer en une opération:

kubectl apply -f right/
Application des droits

Cliquez sur l'image pour l'agrandir.

Si vous le souhaitez, vous pouvez parcourir cet article qui parle de la gestion des droits sous K8S et de la mécanique RBAC qui s’y rattache. Vous comprendrez ainsi mieux la logique de ces trois objets.

Validation

Tout étant maintenant prêt, il ne reste plus qu’à valider tout ça.

Pour cela on va utiliser une pipeline DEMO, très basique, ayant uniquement vocation à lister les pods du cluster.

Voici le contenu de la pipeline:

pipeline {
  agent any

  stages {
      stage('Test Kubernetes Access') {
          steps {
              sh 'kubectl get pods -A'
          }
      }
  }
}
Création de la pipeline

Cliquez sur l'image pour l'agrandir.

Comme mentionné au début de l’article, le but de ce tutoriel n’est pas d’expliquer le fonctionnement de Jenkins, mais de le rendre apte à s’exécuter dans un cluster Kubernetes et à y orchestrer des tâches.

On peut lancer la pipeline.

On observe alors qu’un nouveau pod va être provisionné dans le namespace prd-jenkins-lan.

Création du pod de l'agent

Cliquez sur l'image pour l'agrandir.

Ce pod représente l’agent piloté par le contrôleur. Il va charger l’image que nous avons détaillée dans le fichier value helm et construite au début de l’article.

Cette image contenant kubectl, l’instruction get pod déclarée dans la pipeline va pouvoir être jouée.

L’agent va exécuter la commande sous l’identité du compte de service sa-jenkins-agent qui dispose du rôle clusterrole-jenkins-agent l’autorisant à manipuler les pods.

On obtient donc bien en sortie la liste des pods du cluster.

Sortie de la pipeline

Cliquez sur l'image pour l'agrandir.

Sortie de la pipeline

Cliquez sur l'image pour l'agrandir.

A noter que le pod de l'agent est supprimé une fois la pipeline terminée

Veuillez noter qu’on n’a pas eu à indiquer sur quel cluster agir, Jenkins s’exécutant sur le cluster lui-même, il va utiliser ce dernier par défaut. Cette intégration est possible grâce à l’usage du plugin Kubernetes inclus et configuré de base lors de l’usage de helm.

Conclusion

Jenkins est désormais déployé sur le cluster et en mesure d’y réaliser des actions.

La pipeline DEMO n’a pas grand intérêt, mais elle démontre la faisabilité de la manipulation des objets du cluster par Jenkins, ceci de manière totalement interne.

Il devient donc possible de s’appuyer sur la modularité de Jenkins et de la puissance de ses pipelines pour imaginer des enchainements de taches mêlant à la fois des composants externes au cluster et le cluster lui-même. Par exemple, récupérer les sources sur un repo git et les compiler dans un pod dédié, exécuter des actions au sein des pods… A chacun de trouver ses uses cases ! Il ne reste plus qu’à faire jouer son imagination !