Avec le confinement et le télé-travail, de nombreuses sociétés ont proposé des outils pour le travail collaboratif. Et notamment des outils de discussion en ligne pour vous faciliter la vie, et ainsi vous permettre d'échanger plus facilement avec vos collègues. Top ! 🤨
Non ? Comment ça ?
Les outils de discussion en ligne sont nombreux et parfois gratuits. Mais vous connaissez sûrement cette phrase : "Si c'est gratuit, c'est que vous êtes le produit !". Personnellement j'aime tout simplement avoir la maîtrise de ma donnée. Peu importe laquelle, y compris des conversations professionnelles ou sur des projets communautaires. Si j'ai besoin d'un outil, je regarde d'abord si je trouve une version Open-Source de mon besoin... Et dans le cas des outils de tchat en ligne, ça tombe bien puisqu'on trouve un outil formidable pour ça : Rocket.Chat !
De plus son installation étant assez simple, et n'exigeant que peu de ressources, il est possible d'installer ça très rapidement et à moindre frais. Par exemple chez OVH avec un serveur de la gamme Kimsufi ou même un VPS.
Nous allons donc voir dans cet article comment installer Rocket.Chat et y accéder. Pour cela nous allons pouvoir nous aider de nos outils préférés. Une première dans cet article, je vais donner la configuration pour Docker et Kubernetes !
Let's go !
Rocket.Chat c'est quoi ?
Rocket.Chat est donc un logiciel de communication collaboratif. Le fonctionnement de l'outil est très proche de celui de Slack : vous pouvez échanger avec les autres membres sur de fils publics, ou par le biais de discussion privées de groupe ou même par messages privés.
La mise en forme des messages se fait avec le langage Markdown. Il intègre également de nombreuses fonctionnalités comme par exemple :
- Le partage de fichiers
- Des messages vocaux
- Mais également de l'échange par vidéo.
Techniquement, Rocket.Chat est développé en javascript et nécessite l'utilisation d'une base MongoDB afin de fonctionner.
Afin de réaliser ce tutoriel, je vais estimer que vous avez à disposition un des deux environnements suivants :
- Docker et docker-compose d'installer...
- Ou un environnement Kubernetes fonctionnel.
Au besoin vous pouvez utiliser K3S et suivre cet installation de K3S sur un VPS OVH pour cet essai. De même pour Docker, vous trouverez ce tutoriel d'installation de Docker sur un dédié Kimsufi sur le blog.
Nous allons commencer par l'installation de cette dépendance, la base de donnée MongoDB.
MongoDB
Comme je l'ai précisé en début de cet article, je vais détailler la mise en place de cet outil avec Docker mais également avec Kubernetes. Dans tous les cas je vais suivre l'arborescence suivante sur les deux projets :
Et oui surprise, Traefik sera de la partie afin de me servir de reverse-proxy et de fournisseur de certificat SSL pour Rocket.Chat ! Ce sera l'occasion d'utiliser une nouvelle fois Traefik 2.2 et le DnsChallenge pour délivrer des certificats SSL.
Commençons tout d'abord par Docker.
- Initialisation de notre base de données avec Docker
Je vais donc créer un fichier docker-compose.yml
pour créer un conteneur pour la base de donnée. J'utilise l'image officielle de MongDB
dans sa dernière version :
version: '3.7'
services:
mongo:
image: mongo:4
container_name: mongo
restart: unless-stopped
command: mongod --oplogSize 128 --replSet rs0
volumes:
- mongodb_data:/data/db
networks:
- lan
Je passe plusieurs arguments au démarrage de l'instance :
--oplogSize 128 --replSet rs0
Il s'agit d'option pour la mise en place d'un réplica de base. C'est une nécessité pour Rocket.Chat car je n'ai pas prévu de faire de haute-disponibilité sur mes services :
Rocket.Chat uses the MongoDB replica set to improve performance via Meteor Oplog tailing.
Je vais également utiliser un volume persistant pour stocker les données :
mongodb_data:/data/db
La seule difficulté réside dans le fait que la réplication de base dans MongoDB nécessite une initialisation. Je pourrai réaliser cette initialisation à l'aide d'une connexion manuelle sur le container mais il existe une autre solution.
Je vais utiliser un conteneur pour lancer le processus :
mongo-init-replica:
image: mongo:4
container_name: mongo_replica
restart: on-failure
command: 'mongo mongo/rocketchat --eval "printjson(rs.initiate())"'
depends_on:
- mongo
networks:
- lan
J'utilise une politique de redémarrage on-failure
, une fois son action exécutée avec succès il ne se relancera plus. Par contre en cas d'imprévu, comme le conteneur de la base pas encore prêt, il se relancera : pratique.
Du côté de MongoDB
c'est terminé ! Regardons de plus prêt la configuration avec Kubernetes !
2. Initialisation de notre base de donnée avec Kubernetes
Voici un rapide aperçu de l'organisation de mes fichiers :
J'ai volontairement séparé un maximum les ressources Kubernetes afin de rendre votre relecture des fichiers plus fluide. Dans l'ensemble ces fichiers vont réaliser les actions suivantes :
- Création d'un Namespace dédié,
- Mise en place d'un volume persistant et de son attribution ( claim ) à MongoDB,
- Le service,
- Le déploiement du container.
Je mets bien évidement les fichiers sur github, le lien se trouvera en bas de cet article. Je ne vais détailler ici que le déploiement du conteneur :
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rocketchat-mongo
namespace: rocketchat-mongodb
spec:
selector:
matchLabels:
app: rocketchat-mongo
serviceName: "rocketchat-mongo"
replicas: 1
template:
metadata:
labels:
app: rocketchat-mongo
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mongodb
image: mongo:4
imagePullPolicy: Always
ports:
- containerPort: 27017
command: ["mongod"]
args: ["--oplogSize","128","--replSet","rs0","--bind_ip_all"]
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
volumes:
- name: mongo-persistent-storage
persistentVolumeClaim:
claimName: mongo-persistent-storage-claim
Finalement la configuration du conteneur ressemble fortement à Docker ( encore heureusement, ceux sont des conteneurs quand même ! 😉 ).
À noter que sur Kubernetes je dois passer l'option --bind_ip_all
à MongoDB sinon celui-ci n'écoute que sur l'interface locale.
La seule spécificité provient de l'utilisation d'une ressource Stateful
plutôt qu'un déploiement :
Contrairement à un déploiement, un StatefulSet conserve une identité unique pour chacun de ses pods. Ces pods sont créés à partir de la même spécification, mais ne sont pas interchangeables : chacun a un identifiant persistant qu'il conserve à travers toute replanification.
Je ne compte pas créer de Replica, j'aurais donc pu passer par un déploiement classique mais pour une base de données distribuées l'utilisation de cette ressource est préférable. Autant prendre une bonne habitude tout de suite même si ce n'est pas nécessaire !
Enfin je vais utiliser la même astuce pour réaliser l’initialisation du réplica :
apiVersion: batch/v1
kind: Job
metadata:
name: mongo-replica
namespace: rocketchat-mongodb
spec:
template:
metadata:
name: mongo-replica
spec:
containers:
- name: mongo-replica
image: mongo:4
command: ["bash"]
args: ["-c", "mongo rocketchat-mongo-service.rocketchat-mongodb/rocketchat --eval \"printjson(rs.initiate())\""]
restartPolicy: Never
Vos conteneurs MongoDB
sont prêts, aussi bien sur Docker que Kubernetes ! Attaquons-nous maintenant à Rocket.Chat !
Rocket.Chat
Comme pour MongoDB, commençons par Docker !
- Lancement de Rocket.Chat avec Docker
On utilise des conteneurs alors rien de complexe non ? 😂
rocket:
image: rocket.chat:3
depends_on:
- mongo
container_name: rocket
restart: unless-stopped
environment:
- PORT=3000
- ROOT_URL=https://rocket.mydomain.com
- MONGO_URL=mongodb://mongo:27017/rocketchat
- MONGO_OPLOG_URL=mongodb://mongo:27017/local?replSet=rs0
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.services.myrocketservice.loadbalancer.server.port=3000"
- "traefik.http.routers.myrocketrouter.rule=Host(`rocket.mydomain.com`)"
- "traefik.http.routers.myrocketrouter.entrypoints=websecure"
networks:
- lan
- traefik
Rocket.Chat utilise le port 3000 par défaut, je vais garder cette configuration puisque je vais utiliser Traefik comme reverse-proxy. Je mets d'ailleurs en place les labels afin de créer le routeur pour mon application :
- "traefik.http.services.myrocketservice.loadbalancer.server.port=3000"
- "traefik.http.routers.myrocketrouter.rule=Host(`rocket.mydomain.com`)"
- "traefik.http.routers.myrocketrouter.entrypoints=websecure"
J'utilise un entrypoint websecure, qui sera sur le port 443 et permettra le HTTPS avec un certificat délivré par let's encrypt.
Je précise également l'emplacement de la base de données et de l'OPLOG :
- MONGO_URL=mongodb://mongo:27017/rocketchat
- MONGO_OPLOG_URL=mongodb://mongo:27017/local?replSet=rs0
En lançant mes containeurs avec docker-compose up
, j'obtiens le retour suivant :
Maintenant regardons la configuration avec Kubernetes.
2. Lancement de Rocket.Chat avec Kubernetes
Comme pour MongoDB, j'ai séparé au maximum les ressources dans des fichiers distincts :
Je vais également :
- Créer un Namespace dédié,
- Le service,
- Le déploiement du container.
Le Ingress servira pour router mon application sur Traefik. Nous pourrons le détailler juste après. Regardons d'abord la déclaration du conteneur Rocket.Chat :
apiVersion: apps/v1
kind: Deployment
metadata:
name: rocketchat-server-deploy
namespace: rocketchat-server
labels:
app: rocketchat-server
spec:
replicas: 1
selector:
matchLabels:
app: rocketchat-server
template:
metadata:
labels:
app: rocketchat-server
spec:
containers:
- name: rocketchat-server
image: rocket.chat:3
env:
- name: PORT
value: "3000"
- name: ROOT_URL
value: "https://rocket.dubarbu.fr:3000"
- name: MONGO_URL
value: "mongodb://rocketchat-mongo-service.rocketchat-mongodb:27017/rocketchat"
- name: MONGO_OPLOG_URL
value: "mongodb://rocketchat-mongo-service.rocketchat-mongodb:27017/local?replSet=rs0"
ports:
- containerPort: 3000
Comme pour Docker, Rocket.Chat utilisant le port 3000 par défaut, je vais garder cette configuration puisque je vais utiliser Traefik comme reverse-proxy. On précise également l'emplacement de la base de données et de l'OPLOG.
La configuration de Traefik va se faire au travers d'une route ingress que je définis comme ceci :
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroutetls
namespace: rocketchat-server
spec:
entryPoints:
- websecure
routes:
- match: Host(`rocket.dubarbu.fr`)
kind: Rule
services:
- name: rocketchat-server-service
port: 3000
J'utilise également un entrypoint websecure, qui va nous permettre le HTTPS avec un certificat délivré par let's encrypt.
Tout comme pour Docker, le lancement peut se faire avec kubectl apply -f rocket
. Si vous n'avez pas lancé Traefik vous allez avoir une erreur avec la route. Aucun soucis vous pouvez l'appliquer par la suite.
Et si justement on s'occupait de notre reverse-proxy !?
Traefik 2.2
- Lancement de Traefik avec Docker
J'ai décidé d'utiliser le challenge DNS. Aucun intérêt il s'agit purement de vous montrer comment l'utiliser en cas réel avec Docker et Kubernetes. Voici mon fichier docker-compose.yml
suivant :
version: '3.7'
services:
traefik:
image: traefik:2.2
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- "TZ=Europe/Paris"
- "OVH_ENDPOINT=ovh-eu"
- "OVH_APPLICATION_KEY=XXXXXXX"
- "OVH_APPLICATION_SECRET=XXXXXXXXXX"
- "OVH_CONSUMER_KEY=XXXXXXXXXXXXXXXXXXXXXXXX"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- traefik_ssl:/letsencrypt
networks:
- traefik
volumes:
traefik_ssl:
name: traefik_ssl
networks:
traefik:
name: traefik
et le fichier traefik.yml
pour la configuration statique :
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: :443
http:
tls:
certResolver: myresolver
providers:
docker:
exposedByDefault: false
certificatesResolvers:
caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
myresolver:
acme:
email: "[email protected]"
storage: "/letsencrypt/acme.json"
dnsChallenge:
provider: ovh
delayBeforeCheck: 10
Aussi bien pour Docker que pour Kubernetes, si vous suivez régulièrement le blog, rien de nouveau ! Si ce n'est pas le cas, je vous invite à passer très souvent pour avoir un maximum d'information sur Traefik 😍
2. Lancement de Traefik avec Kubernetes
Voici le fichier de déploiement pour Traefik avec l'utilisation du challenge DNS :
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: default
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.2
imagePullPolicy: Always
env:
- name: OVH_ENDPOINT
value: ovh-eu
- name: OVH_APPLICATION_KEY
value: XXXXXXX
- name: OVH_APPLICATION_SECRET
value: XXXXXXXXXX
- name: OVH_CONSUMER_KEY
value: XXXXXXXXXXXXXXXXXXXXXXXX
volumeMounts:
- name: traefik-config-static
mountPath: /etc/traefik/
- name: letsencrypt
mountPath: "/letsencrypt/"
volumes:
- name: traefik-config-static
configMap:
name: traefik-config-static
- name: letsencrypt
persistentVolumeClaim:
claimName: traefik-letsencrypt-pvc
Je déclare donc les mêmes variables d'environnement dans les deux cas. Voici le fichier ConfigMap avec la configuration statique de Traefik :
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config-static
namespace: default
labels:
app: traefik
data:
traefik.yml: |
entryPoints:
web:
address: :80
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: :443
http:
tls:
certResolver: myresolver
providers:
kubernetesCRD: {}
certificatesResolvers:
caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
myresolver:
acme:
email: "[email protected]"
storage: "/letsencrypt/acme.json"
dnsChallenge:
provider: ovh
delayBeforeCheck: 10
Je ne vais pas détailler les autres fichiers qui servent à initialiser Traefik et que j'ai déjà pu détailler au cours de précédents articles :
Nous voila prêts pour lancer notre fusée...
Go 🚀
Pour les plus impatients, ou les plus pragmatiques, voici comment réaliser cette installation en quelques secondes.
Avec Docker :
Récupérer le dépôt git avec les fichiers docker-compose :
git clone https://github.com/lfache/dockerfiles
Allez dans le dossier Rocket.Chat :
cd dockerfiles/Rocket.Chat
🚩 Pensez à modifier les variables d'environnement concernant le challenge DNS ou passez en TLS challenge.
Modifiez également les variables pour votre nom de domaine. 🚩
Enfin lancez les conteneurs, commençons avec Traefik :
cd traefik
docker-compose up -d
Puis MongoDB :
cd ../mongodb
docker-compose up -d
Enfin Rocket.Chat :
cd ../rocket
docker-compose up -d
Et en vidéo, ça donne :
Avec kubernetes :
Récupérer le dépôt git avec les fichiers yaml
:
git clone https://github.com/lfache/kubernetes
Allez dans le dossier Rocket.Chat :
cd kubernetes/Rocket.Chat
🚩 Pensez à modifier les variables d'environnement concernant le challenge DNS ( adresse e-mail par exemple ) ou passez en TLS challenge.
Modifiez également les variables pour votre nom de domaine et l'emplacement des volumes persistants 🚩
Enfin lancez les conteneurs, commençons avec Traefik :
kubectl apply -f traefik/
Puis MongoDB :
kubectl apply -f mongodb/Namespaces.yaml
kubectl apply -f mongodb/
kubectl apply -f mongodb_replica/
Et enfin Rocket.Chat !
kubectl apply -f rocket/Namespaces.yaml
kubectl apply -f rocket/
Et en vidéo également :
Ces deux vidéos vous permettent également de constater que tout fonctionne aussi bien sur Docker que Kubernetes avec l’utilisation de Docker Windows et WSL2 !
Nous venons donc de voir ensemble comment installer Rocket.Chat en quelques minutes à l'aide de technologie de conteneurisation. Que vous utilisiez Docker ou Kubernetes, vous voila prêts pour installer cet outil collaboratif !
Cela nous a également permis de revoir, toujours et encore, notre reverse-proxy favori : Traefik ! Tout est tellement plus simple lorsque l'on souhaite router du flux HTTP(S) avec cet outil... Comment s'en passer 😂
Bien évidement la solution que je propose ici n'est certainement pas la seule façon d'installer Rocket.Chat. Il existe par exemple un template Helm pour l'installer facilement sur Kubernetes. Mais je trouve qu'il est préférable de connaître un minimum l'outil qu'on souhaite installer avant d'en automatiser son installation !
D’où cette installation plus manuelle afin d'en découvrir - un peu - le fonctionnement !
Cet article - double tutoriel - vous a t-il plu ? Souhaitez vous voir ce type d'installation séparée dans deux articles différents ou cela vous permet-il de comparer l'utilisation des deux solutions ?
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 ! 😇