Ajouter une page hors ligne à afficher lors de la reconstruction ou du démarrage de Discourse

Y a-t-il une raison évidente pour laquelle je ne peux pas télécharger un .png de plus de 500 Ko sur mon forum après avoir suivi ce guide ?

Je vois des discussions sur cette ligne client_max_body_size 0; mais ce ne devrait pas être le problème, n’est-ce pas ?

EDIT : J’ai rapidement trouvé la solution après avoir posté ici. Il fallait cocher :white_check_mark: forcer https dans les paramètres. Je laisserai ce message ici au cas où d’autres personnes rencontreraient un problème à l’avenir.

3 « J'aime »

Je note qu’ici :

nginx recommande de désactiver l’en-tête Connection: close ainsi que de définir proxy_http_version 1.1 — donc quelque chose comme ceci :

    proxy_http_version 1.1;
    # Désactiver la valeur par défaut \"Connection: close\"
    proxy_set_header \"Connection\" \"\";

Je n’ai trouvé aucune documentation sur l’impact de Connection: close sur les sockets de domaine Unix, mais comme cette documentation est également utile pour exécuter un proxy externe sur un système distinct et que je m’attendrais à ce que la suppression de l’en-tête ne nuise pas, cela pourrait avoir du sens de recommander de le supprimer ici ?

1 « J'aime »

Si vous avez déployé ceci sur un système avec SELinux (enforcing), vous ne pouvez pas utiliser le socket de domaine Unix pour que l’hôte communique avec le conteneur, car même si vous re-labelisez le socket de domaine Unix, il sera recréé sans le label chaque fois que vous redémarrez le conteneur. Au lieu de cela, vous devrez apporter deux modifications.

Vous devrez autoriser nginx à accéder aux pages d’erreur et passer du proxy via un socket de domaine Unix à un port. Cela coûtera quelques µs de latence par requête, car les coûts d’exécution de nginx avec SELinux comme une couche de sécurité, mais cela ne sera pas perceptible par vos utilisateurs.

Tout d’abord, exécutez ces commandes pour autoriser nginx à établir des connexions réseau et à accéder aux pages d’erreur :

setsebool -P httpd_can_network_connect 1
semanage fcontext -a -t httpd_sys_content_t /var/www
restorecon -R -v /var/www

Ensuite, dans votre app.yaml, commentez ou supprimez - "templates/web.socketed.template.yml", exposez le port 80 comme un port différent sur la machine locale et reconstruisez le conteneur.

expose:
  - "8008:80"   # http

N’utilisez pas https ici — vous avez terminé le SSL dans le nginx externe, et l’en-tête X-Forwarded-Proto indique à Discourse que la requête est arrivée via https. Assurez-vous que le port 8008 (ou tout autre port que vous avez choisi) n’est pas exposé publiquement par vos paramètres de pare-feu.

Modifiez ensuite la configuration de votre nginx externe pour qu’elle passe du proxy via nginx.http.sock à http://127.0.0.1:8008 (ou votre port choisi) et effacez l’en-tête par défaut Connection: close, afin que le nginx externe n’ait pas à établir une nouvelle connexion IP pour chaque requête.

...
  location / {
    proxy_pass http://127.0.0.1:8008;
    proxy_set_header Host $http_host;
    proxy_http_version 1.1;
    # Désactiver le \"Connection: close\" par défaut
    proxy_set_header "Connection" "";
...
1 « J'aime »

Salut @sam (et peut-être @falco). Je suis chargé de nettoyer certains de ces documents #documentation:sysadmin. Celui-ci a un taux de lecture très élevé et je pense qu’il est l’un des moins utiles.

Pensez-vous qu’il serait judicieux d’écrire un remplacement qui démarre haproxy - Official Image | Docker Hub et nginx - Official Image | Docker Hub, peut-être avec docker compose, en faisant monter au conteneur nginx les certificats du conteneur discourse et en faisant en sorte que haproxy en mode tcp fasse quelque chose comme ceci (je suis sûr que cela ne fonctionnera pas, mais je suppose que je pourrai trouver ce qui fonctionnera) :

backend my_app_be
	balance roundrobin
	option httpchk HEAD /srv/status
        server discourse app:443 check
	server fallback nginx:80 check backup

Je pense que ce serait une solution réalisable et plus facile à suivre que ce sujet. Je laisserais alors celui-ci à des fins historiques (et peut-être le fermerais ?) mais je mettrais un lien vers celui décrit ci-dessus.

3 « J'aime »

Notez que ce sujet recoupe considérablement celui-ci :

Ce sujet a reçu des mises à jour importantes récemment (ce qui a encore accru le recoupement). Il serait peut-être judicieux de fusionner la partie “page hors ligne” d’ici vers là comme une note (puisqu’il est facile de l’ajouter si vous exécutez déjà une instance Nginx distincte), puis de marquer celui-ci comme obsolète (en renvoyant vers les alternatives) ?

Votre sujet HAProxy suggéré aurait toujours du sens en complément, comme méthode par défaut pour les personnes qui ne souhaitent pas installer un Nginx frontal pour d’autres raisons.

2 « J'aime »

Comment étais-je censé savoir ?

Ah.

Mais sérieusement, j’aime mieux votre solution que la mienne !

Et ce sujet dit en grandes lettres que c’est un sujet avancé.

Mais peut-être que ce n’est pas nécessaire non plus, car la plupart des gens comprennent mieux nginx de toute façon. J’ai commencé à y réfléchir maintenant, donc la partie difficile sera de me faire arrêter. :slight_smile:

3 « J'aime »

Il est toujours bon qu’il y ait beaucoup d’alternatives. Mais celle-ci est l’une des plus faciles (il y en a pas mal…), et familière pour beaucoup.

Alors s’il vous plaît, ne touchez pas à celle-ci.

Et laisser ceci tel quel a un autre avantage : les résultats de recherche. En raison du trafic élevé (et de l’utilisation très limitée des tags…) il est devenu assez difficile de trouver quoi que ce soit de spécifique. Mais celle-ci est assez facile à trouver et a un objectif très ciblé. Si ce sujet est déplacé vers un autre, il sera plus difficile à trouver.

Il y a une raison pour laquelle c’est si populaire… peu de gens sont assez inspirés pour utiliser Docker ou HAProxy.

2 « J'aime »

Soupir. Eh bien, je suppose que c’est vrai aussi, mais il a au moins 4 ans de retard. Je ne l’ai pas fait récemment, mais vous n’avez plus besoin de modifier les fichiers à la main car acme (ou quelque chose de similaire ?) s’en chargera pour vous.

Ce que je pense vraiment, c’est qu’il est beaucoup plus logique d’utiliser une installation à deux conteneurs, qui a peu de temps d’arrêt plutôt que de passer par ces étapes pour mettre une page en ligne pendant que vous reconstruisez, mais je ne peux pas non plus convaincre les gens de cela.

Alors peut-être que la chose à faire est de réécrire ceci pour la façon dont les choses fonctionnent aujourd’hui.

1 « J'aime »

Et c’est beaucoup plus difficile. Je pense qu’il est plus facile de corriger les instructions sur la façon d’installer et d’utiliser certbot que d’apprendre comment, quand et où mettre à jour la partie SQL, etc. des conteneurs.

De plus, il y a un autre point contre docker (même Discourse fonctionne ainsi…) : nous pouvons trouver beaucoup de questions de niveau très basique comme la façon d’utiliser docker en premier lieu. Ou comment éviter les fautes de frappe dans les fichiers yml :wink:

Et pourtant, cela fonctionne (sauf que la section SSL est un peu confuse, mais elle l’était même il y a 4 ans :wink: )

Non. Je ne suis pas contre d’autres solutions. Je suis très opposé lorsque de vieux liens et textes doivent être déplacés vers de nouveaux emplacements sans raisons très solides.

2 « J'aime »

Nous devrons être en désaccord sur ce point. Mais je crois qu’il y a probablement beaucoup de gens qui sont d’accord avec vous. (Peut-être que cette solution est une solution “installez et oubliez”, et la solution à deux conteneurs nécessite de faire attention lors d’une mise à niveau de Postgres, ce qui arrive environ tous les 2 ans.)

OK. Sur ce point, nous sommes d’accord ! Je pense donc que la meilleure approche est de voir ce que je peux faire pour clarifier cette partie, et de mettre la solution haproxy en attente.

1 « J'aime »

J’adorerais voir cela fait dans le cadre de Discourse, mais merci @fefrei pour cela ! Travail incroyable ! Je vais utiliser Apache pour le faire, mais au moins les étapes de base devraient être les mêmes.

1 « J'aime »

OK, cela m’a pris seulement 2 heures de bidouillage pour obtenir ce que je voulais !

Page de maintenance Discourse avec Apache2

En tant que root

cd /var/discourse
nano containers/app.yml

Commentez ces lignes :

  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

expose:
  #- "80:80"   # http
  #- "443:443" # https

Ajoutez à la FIN de la section templates (doit être la dernière) :

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

Note : Cela fera que Discourse n’écoutera que sur l’IP interne et Apache2 prendra en charge les ports 80/443 et la terminaison SSL.

Note : Discourse doit être reconstruit pour que cela prenne effet :

cd /var/discourse
./launcher rebuild app

Installer apache2 et certbot

apt install -y apache2 certbot python3-certbot-apache

Créer un répertoire pour la page html :

mkdir /var/www/discourse_maintenance

Page HTML :
/var/www/discourse_maintenance/discourse_maintenance.html

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="refresh" content="5">
        <title>Discourse Maintenance</title>
        <style>
            .center {
                display: flex;
                justify-content: center;
            }
            .container {
                max-width: 500px;
                padding: 50px 50px 30px 50px;
            }
            .title {
                padding-top: 20px;
            }
            h1, p {
                font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
            }
        </style>
    </head>
    <body>
        <div class="center">
            <div class="container">
                <h1>Discourse Maintenance&hellip;</h1>
                <p>We are currently upgrading the site, or performing scheduled maintenance.</p>
                <p>You'll automatically be redirected to the site once it's available.</p>
            </div>
        </div>
    </body>
</html>

Activer le module Proxy :

a2enmod proxy
a2enmod proxy_http
a2enmod headers

Fichier vhost Apache :

<IfModule mod_ssl.c>
<VirtualHost *:443>
  ServerName your.discourse.domain
  ServerAdmin your@email.com
  DocumentRoot /var/www/discourse_maintenance

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

  # Maintenance Mode
  RewriteEngine On
  RewriteCond /var/www/under_maintenance -f
  # safety check to prevent redirect loops 
  RewriteCond %{REQUEST_URI} !/discourse_maintenance.html$
  # redirect internally all requests to maintenance.html 
  RewriteRule ^.*$ /var/www/discourse_maintenance/discourse_maintenance.html

  ProxyPass / unix:///var/discourse/shared/standalone/nginx.http.sock|http://127.0.0.1/
  ProxyPassReverse / unix:///var/discourse/shared/standalone/nginx.http.sock|http://127.0.0.1/

  SSLCertificateFile /etc/letsencrypt/live/your.discourse.domain/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/your.discourse.domain/privkey.pem
  Include /etc/letsencrypt/options-ssl-apache.conf

</VirtualHost>
</IfModule>

Pour activer la maintenance, exécutez touch /var/www/under_maintenance

Pour désactiver la maintenance, exécutez touch /var/www/under_maintenance

Crédits : Add an offline page to display when Discourse is rebuilding or starting up pour l’idée initiale, la page html (tronquée/éditée à ma convenance) et la configuration nginx sur laquelle j’ai basé la configuration Apache.

Edit : Suggestions bienvenues pour rendre cela automatique lorsqu’une réponse 502/503 est reçue. J’ai essayé mais je n’ai pas réussi à obtenir le résultat souhaité, j’ai donc opté pour une méthode connue que j’utilise sur d’autres serveurs web lorsque l’application backend est en maintenance, etc.

2 « J'aime »

Au redémarrage du système, cela retardera la page d’erreur/maintenance jusqu’à ce que docker soit démarré, ce qui prend beaucoup plus de temps que le démarrage du système. Cela ne donne pas non plus la possibilité des protections SELinux fournies par le système pour le nginx du système. L’utilisation du nginx du système peut, au moins sur un système géré par systemd avec un démarrage rapide, vous afficher la page d’erreur en quelques secondes après le démarrage. Pour moi, cela signifie que mes systèmes répondent avec la page de maintenance très rapidement lors des mises à jour du système nécessitant un redémarrage. (Je fais fonctionner AlmaLinux 9 sur l’hôte, et il démarre très rapidement sur nginx.)

Il pourrait être judicieux de documenter une alternative haproxy et de comparer les expériences, mais haproxy dans docker n’est pas un remplacement direct pour nginx externe, et fermer ce sujet serait une erreur.

Il ne s’agit pas seulement de disponibilité.

L’utilisation de docker pour le trafic externe via IPv4 masque les adresses IPv6 externes du nginx et de Discourse internes. Vous aurez le même problème avec haproxy. Regardez vos journaux pour les adresses IP locales uniquement 127.0.0.1 ou 172.* RFC1918. Ne pas utiliser de proxy externe signifie que tout le trafic IPv6 apparaît comme la même adresse IP, ce qui brise la limitation de débit de la zone interne nginx, considérant tout le trafic IPv6 comme une seule zone.

IPv6 est de plus en plus important.

2 « J'aime »

J’ai découvert par accident ce matin que cette étape non seulement évite d’appliquer le socket Unix, mais supprime également l’utilisation du module real_ip, de sorte que la limitation de débit est appliquée en fonction de toutes les connexions ensemble, plutôt que de toutes les connexions par IP. Je devrais probablement contribuer avec un nouveau modèle avec des variables, mais pour l’instant, j’ai juste ajouté ceci à mon fichier YAML de conteneur d’application :

run:
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock;
       set_real_ip_from 172.0.0.0/24;
  - 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 172.0.0.0/24;

Je ne sais pas s’il serait judicieux d’avoir, par exemple, un fichier templates/web.httpratelimit.yml avec quelque chose comme ça, avec une variable pour l’adresse, mais sans utiliser de sockets de domaine Unix. Qu’en pensez-vous ?

2 « J'aime »
server {
  listen 80; listen [::]:80; listen 443 ssl http2; listen [::]:443 ssl http2;
  server_name DOMAIN;
  ssl_certificate      /etc/letsencrypt/live/DOMAIN/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/DOMAIN/privkey.pem;

ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
  ssl_protocols TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  add_header Strict-Transport-Security "max-age=63072000;";
  ssl_stapling on;
  ssl_stapling_verify on;

  client_max_body_size 0;

  location / {
    error_page 502 =502 /errorpages/offline.html;
    proxy_intercept_errors on;

    proxy_pass http://unix:/var/discourse/shared/standalone/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;
  }

  location /errorpages/ {
    alias /var/www/errorpages/;
  }
}

! # change your domain and path of error file

j’ai obtenu ce script avec ssl et il fonctionne

2 « J'aime »

J’ai juste essayé d’utiliser ce guide et je n’ai pas réussi à le faire fonctionner.

J’ai commencé avec un proxy Nginx pour exécuter deux sites à partir d’un seul conteneur Discourse. Je voulais juste ajouter la partie de la page d’erreur, donc j’ai sauté les parties qui semblaient se chevaucher avec Run other websites on the same machine as Discourse. J’ai dû manquer une étape clé cependant. À la fin, j’ai obtenu ce dont j’avais besoin de ce tutoriel DigitalOcean. Ce n’est pas difficile de le configurer manuellement, mais il semble qu’il doit y avoir une meilleure façon.

Étant donné Docker comme moyen standard d’exécuter Discourse, cela semble mieux. Je suppose que ce serait le genre de chose que l’on configure et que l’on oublie.

3 « J'aime »

L’idée de ce fil de discussion est également excellente pour ceux d’entre nous qui utilisent caddy comme proxy inverse, que ce soit en tant qu’application autonome ou en utilisant Cloudflare Tunnels.

discourse.example.org {
        reverse_proxy <host | ip>:port

        handle_errors 5xx {
                root * /path/to/error-pages
                rewrite * /error.html
                file_server {
                        status 404
                }
        }
}

La section status 404 n’est importante que si vous utilisez Cloudflare Tunnels. Si caddy renvoie 5xx à Cloudflare, Cloudflare Tunnel affichera sa propre erreur de déconnexion. Changer le statut indique à Cloudflare qu’il existe une connexion active valide qui servira une page d’erreur.

2 « J'aime »

Peut-être que je ne comprends pas comment cela fonctionne, mais une actualisation n’actualise-t-elle pas simplement la page sur laquelle vous êtes déjà ? Comment cela vous ramène-t-il à une URL différente ?

Il n’est pas nécessaire d’aller sur une URL différente – l’astuce est que la page d’erreur est servie directement sur l’URL que l’utilisateur a essayé d’accéder (par exemple, https://meta.discourse.org/t/add-an-offline-page-to-display-when-discourse-is-rebuilding-or-starting-up/45238/158), et c’est l’URL exacte qui se rafraîchira, renvoyant soit la même erreur à nouveau, soit la page que l’utilisateur voulait :slight_smile:

Oh, je vois. Je n’avais pas lu attentivement toute la configuration car j’utilise un autre serveur pour la page hors ligne, au cas où toute ma machine tomberait en panne pour une raison quelconque.
Ça a du sens pourtant. Maintenant, j’essaie juste de faire fonctionner mon JS qui est censé rediriger vers l’URL d’origine…