問題

systemd timer で60秒間隔に CloudWatch カスタムメトリクスを PutMetricData で送信していたところ、CloudWatch コンソールのグラフが断続的に欠落(歯抜け)する現象が発生した。

原因

systemd timer の OnUnitActiveSec=60 は「前回の実行完了から60秒後」に次を起動する。スクリプトの実行時間(数秒)が毎回加算されるため、送信タイミングが徐々にドリフトする。

CloudWatch の Period=1分(00秒〜59秒)の境界をまたぐタイミングで、その1分間にデータポイントが0個になりグラフが途切れる。

Period:    |--- 16:44 ---|--- 16:45 ---|--- 16:46 ---|
送信:   16:44:02      16:45:05      16:46:08
                                  ↑ ドリフトで 16:45 の Period に入らない場合がある

対策

送信間隔を Period より短くして、どの1分間にも必ず1回以上データポイントが入るようにする。

[Timer]
OnBootSec=50
OnUnitActiveSec=50
AccuracySec=1
  • OnUnitActiveSec=50 — 50秒間隔にすることでドリフトしても1分の Period を超えない
  • AccuracySec=1 — デフォルトの1分から短縮し、タイマーのジッターを抑える

他の選択肢

方法 メリット デメリット
送信間隔を短くする(50秒) シンプル、変更が小さい API コール数が約20%増(無料枠内なら問題なし)
Period を5分にする 確実にデータが入る グラフの粒度が粗くなる
CloudWatch Agent の StatsD 連携 Agent がバッファリング・送信を管理 Agent の導入・設定が別途必要

補足: CloudWatch Agent の StatsD 連携とは

CloudWatch Agent には StatsD プロトコル(UDP)のリスナーが組み込まれている。スクリプトが UDP でローカルの Agent にメトリクスを投げ、Agent が一定間隔でまとめて CloudWatch に送信する構成。Agent 側が送信タイミングを管理するためドリフト問題が起きにくい。ただし Agent の導入が前提となる。

まとめ

PutMetricData を定期送信する場合、送信間隔は CloudWatch の Period より少し短く設定するのが実務上の定番。公式のベストプラクティスとして明文化されてはいないが、タイマーのドリフトを考慮すると必要な対応。