Déployer automatiquement un site Hugo avec Drone CI

Publié dans développement
le

Automatisons le déploiement d'un site statique réalisé avec Hugo grâce à un pipeline Drone ultra simple !

Vous le savez probablement, ce petit site est aujourd'hui propulsé par le générateur de sites statiques Hugo qui fonctionne à merveille.

D'un autre côté, sur mon petit serveur dédié, j'héberge aussi une instance Gogs pour stocker les sources de mes projets confidentiels (ainsi que ce site) et aussi une instance de Drone me permettant d'avoir des pipelines de CI à la manière des actions Github.

Grâce à cette instance de Drone, sur n'importe quel événement Gogs (Push, Pull, etc...), je peux déclencher des actions à exécuter comme vérifier que les tests sont ok par exemple.

Jusqu'à l'heure, quand j'avais envie de publier un article ou de modifier mon site, je construisais mon site en local pour ensuite le déployer via FTP sur mon serveur (bouh!) ... pas très efficace donc !

Mais aujourd'hui, j'ai enfin pris le temps d'automatiser tout ça ! Désormais, un simple push sur la branche master construit et déploie ce site ! Est-ce de la magie 🧙‍♂️ ? Que nenni !

Installation de Drone

Drone est donc un serveur vous permettant d'exécuter des pipelines écrit en Go qui est ultra léger (mais vraiment) et dans lequel chaque étape de pipeline sera exécutée dans un conteneur Docker (bon en vrai, il y'a d'autres modes qui sont apparus récemment...) ce qui vous évitera de poluer votre serveur avec des installations inutiles entre autre.

Cet article s'appuie sur la version de Drone v1.9.0 (qui est la plus récente à l'heure où j'écris ces lignes).

Pour ma part, sur mon dédié, tout est déployé via docker, pour Drone, j'ai donc un docker-compose.yml de ce type :

version: "3"
services:
  drone-server:
    image: drone/drone:1
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock" # nécessaire pour que le serveur exploite le docker de l'hôte
      - "./drone-data:/data" # ou un volume, comme vous préférez...
    networks:
      - default
    environment:
      - "DRONE_GIT_ALWAYS_AUTH=false"
      - "DRONE_GOGS_SERVER=https://votre-url-vers-gogs.com" # à modifier
      - "DRONE_RUNNER_CAPACITY=2"
      - "DRONE_SERVER_HOST=votre-hote-ci.com" # à modifier
      - "DRONE_SERVER_PROTO=https"
      - "DRONE_RPC_SECRET=un-secret-partage-entre-le-serveur-et-le-runner" # à modifier
      - "DRONE_TLS_AUTOCERT=false"
  drone-runner:
    image: drone/drone-runner-docker:1
    restart: unless-stopped
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    networks:
      - default
    environment:
      - "DRONE_RPC_HOST=votre-hote-ci.com" # à modifier
      - "DRONE_RPC_PROTO=https"
      - "DRONE_RPC_SECRET=un-secret-partage-entre-le-serveur-et-le-runner" # à modifier
networks:
  default:

2 services sont déclarés :

  • drone/server : serveur drone avec l'interface web et l'orchestrateur,
  • drone/drone-runner-docker: runner qui exécutera nos pipelines Docker, sans runner, vos jobs ne se lanceront pas.

Ici, le serveur Drone est configuré pour utiliser l'authentification de mon Gogs. Si vous utilisez un autre système de contrôle de code source, il vous suffit de suivre le guide d'installation correspondant.

Pour lancer le tout, rien de plus simple :

$ docker-compose up -d

Voilà, vous avez normalement un serveur Drone connecté à votre Gogs prêt à recevoir des jobs !

Configurer le pipeline

Maintenant que Drone est en route, direction le code source de votre site Hugo. À la racine du dépôt, créer un fichier .drone.yml. Ce fichier sera lu par Drone et détermine les étapes à exécuter.

kind: pipeline
type: docker
name: default

steps:
  - name: build
    image: plugins/hugo # image docker qui récupère la version de Hugo souhaitée et build le site dans public/
    settings:
      hugo_version: 0.74.3
      validate: true # valide la configuration hugo avant de build
      extended: true # version extended de Hugo avec le PostCSS & cie

  - name: deploy
    image: drillster/drone-rsync # image docker permettant d'utiliser rsync pour déployer des fichiers sur un ou plusieurs hôtes
    settings:
      hosts: ["hote.de.deploiement.du.site"]
      target: /var/www/repertoire-de-votre-site
      source: public/*
      user:
        from_secret: rsync_user # ici, l'utilisateur sera tiré d'un secret Drone
      key:
        from_secret: rsync_key # idem pour la clé privée permettant la connexion à l'hôte
    when:
      branch: master # uniquement sur la branche master !

Le fichier devrait être assez parlant, toute la documentation concernant les pipelines se trouve ici si vous voulez en savoir plus. On utilise des images déjà existantes mais on pourrait aussi définir les nôtres au besoin.

Vous voyez qu'on fait référence à des secrets avec from_secret. En effet, on évitera de laisser traîner des mots de passe en clair dans le code source et on utilisera le système fournit par Drone pour gérer ce genre d'informations confidentielles (on verra plus loin comment).

Configurer l'hôte cible de notre déploiement

Étant donné que nous allons déployer de Drone vers notre serveur cible via rsync les fichiers statiques, il va nous falloir un accès, c'est à dire un utilisateur ainsi qu'une clé pour nous connecter de manière sécurisée.

Création de l'utilisateur

Sur votre serveur cible, commençons par créer un utilisateur droneci et accordons lui les droits sur le répertoire qui nous servira à déposer les fichiers puis connectons nous avec ce compte :

# adduser droneci --disabled-password
# chown -R droneci /var/www/repertoire-de-votre-site
# su - droneci

Toutes les étapes qui suivent se font en tant qu'utilisateur droneci.

Génération d'une paire de clés

Avec le compte droneci, on génére une paire de clés grâce à ssh-keygen sans passphrase :

$ ssh-keygen

Dans la suite, je pars du principe que vous avez généré la clé à l'endroit par défaut, donc ~/.ssh/id_rsa et je vous laisse remplacer les chemins si nécessaire.

Ajout de la clé publique aux clés autorisées

Nous avons donc notre clé publique et notre clé privée. Afin de permettre la connexion via la clé privée depuis notre Drone vers ce serveur, il nous faut ajouter la clé publique dans la liste des clés permettant la connexion en tant que droneci :

$ cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys

Garder alors en lieu sûr la clé privée, nous en aurons besoin dans la configuration de Drone.

Activer et configurer la CI pour notre dépôt

Dernière étape, il nous faut activer et configurer notre dépôt au sein de Drone pour que le pipeline se déclenche. Direction l'url de votre serveur Drone pour accéder à l'interface web et connecter vous.

Vous devriez arriver sur une page listant les différents dépôts de votre gestionnaire de code source. Cliquer alors sur Activate en face du dépôt correspondant à votre site Hugo contenant le fichier .drone.yml.

Liste des dépôts au sein de Drone

Ensuite, ajouter les secrets rsync_user et rsync_key dans l'onglet Settings avec, dans notre exemple :

  • rsync_user: droneci, l'utilisateur créé précédemment avec les droits pour écrire dans le répertoire de destination,
  • rsync_key: la clé privée générée plus tôt permettant d'authentifier l'utilisateur auprès du serveur de destination.
Configuration des secrets Drone

Il ne vous reste alors plus qu'à tester le build en poussant des modifications sur votre branche master afin de valider que tout fonctionne !

Déploiement réussi dans Drone

Nous voilà arrivé au bout de ce mini-tuto, j'espère qu'il vous aura été utile. Si vous avez des remarques ou des points de blocage, n'hésitez pas à m'en faire part sur les réseaux sociaux 😉