GitLab

From Tuxunix
Jump to: navigation, search

Avant-Propos

Afin de centraliser et de gérer efficacement les scripts et programmes développés par une équipe UNIX, le produit Gitlab permet de versionner les développements ainsi que de gérer les projets et les accès des utilisateurs.

Processus

Le serveur contenant les projets est server1. Pour chacun d'entre eux, il y aura l'origine, c'est à dire la version finale d'un script ou d'un programme plus des branches qui serviront au développement. Le cycle de vie du projet passe par 4 étapes :


  • PHASE 1 : le développeur va télécharger le projet original.
  • PHASE 2 : il va ensuite créer une branche où les modifications seront apportées.
  • PHASE 3 : une fois cette étape terminée, il exporte cette branche sur le serveur repository (server1).
  • PHASE 4 : pour finir, lui ou une autre personne (dépend de l'importance du projet) va fusionner le projet d'origine avec la branche sur laquelle il a fait ces modifications.
Git.png

Architecture

Voici l'architecture de Gitlab dans la version qui est installée pour le moment (9.2.2) :

Archigitlab.png

L'installation du serveur Gitlab est réalisé sur server1. Il utilise NGinx en tant que serveur Web frontal et Unicorn comme backend. La communication entre Unicorn et NGinx se fait via une socket UNIX. NGinx va afficher les pages statiques, les objets importées comme des images ou autres pièces jointes ainsi que du code précompilé. Les autres pages Web et l'API Gitlab est affiché par Unicorn. Sidekiq est utilisé comme gestion de job qui a son tour utilise REDIS comme base de données non persistente pour les informations des jobs et les méta-données.

Gitlab utilise PostgreSQL pour la gestion des données persistantes (utilisateurs, permissions, incidents, autres meta-données). Il stocke les dépôts GIT bruts dans /var/opt/gitlab/repositories. Il stocke également les branches principales et les hooks.

Lorsque l'on interroge les dépôts à travers HTTPS, Gitlab utilise une API permettant d'effectuer la gestion des autorisations et des accès aux objets GIT.

Le composant Gitlab-Shell est utilisé par les dépôts GIT via SSH. Il gère les clés SSH dans /var/opt/gitlab/.ssh/authorized_keys qui ne doit pas être édité manuellement. Gitlab-Shell accède aux dépôts bruts via Gitaly pour fournir les objets GIT et communique avec REDIS pour soumettre des jobs à Sidekiq. Gitlab-Shell interroge l'API Gitlab pour déterminer les autorisations et les accès.

Gitaly executes git operations from gitlab-shell and the GitLab web app, and provides an API to the GitLab web app to get attributes from git (e.g. title, branches, tags, other meta data), and to get blobs (e.g. diffs, commits, files).

Gitaly exécute des commandes GIT depuis Gitlab-Shell ou l'interface Web et fourni une API pour que l'interface Web obtienne des informations de la part de GIT (titre, branches, tags, méta-données, commits, fichiers ...).

En pratique

Voici un exemple de mise en place et de gestion d'un projet sur GIT.

Création du projet

Pour commencer, nous allons créer le projet GIT à travers l'interface Web de GitLab. Cette dernière est accessible à l'adresse suivante : https://gitlab-unix-prod.domain.fr/users/sign_in. Une fois connecté, nous arrivons sur l'écran suivant :


File:Gitlab 1.png


On voit la liste des projets qui sont affectés à l'utilisateur. On va pouvoir créer une nouveau projet en cliquant en haut à droite sur "New Project" :


File:Gitlab 2.png


On remarque que le chemin d'accès du projet comporte en plus du nom du serveur repository, le nom du groupe (ici unix_team) et le nom du projet (ici nouveau_projet). Si le projet est déjà existant sous un format spécifique (GitHub, Bitbucket, Google Code ...), on peut l'importer. On peut également mettre une description du projet et mettre un degré de visibilité au projet. Par défaut, il n'y a que les utilisateurs qui sont manuellement autorisés qui peuvent le voir et le modifier. Une fois validée, GitLab confirme la création :


File:Gitlab 3.png

Commandes Utiles

Récupération du projet

Dans cet exemple, le poste sur lequel le développement va être réalisé est poste1. Auparavant, il faut s'assurer que GIT soit bien installé sur la machine :

[user1@poste1 git]$ rpm -qa | grep ^git
git-1.8.2.3-1.el5

Une fois cela vérifié, on va commencer par cloner le projet depuis son origine. Il y a deux possibilités :

Étant donné que le serveur server1 est en zone Bastion, il est plus pratique d'utiliser la méthode SSH. De plus, on peut mettre sa clé publique sur son compte Gitlab afin d'être automatiquement authentifié. Pour cela, on va dans les paramétrages de son utilisateurs sur Gitlab :

File:Settings.png

Ensuite, on clique sur SSH Keys puis on colle sa clé publique en lui donnant un nom probant. Maintenant, il est possible de cloner l'environnement :

[user1@poste1 git]$ git clone git@gitlab-unix-prod.domain.fr:unix_team/nouveau_projet.git
Cloning into 'nouveau_projet'...
[user1@poste1 git]$ ls
nouveau_projet

Voici un aperçu de la méthode via HTTPS :

[root@va2346 test_git]# git clone https://gitlab-unix.domain.fr/unix_team/nouveau_projet.git
Cloning into 'nouveau_projet'...
Username for 'https://gitlab-unix.domain.fr': user1
Password for 'https://gitlab-unix.domain.fr':
warning: You appear to have cloned an empty repository.
Checking connectivity... done.

S'il y a un souci avec le certificat du serveur, on peut empêcher sa vérification via cette variable à exporter :

[root@va2346 test_git]# git clone https://gitlab-unix.domain.fr/unix_team/nouveau_projet.git
Cloning into 'etl_hobbit'...
fatal: unable to access 'https://gitlab-unix.domain.fr/unix_team/nouveau_projet.git': Peer certificate cannot be authenticated with known CA certificates

[root@va2346 test_git]# export GIT_SSL_NO_VERIFY=true

Pour pouvoir exporter le projet et créer des branches, on donne quelques renseignements sur notre identité à travers ces quelques commandes :

[user1@poste1 git]$ cd nouveau_projet/
[user1@poste1 nouveau_projet]$ git config --global user.name "USER 1"
[user1@poste1 nouveau_projet]$ git config --global user.email "user1@domain.fr"

Création de la branche master

Le projet étant vide, il faut l'initialiser avec une branche principale dite master. On l'alimente avec un fichier que l'on va ajouter au projet local :

[user1@poste1 nouveau_projet]$ git add test.sh

Puis valider :

[user1@poste1 nouveau_projet]$ git commit -m "add test.sh"
[master (root-commit) 5a51030] add test.sh
 1 file changed, 1 insertion(+)
 create mode 100755 test.sh

Ensuite on exporte les modifications du projet vers la branche master sur le repository :

[user1@poste1 nouveau_projet]$ git push -u origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 243 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@gitlab-unix-prod.domain.fr:unix_team/nouveau_projet.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

Création d'une nouvelle branche de développement

Pour développer des modifications sur le projet, on peut créer une nouvelle branche comme ceci :

[user1@poste1 nouveau_projet]$ git checkout -b branch_1
Switched to a new branch 'branch_1'

La commande suivante permet de voir sur quelle branche on travaille :

[user1@poste1 nouveau_projet]$ git branch
* branch_1
  master

Celui-ci permet de changer de branche :

[user1@poste1 nouveau_projet]$ git checkout master
Switched to branch 'master'

Modification d'une branche

Une fois positionnée dans la branche branch_1, on peut modifier les scripts source puis on les ajoutes et on les valides :

[user1@poste1 nouveau_projet]$ git add test.sh
[user1@poste1 nouveau_projet]$ git commit -m "modification test.sh sur branch_1"
[branch_1 5c6153b] modification test.sh sur branch_1
 1 file changed, 1 insertion(+)

On peut vérifier l'état de la branche courante via cette commande :

[user1@poste1 nouveau_projet]$ git status
# On branch branch_1
nothing to commit, working directory clean

Ensuite, on peut exporter les modifications de la branche branch_1 sur le repository :

[user1@poste1 nouveau_projet]$ git push -u origin branch_1
Counting objects: 5, done.
Writing objects: 100% (3/3), 294 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: To create a merge request for branch_1, visit:
remote:   https://gitlab-unix-prod.domain.fr/unix_team/nouveau_projet/merge_requests/new?merge_request%5Bsource_branch%5D=branch_1
remote:
To git@gitlab-unix-prod.domain.fr:unix_team/nouveau_projet.git
 * [new branch]      branch_1 -> branch_1
Branch branch_1 set up to track remote branch branch_1 from origin.

Quand on regarde le contenu du projet, on voit que le répertoire contient les scripts ou le programme à développer puis un répertoire caché .git contenant les informations relatives à la gestion des sources :

[user1@poste1 nouveau_projet]$ ls -ltra
total 16
drwx------ 3 user1 users 4096 Jun 14 11:35 ..
-rwxr-x--- 1 user1 users    40 Jun 14 11:43 test.sh
drwxr-x--- 3 user1 users  4096 Jun 14 11:43 .
drwxr-x--- 8 user1 users  4096 Jun 14 11:43 .git
[user1@poste1 nouveau_projet]$ ls -ltra .git/
total 52
drwxr-x---  2 user1 users 4096 Jun 14 11:35 info
drwxr-x---  2 user1 users 4096 Jun 14 11:35 hooks
-rw-r-----  1 user1 users   73 Jun 14 11:35 description
drwxr-x---  2 user1 users 4096 Jun 14 11:35 branches
drwxr-x---  3 user1 users 4096 Jun 14 11:40 logs
drwxr-x---  5 user1 users 4096 Jun 14 11:40 refs
-rw-r-----  1 user1 users   25 Jun 14 11:42 HEAD
drwxr-x---  3 user1 users 4096 Jun 14 11:43 ..
-rw-r-----  1 user1 users  104 Jun 14 11:43 index
drwxr-x--- 10 user1 users 4096 Jun 14 11:43 objects
-rw-r-----  1 user1 users   34 Jun 14 11:43 COMMIT_EDITMSG
-rw-r-----  1 user1 users  354 Jun 14 11:43 config
drwxr-x---  8 user1 users 4096 Jun 14 11:43 .

Fusionner une branche à la branche principale

Après avoir modifié notre branche et exporté sur le serveur repository, on se place sur la branche master :

[user1@poste1 nouveau_projet]$ git checkout master
Switched to branch 'master'

Ensuite, on fait une fusion (si un conflit est relevé, un message d'erreur apparait avec des informations dans le fichier concerné) :

[user1@poste1 nouveau_projet]$ git merge branch_1
Updating 5a51030..5c6153b
Fast-forward
 test.sh | 1 +
 1 file changed, 1 insertion(+)

On exporte la branche principale vers le repository :

[user1@poste1 nouveau_projet]$ git branch -d branch_1
Deleted branch branch_1 (was 5c6153b).
[user1@poste1 nouveau_projet]$ git push origin :branch_1
#:# Therefore, please log off immediately.
To git@gitlab-unix-prod.domain.fr:unix_team/nouveau_projet.git
 - [deleted]         branch_1
[user1@poste1 nouveau_projet]$ git branch
* master

Build

Fichier .gitlab-ci.yml

Gitlab-runner permet de réaliser des tests sur les scripts et les programmes déposés sur Gitlab ainsi que d'automatiser la compilation de programmes et la création de RPM. Il s'appuie sur un fichier .gitlab-ci.yml qui doit exister au sein du projet. Voici un exemple :

stages:
 - build
 - clean_up
 
create_rpm_for_rhel5:
 stage: build
 script:
   - version=$(cat .version | cut -f1 -d-)
   - release=$(git log --oneline | wc -l)
   - rpmbuild -bb domain-epops.spec --define "_sourcedir $PWD" --define "_release $release" --define "_version $version" --define "_topdir /home/gitlab-runner/rpmbuild" --define "_tmppath /home/gitlab-runner/rpmbuild/BUILDROOT" --define "_source_filedigest_algorithm 1" --define "_binary_filedigest_algorithm 1" --define "%_source_payload w9.gzdio" --define "%_binary_payload w9.gzdio"
   - scp /home/gitlab-runner/rpmbuild/RPMS/noarch/domain-epops-$version-$release.noarch.rpm root@server2:/base/install/depot/channels/RHEL_5_64/Extra/DOMAIN/epops
   - ssh root@server2 "ln -s /base/install/depot/channels/RHEL_5_64/Extra/DOMAIN/epops/domain-epops-$version-$release.noarch.rpm /base/install/depot/channels/RHEL_5_64/Repositories/2016_03/testing/Packages"
   - ssh root@server2 "chmod 644 /base/install/depot/channels/RHEL_5_64/Extra/DOMAIN/epops/domain-epops-$version-$release.noarch.rpm"
   - ssh root@server2 "umask 0022; createrepo -s sha --update /base/install/depot/channels/RHEL_5_64/Repositories/2016_03/testing"
 only:
   - master
 tags:
   - runner_rhel5

create_rpm_for_rhel6:
 stage: build
 script:
   - version=$(cat .version | cut -f1 -d-)
   - release=$(git log --oneline | wc -l)
   - rpmbuild -ba domain-epops.spec --define "_sourcedir $PWD" --define "_release $release" --define "_version $version" --define "_source_filedigest_algorithm 1" --define "_binary_filedigest_algorithm 1" --define "%_source_payload w9.gzdio" --define "%_binary_payload w9.gzdio"
   - scp /home/gitlab-runner/rpmbuild/RPMS/noarch/domain-epops-$version-$release.noarch.rpm root@server2:/base/install/depot/channels/RHEL_6_64/Extra/DOMAIN/epops
   - ssh root@server2 "ln -s /base/install/depot/channels/RHEL_6_64/Extra/DOMAIN/epops/domain-epops-$version-$release.noarch.rpm /base/install/depot/channels/RHEL_6_64/Repositories/2016_03/testing/Packages"
   - ssh root@server2 "chmod 644 /base/install/depot/channels/RHEL_6_64/Extra/DOMAIN/epops/domain-epops-$version-$release.noarch.rpm"
   - ssh root@server2 "umask 0022; createrepo --update /base/install/depot/channels/RHEL_6_64/Repositories/2016_03/testing"
 only:
   - master
 tags:
   - runner_rhel6

Les runners

Pour fonctionner, Gitlab-runner a besoin d'agent appelé runner. On attribue à chaque projet un ou plusieurs runners permettant d'exécuter les actions décrites dans le fichier YML. La configuration des runners est localisée dans le fichier /etc/gitlab-runner/config.toml :

[root@server1 ~]# cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0

runners
 name = "Runner RHEL 6 on serverAgent2"
 url = "https://gitlab-unix-prod.domain.fr/ci"
 token = "bbf3ec031a0ccf331b66f199e6ea5b"
 executor = "ssh"
 [runners.ssh]
   user = "gitlab-runner"
   password = "xxxxxxxx"
   host = "serverAgent2.domain.fr"
   identity_file = "/home/gitlab-runner/.ssh/id_rsa"
 [runners.cache]

runners
 name = "Runner RHEL 5 on serverAgent1"
 url = "https://gitlab-unix-prod.domain.fr/ci"
 token = "96ef9a030c468e8a8a4b640bc73d43"
 executor = "ssh"
 [runners.ssh]
   user = "gitlab-runner"
   password = "xxxxxxxx"
   host = "serverAgent1.domain.fr"
   identity_file = "/home/gitlab-runner/.ssh/id_rsa"
 [runners.cache]

runners
 name = "Runner RHEL 7 on serverAgent3"
 url = "https://gitlab-unix-prod.domain.fr/ci"
 token = "7b73b3710ff49f1558e8a2317ae7b2"
 executor = "ssh"
 [runners.ssh]
   user = "gitlab-runner"
   password = "xxxxxxxx"
   host = "serverAgent3.domain.fr"
   identity_file = "/home/gitlab-runner/.ssh/id_rsa"
 [runners.cache]

Ici, il y a trois agents. Cela permet d'avoir plusieurs types de machines pour construire un RPM ou compiler un programme. On a donc :

  • serverAgent1 en RHEL 5
  • serverAgent2 en RHEL 6
  • serverAgent3 en RHEL 7

Exécution du build

A chaque commit du projet, il y aura un build de réaliser :

Buildgitlab1.png

On voit en détails ce qu'il se passe au moment du build ce qui permet de débugger rapidement les éventuels soucis :

Buildgitlab2.png

Il faut prendre en compte que lors du build sur un serveur runner, les actions sont lancées avec l'utilisateur gitlab-runner sous le répertoire /home/gitlab-runner. On voit d'ailleurs qu'il y a deux répertoires :

[root@serverAgent2 gitlab-runner]# ls
builds  lost+found  rpmbuild

Le répertoire build contient les répertoires des fichiers sources :

[root@serverAgent2 gitlab-runner]# ls builds/bbf3ec03/0/unix_team/domain-epops
domain-epops.spec  create_fs_epops.sh  epops-conf

Le répertoire rpmbuild contient les répertoire permettant de construire le RPM :

[root@serverAgent2 gitlab-runner]# ls rpmbuild/
BUILD  BUILDROOT  RPMS  SPECS  SRPMS

Gestion des archives

Sauvegardes

L'utilitaire gitlab-rake permet de réaliser des backups de la base de données PostgreSQL, des repositories, des builds, des fichiers uploadés ... :

[root@server1 backups]# gitlab-rake gitlab:backup:create
Dumping database ...
Dumping PostgreSQL database gitlabhq_production ... [DONE]
done
Dumping repositories ...
 * unix_team/domain-epops ... [DONE]
 * unix_team/domain-epops.wiki ...  [SKIPPED]
 * unix_team/etl_hobbit ... [DONE]
 * unix_team/etl_hobbit.wiki ...  [SKIPPED]
 * unix_team/dailycheck ... [DONE]
 * unix_team/dailycheck.wiki ...  [SKIPPED]
 *  unix_team/clist ... [DONE]
 * unix_team/clist.wiki ...  [SKIPPED]
 * unix_team/easyinstall ... [DONE]
 * unix_team/easyinstall.wiki ...  [SKIPPED]
 * unix_team/scripts_nemo ... [DONE]
 * unix_team/scripts_nemo.wiki ...  [SKIPPED]
 ..............
done
Dumping uploads ...
done
Dumping builds ...
done
Dumping artifacts ...
done
Dumping pages ...
done
Dumping lfs objects ...
done
Dumping container registry images ...
[DISABLED]
Creating backup archive: 1497434843_2017_06_14_9.2.2_gitlab_backup.tar ... done
Uploading backup archive to remote storage  ... skipped
Deleting tmp directories ... done
done
done
done
done
done
done
done
Deleting old backups ... skipping

L'archive générée est stocké dans le répertoire /var/opt/gitlab/backups :

[root@server1 backups]# du -ms 1497434843_2017_06_14_9.2.2_gitlab_backup.tar
175     1497434843_2017_06_14_9.2.2_gitlab_backup.tar

Voici l'arborescence de la sauvegarde :

[root@server1 backups]# tar tvf 1497434843_2017_06_14_9.2.2_gitlab_backup.tar
drwx------ git/git           0 2017-06-14 12:07 repositories/
drwx------ git/git           0 2017-06-14 12:07 repositories/unix_team/
-rw------- git/git         585 2017-06-14 12:07 repositories/unix_team/nouveau_projet.bundle
-rw------- git/git      382431 2017-06-14 12:07 repositories/unix_team/puppetlabs.bundle
-rw------- git/git      269851 2017-06-14 12:07 repositories/unix_team/domain-icinga-addons.bundle
-rw------- git/git       12576 2017-06-14 12:07 repositories/unix_team/domain-epops.bundle
-rw------- git/git       13891 2017-06-14 12:07 repositories/unix_team/audit_sudoers.bundle
drwx------ git/git           0 2017-06-14 12:07 db/
-rw------- git/git      111190 2017-06-14 12:07 db/database.sql.gz
-rw------- git/git      250049 2017-06-14 12:07 uploads.tar.gz
-rw------- git/git       94269 2017-06-14 12:07 builds.tar.gz
-rw------- git/git         151 2017-06-14 12:07 artifacts.tar.gz
-rw------- git/git         155 2017-06-14 12:07 pages.tar.gz
-rw------- git/git         151 2017-06-14 12:07 lfs.tar.gz
-rw------- git/git         154 2017-06-14 12:07 backup_information.yml

Une sauvegarde est réalisé tous les jours à 1h du matin suivi d'une purge permettant de garder une semaine d'archives :

[root@server1 backups]# crontab -l
# Sauvegarde GITLAB UNIX
0 1 * * * /opt/gitlab/bin/gitlab-rake gitlab:backup:create > /var/opt/gitlab/backups/backup-$(date +\%d-\%m-\%y).log 2>&1
# Purge de anciennes sauvegardes
30 1 * * * /bin/find /var/opt/gitlab/backups -mtime +7 -exec rm -f {} \;

Restaurations

Il suffit de commencer par arrêter les composants unicorn et sidekiq qui sont connectés avec la base de données. Les autres processus peuvent rester démarrés :

gitlab-ctl stop unicorn
gitlab-ctl stop sidekiq

On vérifie que les deux processus sont arrêtés :

gitlab-ctl status

Ensuite, on restaure l'archive en spécifiant son timestamp qui se situe dans son nom. Il faut que cette dernière soit positionnée dans le répertoire de backup. Ceci va écraser les données de la base :

gitlab-rake gitlab:backup:restore BACKUP=1470126298

On redémarre Gitlab entièrement et on lance un check :

gitlab-ctl start
gitlab-rake gitlab:check SANITIZE=true

PS : il faut que la version installée et celle restaurée soient identiques sinon la restauration va échouer !

Migration de projet

Si l'on souhaite passer d'un serveur Gitlab à un autre et que ces derniers n'ont pas la même version, on peut utiliser cette méthode :

  • Cloner le dépôt depuis le serveur source.
  • Créer une image du projet : git bundle create PROJECT.bundle --all --branches --tags.
  • Créer le projet vide sur l'interface web du serveur cible.
  • Cloner le dépôt vide du serveur cible.
  • Restaurer l'image (on se place dans le dépôt vide que l'on vient de cloner au préalable) : git pull PROJECT.bundle.
  • Pousser les données vers le dépôt du serveur cible.
  • Archiver le projet du serveur source au cas de problème (impossible d'effectuer des commits car en lecture seule).