Vous pouvez héberger plusieurs installations Discourse autonomes sur un seul serveur (conteneurs séparés / ports séparés / app.yml séparés), sans utiliser la fonctionnalité « multisite » de Discourse.
Cela demande plus de travail manuel que le multisite, mais cela maintient les instances isolées et facilite la migration ultérieure d’un site individuel vers son propre serveur.
Un modèle pratique consiste à utiliser :
• une base de données Postgres externe (instance unique)
• un cache Redis externe (instance unique)
• plusieurs conteneurs web Discourse
• un nœud Sidekiq
• un proxy inverse avec des vérifications de santé
Cela évite complètement le multisite tout en permettant des économies de coûts pour des configurations à faible trafic.
PROCÉDURE OPÉRATIONNELLE POUR L’EXÉCUTION MULTI-CONTENEUR DE DISCOURSE
Postgres externe + Redis + HAProxy + app1 / app2
- PAQUETS HÔTE
| Étape | Commande |
|---|---|
| Mettre à jour le système | apt-get update |
| Installer les outils de base | apt-get install -y ca-certificates curl gnupg lsb-release |
| Installer HAProxy + certbot + socat | apt-get install -y haproxy certbot socat |
- RÉSEAU DOCKER (REQUIS)
Un réseau Docker défini par l’utilisateur est requis afin que les conteneurs puissent se résoudre par nom.
| Étape | Commande |
|---|---|
| Créer le réseau | docker network create discourse-net |
| Vérifier | docker network ls | grep discourse-net |
Cela permet :
• DISCOURSE_DB_HOST=pg
• DISCOURSE_REDIS_HOST=redis
de fonctionner correctement.
- SECRETS
| Objectif | Commande |
|---|---|
| Superutilisateur Postgres | export PG_SUPERPASS='REPLACE_ME_super_strong' |
| Mot de passe de la base Discourse | export DISCOURSE_DBPASS='REPLACE_ME_discordb_strong' |
| Mot de passe Redis | export REDIS_PASS='REPLACE_ME_redis_strong' |
| Clé secrète de base | export SECRET_KEY_BASE="$(openssl rand -hex 64)" |
- CONTENEUR POSTGRES
| Étape | Commande |
|---|---|
| Créer le répertoire | mkdir -p /var/discourse/external/postgres |
| Lancer le conteneur | docker run -d --name pg --restart=always --network=discourse-net -e POSTGRES_PASSWORD="$PG_SUPERPASS" -v /var/discourse/external/postgres:/var/lib/postgresql/data postgres:15 |
| Vérifier | docker ps | grep pg |
- CRÉATION DE LA BASE DE DONNÉES
| Étape | Commande |
|---|---|
| Créer le rôle | docker exec -it pg psql -U postgres -c "CREATE ROLE discourse LOGIN PASSWORD '$DISCOURSE_DBPASS';" |
| Créer la base | docker exec -it pg psql -U postgres -c "CREATE DATABASE discourse OWNER discourse ENCODING 'UTF8' TEMPLATE template0;" |
| Recherche textuelle | docker exec -it pg psql -U postgres -d discourse -c "ALTER DATABASE discourse SET default_text_search_config = 'pg_catalog.english';" |
| Tester la connexion | docker exec -it pg psql -U discourse -d discourse -c "select 1;" |
- EXTENSION PGVECTOR
Requis pour les versions récentes de Discourse.
| Étape | Commande |
|---|---|
| Installer | docker exec -it pg bash -lc 'apt-get update && apt-get install -y postgresql-15-pgvector && rm -rf /var/lib/apt/lists/*' |
| Créer l’extension | docker exec -it pg psql -U postgres -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;" |
| Vérifier | docker exec -it pg psql -U postgres -d discourse -c "SELECT extname FROM pg_extension WHERE extname='vector';" |
- CONTENEUR REDIS
| Étape | Commande |
|---|---|
| Créer le répertoire | mkdir -p /var/discourse/external/redis |
Modèle de configuration Redis :
requirepass REPLACE_ME_REDIS
appendonly yes
save 900 1
save 300 10
save 60 10000
| Étape | Commande |
|---|---|
| Écrire la config | tee /var/discourse/external/redis/redis.conf >/dev/null <<EOF |
| Insérer le mot de passe | sed -i "s/REPLACE_ME_REDIS/$REDIS_PASS/" /var/discourse/external/redis/redis.conf |
| Lancer Redis | docker run -d --name redis --restart=always --network=discourse-net -v /var/discourse/external/redis:/data -v /var/discourse/external/redis/redis.conf:/usr/local/etc/redis/redis.conf redis:7-alpine redis-server /usr/local/etc/redis/redis.conf |
| Tester l’authentification | docker exec -it redis redis-cli -a "$REDIS_PASS" ping |
- STRUCTURE DES RÉPERTOIRES DISCOURSE
| Étape | Commande |
|---|---|
| Créer le répertoire de base | mkdir -p /var/discourse |
| Entrer | cd /var/discourse |
| Cloner le dépôt | git clone https://github.com/discourse/discourse_docker.git |
| Répertoire des conteneurs | mkdir -p /var/discourse/containers |
| Journaux partagés | mkdir -p /var/discourse/shared/web-only/log/var-log |
| Lier les conteneurs | ln -sfn /var/discourse/containers /var/discourse/discourse_docker/containers |
| Lancer le lanceur | ln -sfn /var/discourse/discourse_docker/launcher /var/discourse/launcher |
- CONTENEURS D’APPLICATION
app1.yml
• web + sidekiq
• port 8001
docker_args: "--network=discourse-net"
expose:
- "8001:80"
app2.yml
• web uniquement
• port 8002
• sidekiq désactivé
docker_args: "--network=discourse-net"
expose:
- "8002:80"
run:
- exec: bash -lc 'mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down'
- INITIALISATION (BOOTSTRAP)
| Étape | Commande |
|---|---|
| Entrer | cd /var/discourse/discourse_docker |
| Initialiser app1 | ./launcher bootstrap app1 |
| Démarrer app1 | ./launcher start app1 |
| Initialiser app2 | ./launcher bootstrap app2 |
| Démarrer app2 | ./launcher start app2 |
- VÉRIFICATIONS DE SANTÉ
| Étape | Commande |
|---|---|
| app1 | curl -sSf http://127.0.0.1:8001/srv/status |
| app2 | curl -sSf http://127.0.0.1:8002/srv/status |
| sidekiq app1 | docker exec -it app1 pgrep -fa sidekiq |
| sidekiq app2 | `docker exec -it app2 pgrep -fa sidekiq |
- CERTIFICAT TLS
| Étape | Commande |
|---|---|
| Arrêter le proxy | systemctl stop haproxy |
| Émettre le certificat | certbot certonly --standalone -d example.com --agree-tos -m you@example.com --non-interactive |
| Démarrer le proxy | systemctl start haproxy |
- LOGIQUE HAPROXY
frontend fe_discourse
bind :80
bind :443 ssl crt /etc/letsencrypt/live/example.com/haproxy.pem
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
redirect scheme https code 301 if !{ ssl_fc }
use_backend be_discourse if { nbsrv(be_discourse) gt 0 }
default_backend be_maint
backend be_discourse
balance roundrobin
option httpchk GET /srv/status
server app1 127.0.0.1:8001 check
server app2 127.0.0.1:8002 check
backend be_maint
http-request return status 503 content-type text/html string "<h1>Maintenance</h1>"
- RECONSTRUCTIONS SANS INTERRUPTION DE SERVICE
| Étape | Commande |
|---|---|
| Désactiver app1 | echo "disable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| Reconstruire app1 | ./launcher rebuild app1 |
| Activer app1 | echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock |
| Étape | Commande |
|---|---|
| Désactiver app2 | echo "disable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
| Reconstruire app2 | ./launcher rebuild app2 |
| Activer app2 | echo "enable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock |
FIN
Réseau Docker requis
Postgres et Redis externes
pgvector installé
Sidekiq isolé à app1
Vérifications de santé HAProxy activées
Redirection de maintenance active
Reconstructions progressives prises en charge
Migration d'un site vers son propre serveur ultérieurement
Un avantage de l’exécution d’installations Discourse entièrement autonomes (au lieu du multisite) est que la migration est simple et à faible risque.
Chaque instance Discourse dispose déjà de :
• son propre conteneur
• ses propres uploads
• sa propre base de données
• sa propre utilisation de Redis
• son propre app.yml
Aucune séparation du multisite n’est nécessaire.
Étapes de migration de haut niveau
- Provisionner un nouveau VPS
Installer Docker et Discourse normalement sur le nouveau serveur.
Ne pas configurer le multisite.
- Créer une sauvegarde complète
Depuis le site source :
Admin → Sauvegardes → Créer une sauvegarde
Télécharger le fichier de sauvegarde.
Cela inclut :
• la base de données
• les uploads
• les utilisateurs
• les paramètres
• les thèmes
- Restaurer sur le nouveau serveur
Sur le nouveau serveur :
• effectuer la configuration initiale complète
• se connecter en tant qu’administrateur
• télécharger la sauvegarde
• restaurer
Discourse gère automatiquement la compatibilité du schéma.
- Basculement DNS
Mettre à jour l’enregistrement A du domaine pour pointer vers l’adresse IP du nouveau serveur.
Une fois la propagation DNS terminée, les utilisateurs sont transférés de manière transparente.
- Désactiver l’ancien conteneur
Sur le serveur d’origine :
• arrêter l’ancien conteneur
• le supprimer une fois confiant
Les autres installations Discourse sur le même hôte ne sont pas affectées.
Pourquoi c’est plus simple que le multisite
Dans les configurations multisite, la migration nécessite souvent :
• la séparation des bases de données
• l’extraction des données spécifiques au site
• l’ajustement de multisite.yml
• la refonte de Sidekiq
• la reconfiguration des uploads et des e-mails
Avec des installations autonomes, rien de tout cela n’est nécessaire.
Chaque site est déjà indépendant.
Résumé
Cette approche échange un peu de complexité opérationnelle au début
pour une séparation très simple plus tard.
Elle fonctionne particulièrement bien pendant l’expérimentation
ou la construction précoce d’une communauté.
Quand cette approche n’est probablement pas adaptée
Cette configuration n’est généralement pas une bonne idée si :
• les sites s’attendent à un trafic modéré ou élevé dès le début
• vous dépendez fortement du support officiel de Discourse
• vous n’êtes pas à l’aise avec le débogage de Docker, du réseau ou des proxies inverses
• les exigences de disponibilité sont strictes ou critiques pour l’activité
• plusieurs sites sont étroitement couplés opérationnellement
• vous prévoyez des expérimentations fréquentes de plugins sur toutes les instances
Dans ces cas, soit :
• une configuration multisite prise en charge
ou
• une installation Discourse par serveur
entraînera généralement moins de surprises opérationnelles.
Note importante
Cette approche augmente la flexibilité de l’infrastructure,
mais augmente également la responsabilité de l’administrateur.
Elle fonctionne mieux lorsque la personne qui l’exécute est à l’aise de prendre en charge la pile complète
et de considérer les pannes occasionnelles comme faisant partie du processus d’apprentissage.
Si la stabilité et la possibilité de support sont les objectifs principaux,
un support configuré est presque toujours le meilleur choix.