Installation de NodeBB avec Docker

tutos mars 27, 2016

Note aux lecteurs
Cet article est vieillissant et son contenu doit être révisé. Attention donc : les explications, commandes systèmes et autres informations peuvent dorénavant être incorrectes ou inexactes.


1 - Introduction

Pour répondre à un besoin d'organisation pendant un projet de mon école, j'ai mis en place un forum. J'ai découvert NodeBB, un forum développé en NodeJS. Il est gratuit, pratique, très rapide et customisable.

J'ai tout de suite été séduit par son interface, ses différents skins et son système de plugin.

2 - L'image

Mais son installation et son utilisation avec docker n'a pas été une partie de plaisir au début, découvrant l'utilisation de docker. De plus, l'équipe de développeurs du forum n'avait pas mis en place d'image officielle sur le docker hub. Il semble qu'une personne de la communauté ait créé une image pseudo-officielle, mais celle-ci n'était pas du tout à jour lorsque j'ai eu besoin du forum.
Heureusement, un utilisateur (benlubar) dans mon cas a créé une image qu'il met régulièrement à jour.

L'installation est simple, mais la persistance des données m'a donné du fil à retorde (voir mon post précédent) !

Pour commencer, téléchargez l'image de NodeBB et de sa base de données Redis.

docker pull benlubar/nodebb
docker pull redis:alpine

J'ai choisi l'image Redis basé sur Alpine Linux, car :

[...] selon la porte-parole de Docker, Alpine Linux, -est- plus léger, plus rapide et plus sûr [...]

source

Et les images basées sur Alpine sont très petites, ce qui économise la place prise sur le serveur !

3 - Les paramètres

Comme je l'expliquais dans l'article sur docker, j'ai choisi l'utilisation de scripts pour lancer et mettre à jour mes services.

3.1 - Redis

Pour lancer Redis avec une configuration persistante, j'utilise l'option volume de docker. Cela me permets d'être certain que la configuration et le mot de passe ne varie pas d'un lancement à l'autre, pour que le forum puisse toujours y avoir accès.

-v /path/to/redis/redis.conf:/usr/local/etc/redis/redis.conf

Le fichier redis.conf est créé automatiquement au lancement du conteneur, puis vous pouvez l'éditer et le relancer pour prendre en compte vos paramètres.
Dans mon cas, voila un aperçu de ce que j'ai modifié (je vous mets les mêmes titres qui se trouvent dans le fichier):

##### SNAPSHOTTING ##### 
save 120 1          # Sauvegarde toutes les 2 minutes s'il y a au moins 1 modification
save 60 10          # Sauvegarde toutes les minutes s'il y a 10 modifications
save 60 10000       # Idem pour 10 000
##### SECURITY ##### 
requirepass <Votre mot de passe>
##### APPEND ONLY MODE ##### 
appendonly yes
appendfilename "appendonly.aof" # Par defaut
appendfsync always           # Les explications sont dans le fichier même
# appendfsync everysec       # Idem
# appendfsync no             # Idem

Pour la sauvegarde en arrière plan, redis à besoin de vm.overcommit_memory = 1, qui est un paramètre du fichier /etc/sysctl.conf (à éditer en root). Dans mon cas, il était à 0 (un paramètre par défaut de Debian j'imagine).

Une fois les modifications faites, il faut redémarrer ou faire :

$ sysctl vm.overcommit_memory=1 # Commande
vm.overcommit_memory = 1        # Réponse

Lancez le conteneur. Une fois lancé, faites docker logs redis (si le conteneur s'appelle redis). Si tout va bien, vous devriez avoir plus ou moins:

1:C 27 Mar 14:49:05.288 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf # Et pourtant il utilise bien mon fichier de configuration a priori !
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 3.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 1
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

1:M 27 Mar 14:49:05.289 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. # Je n'ai pas encore réussi à régler le warning, qui n'empêche pas le bon fonctionnement.
1:M 27 Mar 14:49:05.289 # Server started, Redis version 3.0.7
1:M 27 Mar 14:49:05.344 * DB loaded from disk: 0.055 seconds
1:M 27 Mar 14:49:05.344 * The server is now ready to accept connections on port 6379
1:M 27 Mar 14:54:06.022 * 100 changes in 300 seconds. Saving...
1:M 27 Mar 14:54:06.022 * Background saving started by pid 11
11:C 27 Mar 14:54:06.092 * DB saved on disk
11:C 27 Mar 14:54:06.092 * RDB: 0 MB of memory used by copy-on-write
1:M 27 Mar 14:54:06.122 * Background saving terminated with success

Concernant la sauvegarde persistante des données, il faut passer en paramètre le dossier dans lequel elles seront copiées.

-v /path/to/redis/data:/data

Enfin, la documentation de Redis propose d'utiliser une solution radicale pour la sauvegarde, appelée appendonly. C'est-à-dire que chaque action dans la base de données sera consignée dans un fichier. Celui-ci sera lu à chaque démarrage, permettant de reconstruire la base.
Étant parti sur une autre solution de persistance des données, je ne l'ai pas testée mais je vous met la commande qu'ils proposent :

docker run --detach --name redis --user 1000:50 --volume=path/to/data:/data --entrypoint redis-server redis --appendonly yes

Pour en finir avec Redis, je vous donne la partie de mon script de lancement :

echo -e "\n\t Lancement de Redis (redis serveur alpine)\n"
  docker run -d \
    --name redis-nodebb \
    --restart=always \
    -v /path/to/data:/data \
    -v /path/to/redis.conf:/usr/local/etc/redis/redis.conf \
    --entrypoint redis-server redis:alpine

3.2 - NodeBB

Comme j'utilise tous mes services sur le même serveur, avec une seule adresse IP, j'utilise un reverse-proxy (toujours avec docker). Il se charge d'acheminer les requêtes web vers le bon service en utilisant des noms de domaines (ou sous domaines).

Cela me permet aussi de ne pas exposer les ports de mes services directement sur internet, mais seulement sur le localhost (auquel accède le reverse-proxy).

Concernant le fichier de configuration du forum, c'est presque identique à ce que l'on a fait pour Redis.

--volume=/path/to/config.json:/usr/src/app/config.json

Et voila le contenu de config.json:

{
    "url": "https://forum.lmilcent.com",   # URL d'accès au forum
    "secret": "<Mot de passe REDIS>",      # Mot de passe de Redis
    "database": "redis",
    "port": 4567,                          # Accessible en localhost seulement
    "redis": {
        "host": "redis",
        "port": "6379",                    # Accessible en localhost seulement
        "database": "0"
    }
}

Ce fichier là est bien plus court !

On indique maintenant au conteneur NodeBB qu'il doit se lier au conteneur Redis. Cela lui permet l'accès presque en direct et donc sans passer par internet.

--link redis:redis                         # --link <nom du conteneur>:<nom du service>

Et pour le moment, voilà tous les dossiers qui sont sauvegardés (pour la persistance des photos téléchargées par exemple):

--volume=/path/to/uploads/files:/usr/src/app/public/uploads/files \
--volume=/path/to/uploads/profile:/usr/src/app/public/uploads/profile \
--volume=/path/to/datas:/data \

Reste à verifier si tous les dossiers importants sont sauvegardés, mais pour le moment je n'ai rien à signaler.
Note: On ne peut pas remplacer les deux premières commandes (j'ai essayé !) par --volume=/path/to/uploads:/usr/src/app/public/uploads. J'ai l'impression que docker ne gère pas bien la récursion dans les sous-dossiers, ou que c'est le forum qui n'apprécie pas.

Enfin, j'utilise trois commandes pour le reverse-proxy et son "compagnon" let's encrypt:

--env 'VIRTUAL_HOST=forum.lmilcent.com' \      # Indique l'URL d'accès
--env 'LETSENCRYPT_HOST=forum.lmilcent.com' \  # Pour générer un certificat HTTPS
--env 'LETSENCRYPT_EMAIL=<email>@lmilcent.com' \ # Idem

La commande complète de lancement devient donc:

echo -e "\n\tLancement de NodeBB (benlubar)\n"
docker run -d \
  --name forum \
  --restart=always \
  --link redis:redis \
  --env 'VIRTUAL_HOST=forum.lmilcent.com' \
  --env 'LETSENCRYPT_HOST=forum.lmilcent.com' \
  --env 'LETSENCRYPT_EMAIL=<email>@lmilcent.com' \
  --volume=/path/to/config.json:/usr/src/app/config.json \
  --volume=/path/to/uploads/files:/usr/src/app/public/uploads/files \
  --volume=/path/to/uploads/profile:/usr/src/app/public/uploads/profile \
  --volume=/path/to/datas:/data \
    benlubar/nodebb

3.3 - Plugins

J'ai remarqué qu'après une suppression des conteneurs (pour une mise à jour des images par exemple), les plugins ne restaient pas (mais leurs réglages oui).

J'ai une astuce, je les installe en ligne de commande au moment où je crée le conteneur.

J'ajoute à la fin des deux commandes précédentes:

echo -e "\n\tInstallation du plugin Q&A\n"
docker exec -ti forum npm install nodebb-plugin-question-and-answer

echo -e "\n\tInstallation du plugin 2factor\n"
docker exec -ti forum npm install nodebb-plugin-2factor

echo -e "\n\tInstallation du plugin Cards\n"
docker exec -ti forum npm install nodebb-plugin-cards

echo -e "\n\tInstallation du plugin Gravatar\n"
docker exec -ti forum npm install nodebb-plugin-gravatar

echo -e "\n\tInstallation du plugin GitHub SSO\n"
docker exec -ti forum npm install nodebb-plugin-sso-github

echo -e "\n\tInstallation du plugin Emoji\n"
docker exec -ti forum npm install nodebb-plugin-emoji-extended nodebb-plugin-emoji-one

echo -e "\n\tActivation du plugin Emoji One\n"
docker exec -ti forum ./nodebb activate nodebb-plugin-emoji-one

echo -e "\n\tInstallation du plugin Poll (questionnaire)\n"
docker exec -ti forum npm install nodebb-plugin-poll

echo -e "\n\tInstallation du plugin custom fields\n"
docker exec -ti forum npm install nodebb-plugin-ns-custom-fields

echo -e "\n\tInstallation du plugin mailjet\n"
docker exec -ti forum npm install nodebb-plugin-emailer-mailjet

echo -e "\n\tInstallation du plugin bbcode parser\n"
docker exec -ti forum npm install nodebb-plugin-mega-bbparser

echo -e "\n\tInstallation du plugin pour permettre la couleur dans les posts\n"
docker exec -ti forum npm install nodebb-plugin-mega-colors

echo -e "\n\tDésactivation du plugin spam-be-gone\n"
docker exec -ti forum ./nodebb reset -p nodebb-plugin-spam-be-gone

echo -e "\n\tTerminé !\n"

echo -e "\n\tRedémarrage de NodeBB\n"
docker restart forum

La commande docker exec -ti forum npm install nodebb-plugin-poll est très pratique, elle permet d'exécuter une commande dans le conteneur et de voir la sortie dans la console courante.

docker exec -ti <Nom du conteneur ou ID> <commande>

4 - Conclusion

C'est le service avec lequel j'ai eu le plus de mal, surtout à cause des données non persistantes. Mais une fois une documentation écrite c'est du gâteau !

Une question, un problème, une faute ? Contactez-moi :

Mots clés

Super ! Vous vous êtes inscrit avec succès.
Super ! Effectuez le paiement pour obtenir l'accès complet.
Bon retour parmi nous ! Vous vous êtes connecté avec succès.
Parfait ! Votre compte est entièrement activé, vous avez désormais accès à tout le contenu.