前の記事で、Claude Code の利用枠を 3.5 インチの USB ディスプレイに出すツールを作りました。そのディスプレイは、黒のつもりで間違えて白を注文したものです。机に置くと思っていたより小さかったので、5.2 インチ(1280×720)を注文し直し、利用枠の表示はそちらに移します。

余った 3.5 インチは、分解して外装を黒く塗装し、当社が運営する RIKKA M&A(rikkama.jp)のサーバーの状態を常に出す卓上モニターにしました。

黒く塗装した 3.5 インチのディスプレイを縦に置き、rikkama.jp の CPU・メモリ・ディスク・スワップの使用率をリングと数字で表示している

CPU・メモリ・ディスク・スワップの使用率と、外形・死活監視の判定(「監視 正常」)を、1 分ごとに出しています。コードは前の記事と同じ studioc-co-jp/claude-usage-display(MIT)の monitor コマンドです。

何を出すか

4 つの値は、サーバーに入れた CloudWatch のエージェントが送っているメトリクスです。しきい値は、既存の CloudWatch のアラームと同じにしました。

区画メトリクスしきい値
CPUcpu_usage_active80%
メモリmem_used_percent85%
ディスクdisk_used_percent90%
スワップswap_used_percent90%

リングは、しきい値の 15 ポイント手前からオレンジ、しきい値以上で赤になります。見出しには、外形・死活監視の判定を出します。

死活は Mac から測らない

最初に考えたのは、この Mac から rikkama.jp へ 1 分ごとに問い合わせることでした。やめた理由は 2 つあります。

  1. **この Mac のネットワークが切れると、サイトは動いているのに「停止」と出る。**卓上の表示が誤って赤くなれば、やがて表示そのものを信用しなくなります
  2. **トップページは CDN のキャッシュから返る。**応答のヘッダーは cf-cache-status: HIT で、この Mac から問い合わせても、届くのは CDN までです。サーバーが止まっていても、キャッシュが残っている間は気付けません

代わりに、AWS の中で 1 分ごとに動いている自前の外形・死活監視(Lambda)の判定を読むことにしました。この監視は、サイトの応答・証明書の残り日数・定期ジョブの実行の記録を判定し、キャッシュされうる URL は問い合わせごとに変わる値を付けてオリジンまで確かめています。結果は S3 の状態ファイル(JSON)に書かれており、表示機はその 1 ファイルを読むだけです。

監視そのものが止まったときに備えて、状態ファイルが 5 分以上更新されなければ「監視が止まっています」として赤にします。同じ地域の障害で監視ごと止まった場合でも、黙って「正常」を出し続けないためです。

赤にするもの、しないもの

画面全体を明るい赤(Apple の Human Interface Guidelines の Red)にするのは、次のときです。

  • 監視が判定した、サイトの応答や証明書の異常
  • 4 つの値が、しきい値以上のまま続いたとき(次の節で直した条件)
  • 値が 5 分以上届かないとき
  • 監視の状態ファイルが 5 分以上更新されないとき

見本の値の異常の画面。画面全体が赤く、見出しの白い帯に「トップ HTTP 503(期待 200)ほか 1 件」、CPU のリングと数字だけが白で、ほかは薄い

赤い地では赤の文字が見えないので、異常の項目を白ではっきり、ほかを薄く出し、見出しの白い帯に最初の異常を出します(画像は見本の値で、名前は架空の example.com です)。

一方、**定期ジョブの障害と監視の設定のずれは、赤にしません。**地を黒のまま、見出しにオレンジで件数を出します。

見本の値のジョブ障害の画面。地は黒のまま、見出しにオレンジの点と「ジョブ障害 2 件」

ジョブの障害には、原因の調べや外部のサービスの都合で、何日も続くものがあります。それも赤にすると赤が出っぱなしになり、その間に起きたサイトの異常を見落とします。赤は「今すぐ見るもの」だけにしました。

この Mac から AWS に届かないときも、赤にはしません。見出しにオレンジで理由を出し、前回の値を残します。サイトの異常と区別するためです。

赤の条件を、アラームと同じにし直した

記事を書く前に、しきい値の出どころである CloudWatch のアラームの設定と、表示機の作りを突き合わせて、食い違いを見つけました。

アラームは、cron やデプロイの瞬間的な高負荷で鳴らないよう、しきい値以上が続いた時間を条件に持っています。

値しきい値アラームが鳴る条件
CPU80%1 分の平均が 5 回続けてしきい値以上
メモリ85%1 分の平均が 5 回続けてしきい値以上
ディスク90%1 分の平均が 1 回しきい値以上
スワップ90%5 分の平均が 3 回(15 分)続けてしきい値以上

一方、表示機は、直近 1 分の平均がしきい値以上になるだけで、画面全体を赤にしていました。表示機と同じ鍵で、過去 14 日(9 月 27 日〜10 月 11 日)の 1 分ごとの値を読み、両方の判定を当てはめました。

判定画面全体が赤になる回数
直近 1 分(直す前)14 回(CPU 13・スワップ 1)
アラームと同じ条件(直した後)2 回(CPU が 32 分・16 分続いたとき)

直す前の 14 回のうち 12 回は、1〜4 分の短い山でした。気付くことを優先して明るい赤にした画面が、1 日に 1 回ほど一時的に赤くなれば、やがて赤を見ても確かめなくなります。

そこで、各値に、アラームと同じ period(平均を取る秒数)と datapoints(続けて超えた回数)を設定に持たせました。いちばん新しい点から途切れずにしきい値以上が datapoints 回続いたときだけ、画面全体を赤にします。1 回だけの山では、リングだけが赤になります。

def over_streak(datapoints, threshold, period):
    """いちばん新しい平均から数えて、しきい値以上が途切れずに続いている回数。"""
    streak, previous = 0, None
    for point in sorted(datapoints, key=lambda p: p["Timestamp"], reverse=True):
        if point["Average"] < threshold:
            break
        if previous is not None and (previous - point["Timestamp"]).total_seconds() > period:
            break
        streak, previous = streak + 1, point["Timestamp"]
    return streak

間の空いた点は、続いていないとみなします。判定のこの 1 行を元の「直近 1 回」に戻すと、1 回だけの山のテストが落ちることも確かめました。

直す途中で、もう 1 つ見つけました。スワップを 5 分ごとの平均で読むと、いちばん新しい点が 5 分より古いことがあります(直した直後の実測で 5.4 分前)。「値が 5 分以上届かない」で赤にする判定のままでは、それだけで赤になります。5 分ごとの値は、2 周期(10 分)まで待つようにしました。

値は GetMetricStatistics で読む

CloudWatch の値を読む API には、GetMetricData と GetMetricStatistics があります。CloudWatch の料金ページの「Free Tier」は、無料の範囲を次のように書いています。

1 Million API requests (not including GetMetricData, GetInsightRuleReport and GetMetricWidgetImage: these 3 operations are always charged)

GetMetricData は、無料の範囲に入らず、常に課金されます。表示機は GetMetricStatistics を 1 分ごとに 4 回呼ぶので、月に約 17 万リクエストで、月 100 万の無料の範囲に収まります。続けて超えた回数は 1 回の呼び出しで読む範囲の中で数えるので、前の節の修正で呼び出しの回数は増えていません。

鍵は読み取り専用で、新しく作った

表示機のために、読み取り専用の IAM ユーザーを新しく作りました。与えた権限は次の 2 つだけです。

  • cloudwatch:GetMetricStatistics
  • 監視の状態ファイル 1 つだけの s3:GetObject

鍵が漏れても、読めるのはメトリクスの時系列と、どの監視が障害かという状態にとどまります。書き込みの権限は 1 つもありません。

本番のサーバーには、すでに読み取り用の鍵があります。それを使い回さなかったのは、本番と Mac で同じ鍵を持つと、片方で鍵を作り直したときにもう片方も止まり、漏れたときにどちらから漏れたかも切り分けられないためです。

鍵は Mac の Keychain に置きました。登録するときは、値をコマンドの引数に載せず、security -i の標準入力から渡しています。引数に載せると、実行中のプロセスの一覧に出るためです。作った後に、メトリクスと状態ファイルが読めること(陽性)と、書き込み・アラーム・バケットの一覧・ほかのファイル・ほかのサービスが拒否されること(否定)を確かめました。

画面は見本の画像を見比べて決めた

画面は、見本の画像を何枚も描いて見比べ、決めました。

  • カードを使わない 3 案(4 つを横に並べる・同心円・2×2)から、2×2 を選んだ
  • しきい値を超えた値だけを赤くする版と、画面全体を赤にする版を見比べ、「アラートに気付くことが大切」として、画面全体を明るい赤にする版を選んだ
  • 「しきい値 80% 超え」のような文言は付けず、しきい値の行は常に「しきい値 80%」とだけ書く

見本の値の正常の画面。縦置きで、名前の下にリング、リングの中に数字が並ぶ

3.5 インチは縦に置くことにして、縦置きの 2 案から「名前の下にリング、リングの中に数字」を選びました。実機に送ると天地が逆だったので、パネルへ送る向きの命令を「縦の逆」にしています。前の記事で書いた向きの命令(命令番号 121)の値は、横が 2・上下逆の横が 3 で、縦は 0・縦の逆は 1 です(いずれも 100 を足して送ります)。実機で見ると文字とリングが小さかったので、名前(15→18 ピクセル)・しきい値(12→14)・リングの半径(46→56)・リングの中の数字(30→34)を大きくしました。

2 台の使い分け

5.2 インチが届いたら、利用枠は 5.2 インチ、サーバーの状態は 3.5 インチに出します。2 台を同じ Mac につなぎ、--device で使うディスプレイを指定して、自動起動を 2 つ登録します。指定を省くと 5.2 インチを先に探す作りなので、5.2 インチを外したときに、サーバーの状態の画面が利用枠のディスプレイを取りに行かないよう、両方に必ず付けます。

5.2 インチは、シリアルではなく USB で直接通信する新しい世代の機種です。Mac で動かせたかは、届いてから改めて書きます。

まとめ

  • 卓上の表示で死活を自分で測ると、自分の回線の切断をサイトの停止と取り違えます。CDN の後ろのサイトなら、届くのは CDN までです。オリジンまで確かめている既存の監視の判定を読むほうが確かです
  • 赤は「今すぐ見るもの」だけにします。何日も続きうる障害は、別の色で件数を出します
  • しきい値だけを真似ても、アラームと同じ判定にはなりません。続いた時間の条件まで合わせ、過去の値に当てて回数を確かめます
  • CloudWatch の値を常に読むなら GetMetricStatistics を使います。GetMetricData は無料の範囲に入りません
  • 鍵は用途ごとに新しく作り、読めることと読めないことの両方を確かめます