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.9k
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
SLOをサービス品質の共通言語にするために 取り組んできたこと
wakana0222
0
540
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
530
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
6.7k
信頼性について考えてみる(SRE NEXT 2026 miniLT)
hayama17
0
200
AIエージェントで 変わるAndroid開発環境
takahirom
2
700
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
220
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
110
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
1
210
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
1k
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
150
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1.2k
Welcome to the "Parametricity" 🏙️ − Generic だけど Specific な世界 −
guvalif
PRO
1
180
Featured
See All Featured
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
810
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
Building Applications with DynamoDB
mza
96
7.1k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
500
It's Worth the Effort
3n
188
29k
Bash Introduction
62gerente
615
220k
The Curious Case for Waylosing
cassininazir
1
440
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.2k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.2k
30 Presentation Tips
portentint
PRO
1
350
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 ご静聴ありがとうございました