Pipeline multibranche Jenkins sur GKE

1. Présentation

Jenkins est l'une des solutions d'intégration continue les plus populaires du marché. Elle permet d'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 faire évoluer rapidement et automatiquement les exécuteurs de compilation en fonction des besoins. Combiné à Cloud Storage, nous pouvons créer et tester une application avec un minimum d'efforts.

Objectifs de l'atelier

  • Déployer Jenkins dans un cluster Kubernetes
  • Déployer et configurer le plug-in GKE Jenkins 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 SpringBoot
  • Créer et publier un conteneur dans Google Container Registry
  • Déployer l'application exemple 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 devez en créer un.

2. Configuration

Cet atelier de programmation peut s'exécuter entièrement sur Google Cloud Platform sans aucune installation ni configuration locale.

Cloud Shell

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

Activer les API

Voici les API que nous devrons activer pour notre projet:

  • API Compute Engine : crée et exécute des machines virtuelles
  • API Kubernetes Engine : crée et gère des applications basées sur des conteneurs
  • API Cloud Build : plate-forme d'intégration et de livraison continue de Google Cloud
  • API Service Management : permet aux producteurs de services de publier des services sur Google Cloud Platform.
  • API Cloud Resource Manager : crée, lit et met à 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 aurons besoin d'un bucket GCS pour importer notre travail de test. Créons un bucket en utilisant notre ID de projet comme nom pour garantir son caractère unique:

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 travail. Le champ d'application supplémentaire indiqué par l'indicateur --scopes permet à 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 les builds de préproduction et de production de notre application exemple:

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 s'exécutent avec gcloud container clusters list.

La colonne STATUS doit contenir RUNNING:

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 paquets d'applications pour Kubernetes, pour 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

Définissez votre répertoire de travail actuel sur le 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 la 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

et l'associer 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 devraient 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 devrait afficher notre pod Jenkins avec l'état RUNNING:

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 si nécessaire. 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'UI, nous allons configurer 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-la:

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 sur le Web 7ddf5a65fd556dd6.png, puis sélectionnez "Preview on port 8080" (Prévisualiser sur le port 8080).

1d614c831a621cff.png

Un écran de connexion à Jenkins devrait s'afficher, dans lequel vous pouvez saisir le admin pour le nom d'utilisateur et le mot de passe renvoyés à l'étape précédente:

9cba23e856cbc84f.png

Lorsque nous cliquons sur Connexion, 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 du plug-in GKE:

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

Configuration automatique des autorisations IAM

Définissez votre répertoire de travail actuel sur 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 de nom du projet, de région et de 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 l'appliquez:

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

Le compte de service aura besoin d'autorisations d'administrateur de stockage 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éployeur 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 un 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".

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 System (Système), cliquez sur "Global credentials" (Identifiants globaux), puis sur "Add credentials" (Ajouter des identifiants) à gauche:

4350c0e68561119b.png

3d3526551cdae8b.png

Dans le menu déroulant "Kind" (Type), sélectionnez Google Service Account from private key (Compte de service Google à partir d'une clé privée). Saisissez "kaniko-role" comme nom, puis importez la clé JSON créée aux étapes précédentes et cliquez sur OK.

b0502213408e730e.png

Variables d'environnement

Nous devons définir certaines variables d'environnement pour Jenkins avant de créer le pipeline à plusieurs branches. Les voici :

  • JENK_INT_IT_ZONE : zone du cluster Kubernetes. Dans notre cas, us-east1-d
  • JENK_INT_IT_PROJECT_ID : fait référence à l'ID de projet GCP hébergeant cette instance de Jenkins
  • JENK_INT_IT_STAGING : nom de notre cluster de préproduction. Pour des raisons 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 à l'étape précédente. La valeur doit correspondre au nom que nous lui avons attribué, kaniko-role.

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

d54f279190a07878.png

Ensuite, accédez à Configure System (Configurer le système) :

ce79d218b2799640.png

Une section intitulée Propriétés globales s'affiche. Lorsque nous cochons la case Variables d'environnement, un bouton Ajouter s'affiche. Nous pouvons cliquer dessus pour ajouter les variables ci-dessus en tant que 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 êtes 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 Build Configuration (Configuration de compilation), saisissez "gke/Jenkinsfile" comme Script Path (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 ultérieure pour chaque branche. Au fur et à mesure de la compilation, des pods sont créés, exécutés et détruits sur la page "Charges de travail Kubernetes".

Une fois les builds terminés, deux éléments nommés jenkins-integration-samples-gke s'affichent sur la page "Charges de travail Kubernetes", chacun correspondant au cluster de production ou de test. L'état indique "OK" :

bdec6b1753d1ba07.png

À l'aide de la commande gcloud suivante, nous verrons 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, récupérez les identifiants du cluster de production:

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

Exécutez 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 sur le Web", puis sélectionnez "Preview on port 8081" (Prévisualiser sur le port 8081).

1b19b5b56f1bae7.png

e80e995e71763bb2.png

8. Nettoyage

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

Supprimer le projet

Si vous préférez, 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 supprimerons les images Google Container Registry à l'aide des récapitulatifs d'images. Commencez par récupérer les récapitulatifs à l'aide de 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 tâches à des 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 dans des clusters Kubernetes.
  • Nous avons configuré Jenkins pour configurer un pipeline multibranche qui distribue le travail aux clusters GKE.