维护页面的变通方法——这能做到吗?

我使用 dual container build 并置于 Cloudflare CDN 之后。双容器模式将停机时间降至最低,我的两个主要生产论坛在重建期间最多只有 30 秒的离线时间。我喜欢使用维护页面,因为默认的网络服务器宕机页面既难看,又无法提示用户离线时间有多长。

例如,对于 your-domain.com 这个站点,我只需设置一个 Cloudflare Workers 路由,指向 *yourdomain.com/*(包含星号)。

步骤 1:创建 Worker 页面

你可以在 Cloudflare 的 workers & pages 设置页面中设置维护页面——点击 create application 按钮:

然后使用 hello world 模板:

然后给它起个名字并点击 deploy

然后进入该维护页面的概览页面,点击 edit code

并将以下代码粘贴到 worker.js 代码窗口中(替换你的域名以及你想要的消息,编辑文本、文字颜色、背景等):

export default {
  async fetch(request, env, ctx) {
    try {
      // Fetch the original request from your server
      const response = await fetch(request);

      // If YOUR SITE is swapping containers, it throws a 502, 521, or 530
      if (response.status === 502 || response.status === 521 || response.status === 530) {
        return returnCustomErrorPage();
      }

      // If everything is fine, return the normal forum traffic
      return response;
    } catch (e) {
      // If the server is completely unreachable
      return returnCustomErrorPage();
    }
  }
};

function returnCustomErrorPage() {
  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>System Refresh - YOUR-SITE.com</title>
      <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; text-align: center; padding: 50px; color: #333; background-color: #f9f9f9; }
        h1 { font-size: 2.5em; margin-bottom: 0.5em; color: #9400D3; }
        p { font-size: 1.2em; line-height: 1.5; }
        .container { max-width: 600px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>Just a quick update window</h1>
        <p><strong>YOUR-SITE</strong> is undergoing a brief 30-second system refresh.</p>
        <p>Grab a quick sip of your drink—this page will automatically refresh as soon as we are back online.</p>
      </div>
      <script>
        // Automatically check if the site is back up every 10 seconds
        setInterval(function() {
          window.location.reload();
        }, 10000);
      </script>
    </body>
    </html>
  `;

  return new Response(html, {
    status: 503, // 503 is best for SEO so Google doesn't penalize you for downtime
    headers: {
      "Content-Type": "text/html;charset=UTF-8",
      "Retry-After": "30"
    }
  });
}

它看起来应该像这样。点击 deploy 以保存:

步骤 2:添加路由

现在进入 Cloudflare worker route 页面,点击 add route 按钮以调出新的路由模态框,填入域名路由并选择你刚刚创建的 worker 页面,然后点击保存:

步骤 3:运行更新或重建

现在,当你通过 ssh 进入服务器并运行系统更新或进行重建时,在真正发生停机时显示的将不再是网站宕机或错误页面,而是这个页面(我的设置是 30 秒,因为这是我的站点的最大停机时间)

我倾向于在维护时添加和删除我的 workers 路由页面,但有些人喜欢一直保留它(我保留页面的实际代码,只是添加和删除路由)。

我想这就完了。

可选:使用脚本运行更新并设置计划

我还使用服务器上的 unix shell 脚本来运行双容器特定的更新,我是这样创建的:

cat << 'EOF' > /root/update-web.sh
#!/bin/bash
cd /var/discourse
echo "➡️ Pulling latest Discourse docker scripts..."
git pull

echo "➡️ Bootstrapping new web container in the background (takes ~8 mins)..."
./launcher bootstrap web_only

if [ $? -eq 0 ]; then
    echo "✅ Bootstrap successful! Swapping containers..."
    ./launcher destroy web_only && ./launcher start web_only
    echo "🚀 Done! Site updated with almost zero downtime."
else
    echo "❌ Bootstrap failed! Aborting swap to keep current site online."
fi
EOF

chmod +x /root/update-web.sh

然后在服务器的 root 提示符下使用命令 ./update-web.sh 运行它。


:warning: 编辑:除非你有付费的 cloudflare 账户,否则不要执行以下步骤。

你可以将其自动化到特定时间,例如你本地时间的周日凌晨 1 点。对我来说,1:00am local8:00 AM UTC,(你可以通过在 ssh 登录时输入 date 来查看服务器的 UTC 日期)。

所以:

运行此命令以打开服务器的任务调度器:

crontab -e

(如果它要求你选择编辑器,请选择你喜欢的编辑器,选择 1 即 nano 可能最容易)

我滚动到注释的底部并粘贴以下内容:

0 8 * * 0 /root/update-web.sh >> /var/log/discourse-update.log 2>&1

(这意味着在分钟 0、小时 8(UTC)、每个周日早上运行 /root/update-web.sh 文件)

然后保存并退出(如果你使用的是 nano):

  1. Ctrl + O 保存。
  2. Enter 确认。
  3. Ctrl + X 退出。

然后我可以在周日早上醒来时运行 cat /var/log/discourse-update.log 来检查它是否正常运行。如果你确实使用 cron 任务调度器,那么你将需要保留 workers 页面路由。

我想这就是全部了。 如果你有问题请告诉我 lol。 这比我看起来要简单得多 lol。 :grin: