添加离线页面,在Discourse重建或启动时显示

在按照本指南操作后,我的论坛无法上传大于 500kb 的 .png 文件,有什么明显的原因吗?

我看到有人讨论了这一行 client_max_body_size 0;,但这不应该是问题所在,对吧?

编辑:发帖后我很快就解决了。需要在设置中勾选 :white_check_mark: 强制 HTTPS。如果以后有人遇到此问题,我会保留此帖。

3 个赞

我注意到这里:

nginx 建议禁用 Connection: close 标头并将 proxy_http_version 设置为 1.1 — 类似这样:

    proxy_http_version 1.1;
    # Disable default “Connection: close”
    proxy_set_header “Connection” “”;

我找不到任何关于 Connection: close 是否对 Unix 域套接字有任何影响的文档,但由于此文档对于在单独的系统上运行外部代理也很有用,并且我预计删除该标头不会造成损害,因此在此处建议删除它是否有意义?

1 个赞

如果您已在启用了 SELinux(enforcing 模式)的系统上部署了此服务,则无法使用 Unix 域套接字让主机与容器通信,因为即使您重新标记了 Unix 域套接字,每次重启容器时它都会被重新创建而没有标签。因此,您需要进行两项更改。

您需要允许 nginx 访问错误页面,并将代理从 Unix 域套接字切换到端口。这将使每次请求的延迟增加几微秒,因为运行启用了 SELinux 的 nginx 作为一层安全措施的成本,但这不会被您的用户察觉。

首先,运行以下命令以允许 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 — 您已在外部 nginx 中终止了 SSL,并且 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 Hubhttps://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。不过我现在已经开始考虑了,所以难的部分将是让我停下来。 :slight_smile:

3 个赞

总是有很多替代方案总是好的。但这是最简单的方案之一(相当多的方案……),而且对很多人来说都很熟悉。

所以请不要动它。

就这样保留它还有另一个好处:搜索结果。由于流量很大(而且标签使用非常有限……),现在在这里找到任何具体的东西都相当困难。但这个很容易找到,而且用途非常明确。如果这个主题转移到另一个地方,找到它会更加困难。

它之所以如此受欢迎是有原因的……不是很多人都受到启发去使用 docker 或 haproxy。

2 个赞

唉。好吧,我猜这也是事实,但它至少有 4 年没有更新了。我最近没有做过,但你不再需要手动修改文件了,因为 acme(或者类似的东西?)会为你完成。

我真正认为的是,使用双容器安装更有意义,停机时间很短,而不是为了在重建时上线页面而费尽周折,但我也无法说服人们。

所以也许应该根据今天的情况重写一下。

1 个赞

而且它更难。我认为修复 certbot 的安装和使用说明,而不是教授如何、何时以及在哪里更新 sql-side 等容器,会更容易。

另外,还有一点不利于 docker(即使 Discourse 也是这样工作的……):我们可以找到很多关于如何首先使用 docker 的基本问题。或者如何在 yml 文件中避免拼写错误 :wink:

然而它仍然有效(除了 SSL 部分有点令人困惑,但它在 4 年前就已经关闭了 ;))

不。我不是反对其他解决方案。我非常反对在没有非常充分的理由的情况下将旧链接和文本移动到新位置。

2 个赞

我们对此意见不一。但我相信有很多人会同意你的看法。(也许这个解决方案是“设置好然后就不用管了”的解决方案,而双容器解决方案确实需要注意 PostgreSQL 升级的时间,这大约每两年发生一次。)

好的。这一点我们达成一致!所以我想接下来的做法是看看我能做些什么来清理那部分,然后暂时搁置 haproxy 解决方案。

1 个赞

我很希望能在 Discourse 中看到这个功能,但还是要感谢 @fefrei 的出色工作!太棒了!我将使用 Apache 来实现,但至少基本步骤应该是相同的。

1 个赞

OK,我花了 2 个小时才把它弄好!

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 模块:

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 时。我尝试过但无法按预期工作,所以我采用了其他 Web 服务器在后端应用程序因维护等原因停机时使用的已知方法。

2 个赞

在系统重启时,这会将错误/维护页面延迟到 docker 启动为止,而 docker 启动需要比系统启动更长的时间。它也没有为系统 nginx 提供系统提供的 SELinux 保护选项。至少在 systemd 管理的系统上,使用系统 nginx 可以实现快速启动,在启动后几秒钟内显示错误页面。对我来说,这意味着在需要重启的系统更新期间,我的系统会非常快速地响应维护页面。(我在主机上运行 AlmaLinux 9,它能非常快速地启动到 nginx。)

记录一个 haproxy 替代方案并比较经验可能是有意义的,但 docker 中的 haproxy 并不是外部 nginx 的直接替代品,关闭这个主题将是一个错误。

这不仅仅是可用性问题。

使用 docker 通过 IPv4 处理外部流量会向内部 nginx 和 Discourse 隐藏外部 IPv6 地址。 您将面临与 haproxy 相同的问题。查看您的日志中是否有 127.0.0.1 或 172.* RFC1918 仅限本地的 IP 地址空间地址。不使用外部代理意味着所有 IPv6 流量都显示为同一 IP 地址,这会破坏内部 nginx 的区域速率限制,因为所有 IPv6 流量都被视为一个区域。

IPv6 越来越重要。

2 个赞

我今天早上偶然发现,这一步不仅避免了应用 Unix 套接字,而且还移除了 real_ip 模块的使用,因此速率限制是基于所有连接的总和来应用的,而不是基于每个 IP 的所有连接。我可能应该贡献一个带有变量的新模板,但现在我只是将它添加到了我的 app 容器 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 的文件,其中包含类似这样的内容,并有一个用于地址但未使用 Unix 域套接字的变量。对此有什么看法?

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 代理来从单个 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
                }
        }
}

仅在使用 Cloudflare Tunnels 时 status 404 部分才重要。如果 caddy 向 Cloudflare 返回 5xx,Cloudflare Tunnel 将显示其自己的断开连接错误。更改状态表示 Cloudflare 存在一个有效的实时连接,该连接将提供错误页面。

2 个赞

也许是我误解了它的工作原理,但刷新不只是刷新你已经在浏览的页面吗?这样怎么能回到不同的网址?

无需转到其他 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:

哦,我明白了。我之前没有仔细阅读整个设置,因为我使用另一个服务器来处理离线页面,以防我的整个机器因为某种原因宕机。不过现在说得通了。现在我只是试图让我的JavaScript正常工作,使其能够重定向回原始的URL……