Passage du conteneur autonome à des conteneurs web et données séparés

Ce processus est-il sûr ? Je peux exécuter une configuration multi-conteneurs sans problème dans un environnement de développement, mais si je l’utilise en production, pendant que des utilisateurs accèdent à l’ancien conteneur et que le nouveau conteneur démarre et exécute l’étape de migration de la base de données, les requêtes vers l’ancien conteneur continueront d’utiliser l’ancienne logique backend et d’enregistrer les données telles que définies dans la version précédente, même après la fin de l’étape de migration de la base de données (mais avant la fin complète du processus de démarrage).

Bien que je sache que ce n’est pas un problème inhérent à Discourse lui-même (un environnement avec plusieurs répliques pourrait rencontrer ce problème si une réplique est mise à jour avant les autres, sauf si vous arrêtez toutes les répliques avant la mise à niveau, ce qui n’est probablement pas souhaitable si vous voulez une haute disponibilité), le processus que vous décrivez reste-t-il sûr de manière générale ?

Une chose à laquelle je peux penser est de veiller à toujours maintenir Discourse à jour pour minimiser les migrations de base de données entre les reconstructions. Mais dans tous les cas, cela n’est toujours pas idéal, et des problèmes pourraient survenir même dans ce cas.

La configuration multi-conteneurs semble être l’une des approches recommandées (bien que non standard avec un seul conteneur), donc je pense qu’elle devrait être sûre, et je fais peut-être trop d’histoires.

Savez-vous si cela fonctionne parfaitement sur des sites en production (en effectuant le démarrage dans un conteneur même si un autre est en cours d’exécution) ? Je demande simplement pour connaître les retours d’expérience de personnes qui l’ont déjà fait en production, pour savoir si cela fonctionne bien, même après plusieurs reconstructions, s’il y a des pièges, etc. Comme je l’ai dit, dans un environnement de développement, cela fonctionne parfaitement.

Si vous voulez éviter toute interruption de service, quelques étapes supplémentaires sont nécessaires : désactivez les « post-migrations » sur les nouveaux conteneurs, déployez complètement le nouveau conteneur, réactivez les post-migrations, puis effectuez à nouveau le déploiement. Cela empêchera l’exécution des migrations de suppression de colonnes tant que l’ancien code est toujours en cours d’exécution.

Il n’existe pas encore de guide pas à pas à ce sujet ; cela est uniquement documenté ici :

Dans l’ensemble, la plupart des forums peuvent tolérer une ou trois minutes d’interruption de service chaque mois.

4 « J'aime »

Oui. Le plus souvent, il n’y a pas de migrations qui cassent le conteneur en cours d’exécution.

Vous pouvez également désactiver les migrations dans web_only.yml, puis, une fois le nouveau conteneur en cours d’exécution, exécuter quelque chose comme :

 cd /var/www/discourse;SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate 

à l’intérieur du conteneur en cours d’exécution.

3 « J'aime »

Oui, cela fonctionne parfaitement en production avec nginx comme serveur proxy inverse frontal.

Voici ce que je fais :

Trois conteneurs :

  • Data (data.yml)
  • Socket1 (socket1.yml)
  • Socket2 (socket2.yml)

Nous spécifions un seul socket de domaine Unix dans la configuration nginx, par exemple :

location / {
       proxy_pass http://unix:/var/run/nginx.http.sock;
       proxy_set_header Host $http_host;
       proxy_http_version 1.1;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto https;
       proxy_set_header X-Real-IP $remote_addr;
}

Ensuite, nous sélectionnons le conteneur que nous souhaitons mettre en production en créant un lien symbolique vers le socket actuel, comme ceci :

Disons que nous voulons que le conteneur socket2 soit en production :

ls -sf /var/discourse/shared/socket2/nginx.http.sock /var/run/nginx.http.sock

Disons que nous voulons apporter une modification à socket1 et mettre socket1 en production :

cd /var/discourse
./launcher rebuild socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

Remarquez qu’il n’y a aucune raison de initialiser uniquement le conteneur socket1, car le conteneur est exposé via un socket de domaine Unix dans son propre répertoire partagé / volume. Ainsi, ces deux conteneurs d’« application web » peuvent fonctionner simultanément :

  • Socket1 : /var/discourse/shared/socket1/nginx.http.sock
  • Socket2 : /var/discourse/shared/socket2/nginx.http.sock

Il n’y a pas de « conflit de liaison de port » exposé comme lorsqu’un port de conteneur TCP/IP est exposé. Pour cette raison, j’expose uniquement un socket de domaine Unix en production (et non un port TCP/IP).

Bien sûr, vous pouvez initialiser si vous le souhaitez :

cd /var/discourse
./launcher bootstrap socket1
./launcher start socket1
ls -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

C’est à vous de décider, mais gardez à l’esprit que si vous exécutez les deux conteneurs simultanément, les deux exécuteront sidekiq et lanceront des tâches planifiées, ce qui a été notre expérience. Nous synchronisons donc occasionnellement nos téléchargements dans les deux conteneurs.

Cela fonctionne parfaitement pour nous, et nous pouvons reconstruire un conteneur d’application web et le mettre en production avec pratiquement aucun temps d’arrêt. Nous prenons très au sérieux les temps d’arrêt en production et les évitons dès que possible.

Note :

Cette méthode (ci-dessus) est conçue pour la partie application web de la solution, et non pour le conteneur de données. Je n’ai pas créé de solution similaire pour les données ; mais qui sait, peut-être qu’un jour, je passerai du temps à construire quelque chose de similaire (mais différent, bien sûr) pour le conteneur de données (une sorte de méthode « deux conteneurs de données, synchronisation des bases de données », totalement à déterminer à ce stade dans mon esprit).

Donc, je fais en réalité l’inverse de cela :

Comme je l’ai dit, dans un environnement de développement, cela fonctionne parfaitement.

Je ne configure généralement pas cela en développement car cela prend plus de temps à mettre en place et n’est pas nécessaire en développement, car un peu de temps d’arrêt est acceptable, puisque c’est juste « moi et le code » (pas de vrais utilisateurs ni de bots accédant au site). De plus, je n’utilise pas Docker en développement** (sur le bureau).

J’espère que cela vous aidera.


Par « développement », j’entends le développement logiciel (par exemple, le développement de plugins) ; pas simplement « mettre en préproduction » une installation Discourse, ce à quoi je fais référence en disant « préproduction » et non « développement » (pour être tout à fait clair).

2 « J'aime »

Merci, je ne le savais pas. C’est une excellente fonctionnalité et je vois bien qu’elle permet d’éviter des problèmes comme celui que j’ai mentionné précédemment (même si cela n’évitera pas les changements de logique utilisant des colonnes déjà existantes, mais ce devrait être un cas plus rare).

Merci pour votre réponse. SKIP_POST_DEPLOYMENT_MIGRATIONS semble être ce que @riking a mentionné et correspond à ce que je recherchais, afin d’éviter que les migrations ne cassent ce qui est fait dans le conteneur en cours d’exécution.

Merci pour votre explication. Cela me semble être une bonne approche, en alternant entre deux conteneurs (pour pouvoir en avoir un en cours d’exécution pendant que l’autre se lance).

Quand j’ai dit « environnement de développement », je voulais dire un environnement de développement distant que j’utilisais pour tester la configuration multiconteneur (à la fois sur la même machine et avec des conteneurs sur des machines différentes).

J’ai dit « développement » et non « préproduction » car dans un environnement de préproduction, j’utiliserais les fichiers yml avec les mêmes plugins et une base de données de production sauvegardée pour tester. Mais c’est vrai, si je veux simplement configurer un environnement de développement, dans la plupart des cas, j’utiliserais l’approche avec un seul conteneur.

2 « J'aime »

Pour autant que je puisse en juger, ce guide consiste en beaucoup de mots autour de :

  • une sauvegarde
  • la création d’une nouvelle instance Discourse complètement nouvelle, avec plus de mots mais les mêmes résultats que l’exécution simple de discourse_setup 2container
  • une restauration

Pourquoi ne pas simplement déplacer ou copier /var/discourse/shared/standalone/{postgres,redis}* vers /var/discourse/shared/data après un arrêt propre et avant de démarrer deux nouveaux conteneurs à partir de fichiers containers/*.yml distincts ? Une sauvegarde/restauration semble être une méthode très lourde pour déplacer toutes ces données, ajoutant inutilement des heures au processus. Est-ce que je manque quelque chose d’évident ici ?

Je viens de tester ce processus sur mon Discourse de test, et j’ai également séparé Redis tant que j’y étais, juste pour m’assurer de couvrir tous les aspects. Édito : J’ai déplacé la description vers un nouveau sujet :

Le site semble fonctionner correctement sans cycle de sauvegarde/restauration. Y a-t-il quelque chose de non évident que je devrais vérifier ?

J’ai effectué le même processus pour un Discourse relativement important et cela fonctionne bien. J’ai décidé qu’en production, je nommerais mon nouveau conteneur web_only app afin que mes doigts continuent naturellement à faire ce qu’il faut. Après avoir écrit les nouveaux fichiers container/*.yml, le temps d’arrêt pour toute la migration a été de 12 minutes, bien plus rapide que ce que cela aurait été avec un cycle de sauvegarde/restauration.

C’est simplement que cela demande un peu plus de travail et une meilleure compréhension du fonctionnement des choses. La restauration de sauvegarde est plus infaillible.

1 « J'aime »

Je suppose qu’on va devoir accepter de ne pas être d’accord. Je pense que si quelqu’un est compétent pour gérer plusieurs conteneurs, il peut exécuter quelques commandes, et je ne pense pas que ces commandes soient plus difficiles à taper que bin/rails c suivi de commandes Ruby ici, ou qu’elles nécessitent des compétences sensiblement plus nombreuses ou différentes que celles requises pour utiliser plusieurs conteneurs. :smiling_face: Mais je vais migrer le contenu vers un nouveau post séparé plutôt que de le laisser enfoui dans un commentaire ici.

1 « J'aime »

C’est un argument raisonnable. Peut-être que rendre la procédure plus effrayante empêcherait ceux qui n’ont pas les compétences de tenter l’expérience.

…Et, s’ils commettent une erreur, rien ne remplace une sauvegarde avant une migration ! J’espère avoir été clair dans mon article lié ci-dessus ! :smiling_face:

1 « J'aime »

Voici le défaut de votre argument. Ajouter 2container à ./discourse-setup n’implique aucune mesure de compétence. Beaucoup de personnes exécutent deux conteneurs simplement parce qu’elles voient des sujets comme celui-ci et supposent qu’il y a une « recette secrète » ou que c’est la « chose à faire ».

Le sujet sur PostgreSQL 12 devrait servir d’avertissement concernant la complexité ajoutée. Utiliser une sauvegarde comme étape entre les états permet à un utilisateur de revenir à un conteneur unique en renommant un seul fichier ; une fois que vous commencez à déplacer des dossiers, cette simplicité est perdue.

2 « J'aime »

@Stephen Il y a une faille dans votre argumentation : la description des conteneurs multiples est remplie d’avertissements indiquant que vous devez assumer la responsabilité des mises à jour et comprendre comment cela fonctionne, et la longue description ci-dessus est tellement obscure que probablement n’importe qui la consultant abandonnerait de toute façon. Allez lire mon Migrate quickly to separate web and data containers et dites-moi que cela n’effraiera pas les personnes qui auront du mal à le suivre, ou que cela ne met pas suffisamment l’accent sur la nécessité d’une sauvegarde et de la possibilité de revenir à une sauvegarde en cas de problème !

J’étais profondément mécontent lorsque j’ai exécuté ./launcher rebuild app peu après avoir migré vers un serveur plus performant (pour une correction de sécurité) et que mon site soit resté hors ligne pendant un temps démesurément long, une grande partie de cette durée étant consacrée à la reconstruction des parties postgres du conteneur. C’est à ce moment-là que j’ai découvert la documentation sur les deux conteneurs et cette documentation, et je ne voulais vraiment pas subir une autre interruption de service de 4 heures pour migrer, alors j’ai continué à accepter des interruptions de service prolongées pour ./launcher rebuild app afin d’éviter les 4 heures d’arrêt qu’aurait prises une restauration. En tant que personne vaguement compétente, j’ai été très agacé pendant longtemps que cette configuration soit effectivement cachée.

Le sujet sur Postgres 12 est une excellente référence, car les gens se retrouvent avec plus d’arrêt de service car ils doivent reconstruire l’ensemble de l’application plusieurs fois, alors qu’ils pourraient se contenter de reconstruire uniquement le conteneur postgres deux fois. Je ne peux pas dire avoir lu l’intégralité du fil en raison de la suppression automatique après 6 jours, mais il n’est absolument pas évident pour moi que les déploiements multi-conteneurs incompétents soient le, ou même un, grand problème là-bas.

(Désolé, parfois j’en ai un peu marre de l’attitude « tous les utilisateurs sont incompétents » ici.)

2 « J'aime »

Cela peut ne pas avoir de sens pour vous, mais pour ceux d’entre nous qui sont présents sur Meta depuis 6 ou 7 ans et qui apportent leur aide aux utilisateurs dans Support, il sera toujours logique de disposer d’une stratégie de retour en arrière.

Des bogues sont parfois présents dans les versions ayant passé les tests ; occasionnellement, les limites de taux de RubyGems impactent les reconstructions, et même GitHub a connu quelques instabilités. Pour cette raison seule, je ne vois aucun intérêt à effectuer un changement d’état qui rendrait plus difficile le simple renommage d’un fichier et l’exécution de ./launcher start app.

Votre tolérance au risque peut être différente, auquel cas vous pouvez emprunter une autre voie. Pour ceux qui aident régulièrement à réparer les dégâts, le guide actuel fonctionne bien.

3 « J'aime »

J’ai l’impression que vous n’avez pas vraiment lu le processus que j’ai rédigé, puisque vous écrivez comme si je n’avais pas souligné la nécessité de pouvoir effectuer la restauration. Veuillez le lire, puis revenir pour modifier ce que vous avez écrit afin qu’il reflète fidèlement mes instructions. À l’heure actuelle, j’ai l’impression que vous me faites une leçon sans même prendre la peine de lire ce que j’ai écrit.

Cependant, j’ai ajouté de nombreuses autres mises en garde supplémentaires, dont une en haut, en plus des avertissements déjà présents dans ce message qui existe depuis cinq ans et qui reste la référence pour les instructions concernant ce processus de migration.

2 « J'aime »

Tout d’abord, merci d’essayer de débroussailler cette « jungle » pour nous, aventuriers :sweat_smile: . Je suivrai probablement vos pas dans quelques jours…
Que contient le fichier multisite.yml ?

Bonjour.

J’ai récemment configuré un forum Discourse auto-hébergé sur un VPS. Les fichiers uploadés sont stockés sur Wasabi, tout comme les sauvegardes. Tout est hébergé chez Linode.

J’ai utilisé le modèle standalone et j’ai trouvé la configuration rafraîchissante par rapport à d’autres logiciels. C’était sublime ! Un pur bonheur. Je souhaite que chaque projet open source accorde autant d’attention à l’installation et à la configuration que Discourse.

Voici le problème cependant. J’ai une instance PostgreSQL dédiée qui tourne sur un serveur séparé, accessible uniquement via une adresse RFC-1918 non exposée à Internet, et que j’aimerais que Discourse utilise. Je n’aime pas trop que les serveurs de base de données tournent sur le même serveur que le serveur web ou d’application.

Existe-t-il un moyen de séparer la base de données standalone et de la déplacer vers mon cluster de base de données dédié ?

Je suppose que tout ce que je dois faire est de faire un pgdump de la base de données Discourse, de la déplacer vers ma base de données dédiée, de la restaurer, puis d’exécuter un vacuum analyze sur toutes les tables après la restauration/importation, et enfin de pointer l’application Discourse vers la nouvelle base de données via le réseau ?

Mais je n’arrive pas à trouver où sont stockées les identifiants de la base de données. J’ai regardé dans app.yml, mais il ne semble pas y avoir d’entrées relatives à la base de données. De plus, lorsque j’ai consulté le dossier ../templates/, aucun des fichiers yml ne semblait contenir d’identifiants de base de données.

Ce que j’essaie de faire est-il même possible ?

1 « J'aime »

Vous pouvez suivre ce guide qui explique comment configurer les identifiants :

3 « J'aime »

Excellent ! C’est exactement ce que je cherchais. Merci beaucoup !

Maintenant, il faut que nginx voie la vraie IP derrière CloudFlare… :wink:

S’il n’y a pas d’identifiants par défaut pour la base de données PostgreSQL intégrée, existe-t-il un moyen d’exécuter un pg_dump et d’exporter la base de données depuis le conteneur autonome ?

1 « J'aime »

La sauvegarde standard de Discourse est un pg_dump dans une archive tar, vous pouvez donc l’utiliser.

Si vous souhaitez faire quelque chose de personnalisé, comme transmettre le dump via un pipeline ou utiliser un format différent, vous le pouvez toujours :

ssh root@forum
cd /var/discourse
./launcher enter app
su postgres
psql # ou pg_dump
2 « J'aime »