Déploiement continu en FTP avec Gitlab CI

Automate de conserve dans une usine

Bonjour !

Aujourd’hui, je vous présente une méthode de déploiement automatique avec Gitlab. Elle vient remplacer un fastidieux déploiement FTP classique.
L’idée est de déployer automatiquement à chaque commit.

Nous verrons même comment déployer sur des sites différents suivant les branches. Dans notre exemple, en Prod pour la branche main et en Préprod pour tout le reste.

Le dépôt du code complet est disponible ici.

Pré-requis

Pour cela, il faut disposer:

  • D’un dépôt sur Gitlab.com
  • D’un serveur déjà configuré avec un nom de domaine
  • D’un accès FTP à ce serveur
  • D’un accès SSH à ce serveur

Dans mon exemple, j’ai utilisé une clé SSH de type Ed25519. Si vous ne savez pas comment générer une clé SSH, je vous renvoie vers cet article.

Configuration des variables d’environnement dans Gitlab

Rendons-nous dans notre dépôt Gitlab, puis dans Settings > CI/CD. Là, ouvrons, le bouton Expand du paragraphe Variables. Sachez que ces variables sont limitées à votre dépôt courant et non votre compte.

Voici les variables à créer, qui seront utilisées dans notre fichier de déploiement :

KeyValueTypeProtectedMaskedExpanded
DEPLOY_HOSTurl de l’hôte FTPVariable
DEPLOY_PATHchemin interne vers votre dossier public Variable
DEPLOY_USERnom d’utilisateur FTPVariable
SSH_PRIVATE_KEYcontenu du fichier de clé privé ed 25519. Attention à laisser une ligne vide à la fin de la valeurVariable
Variables à créer dans votre environnement Gitlab


Notes:

  • Les variables ne sont pas protégées pour être accessibles au job CI
  • Elles sont pour le moment définies dans l’environnement par défaut All
  • Le type de contenu de la clé SSH ne permet pas de rendre masqué la variable SSH_PRIVATE_KEY.

Fichier de déploiement

Nous créons ensuite à la racine, un fichier nommé .gitlab-ci.yml et nous le remplissons ainsi :

# fichier .gitlab-ci.yml

deploiement:
  stage: deploy
  before_script:
    # installation de l'agent SSH (requis par Docker)
    - 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client wget gnupg -y )'
    - wget -qO- https://get.docker.com/gpg | apt-key add
    # lancement de l'agent ssh dans l'environnement de build
    - eval $(ssh-agent -s)
    # ajout de notre clé privée qui existe sur le serveur distant
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    # création du répertoire SSH et attributions des droits adéquats
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - touch ~/.ssh/config
    # création du fichier des hôtes en interrogeant directement le FTP
    - touch ~/.ssh/known_hosts
    - ssh-keyscan -t ed25519 $DEPLOY_HOST >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - apt-get update -qq && apt-get install -y -qq rsync
    - rsync -avzq ./public/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH"

Nous installons un conteneur basique avec l’agent SSH. Ensuite, on copie la clé SSH et on récupère les hôtes autorisés avec ssh-keyscan. Enfin, on utilise rsync pour faire la copie du contenu du répertoire public.

Répertoire public

Créons simplement un dossier public à la racine incluant un fichier index.php. Il affichera l’url du serveur et la date du dernier déploiement.

<!--fichier public/index.php-->

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <meta name="author" content="Kevin Vincendeau - Zionlabs">
    <title>Gitlab auto deploy</title>
</head>
<body>
    <h1>Gitlab auto deploy</h1>
    <hr/>
    <h2>Server:</h2>
    <p>
        <?= $_SERVER['SERVER_NAME'] ?>
    </p>
    <h2>Last update:</h2>
    <p>
        <?= date('d F Y H:i:s', filemtime(__FILE__));  ?>
    </p>
</body>
</html>

Lancement 🚀

Voilà, il nous reste à lancer un commit pour vérifier si ça fonctionne. Après, nous pouvons nous rendre dans la section Build > Jobs de Gitlab pour vérifier.

Vue dans Build > Jobs, avec le job qui a réussi.
Vue dans Build > Jobs, avec le job qui a réussi.

Ici, le job est passé. On peut éventuellement le relancer manuellement avec l’icône de droite (en cas de modification des variables par exemple).

Peaufinage

Nous allons rajouter une distinction pour les branches, avec un déploiement vers deux environnements différents. Dans mon cas, j’utilise le même serveur mais avec deux dossiers, chacun géré par un sous-domaine.

Sous-domaines preprod et prod, chacun dans un dossier
Les deux sous-domaines sont stockés dans le même dossier du serveur

On va utiliser les environnements des variables. Les environnements Gitlab se gèrent dans Operate > Environments. Seul le nom de l’environnement est obligatoire. Créons donc deux environnements nommés Prod et Preprod.

Ensuite, on modifie la précédente variable DEPLOY_PATH en lui attribuant un environnement. Puis, on créé une seconde variable nommée de la même manière, avec l’autre environnement et bien sûr une valeur différente.

Variables Gitlab avec les deux environnement Preprod / Prod

Ensuite, on reporte ça dans le fichier .gitlab-ci.yml:

# fichier .gitlab-ci.yml

# toutes les branches sont déployées en préprod, sauf la principale
deploiement_preprod:
extends: .deploiement
environment: Preprod
except: [ main ]

# seule la branche main est déployée en prod
deploiement_prod:
extends: .deploiement
environment: Prod
only: [ main ]

include:
- local: '/.gitlab/deploiement.yml'

Pour éviter la duplication du bloc de déploiement précédent, on le mutualise dans un fichier dédié deploiement.yml placé dans un dossier .gitlab :

# .gitlab/deploiement.yml

.deploiement:
  stage: deploy
  before_script:
    # installation de l'agent SSH (requis par Docker)
    - 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client wget gnupg -y )'
    - wget -qO- https://get.docker.com/gpg | apt-key add
    # lancement de l'agent ssh dans l'environnement de build
    - eval $(ssh-agent -s)
    # ajout de notre clé privée qui existe sur le serveur distant
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    # création du répertoire SSH et attributions des droits adéquats
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - touch ~/.ssh/config
    # création du fichier des hôtes en interrogeant directement le FTP
    - touch ~/.ssh/known_hosts
    - ssh-keyscan -t ed25519 $DEPLOY_HOST >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - apt-get update -qq && apt-get install -y -qq rsync
    - rsync -avzq ./public/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH"

Vous l’avez compris, c’est l’environnement des variables qui déterminera la bonne valeur.

Désormais, les commits des branches seront automatiquement déployés en preprod et à la validation des Merge Requests vers main, ce sera la prod qui sera mise à jour.

🙏 Quel gain de temps pour la suite !

Dépannage

La mise en place de ce déploiement peut-être problématique (j’ai trimé comme un galérien la première fois 😅). Respectez bien les indications sur les variables et relancez le job pour tester chaque modification.

Vous pouvez aussi tester certaines commandes via un terminal (par exemple le ssh-keyscan) pour vérifier que votre serveur répond correctement.

Le journal du job n’est pas toujours explicite dans la remontée d’erreur. Pour nous aider, Gitlab fournit un validateur (linter) pour le fichier .gitlab-ci.yml ; il suffit de se rendre dans Build > Pipelines, puis de cliquer sur le bouton CI lint en haut à droite. Là, vous copierez le contenu de votre fichier pour le valider.

Pensez aussi à la documentation Gitlab en ligne.

Bon courage et bon déploiement.

Kevin
Développeur web fullstack, j'ai souvent la tête dans le cloud. Après une dizaine d'année d'expérience en entreprise, j'ai choisi la voie de l'indépendance en créant Zion Labs.