Kubernetes - Outils de développement - SonarQube (RaspberryPI4/arm64)

Sonarqube c’est quoi ?

Source : Wikipedia

SonarQube (précédemment Sonar2) est un logiciel libre de qualimétrie en continu de code. Il aide à la détection, la classification et la résolution de défaut dans le code source, permet d’identifier les duplications de code, de mesurer le niveau de documentation et connaître la couverture de test déployée.

SonarQube permet une surveillance continue de la qualité du code grâce à son interface web permettant de voir les défauts de l’ensemble du code et ceux ajoutés par la nouvelle version….

Installer “Sonarqube” dans le cluster Kubernetes/Mytinydc

Pré-requis

  • Cluster Kubernetes opérationnel - Rasbpberry PI4/arm64/64 bits/Ram 8Go
  • Dépôt Gitea (repo git)
  • Chaîne CI/CD Drone
  • Service Postgresql (persistence des données)
  • Vous maîtrisez les notions de Manifest, secrets, volumes Kubernetes
  • Vous savez exposer un service réseau Kubernetes (nodePort, haproxy, traefic,…)

Besoins applicatifs

Cette phase de préparation permet de découvrir les informations nécessaires pour la construction de l’image Docker et du “Manifest” Kubernetes.

  • image docker Arm64 dans la registry du Datacenter

  • le container s’exécute avec l’utilisateur sq/sq (uid 2000/gid 2000 - choix arbitraire) - Elasticsearch, intégré à l’application Sonarqube ne peut être exécuté par “root”, et de toutes manières, pour des raisons de sécurité, il est déconseillé d’exécuter des containers avec l’utilisateur “root”

  • volumes :

    • data
    • conf
    • logs
    • temp
    • extensions

    Ces volumes devront être la propriété de l’utilisateur “sq” (dans mon cas uid/gid : 2000:2000)

  • Un accès à la base de données postgresql (server postgresql du Datacenter)

  • secrets d’accès à la base postgresql

Base de données postgresql

Commencer par créer une base de données pour l’applicatif “Sonarqube”. Cette base de donnée devra être accessible de tous les “Workers” du cluster kubernetes (paramétrage pg_hba.conf nécessaire).

Exemple :

  • Nom de la base de données : sonarqube
  • Utilisateur postgresql : sonarqube
  • Mot de passe de l’utilisateur postgresql : mypassword

Connecté au serveur postgresql :

su - postgres
psql
CREATE USER sonarqube with password 'mypassword';
CREATE DATABASE sonarqube WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8';
GRANT ALL PRIVILEGES ON DATABASE sonarqube to sonarqube;
\q
exit

Modifiez le fichier pg_hba.conf pour que la nouvelle base de données “drone” soit accessible de tous les workers du cluster Kubernetes.

Exemple, mon cluster kubernetes comporte deux “Workers”, dont les adresses IP respectives sont :

  • 192.168.1.20
  • 192.168.1.21

Ajoutez au fichier pg_hba.conf :

host sonarqube    sonarqube   192.168.1.20/32  md5
host sonarqube    sonarqube   192.168.1.21/32  md5

puis exécuter : systemctl reload postgresql

Les informations de comptes (utilisateur/mot de passe, addresseIP/port du serveur postgresql) seront nécessaires pour effectuer le paramétrage de “drone”.

Création de l’image Docker

Sonarqube ne dispose pas d’image pour les plateformes “arm64”, j’ai donc créé une image spécifique à l’aide de cette source

PS : L’intégration d’une application sur mon Datacenter se fait avec l’outil CI/CD “Drone”. Pour des raisons techniques : bug SSL dans le plugin “docker” pour Drone, je n’utilise pas les commandes" “wget” ou “curl” dans le Dockerfile. En amont, je télécharge et dispose les sources nécessaires au fonctionnement de Sonarqube dans le repository de mon application.

Docker

Pour construire et tester cette image, je dispose d’un environnement docker multi-architecture (voir documentation) sur la ma VM de développement. Ceci permet de préparer une image arm64 dans une environnement X86_64.

Ressources

#!/bin/bash
file="sonar.properties"
if [ ! -f "sonarqube/conf/$file" ];then
	cp sonarqube/confinit/$file sonarqube/conf/.
	#Database host/databasename
	echo "Setting Postgresql Host/databasename"
	sed -i "s;^#sonar.jdbc.url=jdbc:postgresql.*;sonar.jdbc.url=$SONARQUBE_POSTGRES_SERVER;" /sonarqube/conf/$file
	#Database user
	echo "Setting Postgresql user"
	sed -i "s;^#sonar.jdbc.username.*;sonar.jdbc.username=$SONARQUBE_POSTGRES_USER;" /sonarqube/conf/$file
	#Database password
	echo "Setting Postgresql password"
	sed -i "s;^#sonar.jdbc.password.*;sonar.jdbc.password=$SONARQUBE_POSTGRES_PASSWORD;" /sonarqube/conf/$file
fi
file="wrapper.conf"
if [ ! -f "sonarqube/conf/$file" ];then
	cp sonarqube/confinit/$file sonarqube/conf/.
fi
# Start sonarqube
/sonarqube/bin/linux-arm64/sonar.sh start && tail -f /dev/null

Ces sources seront disposées dans le repertoires “resources” du projet.

Arborescence

Tres simpliste, à partir de la debian:bullseye, je n’utilise pas wget ou curl, pour les raisons invoquées précédemment. Au moment de l’écriture de cette documentation, j’ai utilisé les versions :

Dockerfile/
   |resources/
      | wrapper-linux-arm-64-xxxxxx.tar.gz/ (3.5.50 du Wrapper Java)
      | sonarqube-xxxxxx.gzip/ (9.5.0.56709 de Sonarqube)
      | entrypoint.sh (shell d'initialisation du container)

Dockerfile

ARG WRAPPERVERSION=3.5.50
ARG SONARVERSION=9.5.0.56709
ARG DEBIAN_FRONTEND=noninteractive

FROM debian:bullseye AS build
ARG WRAPPERVERSION
ARG SONARVERSION
ARG DEBIAN_FRONTEND
RUN apt-get update \
  && apt-get -y install unzip
COPY resources/wrapper-linux-arm-64-${WRAPPERVERSION}.tar.gz .
RUN tar -xvzf wrapper-linux-arm-64-${WRAPPERVERSION}.tar.gz
COPY resources/sonarqube-${SONARVERSION}.zip .
RUN unzip sonarqube-${SONARVERSION}.zip
RUN cp -r sonarqube-${SONARVERSION}/bin/linux-x86-64 sonarqube-${SONARVERSION}/bin/linux-arm64 \
    && cd sonarqube-${SONARVERSION}/bin/linux-arm64 \
    && cp -r /wrapper-linux-arm-64-${WRAPPERVERSION}/bin/wrapper ./wrapper \
    && cp -r /wrapper-linux-arm-64-${WRAPPERVERSION}/lib/libwrapper.so ./lib/. \
    && cp -r /wrapper-linux-arm-64-${WRAPPERVERSION}/lib/wrapper.jar /sonarqube-${SONARVERSION}/lib/jsw/wrapper-3.2.3.jar

FROM debian:bullseye
ARG SONARVERSION
ARG DEBIAN_FRONTEND
RUN apt-get update \
 && apt-get -y install openjdk-11-jdk procps
COPY --from=build /sonarqube-${SONARVERSION} /sonarqube
RUN groupadd -g 2000 sq \
&& useradd -m -u 2000 -g sq sq
RUN chown -R 2000:2000 /sonarqube/ \
    && cp -r /sonarqube/conf /sonarqube/confinit
COPY resources/entrypoint.sh /
RUN chmod 755 /entrypoint.sh
USER sq
EXPOSE 9000
ENTRYPOINT ["/entrypoint.sh"]

Build image dans l’environnement de développement

#!/bin/bash
uid=$(id -u)
if [ "$uid" != "0" ];then echo "You re not root";exit;fi
img=sonarqube
tag=9.5.0.56709

#opt="--no-cache --force-rm"
docker buildx build --progress plain --platform linux/arm64 $opt -t $img:$tag .

Test de l’image dans l’environnement de développement

Le shell pour tester l’image dans un environnement docker “multitarch”, connecté “root” sur votre machine de développement, avec docker installé :

#!/bin/bash
img=sonarqube
tag=9.5.0.56709
# Install the qemu packages
apt-get install qemu binfmt-support qemu-user-static
# This step will execute the registering scripts
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# Port
optrun="-p 9000:9000"
#4 volumes: logs temp data extensions
volumes="extensions data conf temp logs"
volumesdocker=""
for v in $volumes
do
  tmpvol="/volumesdocker/sonarqube/$v"
  if [ ! -d "$tmpvol" ];then
    echo "creating directory $tmpvol"
    mkdir -p "$tmpvol"
  fi
  volumesdocker+="-v $tmpvol:/sonarqube/$v "
done
#Test with postgres, set and uncomment, postgres server must be reachable from developpement station
#evt="-e SONARQUBE_POSTGRES_SERVER=jdbc:postgresql://[postgreshostname]/sonarqube -e SONARQUBE_POSTGRES_USER=sonarqube -e SONARQUBE_POSTGRES_PASSWORD=[postgresuserpassword]"

## sysctl for elasticsearch
sysctl -w vm.max_map_count=52428
sysctl -w fs.file-max=131072

docker run --rm $optrun $volumesdocker $evt -d $img:$tag

Comptez environ 10 mn pour pouvoir accéder à l’interface Web Sonarqube (première utilisation, création environnement par Sonarqube dans les volumes), l’url pour l’atteindre est : http://localhost:9000 Vous pouvez voir l’evolution du démarrage en consultant les logs de la machine de développement : “/volumesdocker/sonarqube/vols”

Registry interne du cluster Kubernetes

Télécharger maintenant, cette nouvelle image dans votre registry Kubernetes…

Nettoyage

# volume
rm -rf /volumesdocker/sonarqube
docker system prune

Objets Kubernetes nécessaires au déploiement

Pour préparer la bonne exécution de l’intégration, créer les volumes sur les serveur GlusterFs du Datacenter, et la base de données sur le serveur Postgresql du Datacenter

Puis le manifest Kubernetes, incluant les objets nécessaires, dans cet ordre :

  • Création du “namespace”
  • Création du “secret” d’accès à la registry interne du Datacenter
  • Création des “secret” d’accès à la base de données Postgresql
  • Création des “PersistentVolume”
  • Création des “PersistentVolumeClaim”
  • Création des “LimitRange”
  • Création du “Deployment”
  • Création du “Service”

Pour installer, exécuter, à partir du master kubernetes : kubectl apply -f manifest.yml

PS : Je ne présenterai dorénavant plus le manifest final, trop fastidieux et dépend de votre organisation interne (utilisation de secrets ou non, exécution des containers avec “root” ou non, gestion des volumes, le mode d’exposition des services réseaux,…).

Pour ma part, j’ai développé un outil (bash) permettant la gestion complète d’un projet applicatif Kubernetes dans le Datacenter Mytinydc.

Exemple de descriptif pour “Sonarqube” (yaml) :

---
# Attention si changement de namespace exécuter make drone
appname: "sonarqube"
# image name is the complete image without tag
image: "sonarqube"
# Always - IfNotPresent (**default) - None
imagepullpolicy: "Always"
# full tag image ( latest is generally not recommanded )
tag: "9.5.0.56709"
# using private registry : true | false default is true, so image will be prefixed with the private docker registry url
useprivatedockerregistry: true
containerenv:
  - "SONARQUBE_POSTGRES_SERVER=jdbc:postgresql://[host]/sonarqube"
  - "SONARQUBE_POSTGRES_USER=sonarqube"
  - "SONARQUBE_POSTGRES_PASSWORD=mypassword"
volumes:
  - name: "data"
    size: "1Gi"
    access: "RWO"
    mount: "/sonarqube/data/"
  - name: "conf"
    size: "1Mi"
    access: "RWO"
    mount: "/sonarqube/conf/"
  - name: "logs"
    size: "500Mi"
    access: "RWO"
    mount: "/sonarqube/logs/"
  - name: "temp"
    size: "500Mi"
    access: "RWO"
    mount: "/sonarqube/temp/"
  - name: "extensions"
    size: "100Mi"
    access: "RWO"
    mount: "/sonarqube/extensions/"

# Ports to expose External#internal#PROTO#servicename#url#HAPROXYBALANCE (for kubernetes)
ports:
  - containerport: 9000
    type: "TCP"
    #HAPROXY mecanisme interne pour auto setup mainloadbalancer
    haproxyexpose: "https://[mydomain]"
    internetexposed: true
    finaltls: false
    maxconnserver: 10
security:
  #readonlyimage: true - impossible, l'application écrit des pid dans le répertoire de démarrage !!!!
  runasuser: 2000
  runasgroup: 2000
#Limitrange for namespace
limitrange:
  max:
    cpu: "2"
    memory: "2500Mi"
  request:
    cpu: "1"
    memory: "1000Mi"

A partir de cette description et au travers d’un “Makefile”, l’outil génère :

  • le fichier “manifest” complet du projet, exécutée par l’administrateur du cluster Kubernetes :

    • “Namespace”,
    • “Secret”,
    • Glusterfs : “Service”, “Endpoints”
    • “PersistentVolume”,
    • “PersistentVolumeClaim”,
    • “LimitRange”,
    • “Secret”,
    • “ConfigMap”,
    • “Deployment”,
    • “Service”
  • uniquement le manifest de “deployment”, utilisé dans le processus CI/CD pour la mise à jour des applications dans le Datacenter.

  • le shell de création des volumes et ses quotas sur le serveur Gluster,

  • les “tags” Docker (processus build et deployment),

CI/CD

Les étapes sont :

  • Récupération du repository git du projet applicatif sonarqube
  • Build de l’image docker avec le Dockerfile décrit ci-avant
  • “Push” de l’image dans la registry du Datacenter
  • Update de Déploiement dans kubernetes.

Ce processus est exécuté par “Drone”, à chaque “push” reçu par “Gitea” sur le repository applicatif “sonarqube”.

Conclusion

L’appel à la “sobriété énergétique” se fait entendre, utiliser Sonarqube en consommant moins d’énergie, c’est possible.

Et en matière de performances :

Rappel : utilisation d’un cluster Kubernetes Raspberry PI4/8GB, 1 master/2Workers

  • 2CPU Max et 2.5G de Ram alloués
  • Projet de 5.1K de lignes de code Javascript
  • Utilisation de “sonar-scanner” sur le poste du développeur (analyse et téléchargement des résultats vers le serveur Sonarqube)

Les temps d’analyse (Analysis report processing) Sonarqube, des résultats envoyés, ont été de :

  • Première fois : 1m33s
  • Deuxième fois : 56 secondes
  • Le monitoring interne Kubernetes (metrics), indique ces consommations : RAM : 1.8G, moins de 1 CPU en moyenne.

Je ne voix de différences concernant les temps de réponse IHM web par rapport à une installation classique (X86_64).

+++