Pipeline multibranche Jenkins sur GKE

1. Présentation

Jenkins est l'une des solutions d'intégration continue les plus populaires. Il est utilisé pour automatiser les parties non humaines essentielles du processus de développement logiciel. En déployant Jenkins sur Kubernetes sur Google Cloud et en utilisant le plug-in GKE, nous pouvons mettre à l'échelle rapidement et automatiquement les exécuteurs de compilation selon les besoins. Combiné à Cloud Storage, il nous permet de créer et de tester une application avec un minimum d'efforts.

Objectifs de l'atelier

  • Déployer Jenkins sur un cluster Kubernetes
  • Déployez et configurez le plug-in Jenkins GKE pour permettre à Jenkins de créer et de détruire des pods en tant que nœuds d'exécution.
  • Créer et tester un exemple d'application Spring Boot
  • Créer et publier un conteneur dans Google Container Registry
  • Déployer l'exemple d'application dans un environnement GKE de préproduction et de production

Prérequis

  • Un projet Google Cloud avec la facturation configurée. Si vous n'en avez pas, vous devrez en créer un.

2. Configuration

Cet atelier de programmation peut être exécuté entièrement sur Google Cloud Platform, sans installation ni configuration locales.

Cloud Shell

Tout au long de cet atelier de programmation, nous allons provisionner et gérer différentes ressources et différents services cloud à l'aide de la ligne de commande via Cloud Shell.

Activer les API

Voici les API que nous devrons activer dans notre projet :

  • API Compute Engine : crée et exécute des machines virtuelles
  • API Kubernetes Engine : créez et gérez des applications basées sur des conteneurs.
  • API Cloud Build : plate-forme d'intégration et de déploiement continus de Google Cloud
  • API Service Management : permet aux producteurs de services de publier des services sur Google Cloud Platform.
  • API Cloud Resource Manager : permet de créer, lire et mettre à jour les métadonnées des conteneurs de ressources Google Cloud.

Activez les API requises à l'aide de la commande gcloud suivante :

gcloud services enable compute.googleapis.com \
container.googleapis.com \
cloudbuild.googleapis.com \
servicemanagement.googleapis.com \
cloudresourcemanager.googleapis.com \
--project ${GOOGLE_CLOUD_PROJECT}

Créer un bucket GCS

Nous allons avoir besoin d'un bucket GCS pour importer nos tests. Créons un bucket en utilisant l'ID de notre projet dans le nom pour garantir son unicité :

gsutil mb gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket/ 

3. Créer des clusters Kubernetes

Créer le cluster

Nous allons ensuite créer un cluster GKE qui hébergera notre système Jenkins, y compris les pods qui seront distribués en tant que nœuds de calcul. Le champ d'application supplémentaire indiqué par l'indicateur --scopes permettra à Jenkins d'accéder à Cloud Source Repositories et à Container Registry. Dans la console Cloud, exécutez la commande suivante :

gcloud container clusters create jenkins-cd \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--scopes "https://www.googleapis.com/auth/source.read_write,cloud-platform" \
--cluster-version latest

Déployons également deux clusters pour héberger nos versions de préproduction et de production de notre exemple d'application :

gcloud container clusters create staging \
--machine-type n1-standard-2 --num-nodes 1 \
--zone us-east1-d \
--cluster-version latest
gcloud container clusters create prod \
--machine-type n1-standard-2 --num-nodes 2 \
--zone us-east1-d \
--cluster-version latest

28b45298e1e82748.png Valider

Une fois les clusters créés, nous pouvons vérifier qu'ils sont en cours d'exécution avec gcloud container clusters list.

Le résultat doit contenir RUNNING dans la colonne STATUS :

NAME        LOCATION    MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
jenkins-cd  us-east1-d  1.15.9-gke.9    34.74.77.124  n1-standard-2  1.15.9-gke.9  2          RUNNING
prod        us-east1-d  1.15.9-gke.9    35.229.98.12  n1-standard-2  1.15.9-gke.9  2          RUNNING
staging     us-east1-d  1.15.9-gke.9    34.73.92.228  n1-standard-2  1.15.9-gke.9  2          RUNNING

4. Déployer Jenkins avec Helm

Installer Helm

Nous allons utiliser Helm, un gestionnaire de packages d'application pour Kubernetes, afin d'installer Jenkins sur notre cluster. Pour commencer, téléchargez le projet qui inclut les fichiers manifestes Kubernetes que nous utiliserons pour déployer Jenkins :

git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes.git ~/continuous-deployment-on-kubernetes

Passez de votre répertoire de travail actuel au répertoire du projet :

cd ~/continuous-deployment-on-kubernetes/

Créez une liaison de rôle de cluster pour vous attribuer les autorisations du rôle cluster-admin :

kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)

Connectez-vous à votre cluster Jenkins en obtenant ses identifiants :

gcloud container clusters get-credentials jenkins-cd --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Téléchargez le binaire Helm dans votre console Cloud :

wget https://storage.googleapis.com/kubernetes-helm/helm-v2.14.1-linux-amd64.tar.gz

Décompressez le fichier et copiez le fichier Helm inclus dans votre répertoire de travail actuel :

tar zxfv helm-v2.14.1-linux-amd64.tar.gz && \
cp linux-amd64/helm .

Tiller est le côté serveur de Helm qui s'exécute sur le cluster Kubernetes. Créons un compte de service nommé tiller :

kubectl create serviceaccount tiller \
--namespace kube-system

Associez-le au rôle de cluster cluster-admin pour qu'il puisse apporter des modifications :

kubectl create clusterrolebinding tiller-admin-binding \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:tiller

Nous pouvons maintenant initialiser Helm et mettre à jour le dépôt :

./helm init --service-account=tiller && \
./helm repo update

28b45298e1e82748.png Valider

Vérifiez que Helm est prêt à l'emploi avec ./helm version. Les numéros de version du client et du serveur doivent s'afficher :

Client: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.1", GitCommit:"5270352a09c7e8b6e8c9593002a73535276507c0", GitTreeState:"clean"}

Installer Jenkins

Maintenant que Helm est installé sur notre cluster, nous pouvons installer Jenkins :

./helm install stable/jenkins -n cd \
-f jenkins/values.yaml \
--version 1.2.2 --wait

28b45298e1e82748.png Valider

Vérifions les pods :

kubectl get pods

Le résultat doit afficher notre pod Jenkins avec l'état "RUNNING" (EN COURS D'EXÉCUTION) :

NAME                          READY     STATUS    RESTARTS   AGE
cd-jenkins-7c786475dd-vbhg4   1/1       Running   0          1m

Vérifiez que le service Jenkins a bien été créé :

kubectl get svc

Le résultat devrait ressembler à ceci :

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
cd-jenkins         ClusterIP   10.35.241.170   <none>        8080/TCP    2m27s
cd-jenkins-agent   ClusterIP   10.35.250.57    <none>        50000/TCP   2m27s
kubernetes         ClusterIP   10.35.240.1     <none>        443/TCP     75m

L'installation de Jenkins utilisera le plug-in Kubernetes pour créer des agents de compilation. Ils seront automatiquement lancés par le maître Jenkins en cas de besoin. Une fois leur travail terminé, ils sont automatiquement arrêtés et leurs ressources sont ajoutées au pool de ressources du cluster.

Se connecter à Jenkins

Jenkins s'exécute sur notre cluster, mais pour accéder à l'interface utilisateur, configurons le transfert de port à partir de Cloud Shell :

export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=cd" -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8080:8080 >> /dev/null &

Un mot de passe administrateur a été généré lors de l'installation. Récupérons-le :

printf $(kubectl get secret cd-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo

En haut de Cloud Shell, cliquez sur l'icône Aperçu Web 7ddf5a65fd556dd6.png, puis sélectionnez "Prévisualiser sur le port 8080".

1d614c831a621cff.png

Un écran de connexion Jenkins devrait s'afficher. Vous pouvez y saisir admin comme nom d'utilisateur et le mot de passe renvoyé à l'étape précédente :

9cba23e856cbc84f.png

Lorsque nous cliquons sur Sign in (Se connecter), nous devrions être redirigés vers la page principale de Jenkins.

9261f3e914829137.png

5. Installer et configurer le plug-in GKE

Le plug-in Google Kubernetes Engine nous permet de publier les déploiements créés dans Jenkins sur nos clusters Kubernetes exécutés dans GKE. Vous devez effectuer une configuration avec les autorisations IAM sur votre projet. Nous allons déployer cette configuration à l'aide de Terraform.

Commencez par télécharger le projet de plug-in GKE :

git clone https://github.com/jenkinsci/google-kubernetes-engine-plugin.git ~/google-kubernetes-engine-plugin

Configuration automatisée des autorisations IAM

Remplacez votre répertoire de travail actuel par le répertoire "rbac" du projet GKE que nous avons cloné précédemment :

cd ~/google-kubernetes-engine-plugin/docs/rbac/

gcp-sa-setup.tf est un fichier de configuration Terraform qui crée un rôle IAM GCP personnalisé avec des autorisations limitées, ainsi qu'un compte de service GCP auquel attribuer ce rôle. Le fichier nécessite des valeurs pour les variables du projet, de la région et du nom du compte de service. Nous fournissons ces valeurs en déclarant d'abord les variables d'environnement suivantes :

export TF_VAR_project=${GOOGLE_CLOUD_PROJECT}
export TF_VAR_region=us-east1-d
export TF_VAR_sa_name=kaniko-role

Initialisez Terraform, générez un plan et appliquez-le :

terraform init
terraform plan -out /tmp/tf.plan
terraform apply /tmp/tf.plan && rm /tmp/tf.plan

Le compte de service aura besoin des autorisations d'administrateur Storage pour enregistrer des données dans notre bucket Cloud Storage :

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
--member serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com \
--role 'roles/storage.admin'

Il aura également besoin d'autorisations de conteneur pour les étapes de déploiement de notre pipeline :

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} --member \
serviceAccount:kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com --role 'roles/container.developer'

Nous pouvons maintenant utiliser Helm pour configurer les autorisations de cluster pour le plug-in GKE à l'aide du déploiement de robot GKE. Remplacez votre répertoire de travail par le répertoire Helm du projet GKE :

cd ~/google-kubernetes-engine-plugin/docs/helm/

Installez-le à l'aide du chart Helm fourni :

export TARGET_NAMESPACE=kube-system && \
envsubst < gke-robot-deployer/values.yaml | helm install ./gke-robot-deployer --name gke-robot-deployer -f -

6. Configurer Jenkins

Clés de compte de service

Pour que le compte de service fonctionne correctement, nous devons générer un fichier de clé privée et l'ajouter en tant que secret Kubernetes. Commencez par générer le fichier avec la commande gcloud suivante :

gcloud iam service-accounts keys create /tmp/kaniko-secret.json --iam-account kaniko-role@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Nous allons créer une clé secrète dans le secret store Kubernetes avec ce fichier :

kubectl create secret generic jenkins-int-samples-kaniko-secret --from-file=/tmp/kaniko-secret.json 

Téléchargez le fichier JSON sur votre disque local en accédant à l'élément "Télécharger le fichier" dans le menu à trois points de Cloud Shell :

c40378e72013b843.png

Saisissez le chemin d'accès au fichier /tmp/kaniko-secret.json, puis cliquez sur "Télécharger".

De retour sur la page Jenkins, dans le volet de gauche, cliquez sur Credentials (Identifiants), puis sur System (Système).

6c140f7e6bb82f8.png

3b874912cdc8019b.png

Dans la section Système de la page, cliquez sur "Identifiants globaux", puis sur "Ajouter des identifiants" à gauche :

4350c0e68561119b.png

3d3526551cdae8b.png

Dans le menu déroulant "Type", sélectionnez Compte de service Google à partir d'une clé privée. Saisissez "kaniko-role" comme nom, puis importez la clé JSON que vous avez créée lors des étapes précédentes et cliquez sur OK.

b0502213408e730e.png

Variables d'environnement

Nous devrons définir certaines variables d'environnement Jenkins avant de créer le pipeline multibranches. Les voici :

  • JENK_INT_IT_ZONE : zone du cluster Kubernetes. Dans notre cas, us-east1-d
  • JENK_INT_IT_PROJECT_ID : ID du projet GCP hébergeant cette instance Jenkins
  • JENK_INT_IT_STAGING : nom de notre cluster de préproduction. À des fins de démonstration, il s'agit de staging.
  • JENK_INT_IT_PROD : nom de notre cluster de production. À des fins de démonstration, il s'agit de prod.
  • JENK_INT_IT_BUCKET : bucket Google Cloud Storage créé à l'étape précédente
  • JENK_INT_IT_CRED_ID : fait référence aux identifiants créés à l'aide du fichier JSON de l'étape précédente. La valeur doit correspondre au nom que nous lui avons attribué, kaniko-role.

Pour les ajouter, accédez à Gérer Jenkins :

d54f279190a07878.png

Ensuite, Configure System (Configurer le système) :

ce79d218b2799640.png

Une section nommée Propriétés globales s'affiche. Lorsque nous cochons la case Variables d'environnement, un bouton Ajouter s'affiche. Nous cliquons dessus pour ajouter les variables ci-dessus sous forme de paires clé/valeur :

81aa222a2b17b2cc.png

Cliquez sur le bouton Enregistrer en bas de la page pour appliquer les modifications.

7. Configurer un pipeline

Dans Jenkins, cliquez sur "New Item" (Nouvel élément) :

8d1270ce4d7b6a8a.png

Saisissez "jenkins-integration-sample" comme nom, sélectionnez "Multibranch Pipeline" (Pipeline multibranche) comme type de projet, puis cliquez sur OK :

eb071ecfbb4d775b.png

Vous serez redirigé vers la page de configuration du pipeline. Sous Sources de branche, saisissez https://github.com/GoogleCloudPlatform/jenkins-integration-samples.git comme dépôt du projet. Sous Configuration de compilation, saisissez "gke/Jenkinsfile" comme chemin d'accès au script.

5135bd6b0374508c.png

Cliquez sur Enregistrer pour appliquer ces paramètres. Une fois l'enregistrement effectué, Jenkins lance une analyse du dépôt et une compilation pour chaque branche. Au fur et à mesure de la progression, vous verrez des pods être créés, exécutés et détruits sur la page "Charges de travail Kubernetes".

Une fois les compilations terminées, vous trouverez deux éléments sur la page "Charges de travail Kubernetes" nommés "jenkins-integration-samples-gke", chacun correspondant au cluster de production ou de test. L'état est défini sur "OK" :

bdec6b1753d1ba07.png

La commande gcloud suivante nous montre que nous avons importé une image de conteneur dans Google Container Registry correspondant à notre pipeline :

gcloud container images list

Pour afficher la charge de travail dans votre navigateur, obtenez les identifiants du cluster de production :

gcloud container clusters get-credentials prod --zone us-east1-d --project ${GOOGLE_CLOUD_PROJECT}

Exécutez ensuite la commande suivante pour configurer un transfert de port du port 8081 de votre shell vers le port 8080 de votre charge de travail :

export POD_NAME=$(kubectl get pods -o jsonpath="{.items[0].metadata.name}") &&
kubectl port-forward $POD_NAME 8081:8080 >> /dev/null &

En haut de Cloud Shell, cliquez sur l'icône Aperçu Web, puis sélectionnez "Prévisualiser sur le port 8081".

1b19b5b56f1bae7.png

e80e995e71763bb2.png

8. Nettoyage

Nous avons vu comment déployer un pipeline Jenkins et un exemple de pipeline multibranches sur Kubernetes. Il est maintenant temps de nettoyer notre projet de toutes les ressources que nous avons créées.

Supprimer le projet

Si vous le souhaitez, vous pouvez supprimer l'intégralité du projet. Dans la console GCP, accédez à la page Cloud Resource Manager :

Dans la liste des projets, sélectionnez celui dans lequel vous avez travaillé, puis cliquez sur Supprimer. Vous serez alors invité à saisir l'ID du projet. Saisissez-le, puis cliquez sur Arrêter.

Vous pouvez également supprimer le projet dans son intégralité directement dans Cloud Shell avec gcloud :

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Si vous préférez supprimer les différents composants facturables un par un, passez à la section suivante.

Cluster Kubernetes

Supprimez l'intégralité du cluster Kubernetes avec gcloud :

gcloud container clusters delete jenkins-cd --zone=us-east1-d

Buckets de stockage

Supprimez tous les fichiers importés et notre bucket avec gsutil :

gsutil rm -r gs://${GOOGLE_CLOUD_PROJECT}-jenkins-test-bucket

Images Google Container Registry

Nous allons supprimer les images Google Container Registry à l'aide des résumés d'image. Tout d'abord, récupérez les résumés avec la commande suivante :

gcloud container images list-tags gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke --format="value(digest)"

Ensuite, pour chaque récapitulatif renvoyé :

gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/jenkins-integration-samples-gke@sha256:<DIGEST>

9. Félicitations !

Bravo ! Bravo ! Vous avez appris à déployer Jenkins sur GKE et à distribuer des jobs aux clusters Kubernetes.

Points abordés

  • Nous avons déployé un cluster Kubernetes et utilisé Helm pour installer Jenkins.
  • Nous avons installé et configuré le plug-in GKE pour permettre à Jenkins de déployer des artefacts de compilation sur des clusters Kubernetes.
  • Nous avons configuré Jenkins pour configurer un pipeline multibranche qui distribue le travail aux clusters GKE.