メンテナンスページの回避策 - これって可能?

dual container build を Cloudflare CDN の背後で運用しています。デュアルコンテナ構成はダウンタイムを最小限に抑え、私の 2 つのメインの本番フォーラムでは、再構築時に最大 30 秒のオフライン時間しか発生しません。メンテナンスページを表示させるのは気に入っています。デフォルトの Web サーバーの停止ページは見た目が悪く、どれくらいの時間オフラインになるのかを示す情報が全くないからです。

例えば、your-domain.com というドメインを持つサイトの場合、Cloudflare Workers のルートを *yourdomain.com/*(アスタリスクを含める)に設定するだけです。

ステップ 1: Worker ページの作成

Cloudflare の「Workers & Pages」設定ページでメンテナンスページを設定できます。「アプリケーションを作成」ボタンをクリックします:

次に、Hello World テンプレートを使用します:

名前を付けて「デプロイ」をクリックします:

次に、そのメンテナンスページの概要ページに移動し、「コードを編集」をクリックします:

そして、このコードを worker.js のコードウィンドウに貼り付けます(あなたのドメインと、表示したいメッセージ、テキスト、文字色、背景色などを編集してください):

export default {
  async fetch(request, env, ctx) {
    try {
      // サーバーから元のリクエストを取得
      const response = await fetch(request);

      // あなたのサイトがコンテナを切り替えている場合、502、521、または 530 エラーを返します
      if (response.status === 502 || response.status === 521 || response.status === 530) {
        return returnCustomErrorPage();
      }

      // 問題がない場合、通常のフォーラムトラフィックを返します
      return response;
    } catch (e) {
      // サーバーが完全に到達できない場合
      return returnCustomErrorPage();
    }
  }
};

function returnCustomErrorPage() {
  const html = `
    <!DOCTYPE html>
    <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>システム更新中 - 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>短時間のシステム更新中</h1>
        <p><strong>YOUR-SITE</strong> で約 30 秒のシステム更新を行っています。</p>
        <p>お飲み物を一口どうぞ。オンラインに戻り次第、このページは自動的にリフレッシュされます。</p>
      </div>
      <script>
        // 10 秒ごとにサイトが復旧したか自動的にチェック
        setInterval(function() {
          window.location.reload();
        }, 10000);
      </script>
    </body>
    </html>
  `;

  return new Response(html, {
    status: 503, // 503 は SEO に最適です。ダウンタイムで Google からペナルティを受けるのを防ぎます
    headers: {
      "Content-Type": "text/html;charset=UTF-8",
      "Retry-After": "30"
    }
  });
}

だいたいこのような見た目になります。保存するには「デプロイ」をクリックします:

ステップ 2: ルートの追加

次に、Cloudflare Workers のルートページに移動し、「ルートを追加」ボタンをクリックして新しいルートのモーダルを開きます。ドメインルートを記入し、先ほど作成した Worker ページを選択して、「保存」をクリックします:

ステップ 3: 更新や再構築の実行

これで、サーバーに SSH で接続してシステム更新を実行したり、再構築を行ったりした際、サイトダウンやエラーページではなく、実際にダウンタイムが発生した際にこのページが表示されます(私のサイトでは最大 30 秒としています)。

メンテナンス中は Workers ルートページを追加・削除する傾向がありますが、常時残しておく方もいます(私は実際のページのコードは残したまま、ルートの追加と削除のみを行っています)。

これでおしまいです。

オプション: スクリプトとスケジュールによる更新の実行

また、サーバー上で Unix シェルスクリプトを使用して、デュアルコンテナ固有の更新を実行しています。以下のように作成しました:

cat << 'EOF' > /root/update-web.sh
#!/bin/bash
cd /var/discourse
echo "➡️ Discourse Docker スクリプトの最新バージョンを取得中..."
git pull

echo "➡️ 背景で新しい Web コンテナをブートストラップ中(約 8 分かかる)..."
./launcher bootstrap web_only

if [ $? -eq 0 ]; then
    echo "✅ ブートストラップ成功!コンテナを切り替え中..."
    ./launcher destroy web_only && ./launcher start web_only
    echo "🚀 完了!ダウンタイムをほぼゼロに抑えてサイトを更新しました。"
else
    echo "❌ ブートストラップ失敗!現在のサイトをオンラインのまま保つため、切り替えを中止します。"
fi
EOF

chmod +x /root/update-web.sh

その後、サーバーの root プロンプトで ./update-web.sh コマンドを実行します。


:warning: 編集: 有料の Cloudflare アカウントをお持ちでない限り、以下の手順は実行しないでください。

cron ジョブを使用して、ローカル時間の日曜日午前 1 時など、特定の時間に自動化できます。私の場合、ローカル時間 1:00UTC 8:00 です(SSH で接続した状態でコマンドプロンプトで date と入力すると、サーバーの UTC 日付を確認できます)。

つまり:

サーバーのタスクスケジューラーを開くには、このコマンドを実行します:

crontab -e

(エディタの選択を求められた場合は、好みのエディタを選択してください。1 の nano が最も簡単です)

コメントの一番下に移動して、これを貼り付けます:

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

(これは、日曜日の朝、UTC 8 時 0 分に /root/update-web.sh ファイルを実行するという意味です)

次に、保存して終了します(nano を使用している場合):

  1. Ctrl + O で保存。
  2. Enter で確認。
  3. Ctrl + X で終了。

その後、日曜日の朝に起きると、cat /var/log/discourse-update.log を実行して、正常に実行されたか確認できます。cron タスクスケジューラーを使用する場合は、Worker ページのルートを残しておく必要があります。

これでおしまいです。質問があれば教えてください。笑。思っていたよりもずっと簡単ですよ。笑 :grin: