Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

自動生成を活用した、運用保守コストを抑える Error/Alert/Runbook の一元集約...

自動生成を活用した、運用保守コストを抑える Error/Alert/Runbook の一元集約管理 / Centralized management of Error/Alert/Runbook to minimize operational costs using automated code generation

Shota Iwami

April 15, 2024
Tweet

More Decks by Shota Iwami

Other Decks in Technology

Transcript

  1. 自 動 生 成を活 用 した 運 用 保守コストを抑える Error/Alert/Runbook

    の 一 元集約管理 株式会社サイバーエージェント AI事業本部 岩 見 彰太 GitHub:@BIwashi X: @B_Sardine DevOpsDays Tokyo 2024
  2. 1 .はじめに 2 .エラー検知の重要性 3 .アラートの重要性と注意点 4 .「アラートが来た!」で終わらせず 行 動に繋げるには

    5 . 自 動 生 成による情報の集約 6 .エラー検知で終わらせずに進捗を追う 7 .まとめ
  3. エラー検知の重要性 • 開発を進めていく中で適切なエラー検知は不可 欠 適切なハンドリングを 行 うことで、エラー発 生 から修正までの時間 を最

    小 化できる • より正確で詳細な情報を事前に仕込むことが重要 • Sentry や Datadog Error Tracking など、エラーの種類 を適切に振り分けらられるような仕組みを利 用 Tag Stack Trace Custom Logger Log Level Error Message Status Code
  4. アラートの属 人 化 エラーメッセージ スタックトレース ログリンク etc. エラー内容 分かる 人

    エラー内容 分からない 人 エンジニア じゃない 人 あーこれは対処しなくていいやつだ。 一 旦放置! よく通知されるけどどこのエラーなんだ…?何が起き てるんだ…?対処する必要あるのか?ちゃんと調査し たほうがいいのかな… なんかヤバそうな雰囲気はあるけど何も分からん。〇〇さ ん(エラー内容分からない 人 )に聞いてもよく分からない と 言 われた。アラートチャンネルはもう 見 るのやめよう😇 #hoge_alert
  5. アラートの属 人 化 エラーメッセージ スタックトレース ログリンク etc. エラー内容 分かる 人

    エラー内容 分からない 人 エンジニア じゃない 人 あーこれは対処しなくていいやつだ。 一 旦放置! よく通知されるけどどこのエラーなんだ…?何が起き てるんだ…?対処する必要あるのか?ちゃんと調査し たほうがいいのかな… なんかヤバそうな雰囲気はあるけど何も分からん。〇〇さ ん(エラー内容分からない 人 )に聞いてもよく分からない と 言 われた。アラートチャンネルはもう 見 るのやめよう😇 そもそもこれは アラートすべきではない #hoge_alert
  6. どう 行 動に移すか • ひたすらエラー内容が流れてくるだけで誰もみていないチャンネル… それはアラートを流している意味がない • アラートするからには… 何か伝えたいことがある それに応じて

    行 動すべきことがあるはず • アラート内容をもとに次の 行 動に繋げていく必要がある 具体的な 行 動 手 順があるとすぐに 行 動できる アラートは エラー →対処 を動かすためのトリガー
  7. どう 行 動に移すか 手 順書のない世界 何すればいいかわからん! どこから 手 をつければええんや! えっと…

    手 順書によるとこれをすればいいのか…! まずはここの値を調査して、XXXしてみるか。 手 順書のある世界 手 順書 Runbook Playbook 手 順書があるとアラートに対してより詳細な情報を付与できる →すぐに 行 動に移せる/ 行 動できる 人 を増やせる
  8. 手 順書(Runbook/Playbook) • アラートへの対処法や調査すべき項 目 などをあらかじめ記載しておく 想定外のエラーが出ても調査にとっかかりやすくなる 想定できうるエラーであれば 手 順書を明記しておくことで、認知コストを下げられる

    • 手 順書はアラートとの紐付けとメンテナンスが肝 メンテナンスされておらず古い情報… 記載場所が決まっておらず同じような 手 順書が複数 見 つかる… 手 順書のどこを 見 ればいいかわからない… खॱॻʢrunbookʣ͸Ξϥʔτ͕དྷͨ࣌ʹ͢͹΍ࣗ͘෼ͷਐΉ΂͖ํ޲Λࣔ͢ૉ੖Β͍͠ํ๏Ͱ ͢ɻ ؀ڥ͕ෳࡶʹͳͬͯ͘ΔͱɺνʔϜͷ୭΋͕֤γεςϜͷ͜ͱΛ஌͍ͬͯΔΘ͚Ͱ͸ͳ͘ͳ Γɺखॱॻ͕஌ࣝΛ޿ΊΔΑ͍ํ๏ʹͳΓ·͢ɻ by ೖ໳؂ࢹ
  9. 情報の分散(Custom Code で紐づける例) Custom Code を埋め込む実装と Runbook が分離してしまう 参照するのもメンテナンスするのもコストがかかる エラーメッセージ

    スタックトレース ログリンク etc. + Custom Code Runbook with Custom Code エラーにCustom Codeを埋め込む Notion Con fl uence Github Wiki etc … Custom Code で紐づける エラー内にある Custom Code から Runbook を検索して内容を確認して 行 動に移す
  10. 情報集約のモチベーション • Custom Code と Runbook によるアプローチはいいが保守コストが 高 い できるだけ集約してそこだけをみれるようにしたい

    • アラート通知の内容をできるだけ誰でもわかるようにしたい エラー内容によってアラート 文 をリッチに変えるようにしたい • リッチな Runbook をログに埋め込むとログ量が増えてしまうため避けたい Custom Code で連携するというアプローチではいきたい そもそもアラートが Runbook にもなっていればいいのでは…? アラートメッセージを Custom Code に応じて出し分ければいいのでは…?
  11. Protoc Plugin • Protocol Bu ff ers に記述された情報を使 用 して

    自 動 生 成などを 行 う • Extend を使 用 して message や fi eld に情報を 追加したり拡張することができる • 今回は protoc plugin をサクッと書ける protoc-gen-star(PG*)を使 用 Go Conference mini 20 23 Winter in KYOTO
  12. enum ReasonCode { RC00000 = 0 [(ext.reason_code) = { message:

    [ "hoge͕ൃੜ͠·ͨ͠", "`http://example.com` Λࢀরͯ͠ϦιʔεΛ", ”֬ೝ͍ͯͩ͘͠͞", "fooͩͬͨ৔߹͸foo͍ͯͩ͘͠͞" ] }]; RC00001 = 1 [(ext.reason_code) = { message: [ "piyo͕μ΢ϯ͍ͯ͠·͢", "PMͱ࿈ܞͯ͠piyoʹ࿈ܞ͍ͯͩ͘͠͞" ] }]; } Reason code proto fi le
  13. enum ReasonCode { RC00000 = 0 [(ext.reason_code) = { message:

    [ "hoge͕ൃੜ͠·ͨ͠", "`http://example.com` Λࢀরͯ͠ϦιʔεΛ", "֬ೝ͍ͯͩ͘͠͞", "fooͩͬͨ৔߹͸foo͍ͯͩ͘͠͞" ] }]; RC00001 = 1 [(ext.reason_code) = { message: [ "piyo͕μ΢ϯ͍ͯ͠·͢", "PMͱ࿈ܞͯ͠piyoʹ࿈ܞ͍ͯͩ͘͠͞" ] }]; } Reason code proto fi le • Reason Code • 実装側とRunbookを紐づける独 自 の code • 後に Datadog のモニタリングを設定する際 に重要
  14. enum ReasonCode { RC00000 = 0 [(ext.reason_code) = { message:

    [ "hoge͕ൃੜ͠·ͨ͠", "`http://example.com` Λࢀরͯ͠ϦιʔεΛ", "֬ೝ͍ͯͩ͘͠͞", "fooͩͬͨ৔߹͸foo͍ͯͩ͘͠͞" ] }]; RC00001 = 1 [(ext.reason_code) = { message: [ "piyo͕μ΢ϯ͍ͯ͠·͢", "PMͱ࿈ܞͯ͠piyoʹ࿈ܞ͍ͯͩ͘͠͞" ] }]; } Reason code proto fi le • Message(Runbook) • その Reason Code に紐づく Runbook • 実際に管理したい Runbook の内容を 入 れて おく
  15. // Code generated by protoc-gen-go-reason-code. type ReasonCode string const (

    RC00000 ReasonCode = "RC00000" // Message: hoge͕... RC00001 ReasonCode = "RC00001" // Message: piyo͕... ) // Custom Error type Error struct { error ... reasonCodes []ReasonCode ... } func WithReasonCode(rc ReasonCode) Option { return func(e *Error) { e.reasonCodes = append(e.reasonCodes, rc) } } Application Code • Custom Error に Reason Codes を 入 れられるようにする • 定数の Reason Code を 自 動 生 成する • 自 動 生 成されたものをエラーハンド リングしている箇所で 入 れる
  16. { "error": { "reason_codes": [ "RC00000" ], ... "message": "hoge

    error" } } Error JSON • 構造化ログの error に reason codes を出しておく • 後に Datadog を使 用 する際に重要
  17. Datadog Monitors • エラーが発 生 した際にアラートを 飛 ばすモニタリングアラートを設定 Error Tracking

    Base で設定 • Datadog の Message (アラート内容)では独 自 の条件付き変数が利 用 できる これを利 用 して reason code によって message (Runbook)を分岐する
  18. is_match によるMessage分岐 • Datadog のMessageでは log.attributes で構造化されたログのフィールド の値を取得できる • error

    に埋め込んだ reason_codes を is_match で検知して、 message(runbook)を表 示 する 配列フィールドの場合、以下のよう に検知されるため ”” で囲っている “RC00000”,“RC00001”, …
  19. {{!-- Monitor generated by protoc-gen-go-reason-code and terraform. DO NOT EDIT.

    --}} {{ log.message }} {{#is_match "log.attributes.error.reason_codes" "\"RC00000\"" }} reason_code: `RC00000` ``` hoge͕ൃੜ͠·ͨ͠ `http://example.com` Λࢀরͯ͠ϦιʔεΛ ֬ೝ͍ͯͩ͘͠͞ fooͩͬͨ৔߹͸foo͍ͯͩ͘͠͞ ``` {{/is_match}} {{#is_match "log.attributes.error.reason_codes" "\"RC00001\"" }} reason_code: `RC00001` ``` piyo͕μ΢ϯ͍ͯ͠·͢ PMͱ࿈ܞͯ͠piyoʹ࿈ܞ͍ͯͩ͘͠͞ ``` {{/is_match}} > `{{{ log.attributes.error.file }}}` > **{{{ log.attributes.error.message }}}** - [Log Link]({{log.link}}) - Issue ID: [{{[issue.id].name}}](https://app.datadoghq.com/logs/error-tracking/issue/ {{[issue.id].name}}) - Version: {{log.attributes.version}} {{#is_alert}} @${notification_alert_channel} {{/is_alert}} Terraform による管理 • Datadog の Monitors は Terraform 管理可能 • proto に記述した内容を元に reason code による分岐と message(Runbook)を 生 成する application_error_alert.pb.md
  20. resource "datadog_monitor" "application_error_alert" { name = "[${var.env}] αʔόʔΤϥʔ͕ൃੜ͠·ͨ͠" type =

    "error-tracking alert" tags = ["env:${var.env}", "managed_by:terraform"] priority = 2 on_missing_data = "resolve" include_tags = true group_retention_duration = "3d" monitor_thresholds { critical = 0 } query = <<-EOT error-tracking-logs( "(issue.age:<=300000 OR issue.regression.age:<=300000) env:${var.env}" ).index("*") .rollup("count") .by("issue.id") .last("5m") > 0 EOT message = templatefile( "${path.module}/messages/application_error_alert.pb.md", { notification_alert_channel = var.notification_alert_channel } ) } Terraform による管理 • protoc plugin で 生 成した Markdown を template fi le 関数で 読み込み設定する monitor.tf
  21. resource "datadog_monitor" "application_error_alert" { name = "[${var.env}] αʔόʔΤϥʔ͕ൃੜ͠·ͨ͠" type =

    "error-tracking alert" tags = ["env:${var.env}", "managed_by:terraform"] priority = 2 on_missing_data = "resolve" include_tags = true group_retention_duration = "3d" monitor_thresholds { critical = 0 } query = <<-EOT error-tracking-logs( "(issue.age:<=300000 OR issue.regression.age:<=300000) env:${var.env}" ).index("*") .rollup("count") .by("issue.id") .last("5m") > 0 EOT message = templatefile( "${path.module}/messages/application_error_alert.pb.md", { notification_alert_channel = var.notification_alert_channel } ) } Terraform による管理 • protoc plugin で 生 成した Markdown を template fi le 関数で 読み込み設定する monitor.tf 複雑かつ 大 量の分岐だが 自 動 生 成してるため コスト0で実現可能
  22. アラート • アラートの中にすでに Runbook の内容 が含まれている Runbook を探すという作業が必要ない • エラーに関する実装上の情報などだけで

    はなくより豊富な情報をアラートで提供 非 実装者や 非 エンジニアでも温度感が分かる 対処すべき 行 動がすぐわかる アラートに対する認知者の増加 注 目 が集まることによる 見 逃し軽減
  23. Error Tracking×Casa Management×Jira • Error Tracking の Issue から Case

    Management を作成 事前に Jira と連携しておくと Case を作成すると 自 動でチケットが切られる 担当者やステータスも双 方 向で同期できる %BUBEPH$BTB.BOBHFNFOU
  24. Error Tracking×Casa Management×Jira • Error Tracking の Issue から Case

    Management を作成 事前に Jira と連携しておくと Case を作成すると 自 動でチケットが切られる 担当者やステータスも双 方 向で同期できる %BUBEPH$BTB.BOBHFNFOU +JSB
  25. まとめ • Alert の適切な管理が重要 • Runbook を活 用 した Error

    に対する認知負荷の軽減 • protoc plugin + Datadog + Terraform Reason Code と Runbook を 自 動 生 成で紐づけて管理コストを軽減 • タスク管理ツールと Error を紐付ける Error とタスクチケットを紐づけて対応の進捗までリンクしておく ステータスを確認するコスト軽減 情報の集約、連携でエラー取り逃がさず追い続ける