Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マイクロサービスの効率的な監視〜不安定な依存先との闘い〜
Search
Tao Watanabe
September 20, 2023
Programming
2.8k
6
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
マイクロサービスの効率的な監視〜不安定な依存先との闘い〜
DMM.go #6 の登壇資料です。
https://dmm.connpass.com/event/295065/
Tao Watanabe
September 20, 2023
More Decks by Tao Watanabe
See All by Tao Watanabe
オンプレMySQLをTiDB Cloudへ移行した手順紹介 @TiUG #0
taowata
0
500
Other Decks in Programming
See All in Programming
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
790
Even G2とAWSで推しのエージェントを召喚しよう!
har1101
1
120
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
160
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
180
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
280
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
140
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.3k
Inside Stream API
skrb
1
720
Datadog × OpenTelemetry 入門と実践のあいだ
kn_to_maxpno
1
160
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
140
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
180
メソッドのジェネリクスでGoの夢は広がるか? / Kyoto.go #65
utgwkk
3
780
Featured
See All Featured
Ethics towards AI in product and experience design
skipperchong
2
310
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
GraphQLとの向き合い方2022年版
quramy
50
15k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
720
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
230
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
290
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
Discover your Explorer Soul
emna__ayadi
2
1.1k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Transcript
© DMM マイクロサービスの効率的な監視 〜不安定な依存先との闘い〜 DMM.go #6 プラットフォーム事業本部 Tao Watanabe 1
© DMM 自己紹介 入社:2022年 新卒入社 所属:プラットフォーム事業本部 マイクロサービスアーキテクトグループ 認証チーム 最近はオンプレMySQL→TiDB Cloud
への 移行プロジェクトに注力しています 2 Tao Watanabe X: @tao_wata
© DMM • DMM プラットフォーム (PF)と マイクロサービスアーキテクトグループの紹介 • 認証認可基盤の紹介 •
認証認可基盤の運用・監視における辛み • それを解消した方法 • 結果と課題・まとめ Agenda 3
© DMM プラットフォーム(PF)事業本部とは DMMの各サービスが利用する共通機能を開発する組織 4 会員登録 ログイン 不正対策 ポイント 購入
決済 継続課金 など カスタマー サポート 120人規模のエンジニア組織
© DMM PF事業本部が長年抱えていた課題 5 組織規模が大きいため、マイクロサービスアーキテクチャを 以前から採用 → 各サービスの開発チームのテクノロジースタックがサイロ化 → 開発効率を上げられない、共通の技術課題を解決できない
これを解決するためマイクロサービスアーキテクトグループが発足
© DMM マイクロサービスアーキテクトグループ 6 DMMプラットフォームにおける開発組織戦略を策定・実行し、 開発効率・セキュリティレベル向上を実現する 以下のエコシステムを提供 • マイクロサービスプラットフォーム (Kubernetesクラスタ)
• 負荷試験基盤 • 監視機能(Datadog) • 認証認可機能 • SLO/SLIの導入 • レビューシステム など
© DMM PFのマイクロサービスアーキテクチャの概要 7 API Gateway 認可サービス 認証サービス 決済サービス 会員サービス
認証認可基盤 …
© DMM 認証認可基盤 8 DMM会員をはじめ、従業員や各アプリケーションが持つリソースに対する認証と認可を 一元管理する基盤。 オンプレのレガシーシステムをプロキシしている 使用技術 言語: Go
インフラ: Kubernetes(GKE) CI: GitHub Actions CD: ArgoCD 監視: Datadog GKE オンプレ API Gateway 認証認可基盤 認証サービス 認可サービス DMMの成長を 支えてきた レガシーシステム
© DMM 認証認可基盤が果たす役目 9 • 段階的なリプレイス先 • 新機能の追加先
© DMM 段階的なリプレイス先 10 レガシーシステムの段階的なリプレイスを容易にする GKE オンプレ API Gateway 認証認可基盤
認証サービス 認可サービス エンドポイントごとの リプレイスが容易
© DMM 新機能の追加先 11 認証認可機能の追加を開発効率よく実現する GKE オンプレ API Gateway 認証認可基盤
認証サービス 認可サービス 新しい 認証・認可機能ニー ズ エコシステムを利 用し 生産性高く開発で きる 古いシステムで 改修が難しい
© DMM 認証認可基盤が果たす役目 12 認証認可機能の負債脱却のための中心的な役割 一方で、リプレイスが進むまでは単なるProxy → 認証認可機能のオーナーシップはProxy先のシステムを管轄するチーム にあるという状況
© DMM 認証認可基盤の運用で どんな困難があったか
© DMM 当初はAPI Gatewayのエンドポイントを監視 14 API Gateway 認証認可基盤 (自サービス) レガシー
システム リバース プロキシ Redis Redis API Gatewayのエンドポイントを起点とし レイテンシを監視 合計 100ms 10ms 5ms 35ms オンプレ 50ms
© DMM 監視・運用における辛さ・難しさ • 依存先レガシーシステム起因のアラートで夜中に起こされる • 依存元API Gatewayの不調にも影響される • 自サービス自体の品質が分からない
15
© DMM 辛い例①
© DMM レガシーシステムのレイテンシが悪化 17 API Gateway 認証認可基盤 (自サービス) レガシー システム
リバース プロキシ Redis Redis レイテンシ悪化でアラート発火 10ms 5ms 35ms レイテンシ悪 化 オンプレ
© DMM an incident occurs …
© DMM 他チーム管轄のプロダクトが原因 19 API Gateway 認証認可基盤 (自サービス) レガシー システム
リバース プロキシ Redis Redis 10ms 5ms 35ms レイテンシ悪 化 自チームの管轄 他チームの管轄
© DMM やれることがない...
© DMM 辛い例②
© DMM 依存元のAPI Gatewayの不調 22 API Gateway 認証認可基盤 (自サービス) レガシー
システム リバース プロキシ Redis Redis 5ms 35ms オンプレ 50ms レイテンシ悪 化 レイテンシ悪化でアラート発火
© DMM an incident occurs …
© DMM API Gatewayも他チーム管轄 24 API Gateway 認証認可基盤 (自サービス) レガシー
システム リバース プロキシ Redis Redis 5ms 35ms 50ms レイテンシ悪 化 自チームの管轄 他チームの管轄
© DMM やれることがない...
© DMM 辛い例③
© DMM オンプレのリバースプロキシの不調 27 API Gateway 認証認可基盤 (自サービス) レガシー システム
リバース プロキシ Redis Redis 5ms 10ms オンプレ 50ms レイテンシ悪 化 レイテンシ悪化でアラート発火
© DMM an incident occurs …
© DMM やれることはない...
© DMM 作業効率・モチベーションの低下 他チーム管轄サービス起因でアラートが発火 →結果、不必要な対応を迫られることもある 30
© DMM これを解決するために 割り切った戦略を採用
© DMM 割り切った戦略を採用 自サービスのレイテンシしか見ない 32
© DMM API Gatewayと依存先システムのレイテンシは含めず監視 33 API Gateway 認証認可基盤 (自サービス) レガシー
システム リバース プロキシ Redis Redis 10ms 5ms 35ms オンプレ 50ms
© DMM Goでどのように実装をしたか 注意: Goのプラクティスに反する方法を利用しています
© DMM Goのプラクティスに反した方法 Contextにtime.Durationへの参照を付加し、 http.Clientの中で外部APIへのリクエスト待ち時間を都度Contextの参照先 へ加算する 基本的にcontext.WithValue()では、一連のリクエストにおいて不変の値を 伝搬するべきです!!! 外部APIへ依存する箇所にすでにctxを引き回していたので 実装が楽だった...
35
© DMM リクエストシーケンス 36 自サービス
© DMM 自サービスのレイテンシを算出 37 自サービスのレイテンシを 全体処理時間 - 依存先システムのレスポンスを待つ時間の総計(外部処理 時間) として算出
© DMM 38 全体処理時間 計測開始 Contextに参照をセット 全体処理時間計測終了 Contextの参照先の値を取得 内部処理時間計算 外部処理時間
計測開始 外部処理時間 計測終了 Contextにセットされた 参照先を更新 ミドルウェアとHTTPクライアントで処理時間を計測 ctx ctx
© DMM 39 全体処理時間 計測開始 Contextに参照をセット 全体処理時間計測終了 Contextの参照先の値を取得 内部処理時間計算 外部処理時間
計測開始 外部処理時間 計測終了 Contextにセットされた 参照先を更新 ミドルウェアで全体処理時間の計測を開始 ctx ctx
© DMM type Store struct { UserID string TraceID string
TotalExternalDuration time.Duration } type storeKeyType struct{} var storeKey = storeKeyType{} 40 func MiddlewareDuration(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { start := time.Now() store := &Store{} ctx := context.WithValue(ctx, storeKey, store) next.ServeHTTP(rw, r.WithContext(ctx)) wholeDuration := time.Now().Sub(start) internalDuration := wholeDuration - store.TotalExternalDuration if span, ok := tracer.SpanFromContext(r.Context()); ok { span.SetTag("internal.duration", internalDuration.Microseconds()) } }) } ミドルウェアで全体処理時間の計測を開始 ①Contextに格納して伝搬する値 をまとめた構造体と そのKeyを定義する ⚠可変の値として外部処理時間の総 計が含まれる ②全体処理時間の計測開始 ④ContextにValue(*Store)を セット ③Storeのポインタを生成
© DMM RoundTripperで外部処理時間の計測 41 全体処理時間 計測開始 Contextに*Storeをセット 全体処理時間計測終了 内部処理時間計算 外部処理時間
計測開始 外部処理時間 計測終了 *Storeを更新
© DMM RoundTripperで外部処理時間の計測 42 type durationRoundTripper struct { base http.RoundTripper
} func (rt *durationRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { start := time.Now() res, err := rt.base.RoundTrip(req) duration := time.Since(start) store := GetStoreFromContext(req.Context()) store.TotalExternalDuration += duration return res, err } func WrapClientWithDurationRoundTripper(c *http.Client) *http.Client { if c.Transport == nil { c.Transport = http.DefaultTransport } c.Transport = &durationRoundTripper{base: c.Transport} return c } ①外部処理時間を計測する ②ContextからStoreの参照を取り出す ③外部処理時間の総計に算出した外部処理時間を加算する ④ 以上の処理を追加したRoundTripperでCllientを ラップする
© DMM 43 全体処理時間 計測開始 Contextに参照をセット 全体処理時間計測終了 内部処理時間計算 外部処理時間 計測開始
外部処理時間 計測終了 Contextにセットされた 参照先を更新 ミドルウェア(帰り)で内部処理時間を算出 ctx ctx
© DMM type Store struct { UserID string TraceID string
TotalExternalDuration time.Duration } type storeKeyType struct{} var storeKey = storeKeyType{} 44 func MiddlewareDuration(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { start := time.Now() store := &Store{} ctx := context.WithValue(ctx, storeKey, store) next.ServeHTTP(rw, r.WithContext(ctx)) wholeDuration := time.Now().Sub(start) internalDuration := wholeDuration - store.TotalExternalDuration if span, ok := tracer.SpanFromContext(r.Context()); ok { span.SetTag("internal.duration", internalDuration.Microseconds()) } }) } ミドルウェア(帰り)で内部処理時間を算出 ①全体処理時間の算出 ②(全体処理時間 - 外部処理時間の総計) を計算し、 内部処理時間を算出 ③トレーシングライブラリのSpanに 時間を付与する
© DMM 自サービスの内部処理時間のみを監視 45 API Gateway 認証認可基盤 (自サービス) レガシー システム
リバース プロキシ Redis Redis 10ms 5ms 35ms オンプレ 50ms
© DMM 静かな夜を手に入れた 自分達では対処できない不具合によるアラート対応が減った 思い切った監視に切り替えた結果... 46
© DMM spanに内部処理時間を付与したことで、シークバーで絞り込めるようになった → 自チーム管轄のRedisの遅延などによる内部処理の遅れ, 不具合を 検知できるようになった 思い切った監視に切り替えた結果... 47
© DMM 課題 GoのContextの使い方 ✖ Contextに可変な値(TotalExternalDuration)を付加する方法で解決したこ と middlewareとhttp.ClientがContextの値を介して結合度が高くなってしまっ たのも気になる → 今後負債化するかも
基本的にcontext.WithValue()では、一連のリクエストにおいて不変の値を 伝搬するべきです!!! 48
© DMM 課題 マイクロサービスのオーナーシップ 今回自サービスの責任範囲のみに監視対象を絞る戦略を採用したが... 依存先も含めたサービスの品質に誰かは責任を持たなければならない ユーザーに提供する機能のオーナーシップを持つ人は誰? 負債脱却中はこの辺りの責任範囲が難しい 49
© DMM まとめ 何らかのProxyや共通基盤の場合、依存先システムの監視を切り捨て、 自サービス自体の可用性を監視すれば必要十分なこともある Contextの誤用・濫用はやめよう Contextの使い方はよく議論されている (トレードオフな部分もあるのでは) 50
© DMM ご静聴ありがとうございました