0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWSの通知をPushoverに中継してスマホから気付けるようにした話

0
Posted at

はじめに

こんにちは、KokoHakotaroです。

最近、私はあるWeb掲示板サイトを定期的にスクレイピングし、更新があったときに通知を送るというシステムを開発しました。
このシステムのキモとなるのが、システム通知を送る手段です。

AWSで通知といえば、SNS経由のメール通知が定番、しかし、大量のメールに埋もれてしまい、すぐに気づくことができないこともしばしば。
理想としているのは、スマホの通知アプリにPush通知を送るという方法でしたが、自分でそんなアプリを作れるはずもなく...と、そんなときに見つけたのが、Pushoverという通知サービスです。

今回は、このPushoverを使って、システム通知に加えてAWSの障害アラートも、まとめてスマホにPush通知してしまおう!というお話です。

通知先に使える様々なサードパーティ製アプリ

AWSの通知をスマホに送る手段には、いろいろあるかと思います。メール、チャットツールへの通知、プッシュ通知専用アプリ……などです。

CloudWatch AlarmやアプリからのイベントをSNSトピックに集約し、そこからLambdaを経由してHTTP/APIを叩けば、通知先には任意のサードパーティ製アプリを選べます。SlackやLINEなどのチャットツールへのWebhook通知も同じ仕組みで実現できますが、これらは業務連絡や他のメッセージと同じ画面に表示されるため、埋もれてしまうという点ではメール通知と同じ課題を抱えています。

そこで今回は、冒頭で触れたPushoverを採用しました。

Pushoverとは

Pushoverは、スマホにプッシュ通知を送れるサービスです。

  • HTTPS APIを叩くだけで通知を送信できる
  • 専用の@pomail.netアドレス宛にメールを送っても通知に変換される(Eメールゲートウェイ)
  • 送信元ごとにApplication Tokenを発行でき、アイコン・アプリ名で見分けられる

IMG_2646_masked_trim.png

  • priorityで緊急度を制御できる
  • 1つのアカウントに複数端末(スマホ・タブレット・デスクトップ等)を登録でき、同じ通知を同時に受け取れる
  • 料金は買い切り制(詳細は後述の「運用コスト」)
  • 無料で送れるのは1アカウントにつき月10,000件まで。この上限はアカウント全体で共有するので、アプリを増やしても送れる量は増えない

Pushoverは通知専用アプリなので、届く内容が常に「通知」だけ。他の情報に紛れることなく一目で気づけるというのが、数あるサードパーティ製アプリの中でもPushoverを選んだ決め手でした。

構成解説

260911_通知システム構成イメージ.drawio.png

  • CloudWatch Alarm:監視対象Lambdaのメトリクス(Errors等)の状態遷移(OK→ALARM等)を検知
  • SNS(単一トピック):Alarmのalarm_actionsから発行されたメッセージを受け取る。このトピックは複数プロジェクトで共用し、監視したいLambda側からalarm_actionsでこのトピックARNを指定するだけで乗っかれるようにしてある
  • Lambda(中継Bot本体):SNSメッセージを受け取り、CloudWatch Alarm形式かどうかを判定して整形。該当すれば「[状態] アラーム名」+理由を日本語で読みやすく組み立て、非該当ならSubject/Messageをそのまま中継するフォールバックを持たせています
  • SSM Parameter Store:Pushoverのuser key(受信先)と、中継Bot自身が使う既定のApplicationトークンをSecureStringで保管。送信元ごとのトークンは中継側には置かず、各送信元プロジェクトが自分の名前空間で管理します(後述)
  • Pushover:最終的にスマホへプッシュ通知。送信元システムが自分用のトークンの在り処をSNSメッセージに添えて送り、中継Lambdaはそれを読んで送信します。Pushoverアプリ内では送信元ごとにアイコン・アプリ名が分かれるので、「アプリ本来の通知」と「インフラの異常」が混ざって見落とすリスクを避けられます

なぜ中継Lambdaを挟んだか

CloudWatch Alarmのalarm_actionsはSNSトピックへの発行が定番ですが、その先の選択肢はいくつかありました。

一番手軽なのは「SNS→Eメール(Pushoverのメール転送アドレス宛)」でLambdaすら要らない構成です。ただこれだと、CloudWatch Alarmの機械的な英語文面(ALARM: "関数名" in ap-northeast-1)がそのまま届いてしまい、内容を整形できません。

日本語で読みやすく、かつ状態に応じて優先度(priority)も制御したかったので、SNS→Lambda→Pushoverという中継構成を選びました。Lambdaを挟むことで、CloudWatch Alarm以外の任意のSNSメッセージにも対応できる汎用性も手に入りました。

実際の整形ロジックはこんな感じです。CloudWatch Alarm形式(AlarmName・NewStateValueを含むJSON)を検知したら、状態に応じてタイトルと優先度を組み立てます。

alert_formatter.py
def _build_cloudwatch_alarm_notification(parsed: dict) -> dict:
    alarm_name = parsed.get("AlarmName", "(不明なアラーム)")
    new_state = parsed.get("NewStateValue", "UNKNOWN")

    title = _truncate(f"[{new_state}] {alarm_name}", PUSHOVER_TITLE_LIMIT)

    body_lines = []
    if parsed.get("AlarmDescription"):
        body_lines.append(parsed["AlarmDescription"])
    if parsed.get("NewStateReason"):
        body_lines.append(parsed["NewStateReason"])
    if parsed.get("StateChangeTime"):
        body_lines.append(f"発生時刻: {parsed['StateChangeTime']}")
    message = "\n".join(body_lines) if body_lines else "(詳細情報なし)"
    message = _truncate(message, PUSHOVER_MESSAGE_LIMIT)

    priority = 1 if new_state == "ALARM" else 0

    return {"title": title, "message": message, "priority": priority}

CloudWatch Alarm形式に該当しない場合は、Subject/Messageをそのまま中継するフォールバックに回します。

実際の通知例

実際に監視対象のLambdaでエラーが発生すると、こんな通知がスマホに届きます。

IMG_2647_masked_trim.png

タイトルの[ALARM]とアラーム名、本文の理由と発生時刻は、いずれも中継Lambdaが組み立てたものです。CloudWatch Alarmがそのまま送ってくる英語の文面と違い、通知を開かなくてもロック画面で「何がどうなったか」が読み取れます。

複数プロジェクトから使い回せるようにする

Webスクレイピングアプリ以外のLambdaが増えても対応できるように、という部分は設計上もう少し工夫しています。

  • SNSトピックは単一のものを共用し、監視したいLambda側はalarm_actionsにこのトピックARNを指定するだけで乗れるようにする
  • トピックARNはSSM Parameter Store(String型、非シークレット)に置いて、消費側がそこから参照できるようにする

ARNを手元に控えておく案も考えましたが、トピックを作り直した際にARNが変わると気付かれずに壊れるリスクがあります。SSM経由にしておけば、消費側は常に最新のARNを拾えます。

送信元をどう見分けるか

送信元ごとにPushoverのアプリを分けるといっても、中継側で送信元の一覧を管理するような作りにはしていません。通知を送るシステムが、自分用のトークンを入れたSSM Parameter StoreのARNをメッセージに添えて送る、それだけです。中継Lambdaは受け取ったARNのパラメータを読み、そこに入っているApplicationトークンで通知を送ります。

ARNを載せる場所は、SNSのメッセージ属性(MessageAttributes)にしました。本文に混ぜる案もありましたが、中継Lambdaは判定に引っかからないメッセージを本文そのまま転送するので、それだとARNの文字列が通知の見た目に出てきてしまいます。

送信元は、publish時に属性を1つ足すだけです。

sns.publish(
    TopicArn=topic_arn,
    Subject="お知らせが更新されました",
    Message=body,
    MessageAttributes={
        "pushover_token_parameter": {
            "DataType": "String",
            "StringValue": "arn:aws:ssm:ap-northeast-1:<account-id>:parameter/<プロジェクト名>/pushover_token",
        }
    },
)

受け取る側は、属性があればそのパラメータを読み、無ければ既定のトークンを使います。

lambda_function.py
def resolve_app_token(sns, default_token, cache):
    arn = extract_token_parameter_arn(sns)
    if arn is None:
        return default_token

    if arn in cache:
        return cache[arn]

    name = resolve_token_parameter_name(sns)
    if name is None:
        logger.warning(...)  # 検証を通らないARN
        cache[arn] = default_token
        return default_token

    try:
        token = get_parameter(name)
    except Exception:
        logger.warning(...)  # パラメータが読めない
        token = default_token

    cache[arn] = token
    return token

この形なら、新しいシステムが増えても「Pushoverでアプリを登録し、トークンをSSMに置き、そのARNを添えて送る」だけで通知に乗れます。中継側のコードも設定も触る必要がありません。ARNが添えられていない通知は、既定のトークンでそのまま届きます。CloudWatch Alarmのalarm_actionsはトピックARNを指定するだけで送信側が情報を足せないため、アラート起点の通知はこの既定ルートに乗ります。

「送信元が指定したARNを中継側が読む」と書くと、なんでも読めてしまいそうに見えますが、中継LambdaのIAMで許可しているのは arn:aws:ssm:<region>:<account>:parameter/*/pushover_token だけです。ワイルドカードは挟むものの、末尾が/pushover_tokenであることを命名規約にして、トークン用のパラメータ以外は読めないようにしてあります。Lambda側でも、SSMを呼ぶ前に「同じリージョン・同じアカウントか」「命名規約に合っているか」を検証しています。

そして、どの段階で失敗しても既定のトークンで送るようにしてあります。ここはアラートの通知経路そのものなので、「アプリ名が想定と違う通知が届く」より「通知が届かない」ほうが明確に困るからです。

なお、Pushoverのuser keyは「どのアカウントで受け取るか」を指す値なので、送信元ごとに変えるのはApplicationトークンだけです。

運用コスト

リソース 概算コスト
Lambda(実行) $0(無料枠内)
SNS(発行・配信) $0(無料枠内)
SSM Parameter Store $0
CloudWatch Logs $0(無料枠内)
Pushover Application追加登録 $0(登録自体は無料)
Pushover買い切り費用 $4.99(初回のみ。2ヶ月目以降は0円)

個人利用の規模であれば、AWS側はほぼ$0/月で運用できています。なお買い切り費用はプラットフォーム単位で、iOS・Android・デスクトップをまたいで使う場合はそれぞれ$4.99かかります(同じプラットフォームなら複数端末でも1回の購入でカバーされます)。

まとめ

「メールよりスマホですぐ気付きたい」という個人的な小さな動機から始まって、気付けば「個人のAWSアカウント全体のアラート中継基盤」になりました。

  • Pushoverは個人開発システム向きの通知アプリ。通知専用なので、メールやチャットツールと違って他の情報に埋もれない
  • 中継Lambda1つで、複数システムの通知を$0で一括管理できる
  • 複数プロジェクトから使い回すなら、単一SNSトピック+SSM Parameter Store経由でのARN公開が個人利用規模にはちょうど良い
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?