Git, Gitlab & Gitlab CI

La CI avec Gitlab

Introduction

$ Whois

Contenu

    Kevin Gautreau
    Développeur Back freelance
    Clermont-Ferrand (depuis 2015)
    Spécialisé Drupal
    Twitter / Github : @Kgaut
    Mail : contact@kgaut.net

 

Image
Kevin Gautreau

 

Et vous ?

Contenu
  • Profil
  • Expérience
  • Où en êtes vous niveau gitlab / CI ?
  • Attentes sur la formation

Cette présentation

Contenu

Accessible sur : https://slides.kgaut.net/node/16

But : construire toute une ci pour un projet

Objectifs de la formation

Contenu
  • Revoir les fondamentaux de GIT
  • Utiliser un serveur Gitlab
  • Comprendre le principe de la CI
  • Appliquer la CI avec Gitlab
  • Expérimenter les possibilités

Autre chose ?

À noter

Contenu
  • Je ne suis pas un expert mais un expérimentateur
  • Domaine en constante évolution
  • => Avoir les clés pour être autonome pour la suite

Git

« Versionner »

Contenu

Mettre sous gestionnaire de version

  • Effectuer des "sauvegarde" de son code à un instant T.
  • Revenir à une version précédente
  • Travailler à plusieurs, fusionner des développements

Wut ?

Contenu

Système de gestion de version à la mode.

Développé à l'origine par Linus Torvalds.

Démocratisé par Github.

Principe simple

Contenu

Création d'un dépo : git init

** Code **

Création d'une version :



git commit

** Code **

Création d'une nouvelle version :



git commit

...

Les « remotes »

Contenu

Copies locale ou distante du dépot, permettant de travailler à plusieurs



git remote add origin chemin/vers/remote

récupération d'un dépot distant



git clone chemin/vers/remote

Remotes possible : github, gitlab, dossier local / réseau...

Le fichier .gitignore

Contenu

Fichier à placer à la racine du dépot ou dans un sous-dossier



Contenant les dossiers / fichiers à ne pas versionner (images, fichiers de config, css...)

db/*.sql
web/uploads
web/sites/default/settings.php

Les workflows Git

Contenu

Comment organiser son dépot pour gérer les cas ?

  • Déploiement sur prod ?
  • Développement de hotfix ?
  • Développement de fonctionnalités plus longues ?
  • Validation en preprod ?

Git flow à la rescousse

Contenu
  • Branche master => Prod (via des tags)
  • develop : contenant tous les derniers développements
  • Pour chaque hotfix / fonctionnalité, une branche spécifique (basée sur master)
    • ex : hotfix/541-email-not-sent
    • ex : feature/542-new-homepage

N'est pas une vérité absolue, à adapter en fonction du fonctionnement de l'entreprise et de l’environnement du projet

https://www.atlassian.com/fr/git/tutorials/comparing-workflows/gitflow-…

Gitlab ❤️

Gitlab ?

Contenu

Solution SASS

  • Pas de serveur à configurer
  • Moins la main sur la configuration

Solution auto-hébergée

  • Configuration au "petits" oignons
  • Possibilité d'héberger où l'on veut
  • Backups facilités

 

Concurents SAAS

Contenu

Concurents SAAS

Contenu
  • Github
  • Bitbucket
  • ...

Repo publics gratuits, privés payants.

Concurents auto-hébergés

Contenu

Avantages de gitlab auto-hébergé

Contenu
  • Solution robuste et éprouvée
  • Beaucoup d'intégrations
  • Intégration de Mattermost
  • CI incluse

One more thing

Contenu
Image
Gitlab tweet blackout

L'intégration continue

Principe

Contenu

Automatiser la construction d'une "build"

  • Génération des assets
  • « Compilation »
  • Tests unitaires
  • Déploiement
  • ...

Solutions SAAS

Contenu

Github + Travis CI (https://travis-ci.org/)

Plus récement : Github Actions

Les hooks git

Contenu
  • Script exécuté lors d'une action
  • Local (pre commit) distant (post receive)
  • scripts dans le .git/hooks d'un dépot

Attention : Gitlab utilise déjà ces hooks pour fonctionner, ne pas les modifier ! utiliser les "custom_hooks"

https://kgaut.net/blog/2015/les-hooks-git-et-comment-automatiser-son-workflow.html

Gitlab CI

Contenu
  • Solution type "travis"
  • Intégrable facilement à côté d'un gitlab
  • Principe de "Runner" qui vont être attaché à un serveur gitlab
  • Gitlab <=> n Runner Gitlab (même sur des serveurs différents)
Image
Gitlab CI

Installation Gitlab CI

Installation de gitlab runner

Les runners

Contenu

Admin / Overview / Runners

Image
Gitlab runners

Les types de runners

Contenu
  • Shell : Lancer des commandes shell en direct (connexion ssh à distance...)
  • Docker : Instancie une image docker pour exécuter des commandes à l'intérieur (génération d'assets front, tests unitaires...)

Enregistrer un runner

Contenu

sudo gitlab-runner register --url https://gitlab.kgaut.net/ --registration-token $REGISTRATION_TOKEN

 

Taguer les runner...

Contenu

En fonction du type :

Image
Tag gitlab Runner

 Pensez à cocher « Indicates whether this runner can pick jobs without tags » dans les paramètres du runner pour les runners secondaires (docker ?)

 

Runner dédiés / partagés

Contenu
Image
Runner partagés / dédiés

Prise en main de gitlab CI

Premiers tests

Contenu
  1. Création d'un dépot
  2. Création du fichier .gitlab-ci.yml
  3. Tests

.gitlab-ci.yml

Contenu

Défini le comportement.


stages:
  - deploy

prod-deployment:
  stage: deploy
  script:
    -  ssh slides_kgaut_net@slides.kgaut.net 'bash -s' < scripts/prod-deployment.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "master"

Proof of concept : un job

Contenu


mail:
  script: "echo 'coucou' | mail -s 'test email' contact@kgaut.net"

Les variables

Contenu
variables:
  DESTINATAIRE: 'contact@kgaut.net'

mail:
  script: "echo 'coucou' | mail -s 'test email' $DESTINATAIRE"

Variables sur gitlab

Contenu

Settings > CI / CD / Variables

Image

Exemples d'utilisation de variables

Contenu

variables:
  PROJECT_NAME: 'MonSite.com'
  PROJECT_SHORTNAME: 'monsite'
  DB_PATH: 'db'
  DRUSH_EXEC: 'drush'
  DIFFS_PATH: 'files/diffs'

  PREPROD_URL: 'preprod.monsite.com'
  PREPROD_DRUSH_ALIAS: '--uri="preprod.monsite.com"'
  PREPROD_DRUSH_ROOT_ARG: '--root="/var/www/html/preprod.monsite.com" --uri="preprod.monsite.com"'
  PREPROD_EMAIL_ALERT: 'contact+montsite-preprod@kgaut.net'
  PREPROD_USER: 'admin'
  PREPROD_IP: 'preprod.monsite.com'
  PREPROD_SSH: 'ssh admin@preprod.monsite.com'
  PREPROD_PROJECT_ROOT: '/var/www/html/preprod.monsite.com'

Définition de plusieurs «jobs»

Contenu


mail:
  script: "echo 'coucou' | mail -s 'test email' contact@kgaut.net"
mail2:
  script: "echo 'coucou' | mail -s 'test email 2' contact+test@kgaut.net"

Ajout condition de branche

Contenu


mail:
  script: "echo 'push sur master' | mail -s 'push sur master' contact@kgaut.net"
  rules:
    - if: $CI_COMMIT_BRANCH == "master"
mail2:
  script: "echo 'coucou' | mail -s 'push une branche' contact+test@kgaut.net"

Les «stages»

Contenu

Groupement de jobs qui seront exécutés en parallèle.

Les noms des stages sont libres

Si plusieurs étapes, alors l'étape N+1 ne sera lancée que si l'étape N s'est correctement exécutée.

NB : la clé allow_failure : true permet de ne pas impacter la suite au niveau d'un job

Exemple «stages»

Contenu


stages:
  - etape_1
  - etape_2

mail_admin:
  script: "echo 'push sur master' | mail -s 'push sur master' contact@kgaut.net"
  stage: etape_1
  rules:
    - if: $CI_COMMIT_BRANCH == "master"
fail:
  script: "lorem"
  stage: etape_1

mail_2:
  script: "echo 'coucou' | mail -s 'push une branche' contact+test@kgaut.net"
  stage: etape_2

Pipeline

Contenu

Ensemble du process de déploiement

Image
Pipeline Gitlab

Les environements

Contenu
  • Permet de définir des variables spécifiques à un environnement (IP, branche...)
  • Autant d'environnements que nécessaires et noms libres

Connexion distante via Gitlab CI (image Shell)

Principe

Contenu

Au push :

  1. Le runner va se connecter à notre serveur distant
  2. Il va exécuter une ou plusieurs commande commande
  3. Nous en retournera le résultat

Autoriser le runner à se connecter

Contenu

Genérer une clé SSH :


sudo -u gitlab-runner -H  ssh-keygen -t ed25519 -C "gitlab-runner@gitlab.moi.com"

Ajouter la clé au trousseau du serveur distant


sudo -u gitlab-runner -H ssh-copy-id user@serveur

Note : Il peut être pratique d'ajouter cette clé dans /etc/skel/.ssh/authorized_keys.

Tester


sudo -u gitlab-runner -H ssh user@serveur

Autoriser notre serveur à se connecter à gitlab

Contenu

Il faut aussi que notre serveur puisse se connecter au dépôt git

Génération d'une clée :


ssh-keygen -t ed25519 -C "prod@monsite.com"

cat ~/.ssh/ed25519.pub

Ajout de cette clé aux clés de déploiements du dépôt.

Script de déploiement

Contenu

Script bash qui sera executé sur le serveur distant :


ssh user@serveur 'bash -s' < scripts/prod-deployment.sh
  • Le script sera versionné.
  • Langage "bash"

Exemple script de déploiement D8

Contenu

#!/bin/bash
set -e
cd ~/httpdocs

git pull

composer install --no-dev

~/vendor/bin/drush deploy

Connexion distante via Gitlab CI (runner docker)

Principe

Contenu

Au push :

  1. Le runner va instancier une image docker en container
  2. Depuis ce container une connexion ssh sera faite à notre serveur distant
  3. Il va exécuter une ou plusieurs commande commande
  4. Nous en retournera le résultat

Création d'une clé pour se connecter au serveur web

Contenu

ssh-keygen -t ed25519 -C "monsite.prod"

cat /home/user/.ssh/id_ed25519.pub

On passe cette clé en variable gitlab : SSH_PRIVATE_KEY

Définition des variables

Contenu
Image
Variable docker

Ajout d'une clé « before_script » à chaque job

Contenu

backup prod:
  before_script:
    - apt-get update -qq
    - apt-get install -qq git
    # Setup SSH deploy keys
    - 'which ssh-agent || ( apt-get install -qq openssh-client )'
    - eval $(ssh-agent -s)
    - ssh-add <(echo "$SSH_PRIVATE_KEY")
    - mkdir -p ~/.ssh
    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  stage: backup
  script:
    - $SSH_CHAIN "$PROJECT_ROOT/$DRUSH_EXEC --root=\"$PROJECT_ROOT\" sql-dump --gzip > \"$PROJECT_ROOT/files/dumps/`date +%Y-%m-%d_%H-%M-%S`-$PROJECT_URL-PRE-$CI_COMMIT_TAG.sql.gz\""
  rules:
    - if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'

 

Livre de recettes

Spécifier le type de runner pour un job

Contenu

Via clée tags


prod assets generation:
  stage : Deploy
  tags:
    - docker
  image: kgaut/node:14
  script:
    - cd ./web/themes/custom/enise
    - npm ci
    - gulp build --production
    - ls -al ./dist
  extends:
    - .prod-assets
  artifacts:
    paths:
      - ./web/themes/custom/enise/dist
    expire_in: 20 minutes

 

Utiliser une image docker spécifique

Contenu

prod assets generation:
  tags:
    - docker
  image: kgaut/node:14
  script:
    - cd ./web/themes/custom/enise
    - npm ci
    - gulp build --production
    - ls -al ./dist
  stage : Deploy
  rules:
    - if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
  environment:
    name: prod

 

Déclencher une tache sur certains type de fichiers

Contenu

generate_assets:
  stage: postdeploy
  image: node:10.15.2
  script:
    - node -v
    - npm install -g gulp
    - npm install --silent
    - gulp build
  rules:
    - if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE != "schedule"
      changes:
        - web/themes/mon_theme/src/assets/css/**/*.scss
        - web/themes/mon_theme/src/assets/js/**/*.js


https://kgaut.net/snippets/2019/gitlab-ci-ne-lancer-une-tache-que-lorsq…

Déployer sur un tag

Contenu

prod deploy:
  script:
    - $SSH_CHAIN 'bash -s' < ./files/gitlab/scripts/deploy.sh $PROJECT_ROOT $DRUSH_ALIAS $DRUSH_EXEC $CI_ENVIRONMENT_NAME $CI_COMMIT_TAG $CI_COMMIT_SHORT_SHA
  stage: Deploy
  rules:
    - if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
  environment:
    name: prod

#!/bin/bash
set -e

PROJECT_ROOT="$1"
PROJECT_DRUSH_ALIAS="$2"
DRUSH_EXEC="$3"
CI_ENVIRONMENT_NAME="$4"
CI_COMMIT_SHORT_SHA="$6"

cd "$PROJECT_ROOT"

if [ $CI_ENVIRONMENT_NAME = "prod" ]
then
  TAG="$5"
  git fetch --tags
  git checkout "$TAG"
  $DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" sset environment_indicator.current_release "$TAG"
else
  BRANCH="$5"
  git pull origin "$BRANCH"
  $DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" sset environment_indicator.current_release "preprod-$CI_COMMIT_SHORT_SHA"
fi

composer install --no-dev
$DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" cr
$DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" updb  --no-post-updates -y
$DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" cim -y
$DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" updb -y
$DRUSH_EXEC "$PROJECT_DRUSH_ALIAS" cr

 


 

Créer des artefacts

Contenu

generate_assets:
  stage: deploy
  image: node:10.15.2
  script:
    - node -v
    - npm install -g gulp
    - npm install --silent
    - gulp build
  rules:
    - if: $CI_COMMIT_BRANCH == "master" && $CI_PIPELINE_SOURCE != "schedule"
      changes:
        - web/themes/mon_theme/src/assets/css/**/*.scss
        - web/themes/mon_theme/src/assets/js/**/*.js
  artifacts:
    paths:
      - ./web/themes/custom/enise/dist
    expire_in: 20 minutes

 


prod assets deploy:
  stage: postdeploy
  script:
    - ls -alh ./web/themes/custom/enise/dist
    - rsync -avhzi --delete --stats ./web/themes/custom/enise/dist -e ssh $SSH_USER@$SSH_HOST:$PROJECT_ROOT/web/themes/custom/enise
    - $SSH_CHAIN "$DRUSH_EXEC $DRUSH_ALIAS cr"
  extends:
  rules:
    - if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
  environment:
    name: prod

 

 

Factoriser via extends

Contenu

.prod:
  rules:
    - if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
  environment:
    name: prod

prod deploy:
  script:
    - $SSH_CHAIN 'bash -s' < ./files/gitlab/scripts/deploy.sh $PROJECT_ROOT $DRUSH_ALIAS $DRUSH_EXEC $CI_ENVIRONMENT_NAME $CI_COMMIT_TAG $CI_COMMIT_SHORT_SHA
  stage: Deploy
  extends:
    - .prod

On peut extends plusieurs règles, et surcharger même des clés dans la définition initiale

Eclater son .gitlab-ci.yml

Contenu


include:
  - local: '/files/gitlab/extends.yml'
  - local: '/files/gitlab/jobs-backup.yml'
  - local: '/files/gitlab/jobs-diff.yml'
  - local: '/files/gitlab/jobs-deploy.yml'
  - local: '/files/gitlab/jobs-postdeploy.yml'
  - local: '/files/gitlab/jobs-scheduled.yml'

 

Sauvegarder son gitlab

Contenu

Crontab :


05      0       *       *       *       gitlab-backup create BACKUP=dump GZIP_RSYNCABLE=yes

Dossiers à sauvegarder :


/etc/gitlab #fichiers avec les clés de chiffrements gitlab-secrets.json + config (gitlab.rb)

/var/opt/gitlab # dossier backups contenant les backups générés

/opt/gitlab # Ceinture et bretelle

CF : https://docs.gitlab.com/ee/raketasks/backup_restore.html