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 :
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 :
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 :
À 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.
- 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 :
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 :
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 🚀
- 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
- 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 !!
À terme notre projet devra ressembler à cela :
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 !