J'aurais pu commencer cet article avec une blague du type Gatsby, Ghost, Docker et Traefik sont sur un bateau. Gatsby tombe à l'eau... Bon vous aurez rapidement compris que je ne travaille pas dans le monde du comique et on comprend pourquoi...

Gatsby + Ghost

L'intérêt du projet est relativement simple :

  • Profitez du CMS que j'utilise déjà actuellement : Récuper mon contenu, la personnalisation du site.
  • Les avantages d'un site JAMstack : Rapidité, sécurité, mais aussi l'apport de technologie comme PWA.
  • Utilisation de Netlify : Je n'aborderai pas ce point aujourd'hui mais sachez que ce point est clairement dans mes objectifs finaux.

Gatsby et Ghost se sont associés afin de fournir une solution "clé en main". Ghost servant dans ce cas de CMS headless ( sans front-end de publication ). Et Gatsby lui justement servira de front-end à votre blog.

Tout ceci est rendu possible par l'API fourni par Ghost, cette API permet de récupérer les contenus afin d'utiliser une technologie front-end  :

Ghost headless

Les CMS headless

Habituellement l'utilisation d'un générateur de site statique ( Hugo ou Gatsby par exemple ) implique l'utilisation de fichier Markdown qui sont stockés directement dans le code du site.

Personnellement j'apprécie cette solution, et je travaille déjà la plupart de mes articles en Markdown. Mais dans le cadre des CMS-headless, on utilise alors son éditeur de texte favori pour créer son contenu puis un workflow  git pour mettre à jour le site. C'est ce workflow qui va reconstruire le contenu statique du site.

L'utilisation de dépôt peut très clairement être un frein pour certains services moins compétents dans le domaine informatique, comme par exemple une équipe de rédacteur web.

Au lieu de cela, comme évoqué plus haut, nous allons utiliser Ghost et son API pour diffuser le contenu dans notre front-end Gatsby. Cela offre quelques avantages dans le cadre d'une utilisation professionnelle :

  • Les rédacteurs ont accès à un outil graphique et dédié à la publication de contenu,
  • Les développeurs peuvent utiliser la technologie qu'ils souhaitent pour le front et l'intégration du contenu,
  • La performance et la sécurité au maximum.

Ghost n'est pas la solution unique dans le monde du CMS headless, il existe d'autres solutions comme Forestry.io mais je choisis pour le moment de rester sur une solution que je connais.

Cas pratique : La construction 👷

Je vais donc mettre en place une stack avec les éléments suivants :

  • Traefik pour la gestion du flux HTTP de mon CMS Ghost et de la version statique du site,
  • Ghost pour créer mon contenu et diffuser l'API,
  • Une base MySQL pour Ghost,
  • Une image Docker custom pour la génération et diffusion du site statique.

⚠️ Il s'agit d'une stack créer au cours des derniers jours dans le cadre de mon besoin personnel. Je partage ici cette recherche à chaud, il est sûrement possible de faire mieux que la solution que je propose. J'améliorerai probablement les éléments qui suivent au cours des prochaines semaines, si cela me paraît pertinent, je mettrai à jour ce contenu avec les derniers éléments. ⚠️

Prenez le temps de lire et comprendre cet article, je vais mettre à disposition sur GitHub les fichiers de configuration :

lfache/Gatsby-ghost-traefik-starter
Contribute to lfache/Gatsby-ghost-traefik-starter development by creating an account on GitHub.

Pré-requis :

Je vais créer au préalable sur ma machine deux enregistrements DNS :

  • cms.docker.localhost
  • prod.docker.localhost

Ces deux enregistrements poitent vers 127.0.0.1

0. Récupération du starter Ghost/Gatsby sur mon poste

Pour cet article je vais structurer mes dossiers de la façon suivante :

Project folder

À la racine de mon projet :

  • .ghost.env et .mysql.env : des fichiers pour les variables d'environnements de Ghost et MySQL.
  • docker-compose.yml : Pour définir mes services.
  • Un dossier traefik pour la configuration de Traefik.
  • Un dossier Gatsby et surtout Gatsby/app avec le code source du projet.

Si ce n'est pas déjà fait, installez npm et l'utilitaire de gatsby-cli :

npm install -g gatsby-cli

Nous devons donc récupérer le code source du starter gatsby-ghost :

Dans le dossier Gatsby/app :

gatsby new my-gatsby-site https://github.com/TryGhost/gatsby-starter-ghost.git

Naviguez dans le projet nouvellement créé et utilisez soit npm install ou yarn pour installer les dépendances.

L'équipe de Ghost préconise l'utilisation de yarn et ce sera donc mon cas également.

  1. Création du conteneur Traefik

Rien d'extraordinaire dans l'utilisation de mon reverse proxy favori. Je n'ai absolument pas besoin pour le moment de HTTPS et d'aucune configuration complexe. Il s'agit juste d'une utilisation simple de routage HTTP, voici la déclaration du service :

  traefik:
    image: traefik:2.1
    container_name: traefik
    ports:
      # The HTTP port
      - "80:80"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
    networks:
      - traefik

Et le fichier traefik.yml:

entryPoints:
    http:
      address: :80

providers:
  docker:
    exposedByDefault: false

2. Ghost et MySQL

Du déjà vu ici aussi :

  ghost_cms:
    depends_on:
      - db
      - traefik
    container_name: ghost_cms
    image: ghost:3
    env_file: .ghost.env
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.cms.rule=Host(`cms.docker.localhost`)"
      - "traefik.http.services.cms.loadbalancer.server.port=2368"
    volumes:
      - www-data:/var/lib/ghost/content
    networks:
      - traefik
      - lan_cms
            
  db:
    image: mysql:5.7
    container_name: mysql
    env_file: .mysql.env
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - lan_cms

Lancez Traefik :

docker-compose up -d traefik

Et Ghost :

docker-compose up -d ghost_cms

Vous ne devriez avoir aucun soucis à accéder à votre installation de Ghost.

Créez ensuite votre compte sur l'interface d'administration et rendez-vous dans le menu Integrations.

Ajoutez une intégration custom, cela va générer une clé d'API :

add custom integration

3. Mon site statique

Dans un premier temps, il faut modifier la clé et l'URL de l'API dans le fichier gatsby/app/.ghost.json :

URL's and Key's API

Nous allons ensuite créer une image pour générer le contenu statique de Gatsby et le diffuser via une instance httpd.

Pour cela nous allons donc construire notre propre image avec un Dockerfile et du multi-stage ! Créez le fichier suivant dans le dossier gatsby :

FROM node:alpine AS build

WORKDIR /app
RUN apk add --no-cache --virtual .gyp python make g++

COPY ./app/ .

RUN yarn --frozen-lockfile --non-interactive \
    && yarn build


FROM httpd:2.4
COPY --from=build --chown=www-data:www-data /app/public /usr/local/apache2/htdocs/

Des explications ? 😃

FROM node:alpine AS build : On part de l'image alpine de Node.js, c'est un choix, l'image Debian peut très bien faire le job également. Par contre on va nommer notre build : build ( original non ? ). Car il va nous servir dans un second build imbriqué ( le multi-stage des Dockerfiles ).

WORKDIR /app : Mon répertoire de travail dans l'image sera /app.

On installe ensuite quelques paquets nécessaires.

COPY ./app/ . : Je copie le contenu de mon dossier gatsby/app sur mon image.

RUN yarn --frozen-lockfile --non-interactive && yarn build : On installe les dépendances via  yarn et on construit les données statiques. Celle-ci vont alors se trouver dans /app/public.

FROM httpd:2.4 : On prend l'image httpd sur le Docker Hub.

COPY --from=build --chown=www-data:www-data /app/public /usr/local/apache2/htdocs/ : On récupère le contenu du dossier /app/public de notre premier context pour le copier dans celui courant, et ceci dans l'emplacement ou httpd stocke les fichiers statiques.  

Pour construire l'image il est nécessaire que Traefik et Ghost soient en cours d'exécution. Si vous avez suivi les lignes de commande, ce doit être le cas. Je vais toutefois repartir d'un premier lancement dans les prochaines lignes afin de résumer au mieux tout cela : 1, 2, 3...

Cas pratique : Le lancement 🚀

  1. Premier lancement :

Le premier lancement nécessite une première construction de l'image statique. Sauf que pour construire celle-ci, l'API Ghost doit être accessible pour récupérer les données.

Nous allons donc lancer Traefik puis Ghost :

docker-compose up -d traefik

Puis :

docker-compose up -d ghost_cms

Vous pouvez maintenant construire une première fois votre image statique :

cd gatsby
docker build --network host --no-cache -t gatsby-static:latest .

Il est nécessaire de construire l'image avec l'option --network host sinon node.js n'accèdera pas à l'API lors du build. De même je vous conseille de laisser l'option --no-cache sinon la commande COPY présente dans le Dockerfile ne récupérera pas les derniers éléments.

Dans le fichier docker-compose.yml, je vais lancer mon instance avec les lignes suivantes :

  ghost_static:
    container_name: static
    depends_on: 
      - traefik
    image: gatsby-static:latest
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.static.rule=Host(`prod.docker.localhost`)"
      - "traefik.http.services.static.loadbalancer.server.port=80"
    networks:
      - traefik

Vous pouvez la lancer avec la commande suivante à la racine du projet : docker-compose up -d ghost_static

  1. Mise à jour de l'image statique

Ok. Vous avez fait votre premier lancement et vous souhaitez mettre à jour le dossier statique. Aucun soucis, il faut reconstruire l'image :

cd gatsby
docker build --network host --no-cache -t gatsby-static:latest .

Et relancer votre instance statique :

#Dans le répertoire racine du projet
docker-compose up -d ghost_static

Voila ! Votre blog Ghost est maintenant prêt pour passer en mode JAMstack !!

MAGIC

À terme notre projet devra ressembler à cela :

ghost jamstack

Alors oui nous avons encore un peu de travail, mais aucun problème. Nous allons y arriver !


Vous venez de construire une stack afin de déployer un environnement de test pour votre futur JAMstack : Ghost + Gatsby ! Encore une fois nous avons utilisé Docker et Traefik pour la création de nos conteneurs et le routage HTTP.

Cet environnement vous permettra de réaliser vos premières modifications et tests. Mais comment mettre tout ceci en production ? Quel est le meilleur service pour diffuser mon site JAMstack ? Netlify ? CloudFlare ?

Nous aurons l'occasion dans un prochain article de comparer ces solutions et mettre en place un workflow pour faire un peu de CI/CD ! 🤤

Et vous, pensez-vous que le mouvement JAMstack est l'avenir des sites webs ?

En tout cas  n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter, c'est toujours un plaisir d'avoir des retours et des échanges !