🐍

Koyeb無料でDiscordBotを立ち上げようとしてヘルスチェックで2回ハマった話

に公開

1. はじめに

Discord Botを無料で24時間運用したい――そんなときに便利なのが、クラウドPaaSの「Koyeb」。
無料枠でもDockerイメージをデプロイでき、Python製のBotも簡単に動かせます。

しかし、いざ運用してみると「一晩経つとBotが止まっている」「Instance stoppedのエラーが出る」など、思わぬ落とし穴が…。
本記事では、KoyebでDiscord Botを安定運用するために実際にハマったポイントと、その解決策をまとめます。

読者ターゲット

  • 無料でDiscord Botを運用したい人
  • Koyebにデプロイしようとしてハマっている人
  • 一回ちゃんとデプロイできたぽいのに時間が経つとダメだな〜となっている人

2. 問題①:ヘルスチェックエラー

「Instance stopped」エラーの発生

KoyebにBotをデプロイしてしばらくは動くものの、翌朝見ると「Instance stopped」がログに出てBotが死んでいることがありました。
Bot自体は1日1回だけ通知を送るだけのシンプルなもの。
なぜ止まるのか、最初は全く分かりませんでした。(ログも出ず突如止まるんですよね。)

ヘルスチェックが通らない原因

調べてみると、Koyebの無料プランでは「ヘルスチェック」が自動で有効になっており、
デフォルトで8000番ポートにHTTPリクエストが飛ばされます。
このヘルスチェックに応答しないと「unhealthy」と判定され、インスタンスが自動で停止されてしまう仕様です。

つまり、Bot本体とは別にHTTPサーバーを立てて、ヘルスチェックに応答する必要があったのです。

公式サイトでも、
ヘルスチェック用のエンドポイントを用意すること」が推奨されています。

If you are deploying an HTTP server, like an API or a web application, we recommend that you define custom HTTP health checks on the relevant ports.

3. 問題①の解決方法:ヘルスチェックサーバーの追加

Flaskでヘルスチェック用エンドポイントを作成

Bot本体とは別に、Flaskなどの軽量Webフレームワークで「/」や「/health」などのエンドポイントを用意します。

サーバ起動用クラス

class HealthCheckServer:
    def __init__(self, host="0.0.0.0", port=8000):
        self.host = host
        self.port = port
        self.thread = None

    def start(self):
        def run():

            app = Flask(__name__)
            @app.route("/")
            def health():
                return "OK", 200
            app.run(host=self.host, port=self.port)
        self.thread = threading.Thread(target=run, daemon=True)
        self.thread.start()

Botのエンドポイント

from healthcheck import HealthCheckServer

@bot.event
async def on_ready():
    await bot.tree.sync()
    scheduler = Scheduler(bot)
    scheduler.start()

https://github.com/kannna5296/task-boyfriend/commit/3f0c70075950b2629e298def10693abb263263f1

⇩こちらの記事を参考に実装しました。

PythonのWebフレームワークには詳しくないのですが、より軽量っぽかったのでFlaskを選びました。

https://zenn.dev/amano_spica/articles/24c5f288cf9595

ヘルスチェックの成功

上記実装の上でデプロイすると、ヘルスチェックが通るようになりました。この時、Koyebの「Instance is healthy. All health checks are passing.」という表示が出ます。

Koyebのサービス画面上には、こういうマークが出ているはず。(紫部分はIDなので隠しています)

4. 問題②:Sleeping状態の回避

Koyebでヘルスチェックを通しても、しばらく外部からのアクセスがないとインスタンスが「Sleeping」状態になり、Botが一時停止してしまうことがあります。これは無料プランの仕様で、一定時間リクエストがないと自動的にスリープしてしまうためです。

公式サイトより引用

They scale down to zero when they don’t receive any traffic for 1 hour.

問題②の解消:cron-job.orgを使った定期起動設定

この問題を回避するために、外部サービス「cron-job.org」を利用します。cron-job.orgは、指定したURLに定期的にGETリクエストを送ってくれる無料サービスです。

30分ごとのGETリクエスト設定

  1. cron-job.orgに無料登録
  2. 新規ジョブ作成で、Koyebのヘルスチェック用エンドポイント(例: https://<your-app>.koyeb.app/)を指定
  3. 実行間隔を「30分ごと」などに設定

これで、Koyeb側のインスタンスが定期的にアクセスされるため、Sleeping状態を防ぎ、Botが24時間稼働し続けます。


5. 実際の動作確認

デプロイ後のログ確認

Koyebの管理画面でデプロイ後のログを確認し、

  • Flaskサーバーが8000番ポートで起動しているか
  • Discord Botが正常に起動しているか
    をチェックします。

Botの正常動作確認

Botが指定した時間にメッセージを送信できていればOKです。


6. まとめとベストプラクティス

  • Koyebの無料プランでDiscord Botを運用する場合、ヘルスチェック用のHTTPサーバーを必ず用意する
  • cron-job.orgなどの外部サービスで定期的にアクセスを送ることで、Sleeping状態を防げる
  • 無料枠の制限や仕様変更には注意し、公式ドキュメントも随時チェックする

参考リンク

Discussion