Datadog で「リリース後にだけ」利用するモニターを作成する

こんにちは。
vivit で SRE をやっている宮本(https://github.com/tatsuro-m)です!

今回は Datadog で「リリース後にだけ」利用するモニタリングリソースをどうやって作成、運用していったかを書いてみようと思います。

基本的には常時 OFF(ミュート)で、リリース後に ON(アンミュート)になり、1時間後に自動で再度ミュートします。

背景

一般的に、何らかのリリースが起点となって障害が発生することは多いと思います。

vivit では常時 Datadog モニターを利用してエラー率、 SLO の監視をしていますが、タイムウィンドウが1週間と長くなっています。

リリース後の1時間など、もっと小さいタイムウィンドウで突発的なエラー率の上昇を監視したいという要件があり、今回の仕組みを構築しました。

前提知識

  • モニターにはミュート、アンミュートの機能があり、ある期間のアラートを無視することができる(モニターの管理)

  • ミュート時にはミュートを解除する時間を指定できる

  • アンミュートする際には再度ミュートする時間を指定する機能は存在しない
  • Datadog は多くのリソースに対して REST API を提供していて、モニターもある(https://docs.datadoghq.com/ja/api/latest/monitors

よって、時間経過によって自動で再ミュートする仕組みは自分で構築しなくてはなりません。

アーキテクチャ

f:id:tatsurom:20220111151024p:plain

順番に解説します。 対象となるモニターは予め作成した上でミュート状態にしておき、 monitor_id を控えます。 monitor_id は Datadog 画面から確認できます。

  1. リリースをトリガーにして1つ目の Cloud Functions を gcloud で呼び出す
  2. unmute エンドポイントを叩いてミュート解除する
  3. Cloud Tasks のキューにタスクを積む。この時、起動時刻のオプションで特定時間を設定する
  4. 指定した時間に HTTP 呼び出しで mute エンドポイントを叩く Cloud Functions を呼び出して再度ミュート

ポイント

トリガーは何でも良いのですが、今回は特定マイクロサービスの本番環境へのリリースです。

vivit では GitOps を採用しているので、Kubernetes マニフェストの更新を検知して GitHub Actions のワークフローが起動するところからスタートできます。

vivit での GitOps については 、

vivit.hatenablog.com

こちらの記事をご参照下さい !!


以下、Cloud Functions で実行される Cloud Tasks キューにタスクを作成する部分のコードです。

ts := &timestamppb.Timestamp{Seconds: time.Now().Add(1 * time.Hour).Unix()}
req := &taskspb.CreateTaskRequest{
    Parent: queuePath,
    Task: &taskspb.Task{
        ScheduleTime: ts,
        MessageType: &taskspb.Task_HttpRequest{
            HttpRequest: &taskspb.HttpRequest{
                HttpMethod: taskspb.HttpMethod_POST,
                Url:        os.Getenv("MUTE_FUNCTION_URL"),
                AuthorizationHeader: &taskspb.HttpRequest_OidcToken{
                    OidcToken: &taskspb.OidcToken{
                        ServiceAccountEmail: os.Getenv("UNMUTE_FUNCTION_SA_EMAIL"),
                    },
                },
            },
        },
    },
}

Go で提供されているクライアントライブラリを利用します。
ScheduleTime に起動時刻を設定することができます。 Cloud Functions の HTTP 呼び出しには認証必須にしているので、 OIDC トークンも忘れずにセットします(ここでハマりました 🤧)。


Datadog は様々な言語の API Client を提供しており、Go のライブラリも存在します(GitHub - DataDog/datadog-api-client-go: Golang client for the Datadog API)。

ただし今回は API Client を使うまでもないと判断したので、Go の標準パッケージで REST API を呼び出しています。

   req, err := http.NewRequest(http.MethodPost,
        fmt.Sprintf("https://api.datadoghq.com/api/v1/monitor/%s/unmute", os.Getenv("MONITOR_ID")),
        nil)
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("DD-API-KEY", os.Getenv("DD_API_KEY"))
    req.Header.Set("DD-APPLICATION-KEY", os.Getenv("DD_APP_KEY"))

    if err != nil {
        return err
    }

    resp, err := http.DefaultClient.Do(req)

Datadog の API を呼び出すには、2つの秘匿情報が必要です。

  • api key
  • app key

これらの情報を Cloud Functions に伝えるために、Secret Manager を利用して環境変数に登録しました。

Using secrets  |  Cloud Functions Documentation  |  Google Cloud


「特定時間経過後に Cloud Functions を呼び出したい」という要件だけ聞くと、Cloud Scheduler の利用を検討される方もいると思います。

しかし Cloud Scheduler は Cron を使用する以上、単発の実行には不向きです。

cloud.google.com

単発で実行したいという理由で、今回は Cloud Tasks の scheduleTime を利用する構成で作ってみました。

最後に

割と特殊なケースですが、 Datadog APIGCP のサービスを上手く組み合わせることで、このような構成も可能であることが伝われば幸いです。

vivit では一緒に働くエンジニアを大募集しています。
www.wantedly.com

少しでも興味を持って頂いた方は、是非カジュアル面談の応募をお待ちしております!