SonarQube sur son poste, c’est top ! Mais il peut être nécessaire de le rendre accessible depuis Internet, depuis Azure DevOps ou simplement partager l’instance entre plusieurs personnes et plusieurs projets.
Il existe différentes stratégies pour héberger SonarQube sur Azure et le rendre accessible :
-
Héberger SonarQube sur une machine virtuelle
-
etc.
J’ai choisi ici d’utiliser Docker pour les raisons déjà évoquées :
-
Simplicité : en utilisant l’image officielle de Docker, on peut démarrer sans passer de temps à installer le logiciel
-
Portabilité : je peux utiliser le même genre de configuration sur mon poste ou dans Azure
Il n’existe pas moins de 6 façons d’exécuter Docker dans Azure. J’ai donc retenu d’utiliser App Service dont les coûts pour un App Service Plan Linux ont diminué. Ainsi, nous allons pouvoir faire tourner App Service sur une instance B2 à ~€21.547/mois (offre promotionnelle visiblement).
Azure Container Instance nous permettrait de régler plus finement la mémoire et le CPU alloués ainsi que d’arrêter le container au besoin mais App Service intègre une connexion HTTPS par défaut avec un certificat reconnu, ce qui particulièrement intéressant pour des raisons de sécurité évidentes. |
Architecture
L’architecture se composera simplement de :
-
Un App Service Plan Linux
-
Un App Service s’appuyant sur une image Docker
Pas de base de données externe.
Version simple
Dans un premier temps, nous allons procéder à la création d’un container sur la base de l’image officielle seule. Pour ce faire, ouvrir un prompt PowerShell et exécuter les commandes AZ CLI suivantes :
# Variables à adapter
$rg="<resource-group-name>" # Nom du groupe de ressources
$ASP="<app-service-plan>" # Nom de l'App Service Plan
$appName="<app-name>" # Nom de l'App Service (va déterminer l'URL d'accès)
# Création du groupe de ressources. La localisation est à adapter. Ici "France Central"
az group create --location "France Central" --name $rg
#Création de l'App Service Plan.
az appservice plan create --name $ASP --resource-group $rg --sku B2 --is-linux
# Création de L'App Service sur la base de l'image officielle
az webapp create --resource-group $rg --plan $ASP --name $appName --deployment-container-image-name sonarqube:8.1-community-beta
# Publication du port 9000
az webapp config appsettings set --resource-group $rg --name $appName --settings WEBSITES_PORT=9000
# Configuration du stockage permanent
az webapp config appsettings set --resource-group $rg --name $appName --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
# Configuration des logs
az webapp log config --resource-group $rg --name $appName --docker-container-logging filesystem
# Petit redémarrage
az webapp restart --resource-group $rg --name $appName
# Accès à SonarQube
start "https://$appName.azurewebsites.net"
# Récupération des logs
az webapp log tail --resource-group $rg --name $appName
Sans l’accès à l’URL, le déploiement de l’image ne semble pas se faire |
Le premier démarrage, incluant le téléchargement de l’image est assez long. |
Docker Compose
Le support de Docker Compose est encore en preview mais il permet de :
-
Contrôler les variables d’environnements
-
Déclarer des volumes
-
Ajouter une base de données externe telle que postgre
Malheureusement, plusieurs problèmes se sont posés, nécessitant la création d’une image personnalisée (mais héritée de l’image officielle). L’utilisation d’une image personnalisée a pu apporter le support de SSH dans App Service.
Les problèmes étaient, par ordre d’apparition :
-
Erreur "max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]". C’est erreur est remontée par Elastic Search. Sachant qu’il n’est pas possible de modifier cette valeur sur un App Service, plusieurs solutions sont possibles :
-
Modifier les paramètres d’Elastic Search (cf. https://jira.sonarsource.com/browse/SONAR-12264)
-
Empêcher SonarQube de forcer la vérification. Cette vérification est forcée lorsqu’une base autre que H2 est utilisée.
-
-
App Service remplace les "." des variables d’environnement par des "_", ce qui ne m’a pas permis de facilement surchargé les paramètres de SonarQube
-
Le container s’exécute en tant que l’utilisateur "sonarqube" par défaut, ce qui est une bonne chose d’un point de vue sécurité, mais ce qui ne permet pas de démarrer le service SSH simplement
Le projet est disponible sur GitHub
Commençons donc par la création d’une image Docker.
Image Docker
L’image est préparée à partir de l’image officielle sonarqube:8.1-community-beta
.
FROM sonarqube:8.1-community-beta
# ssh
ENV SSH_PASSWD "root:Docker!"
USER root
RUN apt-get update \
&& apt-get install -y openssh-server dialog --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& echo "$SSH_PASSWD" | chpasswd
ADD sshd_config /etc/ssh/
EXPOSE 9000 2222
COPY --chown=sonarqube:sonarqube run.sh "$SONARQUBE_HOME/bin/"
Il consiste en :
-
Devenir root pour pouvoir installer le serveur OpenSSH et exécuter le container en tant que root
-
Personnaliser la configuration d’OpenSSH à partir d’un fichier d’exemple
-
Définir le mot de passe de root
-
Modifier la directive
EXPOSE
pour exposer le serveur OpenSSH en plus de SonarQube -
La copie du fichier personnalisé
run.sh
Le fichier run.sh
reprend quasiment entièrement le fichier de l’image officielle, à quelques détails près.
while IFS='=' read -r envvar_key envvar_value
do
if [[ "$envvar_key" =~ sonar.* ]] || [[ "$envvar_key" =~ ldap.* ]]; then
sq_opts+=("-D${envvar_key}=${envvar_value}")
fi
if [[ "$envvar_key" =~ sonar_* ]]; then
# Replacing '_' by '.'
envvar_key="$(sed s/_/./g <<<$envvar_key)"
sq_opts+=("-D${envvar_key}=${envvar_value}")
fi
done < <(env)
...
if [ "$init_only" = false ]; then
echo "Starting SSH ..."
service ssh start
su sonarqube -c 'java -jar "lib/sonar-application-$SONAR_VERSION.jar" -Dsonar.log.console=true "$@"' -- "run.sh" "${sq_opts[@]}" "$@"
fi
La première partie se charge de restaurer le "." sur les variables d’environnement.
La deuxième partie se charge de :
-
Démarrer le service SSH
-
Exécuter SonarQube en tant qu’utilisateur sonarqube. En effet, Elastic Search ne démarre pas s’il est exécuté en tant que root. Peut-être existait-il un flag à chercher au fin fond d’une doc mais cela semblait une bonne pratique de ne pas l’exécuter en tant que root.
Construction de l’image
L’image va être construite et poussée sur un Azure Container Registry.
Pour ce faire, dans un prompt PowerShell :
-
Création d’un Azure Container Registry
$acr="myregistry" $rg="<resource-group-name>" az acr create -n $acr -g $rg --sku Basic --admin-enabled true
-
Login
az acr login -n $acr
-
Récupération des credentials
az acr credential show -n $acr --password-name password
-
Construction de l’image et publication
$tag="8" $image="sonarqubeonazure" docker build -t $image:$tag -f ".\8.Dockerfile" . docker tag $image:$tag $acr.azurecr.io/$image:$tag docker push $acr.azurecr.io/$image:$tag
Préparation du Docker Compose
SonarQube fournit un exemple assez proche de la cible
version: '3.3'
services:
sonarqube:
depends_on:
- db
image: myregistry.azurecr.io/sonarqubeonazure:7
ports:
- "7000:9000"
networks:
- sonarnet
environment:
- sonar.forceAuthentication=true
- sonar.telemetry.enable=false
- sonar.es.bootstrap.checks.disable=true
- SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONARQUBE_JDBC_USERNAME=sonar
- SONARQUBE_JDBC_PASSWORD=sonar
volumes:
- sonarqube_conf:/opt/sonarqube/conf
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
db:
image: postgres
networks:
- sonarnet
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
volumes:
- postgresql:/var/lib/postgresql
# This needs explicit mapping due to https://github.com/docker-library/postgres/blob/4e48e3228a30763913ece952c611e5e9b95c8759/Dockerfile.template#L52
- postgresql_data:/var/lib/postgresql/data
networks:
sonarnet:
driver: bridge
volumes:
sonarqube_conf:
sonarqube_data:
sonarqube_extensions:
postgresql:
postgresql_data:
Les différences avec l’exemple concernent :
-
L’utilisation des variables d’environnement SONARQUBE_JDBC_USERNAME et SONARQUBE_JDBC_PASSWORD
-
Ajout de la directive
depends_on
-
Et bien sûr, ajout de la variable d’environnement
sonar.es.bootstrap.checks.disable
pour désactiver le check
On peut alors tester le fichier docker compose à l’aide de la commande :
docker-compose up
Les containers peuvent être supprimés, ainsi que les volumes avec la commande
docker-compose down -v
Création de l’App Service avec Docker Compose
Le déploiement d’un App Service avec Docker Compose est assez proche de la version simple.
Ouvrir un prompt PowerShell et exécuter les commandes suivantes :
# Variables à adapter
$rg="<resource-group-name>" # Nom du groupe de ressources
$ASP="<app-service-plan>" # Nom de l'App Service Plan
$appName="<app-name>" # Nom de l'App Service (va déterminer l'URL d'accès)
$dockerComposePath=".\docker-compose.yml" # Chemin vers le fichier Docker Compose
# Création du groupe de ressources. La localisation est à adapter. Ici "France Central"
az group create --location "France Central" --name $rg
# Création de l'App Service Plan.
az appservice plan create --name $ASP --resource-group $rg --sku B2 --is-linux
# Création de L'App Service à partir du fichier Docker Compose
az webapp create --resource-group $rg --plan $ASP --name $appName --multicontainer-config-type compose --multicontainer-config-file $dockerComposePath --docker-registry-server-user $acr --docker-registry-server-password "3...9bHTFzFd"
# Publication du port 7000
az webapp config appsettings set --resource-group $rg --name $appName --settings WEBSITES_PORT=7000
# Configuration du stockage permanent
az webapp config appsettings set --resource-group $rg --name $appName --settings WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
az webapp config appsettings set --resource-group $rg --name $appName --settings DOCKER_REGISTRY_SERVER_URL=https://$acr.azurecr.io
az webapp config appsettings set --resource-group $rg --name $appName --settings DOCKER_REGISTRY_SERVER_USERNAME=$acr
az webapp config appsettings set --resource-group $rg --name $appName --settings DOCKER_REGISTRY_SERVER_PASSWORD=3...9bHTFzFd
# Configuration des logs
az webapp log config --resource-group $rg --name $appName --docker-container-logging filesystem
# Petit redémarrage
az webapp restart --resource-group $rg --name $appName
# Accès à SonarQube
start "https://$appName.azurewebsites.net"
# Récupération des logs
az webapp log tail --resource-group $rg --name $appName
Le téléchargement de l’image, le démarrage de l’application sont toujours aussi long. Mais éventuellement, cela marchera… |
Accès SSH
Comme évoqué précédemment, si nécessaire, il est possible d’accéder à SonarQube en SSH en allant à l’adresse https://$appName.scm.azurewebsites.net/webssh/host