
- 発生した事象と経緯
- SLI検討
- SLI検討案1: 各GKEのNodeにてDiskスロットリングが発生していない時間の割合
- SLI検討案2: GKE上にアプリケーションのLivenessProbeの失敗回数が閾値以内である時間の割合
- まとめ
こんにちは、DMM.comに2022年新卒として入社した中井 綾一です。
現在はプラットフォーム事業本部のマイクロサービスアーキテクトグループのSREチームで働いています。
SREチームは、DMMプラットフォームを開発・運用している120人規模の開発組織にマイクロサービスプラットフォームを提供しています。
マイクロサービスプラットフォームは k8s とその周辺のエコシステムにて構築されているアプリケーションプラットフォームで、弊チームが開発・運用を行い、プラットフォーム利用者はアプリケーション開発に集中できるようになっています。
そして、このプラットフォームがどのぐらい稼働しているかを可視化するために、プラットフォーム利用者に対してのSLI/SLOを策定しています。
そもそも、SLI/SLOてどんな感じだっけ?やDMMプラットフォームでのSLI/SLOを策定に関しては、以下でSREチームの工藤さんが「Cloud Operator Days Tokyo 2023」で発表しているので、こちらも併せて確認していただけると幸いです。
DMMプラットフォームにゼロベースでSLO導入している取り組み 適切なSLI模索の規制
本記事では、SREチームで運用しているGKEにてノイジーネイバーが発生し、その問題の発生を検知する 、その問題が起きていないことを可視化できるように、新たなSLI/SLOを追加しようとなったのですが、かなり紆余曲折したため、そのプロセスやSLIを定義する上での難しさについて紹介できればと思います。
結論としては、技術的な問題でSLI/SLOに落とし込むことはできませんでしたが、代替案で検知のみできるように、Datadogモニターを作成しようという話で落ち着きました。
発生した事象と経緯
発生した事象と経緯を簡単に説明します。
SREチームでは、GKEを運用しており、クラスタを運用する上で必要な各種オペレータを3カ月ごとにアップデートしています。
オペレータの1つであるArgo Workflowをアップデートした際に、Argo WorkflowのPodがあるGKE NodeにあるアプリケーションPod全般でDisk I/Oに関わる処理が重くなる事象が発生しました。
具体的には、あるアプリケーションではアクセスログ等のログ出力処理に15s以上もかかっていたり、アクセスログの出力が重くなったことにより、ヘルスチェックエンドポイントのレイテンシも悪化したことから、LivenessProbeが失敗回数も増加し、リスタートするPodも増える事態になりました。
これらの事象を踏まえて、特定のGKE Node(Argo WorkflowのPodがいるNode)にて、Diskスロットリングが発生し、同Nodeに存在するアプリケーションパフォーマンスに影響を与えるノイジーネイバー問題が起こっているのではないか?と推察しました。
そして、SREチームでそれまで運用していたSLI/SLOでは、この事象を検知できなかったため、新たなSLI/SLOを検討しようとなりました。
SLI検討
先述の事象を検知し、ノイジーネイバーが起こっていないことを可視化できるように、以下の2種類のSLIを検討しました。
- 各GKEのNodeにてDiskスロットリングが発生していない時間の割合
- GKE上にアプリケーションのLivenessProbeの失敗回数が閾値以内である時間の割合
結論から言うと、先述の通り、この2つの案のどちらも技術的な問題でSLIに落とし込むことができませんでした。
それぞれの案についてなぜこの案を検討したのか、なぜ採用できなかったのかを説明できればと思います。
SLI検討案1: 各GKEのNodeにてDiskスロットリングが発生していない時間の割合
検討案概要
まず最初に、今回のノイジーネイバー根本原因であるGKE NodeのDiskスロットリングのメトリクスを監視し、問題ない時間の割合をSLIとして定義すればいいのではないかと考えました。
しかし、以前までは使えていたスロットリングを計測するinstance/disk/throttled_write_bytes_count やinstance/disk/throttled_read_bytes_count 等のメトリクスが、Deprecatedになっていて使えないという問題があり、こちらの採用は見送りました。
以下のURLにて、こちらのメトリクスがDeprecatedになっていることが確認できます。
https://issuetracker.google.com/issues/2884t40422?pli=1
また、DiskへのIOPSやスループット等のパフォーマンスに関するメトリクスはありましたが、この情報だけだとメトリクスがどうなった場合にスロットリングが起きるのかの判断が難しく、採用を断念しました。
DiskへのIOPSやスループット等のパフォーマンスに関するメトリクス
(余談)NodeのDiskスロットリングについて
先述のスロットリングを判断するためのメトリクスがDeprecatedになっていたことから、スロットリングが起きていたとはっきりと断定できる材料がない状況でした。
なので、SLI/SLOの検討と並行して、本当にNodeでDiskスロットリングが起きていたのかの調査を行なっていました。
諸々調査したところ、インスタンスにアタッチされているPersistent DiskのDiskタイプもしくは容量に問題があり、いつDiskスロットリングが発生してもおかしくない状態になっている可能性があることがわかりました。
どういうことかを説明すると、そもそもインスタンスからPersistent Diskのパフォーマンス値(=最大スループットやIOPS)は、以下の2種類の値の小さいほうになります。
- インスタンスのマシンタイプ、インスタンスのvCPU数、Diskタイプで決定するパフォーマンス値
- Disk容量、Diskタイプで決定するパフォーマンス値
公式ドキュメントに上記のことが記載されています。
https://cloud.google.com/compute/docs/disks/performance?hl=ja#size_price_performance
SREチームでは問題が発生した当時、インスタンスはN2タイプ16vCPUで、Persistent Diskは50GBのpd-standardタイプを使っていました。
この場合、公式ドキュメントを参照すると、それぞれのパフォーマンス値は以下のようになります。
- インスタンスのマシンタイプ、インスタンスのvCPU数、Diskタイプによって決まるパフォーマンス値
- 最大書き込みIOPS : 15,000 IOPS
- 最大書き込みスループット : 7500 MB/s
- Disk容量、Diskタイプで決定するパフォーマンス値
- 最大書き込みIOPS : 1.5 IOPS(1GBあたりの書き込みIOPS) * 50GB = 75 IOPS
- 最大書き込みスループット : 0.12 MB/s (1GBあたりの書き込みスループット) * 50 = 6 MB/s
つまり、Persistent Diskのパフォーマンス値は「Disk容量、Diskタイプで決定するパフォーマンス値」となっており、理論上だと書き込みIOPSが75、スループットが6MB/sしか出ない状況になっていました。
そして、このパフォーマンス値を上回るような値が今回の問題の発生有無にかかわらず確認されていたため、Diskスロットリングが発生しやすい状態だった、かつArgo WorkflowのアップデートのタイミングでDiskスロットリングが発生してしまった可能性が高そうという結論になりました。
この問題の対策として、Persistent Diskのタイプをpd-standardからpd-ssdに変更することで、パフォーマンス値を引き上げました。
SLI検討案2: GKE上にアプリケーションのLivenessProbeの失敗回数が閾値以内である時間の割合
検討案概要
NodeのDiskスロットリングが起きる → ノイジーネイバーで複数PodのLivenessProbeのヘルスチェックが失敗する → 複数Podがリスタートするという流れでPodのリスタートが頻発していたので、LivenessProbe失敗のメトリクスを監視し、設定した閾値以内である時間の割合をSLIとして定義できればいいのではないかと考えました。
ただ、LivenessProbeの失敗をSLIにするとなった場合に、以下の場合にノイズが入ってしまう懸念がありました。
- アプリケーションのバグやマニフェストの設定値起因のLivenessProbeの失敗
- 負荷試験による性能悪化起因のLivenessProbeの失敗
- etc...
SLI検討案1が実現できなかったため、他にも案がなかったことと本当にノイズが起きすぎるかは運用してみてから判断しようとなったことから、先述の懸念はありつつ、LivenessProbeの失敗回数をSLIにしてみようとなりました。
LivenessProbeの失敗を監視するメトリクスの問題点
KubernetesのLivenessProbeの失敗を監視できるメトリクスとして、kubernetes.liveness_probe.failure.total というものがあります。
こちらのメトリクスですが、ある時刻までのPodへのLivenessProbeが失敗した累積数 しか取れないという問題点がありました。
具体的に何が問題になるのか?を説明します。
画像はあるPodに対しての kubernetes.liveness_probe.failure.total のメトリクスです。
実際にLivenessProbeが失敗したのは17:30頃と21:00頃ですが、その度に値がインクリメントされ、累積数が表示され続けています。

検討しているSLIは「LivenessProbeの失敗回数が閾値以内である時間の割合」だったのですが、LivenessProbe失敗の累積数が一度でも閾値を超えてしまうと、Podがリスタートするまでの間、常に閾値を超えた状態になってしまいます。
この状態になってしまうと、クラスタ上でノイジーネイバーのような問題がない場合でも、エラーバジェットを消費してしまうため、このメトリクスをそのままSLIにすることは厳しそうと判断しました。
ただ、他にLivenessProbeの失敗を監視できるメトリクスもなかったので、このメトリクスに関数を適応してみることで、先述の問題を解決できるかを試してみました。
monotonic_diff関数を使って解決できるかを試してみる
先述の問題に対して、monotonic_diff関数というメトリクスのデータポイント間の正の差分を出力してくれる関数を使い、解決できるかを試してみました。
具体的には、この関数を使ってkubernetes.liveness_probe.failure.totalの各データポイントの差分を取り、累積和ではなく、LivenessProbeが失敗した時刻での失敗回数のみが表示されるようにしたかった形になります。
こうすることで、失敗回数が増加した場合のみ検知ができ、エラーバジェットを消費し続ける問題を解決できるのではないかと考えました。
いい案だと思って試してみるまではよかったのですが、この関数を使うと、実際にLivenessProbeが失敗している時刻や回数と比較したときに、ずれが生じてしまうという別の問題に直面しました。
以下の画像は実際にLivenessProbeが失敗したイベントを示したものです。
14:00~15:00頃にPodのLivenessProbeが複数回失敗していることがわかります。

以下はmonotonic_diff関数を適応したkubernetes.liveness_probe.failure.totalのメトリクスです。
14:00~15:00頃にLivenessProbeが失敗しているはずなのに、0となったままです。

上記はわかりやすく示すための一例ですが、このような事象が他の時刻でも見受けられたり、イベント上ではLivenessProbeが失敗していない時刻でもLivenessProbeが失敗している事象も発生したり、とmonotonic_diff関数を使うと実際のデータと異なるメトリクスが表示されるようになってしまったという感じです。
この問題に関しても調査しましたが、解決ができずにこちらも採用を断念した形になります。
他の関数等も諸々検討しましたが、kubernetes.liveness_probe.failure.total のメトリクスを使い、SLIを定義するのは難しそうと判断しました。
イベントモニターでLivenessProbeの失敗を検知する
先述した2つのSLIの検討案が見事に実現できないとなり、代替案を模索していました。
先述しているLivenessProbeの失敗回数はイベントモニターを使えば、LivenessProbeの失敗数が増加した際に検知ができることはわかっていたのですが、イベントモニターはDatadogの仕様上SLOには追加できないという制約がありました。
SLOに設定できるモニターの種類
最終的には代替案も特に思いつかず、SLI/SLOには追加できなくても、再度Diskスロットリングを起きた際に、この問題を検知することができないという状況を避けるため、イベントモニターにてLivenessProbeの失敗数増加を検知する案を取りました。
まとめ
今回はSREチームで発生したNodeのDiskスロットリングによるノイジーネイバーの問題を検知する、問題が起きていないかを可視化できるようにSLI/SLOを検討したプロセスについて紹介しました。
結論としては、先述の通り、検討したSLI案が2つとも実現ができなさそうで、当時は他にもいい案がなさそうだなとなったため、イベントモニターで検知だけ行おうということになりました。
個人的には初めてSLI/SLOの策定を行うタスクに取り組んだのですが、定義したいSLI/SLOがことごとく技術的な問題によって正確に計測することができなさそうとなり、かなり苦戦しました。
また、仮にLivenessProbeの失敗回数にてSLI/SLOを定義したとしても、LivenessProbeの失敗自体はノイジーネイバーのみによって発生するものではないため、あまりにノイズ(アプリケーション起因のLivenessProbeの失敗等)が多いと、誤検知が多すぎて、使い物にならない可能性もあります。(これは実際に運用してみないとわからないですが)
このSLI/SLO検討を通して、適切なSLIの設計と実現がいかに難しいかを少し学べたと考えています。