[{"data":1,"prerenderedAt":537},["ShallowReactive",2],{"blog-ja-k3s-memory-qos-cgroup-v2-memory-high-throttling":3,"blog-related-ja-k3s-memory-qos-cgroup-v2-memory-high-throttling":479,"blog-ja-k3s-memory-qos-cgroup-v2-memory-high-throttling-alt":467},{"id":4,"title":5,"author":6,"body":7,"date":461,"description":462,"extension":463,"image":464,"locale":465,"meta":466,"navigation":467,"path":468,"seo":469,"stem":470,"tags":471,"__hash__":478},"blog\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling.md","requestsもlimitsも正しいのに、K3sのPodが突然重くなった。犯人はOOMKillではなくcgroup v2のmemory.high早期スロットリングだった","Kubo Team",{"type":8,"value":9,"toc":449},"minimark",[10,15,23,31,34,46,50,56,59,85,96,100,106,116,132,142,146,152,161,194,201,210,214,220,225,235,246,250,256,259,305,308,312,318,321,336,342,365,396,403,406,412,418,430],[11,12,14],"h2",{"id":13},"_1-再現する障害-requestslimitsは正しいのにpodが突然重くなる","1. 再現する障害 — requests\u002Flimitsは正しいのに、Podが突然重くなる",[16,17,18],"p",{},[19,20],"img",{"alt":21,"src":22},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection01.webp",[16,24,25,26,30],{},"requestsとlimitsをマニフェスト通りに設定しているのに、ある日突然、特定のPodだけ応答が遅くなる。再起動はしていない。",[27,28,29],"code",{},"kubectl get events","を見てもOOMKilledは記録されていない。ノードのメモリにも余裕がある。それでもレイテンシだけが悪化し続ける——こういう障害に遭遇したことはないだろうか。",[16,32,33],{},"K3s \u002F Kubernetesの運用でよくある思い込みは、「メモリ周りの異常はOOMKillとして現れる」というものだ。limitsを超えたら強制終了され、Podイベントに刻まれる。だからOOMKilledが出ていなければメモリは無関係だと、多くのエンジニアが判断してしまう。",[16,35,36,37,41,42,45],{},"だが実際には、",[38,39,40],"strong",{},"killされずに、ただ重くなる","というメモリ起因の障害が存在する。その正体は、cgroup v2の",[27,43,44],{},"memory.high","という、limitsよりも手前に置かれたスロットリングの壁だ。",[11,47,49],{"id":48},"_2-最初に疑った場所-メモリリークgc設定ノードのメモリ圧迫どれもハズレ","2. 最初に疑った場所 — メモリリーク、GC設定、ノードのメモリ圧迫。どれもハズレ",[16,51,52],{},[19,53],{"alt":54,"src":55},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection02.webp",[16,57,58],{},"このような症状に遭遇した場合、インフラエンジニアがまず疑うのは次のような定番ポイントだろう。",[60,61,62,69,75],"ul",{},[63,64,65,68],"li",{},[38,66,67],{},"アプリケーション側のメモリリーク",": ヒープダンプを取得して確認するが、使用量はrequestsの範囲内で安定している",[63,70,71,74],{},[38,72,73],{},"GC（ガベージコレクション）のチューニング不足",": GCログを確認しても、極端なフルGCの連発は見られない",[63,76,77,80,81,84],{},[38,78,79],{},"ノードのメモリ圧迫（Node Pressure Eviction）",": ",[27,82,83],{},"kubectl describe node","を確認しても、MemoryPressureのConditionは立っていない",[16,86,87,88,95],{},"一つずつ潰していっても原因にたどり着けない。こうなると、調査は「アプリケーション層」から「カーネル・cgroup層」へと視点を移す必要がある。こうした間欠的で原因が掴みにくい障害は、自己運用のクラスタでは調査に半日以上かかることも珍しくない。",[89,90,94],"a",{"href":91,"rel":92},"https:\u002F\u002Fkubo.hexabase.io\u002F",[93],"nofollow","Kubo","のようなマネージド環境であれば、こうした切り分けの初動を運用側のノウハウで短縮できる場面もある。",[11,97,99],{"id":98},"_3-真因-cgroup-v2のmemoryhighによる早期スロットリング","3. 真因 — cgroup v2のmemory.highによる早期スロットリング",[16,101,102],{},[19,103],{"alt":104,"src":105},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection03.webp",[16,107,108,109,112,113,115],{},"結論から言うと、犯人は",[27,110,111],{},"memoryThrottlingFactor","という設定によって有効化される、cgroup v2の",[27,114,44],{},"だった。",[16,117,118,119,122,123,128,129,131],{},"Kubernetesには",[38,120,121],{},"Memory QoS","という仕組みがあり、Podのrequests\u002Flimitsをcgroup v2のメモリ制御インターフェースにマッピングする（",[89,124,127],{"href":125,"rel":126},"https:\u002F\u002Fwww.kubernetes.dev\u002Fresources\u002Fkeps\u002F2570\u002F",[93],"KEP-2570: Memory QoS","）。この仕組みを有効化すると、limitsとは別に、もう一段階手前の「スロットリング境界」がカーネルレベルで設定される。これが",[27,130,44],{},"だ。",[16,133,134,135,137,138,141],{},"今回のケースでは、このMemory QoSの",[27,136,111],{},"が、",[38,139,140],{},"古いドキュメントの推奨値や過去バージョンの設定を引き継いだ形で明示的に設定されていた","ことが根本原因だった。そのため、アプリケーションのメモリ使用量がlimitsに達するよりずっと手前の段階で、静かにスロットリングが発動していた。",[11,143,145],{"id":144},"_4-cgroup-v2のメモリ制御4段階-memorymaxminlowhighの役割分担","4. cgroup v2のメモリ制御4段階 — memory.max\u002Fmin\u002Flow\u002Fhighの役割分担",[16,147,148],{},[19,149],{"alt":150,"src":151},"section04","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection04.webp",[16,153,154,155,160],{},"ここで、cgroup v2のメモリ制御を正しく理解しておきたい。Linuxカーネルの公式ドキュメントでは、メモリコントローラは次の4つのインターフェースファイルで構成されると定義されている（",[89,156,159],{"href":157,"rel":158},"https:\u002F\u002Fdocs.kernel.org\u002Fadmin-guide\u002Fcgroup-v2.html",[93],"Control Group v2（Linux kernel 公式ドキュメント）","）。",[60,162,163,169,175,188],{},[63,164,165,168],{},[38,166,167],{},"memory.min",": メモリ使用がこの境界内であれば、いかなる条件下でもそのcgroupのメモリは回収されないハード保護",[63,170,171,174],{},[38,172,173],{},"memory.low",": memory.minよりも緩いベストエフォート保護。回収可能な他のメモリがない場合のみ対象になる",[63,176,177,179,180,183,184,187],{},[38,178,44],{},": 超過すると、プロセスが",[38,181,182],{},"スロットリングされ、ダイレクトメモリ回収の圧力下に置かれる","。重要なのは、この時点では",[38,185,186],{},"OOM killerは呼び出されない","という点だ",[63,189,190,193],{},[38,191,192],{},"memory.max",": ハードリミット。この制限に達してメモリを削減できない場合に、初めてOOM killerが呼び出される",[16,195,196,197,200],{},"つまり、Podの監視対象は「limits = memory.max」だけでは不十分で、",[38,198,199],{},"memory.highという別のレイヤーが存在し、しかもそれはkillを伴わない","という事実が、今回の障害を見えにくくしていた。",[16,202,203,204,209],{},"Red Hatのエンジニアも、cgroup v2がもたらす改善点として「OOM圧迫時のノード安定性向上」を挙げている（",[89,205,208],{"href":206,"rel":207},"https:\u002F\u002Fwww.redhat.com\u002Fja\u002Fblog\u002Fcgroups-v2-goes-ga-in-openshift-4.13",[93],"cgroups v2 goes GA in OpenShift 4.13（Red Hat公式ブログ）","）。memory.highによる早期のリクレイム圧は、ノード全体を巻き込むOOM Killよりも\"穏便な\"挙動として設計されているが、その穏便さが裏目に出て障害調査を難しくするケースがある。",[11,211,213],{"id":212},"_5-なぜoomkilledが出ないのか-memoryhighとmemorymaxの挙動の違い","5. なぜOOMKilledが出ないのか — memory.highとmemory.maxの挙動の違い",[16,215,216],{},[19,217],{"alt":218,"src":219},"section05","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection05.webp",[16,221,222,224],{},[27,223,192],{},"を超過した場合、カーネルはOOM killerを呼び出し、対象プロセスを強制終了する。これがKubernetesのPodイベントに記録される、おなじみの「OOMKilled」だ。",[16,226,227,228,230,231,234],{},"一方、",[27,229,44],{},"を超過した場合はまったく異なる挙動になる。カーネルはプロセスをkillせず、代わりに**ダイレクトメモリ回収（direct reclaim）**を発動し、ページキャッシュの追い出しなどでメモリ使用量を抑え込もうとする。この処理自体に時間がかかるため、アプリケーションのレイテンシが悪化する。しかしプロセス自体は生き続けているため、",[38,232,233],{},"Podイベントには何も記録されない","。",[16,236,237,238,241,242,245],{},"この非対称性が、「OOMKilledが出ていない＝メモリは無関係」という調査者の直感を裏切る。",[27,239,240],{},"memory.events","ファイルを確認すると、",[27,243,244],{},"high","カウンタが増加し続けていることが分かるはずだが、Podのイベントログだけを見ている限り、この情報には決してたどり着けない。",[11,247,249],{"id":248},"_6-切り分け手順-cgroupファイルでmemoryhighmemorycurrentmemoryeventsを確認する","6. 切り分け手順 — cgroupファイルでmemory.high\u002Fmemory.current\u002Fmemory.eventsを確認する",[16,251,252],{},[19,253],{"alt":254,"src":255},"section06","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection06.webp",[16,257,258],{},"実際の切り分けは、Pod単位ではなくノード上のcgroupファイルを直接確認することから始まる。",[260,261,262,271,281,291,297],"ol",{},[63,263,264,80,267,270],{},[38,265,266],{},"対象Podの稼働ノードを特定する",[27,268,269],{},"kubectl get pod \u003Cpod-name> -o wide","でノード名を確認",[63,272,273,276,277,280],{},[38,274,275],{},"cgroupパスを特定する",": 該当ノードにアクセスし、コンテナIDからcgroup v2のパス（",[27,278,279],{},"\u002Fsys\u002Ffs\u002Fcgroup\u002Fkubepods.slice\u002F...","配下）を特定する",[63,282,283,286,287,290],{},[38,284,285],{},"現在のmemory.high設定値を確認する",": 値が",[27,288,289],{},"max","（無制限）になっていなければ、何らかの設定でスロットリング境界が入っている",[63,292,293,296],{},[38,294,295],{},"memory.currentで実使用量を確認する",": memory.highの値に実使用量が張り付いていないかをチェック",[63,298,299,80,302,304],{},[38,300,301],{},"memory.eventsでhighカウンタを確認する",[27,303,244],{},"の値が増加し続けていれば、まさにスロットリングが発生している証拠",[16,306,307],{},"このステップを踏めば、「OOMKilledは出ていないが、確実にメモリ制御が関与している」ことを客観的な数値で証明できる。cgroup v2ベースでのリソース分離の考え方そのものについては、OpenShift\u002FKubernetesの公式解説も参考になる。",[11,309,311],{"id":310},"_7-恒久対策-memorythrottlingfactorの見直しとrequestslimits設計","7. 恒久対策 — memoryThrottlingFactorの見直しとrequests\u002Flimits設計",[16,313,314],{},[19,315],{"alt":316,"src":317},"section07","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection07.webp",[16,319,320],{},"対策の方向性は大きく2つある。",[16,322,323,329,330,335],{},[38,324,325,326,328],{},"1つ目は",[27,327,111],{},"そのものを見直すこと。"," CNCFのブログでは、この値が積極的すぎる設定になっていると、典型的なJavaアプリケーションのようにメモリ使用率が常時高めに推移するワークロードで、頻繁なスロットリングを引き起こす実例が報告されている（",[89,331,334],{"href":332,"rel":333},"https:\u002F\u002Fwww.cncf.io\u002Fblog\u002F2024\u002F04\u002F25\u002Fhow-katalyst-guarantees-memory-qos-for-colocated-applications\u002F",[93],"How Katalyst guarantees memory QoS for colocated applications（CNCF blog）","）。値を1.0に近づけることで、スロットリング境界をmemory.maxに近づけ、早期発動を抑制できる。",[16,337,338,341],{},[38,339,340],{},"2つ目はrequests\u002Flimitsの設計そのものを見直すこと。"," memory.highはrequestsとlimitsの間のどこかに設定される性質上、両者の差が大きいワークロードほど早期スロットリングの影響を受けやすい。差を意図的に縮めることも、根本的な対策になり得る。",[16,343,344,345,348,349,354,355,357,358,361,362,364],{},"ここで一点、正確に書いておきたい。",[38,346,347],{},"Memory QoS自体はKubernetes 1.37でBetaに昇格し、フィーチャーゲートはデフォルトで有効になった","（",[89,350,353],{"href":351,"rel":352},"https:\u002F\u002Fkubernetes.io\u002Fblog\u002F2026\u002F09\u002F14\u002Fkubernetes-v1-37-memory-qos-graduates-to-beta\u002F",[93],"Kubernetes v1.37: Memory QoS Graduates to Beta（公式ブログ）","）。だが「v1.37にアップグレードしただけで自動的にスロットリングが始まる」わけではない。公式ブログは「ベータ機能の有効化だけで既存ワークロードのレイテンシが黙って変わってはならない」という方針を明言しており、",[27,356,111],{},"のデフォルト値は",[27,359,360],{},"null","に変更されている。明示的にこの値を設定しない限り、kubeletは",[27,363,44],{},"を書き込まない。",[16,366,367,368,373,374,379,380,383,384,389,390,395],{},"今回のような障害が起きるのは、",[38,369,370,372],{},[27,371,111],{},"を明示的に設定している場合","だ。実際、公式ドキュメント（",[89,375,378],{"href":376,"rel":377},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fworkloads\u002Fpods\u002Fpod-qos\u002F",[93],"Pod Quality of Service Classes","）のKubeletConfiguration設定例には",[27,381,382],{},"memoryThrottlingFactor: 0.9","という値が載っている。こうしたサンプルや古いバージョンのドキュメント、一部のマネージドKubernetesサービスの推奨設定をそのまま適用している、過去バージョンの設定を引き継いでいる、といったケースが典型的だ（経緯の詳細: ",[89,385,388],{"href":386,"rel":387},"https:\u002F\u002Fkubernetes.io\u002Fblog\u002F2026\u002F04\u002F29\u002Fkubernetes-v1-36-memory-qos-tiered-protection\u002F",[93],"Kubernetes v1.36: Tiered Memory Protection（公式ブログ）","、",[89,391,394],{"href":392,"rel":393},"https:\u002F\u002Fdevopsboys.com\u002Fblog\u002Fkubernetes-1-37-memory-qos-guide",[93],"Kubernetes 1.37 Memory QoS解説","）。なお計算式や係数はバージョンで記述が異なるため、本記事では断定せず、導入・変更時は手元のクラスタのkubeletログとcgroupファイルで実測確認することを勧める。",[16,397,398,399,402],{},"自己運用のK3sクラスタでこうした設定を一つひとつ追いかけ、バージョンアップのたびに挙動変化を把握し続けるのは軽くない運用負荷だ。",[89,400,94],{"href":91,"rel":401},[93],"のようなマネージドK3sであれば、こうしたデフォルト挙動の変化を運用側で把握し、設定の妥当性を可視化した状態で提供できる。",[11,404,405],{"id":405},"まとめ",[16,407,408],{},[19,409],{"alt":410,"src":411},"section08","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Fsection08.webp",[16,413,414,415,417],{},"「OOMKilledが出ていない＝メモリは無関係」という前提は、cgroup v2のmemory.highの存在によって必ずしも正しくない。requests\u002Flimitsを正しく設定していても、",[27,416,111],{},"が明示的に設定されていれば、limitsに達するよりずっと手前でスロットリングが発生し得る。",[16,419,420,421,423,424,423,427,429],{},"今回整理した切り分け手順——cgroupパスの特定、",[27,422,44],{},"\u002F",[27,425,426],{},"memory.current",[27,428,240],{},"の確認——を押さえておけば、次に同様の「原因不明の重さ」に出会ったときも、アプリケーション層だけでなくカーネル・cgroup層まで視野を広げて調査できるはずだ。",[16,431,432,433,438,439,442,443,448],{},"こうしたKubernetesのバージョンアップごとの挙動変化や、cgroup v2レベルの細かな設定差分まで自前で追い続けるのは、インフラチームにとって決して軽くない負担だ。",[89,434,437],{"href":435,"rel":436},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fkubo\u002Fon-premise",[93],"Kubo On-Premise","や",[89,440,94],{"href":91,"rel":441},[93],"のようなマネージドK3s環境を活用し、こうした調査・運用の負担そのものを見直すことも、選択肢の一つとして検討する価値がある。気になる方は",[89,444,447],{"href":445,"rel":446},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[93],"お問い合わせ","から相談してみてほしい。",{"title":450,"searchDepth":451,"depth":451,"links":452},"",2,[453,454,455,456,457,458,459,460],{"id":13,"depth":451,"text":14},{"id":48,"depth":451,"text":49},{"id":98,"depth":451,"text":99},{"id":144,"depth":451,"text":145},{"id":212,"depth":451,"text":213},{"id":248,"depth":451,"text":249},{"id":310,"depth":451,"text":311},{"id":405,"depth":451,"text":405},"2026-11-06","K3sでrequests\u002Flimitsを正しく設定したPodが、OOMKilledイベントもなく突然重くなった謎を追った記録。原因はcgroup v2のmemory.highによる早期スロットリング。切り分け方法と対策を解説する。","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling\u002Ftitle.webp","ja",{},true,"\u002Fblog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling",{"title":5,"description":462},"blog\u002Fja\u002Fk3s-memory-qos-cgroup-v2-memory-high-throttling",[472,473,474,475,476,477],"k3s","kubernetes","cgroup-v2","memory-qos","memory-high","resource-management","PvbAbwaFDB4i1puJ94DL6x32ww5QWbeIqmqudoQCQtk",[480,490,498,507,517,527],{"path":481,"title":482,"description":483,"date":484,"tags":485},"\u002Fblog\u002Fja\u002Fk3s-gpu-oomkilled-vpa-resource-estimation","GPUメモリ不足でAIトレーニングジョブがOOMKilled。K3sでVPAが効かない理由とリソース設計","K3s上でAIトレーニングジョブがGPUメモリ不足でOOMKilledされる原因と、Vertical Pod Autoscalerが GPU リソースには効かない理由を解説。requests\u002Flimitsの正しい見積もり方法と実測ベースの設計手順まで、運用者向けに具体的に紹介します。","2026-09-20",[472,473,486,487,488,489,477],"gpu","oomkilled","vpa","ai-infrastructure",{"path":491,"title":492,"description":493,"date":494,"tags":495},"\u002Fblog\u002Fja\u002Fai-generated-kubernetes-manifest-resource-overprovisioning","Kubernetesリソース設計はAI任せにできない。「動く」YAMLがクラウド代を69%溶かす理由","AIが生成したKubernetesマニフェストはkubectl applyが通り「動く」。しかしKubernetesリソース設計を誤ると過剰プロビジョニングでクラウド代が膨らむ。AIの限界と本番品質のrequests\u002Flimits設計を解説する。","2026-08-04",[472,473,477,496,497],"capacity-planning","managed-kubernetes",{"path":499,"title":500,"description":501,"date":502,"tags":503},"\u002Fblog\u002Fja\u002Fai-manifest-generation-kubernetes-architecture-bottleneck","AIがYAMLを1秒で書いても、クラスタは1秒も速くならない。Kubernetes運用の本当のボトルネックはコードではなくアーキテクチャという話","AIでKubernetesマニフェストを爆速生成しても、本番のKubernetes運用が速くなるとは限らない。CPUスロットリング、HPAとVPAの競合、DB接続不足という3つの本当のボトルネックと、AIと人間の役割分担を解説する。","2026-07-18",[473,472,477,504,505,506],"autoscaling","ai-ops","devops",{"path":508,"title":509,"description":510,"date":511,"tags":512},"\u002Fblog\u002Fja\u002Fk3s-ai-agent-audit-log-traceability-gap","AIエージェントがKubernetes APIを叩いた後、何をしたのか追えなかった。Audit Logの設定に空いていた穴","AIエージェントをKubernetes上で動かし始めたら、トラブル発生時にAudit Logだけでは「誰が・何の目的で」操作したのか追えないことに気づいた。Audit Policyの盲点とトレーサビリティ確保の実践を解説する。","2026-11-09",[472,473,513,514,515,516],"ai-agent","audit-log","rbac","observability",{"path":518,"title":519,"description":520,"date":521,"tags":522},"\u002Fblog\u002Fja\u002Fk3s-kubelet-eviction-inode-exhaustion-diskpressure","K3sでPodが次々Evictedされた。ディスクは余っているのにinodeだけ枯渇していた","K3sでPodが次々Evictedされ、df -hは正常なのに復旧しない。原因はディスク容量ではなくinode枯渇だった。kubeletのeviction閾値とdf -iでの診断、再発防止策を解説する。","2026-11-08",[472,473,523,524,525,526],"kubelet","eviction","diskpressure","troubleshooting",{"path":528,"title":529,"description":530,"date":531,"tags":532},"\u002Fblog\u002Fja\u002Fk3s-cilium-kube-proxy-replacement-nodeport-unreachable","K3sでCiliumのkube-proxy置き換えをしたら、NodePortだけ繋がらなくなった","K3sにCiliumのkube-proxy replacementを公式手順通りに導入したら、NodePort経由の通信だけが届かなくなった。原因はFlannelやServiceLB(Klipper)との競合、k8sServiceHost未設定などK3s特有の設定差分にあった。本番投入前のチェックリストを解説。","2026-11-07",[472,473,533,534,535,536],"cilium","ebpf","kube-proxy","networking",1794305485011]