On souhaite créer une VM Linux sous Windows 10 Pro. On va donc passer directement par l’outil Hyper-V (fourni avec Windows 10 Pro), et installer une VM Ubuntu par exemple. Mais cela impose pas mal de taches manuelles interactives, donc on va essayer de l’automatiser.
Pour cela on va utiliser un outil d’Infrastructure as Code, en l’occurrence Vagrant. D’autres existent comme Terraform, Ansible, Chef, Puppet…
Pourquoi Hyper-V ? En effet parfois on n’a pas la possibilité d’utiliser WSL 2 par exemple, ou ce dernier ne répond pas au besoin. Si on ne dispose pas de Windows 10 Pro et Hyper-V, on pourrait aussi simplement utiliser VirtualBox, compatible avec Vagrant.
Pour information, Vagrant est plutôt destiné à des environnement hors production, comme pour du développement ou des tests.
Installer Vagrant
Tout d’abord il faudra installer Vagrant pour votre OS, et en l’occurrence, pour Windows dans notre cas.
Sinon on peut aussi l’activer via le menu Programmes et fonctionnalités, comme suit :
Fichier Vagrantfile
Le fichier Vagrantfile va permettre de décrire en lignes de code la machine que l’on veut créer, en essayant de garder une certaine portabilité entre les différentes plateformes.
On trouvera notamment le paramètre config.vm.box, indiquant à partir de quelle box ou image on veut créer notre VM. Un catalogue de box est disponible ici : https://app.vagrantup.com/boxes/search.
Dans notre cas, on va choisir generic/ubuntu2004, qui est compatible Hyper-V.
Exemple de script Vagrantfile :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cela pourra être pratique de mettre en place un partage de fichier entre l’hôte et la VM invitée, bien que ce ne soit pas obligatoire. C’est l’objet de la propriété :
Dans ce cas il faudra s’authentifier pour mettre en place le partage SMB.
Une fois dans la VM, on pourra voir ce dossier partagé dans la liste des mounts, et accéder au chemin /vagrant_data.
Exécution du script
On va maintenant demander à Vagrant de créer une VM correspondant au Vagrantfile. Pour ce faire, se placer dans le répertoire contenant le fichier Vagrantfile et lancer :
C’est le plugin vagrant-proxyconf qui sera utilisé dans ce cas, et qui devra auparavant avoir été installé comme suit :
vagrant plugin install vagrant-proxyconf
Il faudra simplement penser à définir les variables d’environnementVAGRANT_HTTP_PROXY, VAGRANT_HTTPS_PROXY, voire VAGRANT_NO_PROXY. Comme précisé dans la réponse plus haut, les variables http_proxy et https_proxy sont nécessaires pour pouvoir télécharger la box sur internet.
La bonne nouvelle c’est que ce plugin permet non seulement de télécharger la box Vagrant nécessaire pour initialiser la VM, mais aussi de configurer le proxy de différents programmes.
Par exemple pour Apt c’est ce plugin qui va configurer le fichier /etc/apt/apt.conf.d/01proxy. Donc plus besoin de suivre cette manip : Configure proxy for APT.
GitLab est un serveur d’hébergement de code source (SCM) dédié à Git. GitLab peut être utilisé avec une instance publique sur Internet (https://gitlab.com/), de façon similaire à GitHub (https://github.com/), ou bien avec une instance hébergée chez soi ou dans une entreprise (on premise).
GitLab intègre une partie « Intégration Continue », connue sous le nom de GitLab CI. Cela permet de vérifier que le logiciel peut être construit correctement, éventuellement d’exécuter des tests unitaires, voire de publier le logiciel. Et bien d’autres choses encore.
La particularité de GitLab CI est de déléguer l’exécution du pipeline d’intégration continue, découpé en tâches (nommées jobs), à d’autres machines, appelées Runners. Ces runners peuvent être mis à disposition d’une instance GitLab via des shared runners, mais on peut également dédier des runners spécifiques à son projet ou son groupe de projets.
On peut ainsi exécuter des runners sur sa propre machine, en installant des GitLab Runners, que l’on soit sous Windows, Mac, Linux, ou même avec une infrastructure plus complexe comme Kubernetes.
Cas d’utilisation
L’objectif ici est de permettre de construire un projet Java dans GitLab CI, à l’aide de Maven.
Bien souvent en entreprise on va aussi utiliser notre propre dépôt (repository) de librairies Java compatible avec Maven notamment. Cela permet à la fois de servir de cache et de proxy pour tous les développeurs qui auraient besoin d’accéder à des librairies externes, disponibles sur Internet. Cela va également permettre d’héberger nos propres librairies ou artefacts, sur un serveur privé. Pour ce faire, on peut par exemple utiliser Sonatype Nexus. Il existe aussi d’autres solutions, comme Artifactory.
Pour faciliter les choses, on souhaite également utiliser Docker, une solution à base de containers.
L’enjeu sera essentiellement ici de pouvoir exécuter les tâches Maven dans GitLab CI, en configurant le settings.xml de Maven afin d’utiliser un repository Nexus interne.
L’exécution de Maven se fera dans chaque job à l’aide d’un executor défini dans le GitLab Runner et configuré pour utiliser Docker. Ainsi, chaque exécution de job consistera à instancier un container Docker et exécuter un script à l’intérieur. On tentera donc au maximum d’utiliser les images Maven officielles présentes sur Docker Hub pour cela. Un des avantages de Docker est notamment d’éviter l’installation de toute une série d’outils sur la machine hôte, comme un JDK et une version de Maven dans ce cas. Ces derniers sont simplement packagés dans l’image Docker correspondante, en isolation de la machine hôte.
Environnement
Dans cet article on va considérer que l’on travaille sur une machine Linux. C’est possible de le faire fonctionner sous Windows, mais cela rajoute quelques contraintes, et le cas de Linux est sûrement plus pertinent en production.
Installation du runner
Si ce n’est déjà fait, il faut installer le GitLab Runner qui fonctionnera comme un service. Globalement on va suivre la procédure expliquée à cette page : https://docs.gitlab.com/runner/install/linux-manually.html. Pour Ubuntu / Debian on aura donc un package nommé gitlab-runner_x86_64.deb, à installer avec la commande dpkg. Il est aussi possible de télécharger le binaire pour d’autres architectures comme expliqué dans le lien plus haut.
Pour vérifier que le service est actif, exécuter au choix :
Ensuite, un ou plusieurs runners peuvent être enregistrés. Dans notre cas, on utilisera les runners spécifiques à un projet. Il faudra au préalable aller dans le projet GitLab dans la section Settings > CI/CD et ouvrir Runners pour récupérer l’URL et le token. Par exemple, on va enregistrer un runner spécifique aux jobs Maven.
Normalement à ce stade le runner est enregistré et prêt à exécuter des jobs du projet. Si un fichier .gitlab-ci.yml a déjà été ajouté aux sources du projet, vous pouvez tester la bonne exécution du pipeline en rajoutant un commit dans le projet ou en forçant son exécution.
Comme on a associé un tag à notre runner, il ne faut pas oublier de le spécifier dans la déclaration du job, comme par exemple :
Bon si vous êtes derrière un proxy, il est probable de rencontrer des problèmes. Il faudra peut-être regarder ici : Running GitLab Runner behind a proxy.
Ces settings doivent ensuite être passés au conteneur Docker au moyen d’un bind mount en utilisant les volumes Docker. Les volumes sont configurés au niveau du runner GitLab.
Dans la configuration config.toml, on ajoutera ou modifiera donc cette section :
En pratique, on peut valider que les settings Maven sont corrects en exécutant simplement un container Docker comme ici :
docker run --rm -v $(pwd)/custom-settings.xml:/root/.m2/settings.xml:ro maven:3 mvn help:effective-settings
Au minimum le plugin help devrait être téléchargé via Nexus, ainsi que ses dépendances. Cela permet aussi de valider que le montage de volume du fichier settings.xml est correct et bien pris en compte dans le container. Un chemin absolu est nécessaire quand on passe par Docker CLI (contrairement à Docker Compose). Le résultat de la commande précédente devrait ressembler à çà au final :
Accès au serveur Nexus à partir du conteneur Docker
Attention si le conteneur souhaite accéder à un service hébergé sur son hôte, il ne pourra pas résoudre localhost, et il faudra remplacer par exemple localhost par son ip résolue. Voir :
Normalement à ce point, tout devrait fonctionner. Dans le log du job Maven du pipeline on devrait avoir quelque chose comme çà :
Running with gitlab-runner 13.9.0 (2ebc4dc4)
on Runner Maven XHUSESdz
Preparing the "docker" executor
00:06
Using Docker executor with image maven:3.6-jdk-8 ...
Pulling docker image maven:3.6-jdk-8 ...
Using docker image sha256:75cbe9c11be24bae1cb8064cbc33c26df79321b5047c30ff8e29abcd2e5289a9 for maven:3.6-jdk-8 with digest maven@sha256:d4a4bb5d83a95c012615c5294f665ee042e721298c941eea32db3ea0ca588e9b ...
Preparing environment
00:02
Running on runner-xhusesdz-project-23778885-concurrent-0 via guillaume-F3JP...
Getting source from Git repository
00:05
Fetching changes with git depth set to 50...
Reinitialized existing Git repository in /builds/ghusta/maven-git-flow-test/.git/
Checking out c170a61a as feature/gitlab-ci-tags-custom-runner...
Removing .m2/
Removing target/
Skipping Git submodules setup
Restoring cache
00:03
Checking cache for default...
No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted.
Successfully extracted cache
Executing "step_script" stage of the job script
00:10
Using docker image sha256:75cbe9c11be24bae1cb8064cbc33c26df79321b5047c30ff8e29abcd2e5289a9 for maven:3.6-jdk-8 with digest maven@sha256:d4a4bb5d83a95c012615c5294f665ee042e721298c941eea32db3ea0ca588e9b ...
$ mvn $MAVEN_CLI_OPTS verify
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/share/maven
Java version: 1.8.0_282, vendor: Oracle Corporation, runtime: /usr/local/openjdk-8/jre
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-66-generic", arch: "amd64", family: "unix"
3361 [INFO] Error stacktraces are turned on.
3477 [INFO] Scanning for projects...
...
7330 [INFO] ------------------------------------------------------------------------
7336 [INFO] BUILD SUCCESS
7337 [INFO] ------------------------------------------------------------------------
7340 [INFO] Total time: 3.910 s
7345 [INFO] Finished at: 2021-02-28T21:08:20Z
7348 [INFO] ------------------------------------------------------------------------
Saving cache for successful job
00:01
Creating cache default...
.m2/repository: found 977 matching files and directories
Archive is up to date!
Created cache
Cleaning up file based variables
00:01
Job succeeded
Et voilà çà marche !
Et n’importe quelle image Docker Maven peut être configurée dans le fichier .gitlab-ci.yml, au choix du développeur. Comme par exemple :