إضافة صفحة غير متصلة لتظهر عند إعادة بناء أو بدء تشغيل Discourse

هل هناك سبب واضح لعدم قدرتي على تحميل ملف .png بحجم أكبر من 500 كيلوبايت على منتداي بعد اتباع هذا الدليل؟

أرى بعض الحديث حول هذا السطر client_max_body_size 0; ولكن هذا لا ينبغي أن يكون المشكلة، أليس كذلك؟

تعديل: لقد اكتشفت الأمر بسرعة بعد النشر هنا. احتجت إلى التحقق من :white_check_mark: فرض HTTPS في الإعدادات. سأترك هذا المنشور هنا في حال واجه أشخاص مستقبليون مشكلة

3 إعجابات

ألاحظ هنا:

يوصي nginx بتعطيل ترويسة Connection: close بالإضافة إلى تعيين proxy_http_version 1.1 - لذا شيء كهذا:

    proxy_http_version 1.1;
    # تعطيل \"Connection: close\" الافتراضي
    proxy_set_header "Connection" "";

لا يمكنني العثور على أي وثائق حول ما إذا كان لـ Connection: close أي تأثير على مقابس المجال الموحد، ولكن نظرًا لأن هذه الوثائق مفيدة أيضًا لتشغيل وكيل خارجي على نظام منفصل وأتوقع أن إزالة الترويسة لن تضر، فقد يكون من المنطقي التوصية بإزالتها هنا؟

إعجاب واحد (1)

إذا قمت بنشر هذا على نظام يحتوي على SELinux (فرض)، فلا يمكنك استخدام مقبس المجال الموحد (unix domain socket) لكي يتواصل المضيف مع الحاوية، لأنه حتى لو قمت بإعادة تسمية مقبس المجال الموحد، فسيتم إنشاؤه مرة أخرى بدون التسمية في كل مرة تقوم فيها بإعادة تشغيل الحاوية. بدلاً من ذلك، ستحتاج إلى إجراء تغييرين.

ستحتاج إلى السماح لـ nginx بالوصول إلى صفحات الخطأ، والتبديل من التوجيه عبر مقبس المجال الموحد إلى منفذ. سيكلف هذا بضع ميكروثوانٍ من زمن الاستجابة لكل طلب حيث أن أسعار تشغيل nginx مع SELinux كطبقة أمان واحدة، ولكن هذا لن يكون ملحوظًا للمستخدمين لديك.

أولاً، قم بتشغيل هذه الأوامر للسماح لـ nginx بإجراء اتصالات شبكة والوصول إلى صفحات الخطأ:

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

ثم في ملف app.yaml الخاص بك، قم بالتعليق أو إزالة - "templates/web.socketed.template.yml"، واكشف عن المنفذ 80 بمنفذ مختلف على الجهاز المحلي وأعد بناء الحاوية.

expose:
  - "8008:80"   # http

لا تستخدم https هنا - لقد أنهيت SSL في nginx الخارجي، ويخبر الرأس X-Forwarded-Proto Discourse أن الطلب جاء عبر https. تأكد من أن المنفذ 8008 (أو أي منفذ آخر اخترته) غير مكشوف علنًا بواسطة إعدادات جدار الحماية الخاص بك.

ثم قم بتعديل تكوين nginx الخارجي الخاص بك من التوجيه عبر nginx.http.sock إلى http://127.0.0.1:8008 (أو المنفذ الذي اخترته) وقم بمسح الرأس الافتراضي Connection: close، حتى لا يضطر nginx الخارجي إلى إنشاء اتصال IP جديد لكل طلب.

...
  location / {
    proxy_pass http://127.0.0.1:8008;
    proxy_set_header Host $http_host;
    proxy_http_version 1.1;
    # Disable default "Connection: close"
    proxy_set_header "Connection" "";
...
إعجاب واحد (1)

مرحباً @sam (وربما @falco). تم تكليفي بتنظيف بعض وثائق #documentation:sysadmin هذه. هذه الوثيقة لديها قراءات عالية جدًا وأعتقد أنها واحدة من أقلها فائدة.

هل تعتقد أنه من المنطقي كتابة بديل يقوم بتشغيل haproxy - Official Image | Docker Hub و https://hub.docker.com/_/nginx، ربما باستخدام docker compose، مع جعل حاوية nginx تركب الشهادات من حاوية discourse وجعل haproxy في وضع tcp للقيام بشيء مثل هذا (أنا متأكد من أن هذا لن ينجح، لكنني أفترض أنه يمكنني معرفة ما سينجح):

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

أعتقد أن هذا قد يكون حلاً قابلاً للتطبيق وأسهل في المتابعة من هذا الموضوع. سأترك هذا الموضوع لأغراض تاريخية (وربما أغلقه؟) ولكن سأربطه بالذي تم وصفه أعلاه.

3 إعجابات

لاحظ أن هذا الموضوع يتداخل بشكل كبير مع ذلك الموضوع:

لقد تلقى هذا الموضوع تحديثًا كبيرًا مؤخرًا (مما زاد من التداخل أكثر). ربما يكون من المنطقي دمج جزء صفحة عدم الاتصال بالإنترنت من هنا إلى هناك كملاحظة (نظرًا لأنه من السهل إضافته إذا كنت تشغل بالفعل مثيل Nginx منفصل)، ثم وضع علامة على هذا الموضوع على أنه مهمل (مع ربط البدائل)؟

سيظل موضوع HAProxy المقترح الخاص بك منطقيًا بالإضافة إلى ذلك، كطريقة افتراضية للأشخاص الذين لا يرغبون في تثبيت Nginx أمامي لأسباب أخرى.

إعجابَين (2)

كيف كان من المفترض أن أعرف؟

أوه.

لكن بجدية، أنا أحب حلك أكثر من حلي!

ويقول هذا الموضوع بأحرف كبيرة إنه موضوع متقدم.

ولكن ربما لا يكون ذلك ضروريًا أيضًا، حيث يفهم معظم الناس Nginx بشكل أفضل على أي حال. لقد بدأت في التفكير في الأمر الآن، لذا سيكون الجزء الصعب هو إقناعي بالتوقف. :ابتسامة:

3 إعجابات

من الجيد دائمًا أن تكون هناك بدائل وفيرة. لكن هذا هو أحد أسهل الخيارات (وهناك العديد منها…)، ومألوف للكثيرين.

لذا، من فضلك، لا تلمس هذا.

وترك هذا كما هو له نقطة أخرى أيضًا: نتائج البحث. بسبب حركة المرور العالية (والاستخدام المحدود جدًا للعلامات…)، أصبح من الصعب جدًا العثور على أي شيء محدد هنا في الوقت الحاضر. لكن هذا سهل العثور عليه وله غرض مستهدف للغاية. إذا انتقل هذا الموضوع إلى موضوع آخر، فسيكون العثور عليه أكثر صعوبة.

هناك سبب لكون هذا شائعًا جدًا… ليس الكثيرون مستوحون لاستخدام دوكر أو هابروكسي.

إعجابَين (2)

تنهد. حسناً، أعتقد أن هذا صحيح أيضاً، ولكنه قديم منذ 4 سنوات على الأقل. لم أقم بذلك مؤخراً، ولكنك لم تعد بحاجة إلى تعديل الملفات يدوياً لأن acme (أو شيء من هذا القبيل؟) سيفعل ذلك نيابة عنك.

ما أعتقده حقاً هو أنه من المنطقي أكثر استخدام تثبيت من حاويتين، والذي لديه وقت تعطل قصير بدلاً من القفز عبر هذه العقبات لوضع صفحة أثناء إعادة البناء، لكنني لا أستطيع إقناع الناس بذلك أيضاً.

لذا ربما يكون الشيء الذي يجب القيام به هو إعادة كتابة هذا لما تعمل به الأشياء اليوم.

إعجاب واحد (1)

وهو أصعب بكثير. أعتقد أنه من الأسهل إصلاح التعليمات حول كيفية تثبيت واستخدام certbot التي تبدأ في تعليم كيفية ومتى وأين يتم تحديث sql-side وما إلى ذلك. الحاويات.

بالإضافة إلى ذلك هناك نقطة أخرى ضد docker (حتى Discourse يعمل بهذه الطريقة …): يمكننا العثور على الكثير من الأسئلة الأساسية حول كيفية استخدام docker في المقام الأول. أو كيفية تجنب الأخطاء المطبعية في ملفات yml ؛)

ومع ذلك فإنه يعمل (باستثناء قسم SSL مربك بعض الشيء، ولكنه كان معطلاً حتى قبل 4 سنوات ؛))

لا. أنا لست ضد حلول أخرى. أنا ضد بشدة عندما يتم نقل الروابط والنصوص القديمة إلى مواقع جديدة دون أسباب وجيهة للغاية.

إعجابَين (2)

سيتعين علينا الاتفاق على الاختلاف في هذا الشأن. لكنني أعتقد أن هناك على الأرجح الكثير من الأشخاص الذين يتفقون معك. (ربما يكون هذا الحل هو حل “اضبطه وانساه”، ويتطلب الحل المكون من حاويتين الانتباه إلى وقت ترقية Postgres، والذي يحدث كل عامين تقريبًا.)

حسنًا. نتفق على هذا الجزء! لذلك أعتقد أن الطريق إلى الأمام هو رؤية ما يمكنني فعله لتنظيف هذا الجزء، ووضع حل haproxy قيد الانتظار.

إعجاب واحد (1)

أود أن أرى هذا يتم كجزء من Discourse ولكن شكراً لك @fefrei على هذا! عمل مذهل! سأستخدم Apache للقيام بذلك ولكن على الأقل يجب أن تكون الخطوات الأساسية كما هي.

إعجاب واحد (1)

حسنًا، لقد استغرق الأمر مني ساعتين فقط من العبث لجعله كما أريد!

صفحة صيانة Discourse مع Apache2

بصفتك مستخدمًا جذريًا (root)

cd /var/discourse
nano containers/app.yml

علّق هذه الأسطر:

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

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

أضف في نهاية قسم القوالب (templates) ما يلي (يجب أن يكون الأخير):

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

ملاحظة: سيؤدي هذا إلى جعل Discourse يستمع فقط على عنوان IP الداخلي وسيتحمل Apache2 منافذ 80/443 وإنهاء SSL.

ملاحظة: يجب إعادة بناء Discourse ليصبح هذا ساري المفعول:

cd /var/discourse
./launcher rebuild app

تثبيت Apache2 و Certbot

apt install -y apache2 certbot python3-certbot-apache

إنشاء دليل للصفحة HTML:

mkdir /var/www/discourse_maintenance

صفحة 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 class="title">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>

تمكين وحدة الوكيل (Proxy Module):

a2enmod proxy
a2enmod proxy_http
a2enmod headers

ملف مضيف Apache الافتراضي (vhost):

<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>

لتمكين الصيانة قم بتشغيل touch /var/www/under_maintenance

لتعطيل الصيانة قم بتشغيل touch /var/www/under_maintenance

الشكر والتقدير: Add an offline page to display when Discourse is rebuilding or starting up للفكرة الأولية، وصفحة HTML (تم تقليمها/تعديلها حسب رغبتي) وتكوين nginx الذي استندت إليه في تكوين Apache.

تعديل: اقتراحات مرحب بها لجعلها تلقائية عند الاستجابة 502/503. حاولت ولكن لم أتمكن من تحقيق ذلك بالطريقة التي أردتها، لذا لجأت إلى طريقة معروفة أستخدمها على خوادم الويب الأخرى عندما يكون التطبيق الخلفي معطلاً للصيانة وما إلى ذلك.

إعجابَين (2)

عند إعادة تشغيل النظام، سيؤدي ذلك إلى تأخير صفحة الخطأ/الصيانة حتى يتم تشغيل docker، وهو ما يستغرق وقتًا أطول بكثير من تشغيل النظام. كما أنه لا يوفر خيارًا للحماية التي يوفرها النظام SELinux لـ nginx النظام. يمكن أن يوفر استخدام nginx النظام، على الأقل على نظام مُدار بواسطة systemd مع تمهيد سريع، صفحة الخطأ في غضون ثوانٍ قليلة من التمهيد. بالنسبة لي، هذا يعني أن أنظمتي تستجيب بصفحة الصيانة بسرعة كبيرة أثناء تحديثات النظام التي تتطلب إعادة التشغيل. (أقوم بتشغيل AlmaLinux 9 على المضيف، وهو يتم تمهيده إلى nginx بسرعة كبيرة.)

قد يكون من المنطقي توثيق بديل haproxy ومقارنة التجارب، لكن haproxy-in-docker ليس بديلاً متكافئًا لـ nginx الخارجي، وإغلاق هذا الموضوع سيكون خطأ.

الأمر لا يتعلق بالتوافر فقط.

استخدام docker لحركة المرور الخارجية عبر IPv4 يخفي عناوين IPv6 الخارجية عن nginx الداخلي و Discourse. ستواجه نفس المشكلة مع haproxy. انظر إلى سجلاتك بحثًا عن عناوين IP المحلية فقط مثل 127.0.0.1 أو 172.* RFC1918. عدم استخدام وكيل خارجي يعني أن كل حركة مرور IPv6 تظهر بنفس عنوان IP، مما يكسر تحديد المعدل للمنطقة الداخلية لـ nginx، مع اعتبار كل حركة مرور IPv6 منطقة واحدة.

IPv6 يزداد أهمية.

إعجابَين (2)

اكتشفت عن طريق الخطأ هذا الصباح أن هذه الخطوة لا تتجنب فقط تطبيق مقبس يونكس، بل تزيل أيضًا استخدام وحدة real_ip، بحيث يتم تطبيق تحديد المعدل بناءً على جميع الاتصالات معًا، بدلاً من جميع الاتصالات لكل IP. ربما يجب أن أساهم بقالب جديد مع متغيرات، ولكن في الوقت الحالي أضفت هذا فقط إلى ملف app container YAML الخاص بي:

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;

لا أعرف ما إذا كان من المنطقي وجود، على سبيل المثال، ملف templates/web.httpratelimit.yml مع شيء مثل ذلك مع متغير للعنوان ولكن دون استخدام مقابس نطاق يونكس. أفكار حول ذلك؟

إعجابَين (2)
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/;
  }
}

! # قم بتغيير اسم النطاق الخاص بك ومسار ملف الخطأ

لقد حصلت على هذا النص البرمجي مع SSL ويعمل

إعجابَين (2)

لقد جربت للتو استخدام هذا الدليل ولم أتمكن من تشغيله.

بدأت بـ Nginx proxy لـ تشغيل موقعين من حاوية Discourse واحدة. أردت فقط إضافة جزء صفحة الخطأ، لذلك تخطيت الأجزاء التي بدت متداخلة مع Run other websites on the same machine as Discourse. لا بد أنني فاتني خطوة رئيسية. في النهاية حصلت على ما أحتاجه من هذا البرنامج التعليمي لـ DigitalOcean. إنه ليس من الصعب إعداده يدويًا، ولكنه يبدو وكأنه يجب أن تكون هناك طريقة أفضل.

نظرًا لأن Docker هي الطريقة القياسية لتشغيل Discourse، فهذا يبدو أفضل. أفترض أنه سيكون من النوع الذي تقوم بإعداده وتنساه.

3 إعجابات

الفكرة في هذا الموضوع رائعة أيضًا لأولئك منا الذين يستخدمون caddy كوكيل عكسي، سواء كان ذلك في تطبيق مستقل أو باستخدام 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
                }
        }
}

قسم status 404 مهم فقط إذا كنت تستخدم Cloudflare Tunnels. إذا أعاد caddy 5xx إلى Cloudflare، فسيعرض Cloudflare Tunnel خطأ انقطاع الاتصال الخاص به. تغيير الحالة يشير إلى Cloudflare بوجود اتصال مباشر صالح سيقدم صفحة خطأ.

إعجابَين (2)

ربما أكون أسيء فهم كيف يعمل هذا، لكن أليس التحديث مجرد تحديث للصفحة التي أنت عليه بالفعل؟ كيف يعيدك ذلك إلى عنوان URL مختلف؟

لا داعي للانتقال إلى عنوان URL مختلف - الخدعة هي أن صفحة الخطأ يتم تقديمها مباشرة على عنوان URL الذي حاول المستخدم الوصول إليه (على سبيل المثال، https://meta.discourse.org/t/add-an-offline-page-to-display-when-discourse-is-rebuilding-or-starting-up/45238/158)، وهذا هو عنوان URL الدقيق الذي سيتم تحديثه، مما يؤدي إما إلى نفس الخطأ مرة أخرى، أو إلى الصفحة التي أرادها المستخدم :slight_smile:

آه، فهمت. لم أقرأ الإعداد بالكامل بعناية لأنني أستخدم خادمًا آخر لصفحة الحالة، تحسبًا لانهيار كامل لجهازي لسبب ما.
لكن الأمر منطقي. الآن أنا فقط أحاول جعل جافا سكريبت الخاص بي الذي من المفترض أن يعيد التوجيه إلى URL الأصلي يعمل…