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

Bonjour Jay @pfaffman, merci pour ce message et les autres sur ce sujet des « deux conteneurs », y compris les écrits de Sam à ce sujet.

Question :

Nous avons essayé de configurer deux conteneurs comme vous le mentionnez : un conteneur pour data et un pour web-only, mais nous rencontrons plusieurs problèmes pour faire fonctionner cela sur macOS.

Mais avant de nous inquiéter du débogage de cette « configuration à deux conteneurs » sur macOS ou Ubuntu, nous souhaitons nous assurer que nous le faisons pour la bonne raison.

La raison pour laquelle nous voulons faire cette « danse à deux conteneurs » est d’éviter que le site ne soit hors ligne lorsque nous reconstruisons l’application web, par exemple lors de l’installation d’un plugin. De plus, lorsque nous ajustons un plugin maison, nous avons remarqué que parfois la seule façon de nous assurer que nos modifications fonctionnent est de reconstruire (c’est une autre histoire pour un autre jour). J’ai également eu du mal à mettre en place une configuration de développement web « rapide et conviviale » à ma satisfaction ; mais c’est un autre sujet pour un autre jour.

Ma question est donc la suivante : l’installation en « deux conteneurs » réduit-elle significativement les temps d’arrêt lorsque la partie « web-only » de l’application est reconstruite ?

C’est ainsi qu’il faut envisager les choses, n’est-ce pas ?

Lorsque nous installons un plugin ou que nous en ajustons un, devons-nous reconstruire uniquement le fichier yml « web-only » et non le fichier yml « data » ?

Nous venons d’un environnement LAMP où les modifications apportées aux plugins peuvent et sont majoritairement effectuées en temps réel sur le site en direct (sans temps d’arrêt, sauf si nous faisons une erreur). De plus, nous venons de certaines applications web VueJS où nous compilons sur le bureau, puis nous téléchargeons et déployons simplement la nouvelle application, ce qui permet des mises à jour sans temps d’arrêt quasi nul pour la partie VueJS du site. Cependant, avec Discourse, nous constatons des temps d’arrêt, ce que nous ne souhaitons pas (même quelques secondes).

La solution « deux conteneurs » apporte-t-elle des améliorations significatives en termes de temps d’arrêt lorsque nous (1) reconstruisons l’application (pour les plugins, les ajustements de code, etc.) ou (2) restaurons à partir d’une sauvegarde complète ?

J’ai l’impression que je vais me faire « taper dessus » (encore une fois) pour avoir posé cette question, car nous cherchons un moyen d’exécuter Discourse en production et d’apporter des modifications avec un temps d’arrêt quasi nul, et nous n’avons pas encore trouvé de méthode pour faire des choses aussi simples à réaliser avec une application LAMP ou VueJS (par exemple).

D’où la difficulté / l’intérêt pour la méthode « deux conteneurs » que nous n’avons pas encore réussi à mettre en place.

Merci !

Oui. Le conteneur web existant continue de fonctionner pendant que le nouveau conteneur est en cours de construction. Le temps d’arrêt se limite donc au temps nécessaire pour démarrer le nouveau serveur web, ce qui prend généralement moins d’une minute, bien que cela ne garantisse en aucun cas un arrêt nul. Si vous souhaitez un arrêt nul, vous avez besoin d’un proxy inverse placé devant, qui permettra au nouveau conteneur de se lancer et de commencer à fonctionner avant que l’ancien ne soit arrêté. (Et si les migrations de base de données pour le nouveau conteneur perturbent le fonctionnement de l’ancien, vous aurez alors des temps d’arrêt, sauf si vous mettez en œuvre d’autres mécanismes).

Aucune différence lors de la restauration à partir d’une sauvegarde.

4 « J'aime »

Merci, Jay @pfaffman,

Vous êtes vraiment une ressource précieuse de premier plan ici, sans aucun doute !

Qu’en pensez-vous, cette idée peut-être folle (basée sur ma compréhension encore limitée) :

Mettre en place nginx comme proxy inverse en façade ; selon ce tutoriel :

Ensuite, avoir deux répertoires / instances avec discourse_docker (standalone) configurés, par exemple :

  1. /var/discourse1
  2. /var/discourse2

Dans ces deux instances, configurez discourse_docker (standalone) pour qu’il écoute sur un socket différent, en modifiant ce modèle dans chaque instance :

 - "templates/web.socketed.template.yml"

Ainsi, en résumé, nous avons simplement reconstruit la production (à un moment calme) pour qu’elle s’exécute dans un conteneur différent en écoutant sur un socket différent (nginx.https.sock2), évitant ainsi tout conflit de socket ; ce que nous pouvons également construire en mode standalone (dans le but d’éliminer le besoin de deux conteneurs, data et web-only).

Par exemple (pour discussion / illustration), dans web.socketed.template.yml de discourse1 :

  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock;
       set_real_ip_from unix:;
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 443 ssl http2;/
     to: |
       listen unix:/shared/nginx.https.sock ssl http2;
       set_real_ip_from unix:;

et dans discourse2 :

 - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock2;
       set_real_ip_from unix:;
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 443 ssl http2;/
     to: |
       listen unix:/shared/nginx.https.sock2 ssl http2;
       set_real_ip_from unix:;

Cependant, au lieu de laisser le modèle Discourse faire la magie, nous basculons simplement manuellement les sockets dans /etc/nginx/conf.d/discourse.conf et redémarrons nginx. Nous supprimerions donc la directive replace: dans le modèle web.socketed.template.yml.

Dans cette configuration proposée (peut-être folle), nous pouvons avoir deux conteneurs standalone écoutant sur deux sockets différents (sans conflit) et simplement configurer nginx pour se connecter au socket souhaité, puis redémarrer nginx.

Cela semble clair, simple et peut-être utile (pendant une période calme sans nouveaux messages sur l’instance en direct) pour ceux qui ne veulent pas (ou n’ont pas besoin) de la complexité de deux conteneurs (data et web-only) par instance Discourse unique (app).

Bien sûr, la configuration la plus robuste (d’un point de vue des données), cependant, pour la perfection sur les sites occupés, serait la solution à « deux conteneurs », car nous voudrions avoir les instances data et web-only (écoutant maintenant sur deux sockets différents, sock et sock2).

Dans la « solution à deux conteneurs » avec la façade nginx, la « configuration standard » consiste à faire écouter les deux conteneurs web-only sur le même socket, de sorte qu’ils ne peuvent pas fonctionner simultanément ; mais si (par exemple uniquement) nous les faisions écouter sur un socket différent, ils pourraient tous deux fonctionner en même temps et nous pourrions simplement utiliser le fichier de configuration nginx (et un redémarrage de nginx) pour basculer entre les deux.

Est-ce la bonne compréhension ?

Commençais-je à (lentement mais j’espère sûrement) à comprendre cela ?

Merci !

Note de suivi uniquement : J’ai la configuration « deux conteneurs » fonctionnelle sur l’un de mes Mac de bureau :

Screen Shot 2020-04-11 at 12.41.24 PM

Les seules précautions dans notre installation étaient la nécessité de créer manuellement ces répertoires (et de définir la propriété et les permissions), car ces répertoires ne sont pas créés pour une raison quelconque par les scripts :

~discourse/discourse/shared/data
~discourse/discourse/shared/web-only

et bien sûr, au début, j’ai essayé avec un mot de passe vide pour la base de données, et cela n’a pas fonctionné (les instructions disent bien de définir un mot de passe, mais je faisais juste des expériences).

Ensuite, je vais configurer la façade nginx et essayer de passer à cette configuration avec websocket pour l’application web-only.

C’est beaucoup à assimiler. Mais il n’y a aucune raison d’avoir deux répertoires Discourse. Créez simplement plusieurs fichiers yml dans le répertoire des conteneurs. Donnez-leur le nom que vous souhaitez.

2 « J'aime »

Merci pour la confirmation. C’est exactement ainsi que nous sommes configurés (répertoire unique) après nos tests d’aujourd’hui.

Tout s’est bien passé avec la configuration à 2 conteneurs (2CC), mais je rencontre des difficultés avec la configuration du proxy inverse nginx sur macOS.

Je n’arrive pas à établir une connexion fonctionnelle au socket Unix dans le répertoire /shared, même si le socket est accessible en dehors du conteneur. J’ai essayé avec nginx, ainsi qu’avec Python et socat (pour les tests). J’obtiens systématiquement une erreur 61 : connexion refusée. Hmmmm…

Je suis bloqué sur cette « connexion refusée » depuis toute la journée !!

Demain est un autre jour.

J’ai une petite question.
Si nous avions une seule configuration de conteneur (juste ‘app.yml’). Et que nous exécutions ./launcher bootstrap app.
Notre site web/front-end s’arrêterait-il ou non ?

Si oui, alors pourquoi ‘bootstrap web-only’ n’arrête-t-il pas notre site web ?

Si non, alors quel est l’avantage d’une configuration à deux conteneurs, en termes de gain de temps lors de la reconstruction de notre conteneur ? En d’autres termes, si nous pouvons garder notre site web en fonctionnement, même lorsque nous amorçons notre unique conteneur, alors pourquoi aurions-nous besoin d’avoir deux conteneurs séparés ?

1 « J'aime »

Non, si vous créez un nouveau fichier .yml, par exemple appelé new_image.

L’initialisation (bootstrap) ne démarre ni n’arrête aucun service, lorsqu’elle est correctement configurée. Les images initialisées ne sont pas en cours d’exécution. C’est pourquoi on les appelle « initialisées ».

Cependant, vous devez créer un nouveau fichier yml car vous devez créer une nouvelle image avec un nouveau nom.

Ainsi, avec un nouveau nom d’image, la nouvelle image initialisée n’est pas encore en cours d’exécution. Elle est seulement « initialisée ».

Vous pouvez construire la nouvelle image avec un nouveau nom (différent de app) et la phase de construction est terminée. Appelons cela « new_image ».

Ensuite, si nous voulions remplacer une image en cours d’exécution, appelons-la « old_image », vous pouvez faire ceci :

./launcher stop old_image; ./launcher start new_image

Dans votre cas :

./launcher bootstrap new_image          # image de données et image web "app" en cours d'exécution
./launcher stop app; ./launcher start new_image

Comme le conteneur de données est déjà construit, vous gagnez du temps : (1) vous ne reconstruisez pas l’image de données et (2) il n’y a pas d’interruption de service lors de la reconstruction de l’image web, car vous pouvez la construire (initialiser l’image) pendant que les autres images sont en cours d’exécution.

C’est beaucoup plus rapide ; mais ce n’est pas aussi rapide que d’utiliser un proxy inverse devant les conteneurs.

Dans votre message initial, vous avez une image en cours d’exécution appelée app. Si vous essayez d’initialiser à nouveau cette image appelée app, vous reconstruisez la même image (même nom). Cela ne vous fera gagner aucun temps, comme vos instincts vous l’ont dit.

Est-ce clair maintenant ?

Si non, n’hésitez pas à poser des questions. Tout le monde peut apprendre ; et l’apprentissage est passionnant. C’est (en fait) facile à comprendre, mais cela prend un peu de temps si vous êtes nouveau dans ces concepts.

2 « J'aime »

Pour être honnête, je n’ai rien compris. J’ai essayé, mais j’ai échoué.

Ma question était simple.
Si nous avons une configuration à deux conteneurs et que nous ‘amorçons’ (sans reconstruire) notre conteneur ‘web-only’, alors notre conteneur web actuel continue de fonctionner (car notre site web apparaît comme fonctionnel/ok).

Et si nous avons une configuration à conteneur unique, comme ‘app.yml’, et que nous amorçons ce conteneur ‘app’, notre site web continuera-t-il de fonctionner ?

Et si l’explication est simple, pourquoi est-ce le cas ?

1 « J'aime »

Peut-être devriez-vous embaucher quelqu’un pour vous aider ?

1 « J'aime »

Pas de problème.

C’était juste un petit doute. Une partie de celui-ci pourrait peut-être être répondue par « oui/non ».

Rien de bien grave.

1 « J'aime »

Ma suggestion est de simplement prendre plaisir à essayer par vous-même.

En réalité, cela prend moins de temps d’essayer par vous-même dans votre propre environnement de test que de poser une question et d’attendre une réponse sur un forum.

Vous obtiendrez la réponse à votre question si vous essayez ; c’est pourquoi vous devriez essayer ces choses dans un scénario de test afin de ne pas casser votre application de production :slight_smile:

Discourse est open source et gratuit à télécharger et à configurer, grâce à la générosité de ses cofondateurs. Cela signifie que vous pouvez et devriez profiter de cette open source pour créer, détruire, recréer et détruire à nouveau des applications de test (autant de fois que vous le souhaitez).

Si vous n’êtes pas disposé à fournir un certain effort pour des tâches d’administration système de base, @codinghorror avait une excellente suggestion concernant l’embauche de talents locaux ici.

1 « J'aime »

Oui. (sauf si l’amorçage migre la base de données de telle manière que le conteneur en cours d’exécution ne puisse plus l’utiliser). Vous aurez une interruption de service pendant que l’ancien conteneur s’arrête et que le nouveau démarre.

Non. Vous ne pouvez pas avoir deux processus de base de données accédant aux mêmes fichiers en même temps.

3 « J'aime »

Oh !!
Merci d’avoir éclairci cette question.

1 « J'aime »

Ainsi, en ne plaçant pas la base de données dans le conteneur que vous construisez, vous pouvez créer un nouveau conteneur web qui interagit avec la base de données tandis que l’autre conteneur web continue de fonctionner.

1 « J'aime »

Une petite question concernant cette approche : comment procéder avec les rebuilds dans cette configuration ?

En supposant que nous partions de zéro avec la configuration à deux conteneurs ajoutée par @pfaffman, nous aurons alors deux conteneurs : “app” : “data” et “web_only”.

Toutes les opérations de rebuild et autres sont dirigées vers le conteneur “web_only”, mais devons-nous faire quelque chose avec le conteneur “data”, ou le bootstrap de “web_only” s’en charge-t-il ? (Je pose la question car je réalise quelques tests et le conteneur de données ne s’arrête jamais).

Nous utilisons « trois conteneurs » et un proxy inverse nginx :

  1. data
  2. socket1
  3. socket2

Supposons que nous exécutons actuellement socket1 (ce que vous appelez « web-only ») et que nous voulons reconstruire et tester quelque chose. Nous allons reconstruire socket2 tandis que socket1 reste en cours d’exécution. Comme chacun de ces conteneurs utilise un socket de domaine Unix, ils peuvent tous deux fonctionner simultanément car les sockets partagés se trouvent dans des fichiers différents (emplacements distincts).

Ensuite, supposons que socket2 soit construit et prêt à fonctionner.

Je fais ceci :

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

Nous sommes maintenant en production sur socket2.

Si, par exemple, il y avait un problème, je peux simplement faire ceci :

ln -sf /var/discourse/shared/socket1/nginx.http.sock /var/run/nginx.http.sock

et nous sommes de nouveau à l’état précédent, en cours d’exécution sur socket1.

Pour le plaisir, j’ai ajouté du code à mon profil de connexion bash, de sorte que lorsque je me connecte au serveur, il m’indique toujours quel socket (conteneur) est en cours d’exécution :

Dernière connexion : ven. 15 mai 09:39:39 2020 depuis 159.192.33.138
srw-rw---- 1 root docker  0 5 mai 07:38 /var/run/docker.sock
lrwxrwxrwx 1 root root   44 15 mai 09:16 /var/run/nginx.http.sock -> /var/discourse/shared/socket/nginx.http.sock

J’espère que cela vous aidera.

À mon avis, c’est (en général) la « meilleure approche » (et c’est mon « standard par défaut », en production et non en staging ou en développement), mais cela ne dépend que de moi, vos résultats peuvent varier. Cela nécessite un peu plus de travail pour la configuration, mais je pense que cela en vaut vraiment la peine (en production).


Avertissement :


Il est judicieux de conserver des copies de vos fichiers modèle yml originaux en lieu sûr, car les modèles peuvent être écrasés et cela pourrait poser problème. Nous avons donc tendance à « sauvegarder tous les yml », à « faire un pull d’abord », puis à « vérifier les yml » avant de procéder au bootstrap (par excès de prudence).

2 « J'aime »

Vous n’avez pas besoin de reconstruire le conteneur de données sauf en cas de mise à niveau de PostgreSQL (comme cela vient d’arriver) ou de Redis (ce qui s’est produit il y a environ six mois).

Généralement, il vous suffit de remplacer web_only par app dans les instructions que vous consultez. J’ai pris des notes à l’adresse suivante : Managing a Two-Container Installation - Documentation - Literate Computing Support

5 « J'aime »

Donc, dans cette approche, vous avez deux instances web et vous utilisez celle qui est inoccupée pour tester des choses avec les mêmes données ? C’est intéressant, je vais certainement l’essayer une fois que j’aurai défini ma stratégie pour « essayer d’économiser de l’espace » :stuck_out_tongue:

Merci beaucoup. J’ai lu votre article de bout en bout, juste une petite question : ici, vous vouliez dire « data », n’est-ce pas ? (c’est-à-dire, reconstruire data au lieu de web_only) comme indiqué dans l’article ? Ou peut-être y a-t-il une astuce différente que je manque (par exemple : lors d’une grosse mise à niveau, il faut les regrouper puis les séparer à nouveau).

Oui, nous testons, ajoutons et reconstruisons sur un conteneur basé sur des sockets tout en l’exécutant sur l’autre.

Oui, les deux utilisent le même conteneur de données. Le conteneur de données ne « se soucie » pas de l’application web à laquelle il « parle ».

C’est très simple en réalité, une fois que vous avez compris l’idée de base pour exécuter les conteneurs sur des sockets de domaine Unix partagés et non sur des ports TCP/IP exposés externemment.

Nous ne constatons pas que cela prenne beaucoup d’espace, mais encore une fois, nous n’exécutons pas (en production) avec un espace limité car l’espace disque n’est pas cher.

1 « J'aime »

Merci pour ces détails. J’ai justement essayé d’avoir deux « conteneurs d’application » pointant vers un Data One dans un environnement de test, et cela fonctionne à merveille.

Je ne peux pas le déplacer en PRD pour l’instant, car je ne peux pas reconstruire le conteneur de données en PRD et je ne souhaite rien modifier tant que ce n’est pas résolu. Cela arrête simplement les opérations avec un message discourse@discourse FATAL: terminating connection due to administrator command.