前回、空っぽのサイトを公開まで一通りやり切って、次はこの箱にAIを常駐させてスマホから話しかける、と予告して終わった。今回はまだそこへは行かない。その前に、もう一つだけ土台を作っておくことにした。サーバーの「見張り番」だ。
見張り番を、いつもの型で立てる
使うのはUptime Kumaという、死活監視ツールだ。「登録した相手に一定間隔でアクセスして、返事があるか(生きているか)を確かめ、様子が変わったら知らせてくれる」というだけの、単純な仕組みだと理解した。
立て方は、前回の空っぽサイトのときとほぼ同じだった。サービスごとに専用のフォルダを作り、そこにcompose.yamlという設定ファイルを1枚置いて、docker compose up -dで起動する。イメージの名前とポート番号が違うだけで、書いた内容は驚くほど前回の使い回しだった。フェーズ3で一つの型を覚えると、次のサービスはその型に乗せるだけでいい、というのを実感した回でもあった。
管理画面を開いて、最初の管理者アカウントを作る。外部にパスワードの再発行手段がないツールなので、決めたパスワードはすぐに控えた。しばらくすると、見張り対象を登録する画面が出てきて、いよいよ本題に入れる。
表には一切出さない、という選択
前回の空っぽサイトは「世界に公開する」ことが目的だったが、今回は逆だ。この見張り番の画面は、絶対に世界には出さない。ポートは一つも開けず、Cloudflareの公開の仕組みにも載せなかった。開けるのは、家の中からと、前々回に設定したTailscale(自宅の箱と自分の端末だけを繋ぐ、専用の通路のようなもの)越しの2つだけにした。
「見張り番は家の中に置く」——サーバーの調子を見るための画面が世界中から覗ける状態は、さすがに気持ちが悪い。ここは最初から迷わず決められた。
途中、ちょっとした引っかかりがあった。見張り対象のURLを最初「自分自身」を指す書き方で入れてしまい、赤(ダウン扱い)のままだった。調べると、Uptime Kumaは自分の入っている箱の中が世界のすべてで、箱の中で「自分自身」と言うと、隣にある別のサービスの箱ではなく、Uptime Kuma自身を指してしまうらしい。だから、隣の箱には家の中の住所を使って、正面から改めてノックしてやる必要があった。直したらすぐ緑になった。コンテナというのは、思っていた以上に自分の中に閉じているものらしい。
見張る相手は3つ、間隔は行儀よく
登録したのは3つだ。自宅の空っぽサイト、そして会社で動いている本番サービスを2つ。
自宅のサイトは自分の持ち物なので、60秒おきに様子を見に行くことにした。一方、会社の本番サービスは相手が自分のものではない。あまり短い間隔で叩き続けるのは、いくら軽い確認とはいえ迷惑になりうる。そちらは5分おきという、少し余裕を持たせた間隔にした。自分のものは短く、他人(外部・本番)は長く——これが「行儀の良い間隔」というものらしいと、今回初めて意識した。
3つとも登録し終えて、しばらくすると全部が緑になった。1画面で自宅と会社のサービスの生死が同時に見えるというのは、地味だが安心感がある。
Discordへの片道配線 — 今回の本題
ここが今回、一番やりたかったことだ。
前回の終わりに書いた通り、次にやるのはこの箱にAIを常駐させて、スマホからDiscordで話しかけたら動いてくれる、という構成だ。そのためには「箱からDiscordへメッセージを送る」という配線が必要になる。いきなり本番でそれを作るのは怖いので、今回は「壊れても誰も困らない」見張り番を使って、その配線だけ先に練習しておくことにした。
自分だけのDiscordサーバーを一つ作り、通知を受け取るチャンネルを一つ用意した。そのチャンネルの設定から「ウェブフック」というものを発行する。これは「このURLにメッセージを投げ込めば、指定したチャンネルに書き込まれる」という、投げ込み専用の受け口らしい。長い文字列のURLが1本発行され、これを知っている人は誰でもそのチャンネルに書き込めてしまうと知って、他人に見せないよう気をつけた。
発行したURLをUptime Kumaの通知設定に貼り付けて、「テスト」ボタンを押した。Discordのチャンネルに、テストメッセージがすっと届いた。ここまでは想定通りだった。
驚いたのはこのあとだ。3つの見張り対象すべてにこの通知を割り当ててから、スマホのDiscordアプリの通知がオンになっているかを確かめて、もう一度テストを押してみた。手元にはスマホしかない、いつもの外出のときと同じ格好で。
ロック画面に、Discordの通知がふっと表示された。自宅の小さな中古PCで起きたことが、ポケットの中のスマホまで飛んできた瞬間だった。理屈は分かっていたつもりだったが、実際に自分の手で届かせてみるまでは、どこかで半信半疑だったのだと思う。
これで、Discordへの片道の配線は通った。次にやることは、この配線に「自分がDiscordに書いた内容を、箱の中のAIが受け取って、また返してくる」という逆向きを足すだけだ、と今なら言える。半分はもう、今回で体験できている。
バックアップにも一つ新しい罠があった
前回、週に1回サーバーの中身を固めてよそへコピーする仕組みを作った。同じ枠に、Uptime Kumaの設定も足しておく。
ここで初めて見る類の罠があった。前回の空っぽサイトとは違い、Uptime Kumaの箱は中で管理者権限(root)として動いているらしい。設定を貯めているフォルダの中身が、その権限で作られたファイルになっている可能性があり、普段使っている自分のユーザーでは読めないかもしれない、と身構えた。そのまま固めると、見た目はバックアップができたように見えて、中身が一部欠けた不完全なものになりかねない。
言われた通り、固めたあとのファイルの中身を覗いて、本体のデータベースがちゃんと入っているかを確認してから、定期実行の設定に登録することにした。開けてみると、今回は幸い、普段のユーザー権限のままで中身まで問題なく取れていることが確認できたので、権限を上げずに済んだ。root所有だから必ず読めない、というわけでもないらしい。中身を見ずに「できたつもり」で済ませていたら、この違いには気づかなかったと思う。
定期実行の時刻は、前回登録した空っぽサイトの分と5分ずらして、日曜の朝に登録した。同じ時間に重なって両方が固まり始めると、サーバーに余計な負担がかかるかもしれない、という理由だ。
今回は、一度も詰まらなかった
作業を振り返って、少し意外だったことがある。今回は、これといって大きく詰まった場面がなかった。前回までは毎回どこかで「あれ、動かない」に出会って、調べて、直して、という時間があったが、今回はほとんどそれがなかった。
理由は、たぶん覚えたことがそのまま使えたからだと思う。フォルダを分ける、compose.yamlを書く、restart: alwaysを入れる、フォルダごと固めてバックアップする——前回までに一つずつ確かめながら身につけた型が、今回はもう手癖になっていた。新しく出会ったのは「箱の中は自分だけの世界」ということと、「権限によっては見えないファイルがある」ということくらいで、それも一度理解すればすんなり進んだ。
サーバーはこれで、自分が見ていなくても異常を教えてくれる存在になった。次はこの箱に、AIを住まわせる番だ。今回作ったDiscordへの片道の配線が、そのまま両方向のやり取りの土台になる。