[{"data":1,"prerenderedAt":439},["ShallowReactive",2],{"blog-ja-k3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending":3,"blog-related-ja-k3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending":380,"blog-ja-k3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending-alt":368},{"id":4,"title":5,"author":6,"body":7,"date":362,"description":363,"extension":364,"image":365,"locale":366,"meta":367,"navigation":368,"path":369,"seo":370,"stem":371,"tags":372,"__hash__":379},"blog\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending.md","ノードをdrainしたらPodがPendingのまま戻らなかった。K3sでTopology Spread ConstraintsとPodDisruptionBudgetが衝突する理由","Kubo Team",{"type":8,"value":9,"toc":351},"minimark",[10,15,23,26,46,50,56,81,87,91,97,113,137,167,171,177,197,203,207,213,226,248,252,258,267,283,286,308,317,321,328,338],[11,12,14],"h2",{"id":13},"_1-事象-定期メンテナンスでノードをdrainしたらpodが戻ってこなかった","1. 事象 — 定期メンテナンスでノードをdrainしたら、Podが戻ってこなかった",[16,17,18],"p",{},[19,20],"img",{"alt":21,"src":22},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection01.webp",[16,24,25],{},"K3sクラスタで月次のOSパッチ適用のため、いつもどおりノードを1台ずつcordon\u002Fdrainしていたときのことです。対象ノードのPodは他ノードへ無事退避し、ほとんどのワークロードは数秒で復旧しました。ところが、あるDeploymentだけ1つのPodがPending状態のまま動かなくなりました。",[16,27,28,32,33,37,38,45],{},[29,30,31],"code",{},"kubectl get pods"," を見る限りPodDisruptionBudgetは正しく設定されており、削除自体は許可されているはずでした。にもかかわらず、新しいPodはどのノードにもスケジュールされません。「PDBの設定ミスでは」という声が上がりましたが、結論から言うと原因は別の場所にありました。本記事では、Topology Spread ConstraintsとPodDisruptionBudgetが",[34,35,36],"strong",{},"それぞれ正しく動作しているにもかかわらず","Podが戻ってこなくなる仕組みを、公式ドキュメントベースで検証します。こうした設定差分の切り分けを毎回手作業で追うのは地味に負荷が高く、",[39,40,44],"a",{"href":41,"rel":42},"https:\u002F\u002Fkubo.hexabase.io\u002F",[43],"nofollow","Kubo","のCaptain UIのようにクラスタ状態を可視化できる環境のありがたみを実感する場面です。",[11,47,49],{"id":48},"_2-原因調査-poddisruptionbudgetは正常問題はスケジューラ側だった","2. 原因調査 — PodDisruptionBudgetは正常、問題はスケジューラ側だった",[16,51,52],{},[19,53],{"alt":54,"src":55},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection02.webp",[16,57,58,59,62,63,68,69,72,73,76,77,80],{},"まず疑ったのはPDBです。Kubernetes公式ドキュメントによれば、Eviction APIはPDBを評価し、削除が許可されない場合は",[29,60,61],{},"429 Too Many Requests","を返す仕様です（",[39,64,67],{"href":65,"rel":66},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fscheduling-eviction\u002Fapi-eviction\u002F",[43],"Kubernetes公式: API-initiated Eviction","）。しかし",[29,70,71],{},"kubectl get pdb","では",[29,74,75],{},"disruptionsAllowed","は1以上で、eviction自体は通っていました。Podは一度正しく退避し、",[34,78,79],{},"新しいPodがスケジュールされないだけ","だったのです。",[16,82,83,86],{},[29,84,85],{},"kubectl get events --sort-by='.lastTimestamp'","を確認すると、スケジューラがトポロジースキューの超過を理由にPodを配置できていないイベントが記録されていました。犯人はPDBではなく、同じDeploymentに設定していたTopology Spread Constraintsでした。",[11,88,90],{"id":89},"_3-仕組み解説-maxskewとdonotscheduleがハード制約になる条件","3. 仕組み解説 — maxSkewとDoNotScheduleが「ハード制約」になる条件",[16,92,93],{},[19,94],{"alt":95,"src":96},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection03.webp",[16,98,99,104,105,108,109,112],{},[39,100,103],{"href":101,"rel":102},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fscheduling-eviction\u002Ftopology-spread-constraints",[43],"Kubernetes公式ドキュメント","によると、",[29,106,107],{},"maxSkew","は「対象トポロジ内の最小マッチングPod数との許容される最大差分」を定義するパラメータです。",[29,110,111],{},"whenUnsatisfiable","には2値あります。",[114,115,116,129],"ul",{},[117,118,119,124,125,128],"li",{},[34,120,121],{},[29,122,123],{},"DoNotSchedule","（ハード制約）: 条件を満たせない場合、Podはスケジュールされず",[34,126,127],{},"Pending","のままになる",[117,130,131,136],{},[34,132,133],{},[29,134,135],{},"ScheduleAnyway","（ソフト制約）: スキューを減らす配置を優先しつつ必ずスケジュールされる",[16,138,139,140,143,144,143,147,150,151,156,157,159,160,162,163,166],{},"今回の設定は",[29,141,142],{},"topologyKey: kubernetes.io\u002Fhostname","、",[29,145,146],{},"maxSkew: 1",[29,148,149],{},"whenUnsatisfiable: DoNotSchedule","でした。",[39,152,155],{"href":153,"rel":154},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fprescriptive-guidance\u002Flatest\u002Fha-resiliency-amazon-eks-apps\u002Fspread-workloads.html",[43],"AWS Prescriptive Guidance","も、",[29,158,123],{},"は",[29,161,107],{},"や",[29,164,165],{},"minDomains","でハード要件を作る設定だと説明しています。問題は、このハード要件がドレイン中にどう崩れるかでした。",[11,168,170],{"id":169},"_4-核心-drainがノード数不足を自ら作り出していた","4. 核心 — drainが「ノード数不足」を自ら作り出していた",[16,172,173],{},[19,174],{"alt":175,"src":176},"section04","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection04.webp",[16,178,179,180,183,184,186,187,190,191,196],{},"技術解説サイトStackHarborは、この種の事象を「Pending-Forever Trap（永遠にPendingのままになる罠）」と呼んでいます。",[29,181,182],{},"maxSkew:1","のhostname制約を",[29,185,123],{},"で設定した状態で",[34,188,189],{},"レプリカ数がノード数を上回る","と、スケジューラにとって構造的に解決不可能な配置要求になり、Podは無期限にPendingのままになるという指摘です（",[39,192,195],{"href":193,"rel":194},"https:\u002F\u002Fstackharbor.com\u002Fen\u002Fknowledge-base\u002Fk8s-topology-spread-constraints\u002F",[43],"StackHarbor: Kubernetes Topology Spread Constraints","）。",[16,198,199,200,202],{},"今回のケースはまさにこれでした。平常時はノード3台にレプリカ3つで、各ノードに1つずつという完璧な分散が成立していました。ところがメンテナンスでノードを1台cordonした瞬間、スケジュール対象のノードは実質2台に減ります。レプリカ数(3)がノード数(2)を上回るため",[29,201,182],{},"を満たす配置は存在しなくなり、退避してきたPodは行き場を失ってPendingのまま待ち続けていたのです。PodDisruptionBudgetは「削除してよいか」だけを判断し、「削除後どこに置くか」までは関知しません。ドレインという運用操作そのものが、Topology Spread Constraintsの前提条件を内側から崩していたわけです。",[11,204,206],{"id":205},"_5-poddisruptionbudgetだけでは守れない理由と見落としがちな落とし穴","5. PodDisruptionBudgetだけでは守れない理由と、見落としがちな落とし穴",[16,208,209],{},[19,210],{"alt":211,"src":212},"section05","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection05.webp",[16,214,215,216,219,220,225],{},"この一件から見えてくるのは、PodDisruptionBudgetとTopology Spread Constraintsが",[34,217,218],{},"別の問題を解決する独立した機能","だという点です。CAST AIのブログでも、スケジューラは既定でクラスタの全ゾーンを把握しておらず、ノードプールがゼロにスケールダウンするとトポロジドメイン自体が消失し制約を満たすノードがなくなるリスクが指摘されています（",[39,221,224],{"href":222,"rel":223},"https:\u002F\u002Fcast.ai\u002Fblog\u002Ftopology-spread-constraints-for-increased-cluster-availability-and-efficiency-and-a-much-better-cost\u002F",[43],"CAST AI: Topology Spread Constraints","）。ノードのdrainも一時的に同じ状況を引き起こします。",[16,227,228,229,231,232,235,236,241,242,247],{},"さらに",[29,230,135],{},"（ソフト制約）に緩めても油断はできません。スケジュール時にしか評価されないため、分散が崩れても稼働中のPodは自動では再配置されないからです。公式Deschedulerプロジェクトの",[29,233,234],{},"RemovePodsViolatingTopologySpreadConstraint","戦略は違反Podを事後的に退避させますが、これも「PDBを侵害する削除はしない」原則に従うため、3つの機能を互いの役割を理解したうえで組み合わせる必要があります（",[39,237,240],{"href":238,"rel":239},"https:\u002F\u002Fgithub.com\u002Fkubernetes-sigs\u002Fdescheduler",[43],"kubernetes-sigs\u002Fdescheduler","）。元のKEP（Kubernetes Enhancement Proposal）でも、Pod Anti-Affinityの「1ドメインにつき最大1Pod」という厳格ルールがローリングアップデート時に配置不可を招きやすいことが提案動機だったとされています（",[39,243,246],{"href":244,"rel":245},"https:\u002F\u002Fwww.kubernetes.dev\u002Fresources\u002Fkeps\u002F895\u002F",[43],"KEP-895: Pod Topology Spread","）。Anti-Affinityの問題を避けた機能が、drainと組み合わさると同種の問題を再現する皮肉な構図です。",[11,249,251],{"id":250},"_6-k3s本番でのメンテナンス手順への落とし込み","6. K3s本番でのメンテナンス手順への落とし込み",[16,253,254],{},[19,255],{"alt":256,"src":257},"section06","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Fsection06.webp",[16,259,260,261,266],{},"K3s公式ドキュメントでは、APIサーバーの短時間停止に敏感なワークロードがある場合、アップグレード時に手動でノードをdrain・cordonすることが推奨され、サーバーノードを1台ずつ先に、その後エージェントノードを行う順序厳守も明記されています（",[39,262,265],{"href":263,"rel":264},"https:\u002F\u002Fdocs.k3s.io\u002Fupgrades\u002Fmanual",[43],"K3s公式: Manual Upgrade","）。この「1台ずつ」という単位こそ、ノード数が一時的に減る瞬間を生み出す原因です。",[16,268,269,270,273,274,277,278,196],{},"Longhorn併用時はさらに注意が必要です。公式ドキュメントでは",[29,271,272],{},"kubectl drain --timeout","はレプリカ再構築に十分な値（または無制限）にし、複数ノードを同時アップグレードする場合は",[34,275,276],{},"同時台数を各ボリュームのレプリカ数より少なく","する必要があるとされています（",[39,279,282],{"href":280,"rel":281},"https:\u002F\u002Flonghorn.io\u002Fdocs\u002F1.9.2\u002Fmaintenance\u002Fmaintenance\u002F",[43],"Longhorn公式: Node Maintenance",[16,284,285],{},"実務上の対策は次の3点が有効です。",[114,287,288,300,305],{},[117,289,290,291,293,294,296,297,299],{},"メンテナンス直前だけ対象Deploymentの",[29,292,111],{},"を",[29,295,123],{},"から",[29,298,135],{},"へ一時的に緩和し、メンテナンス後に戻す",[117,301,302,304],{},[29,303,107],{},"をレプリカ数とノード数の比率に応じて現実的な値に見直す",[117,306,307],{},"メンテナンス手順にTopology Spread ConstraintsとPodDisruptionBudgetの双方を加味したチェックリストを組み込む",[16,309,310,311,316],{},"こうした手順の標準化は運用チームの勘に頼りがちな領域です。オンプレミスやエアギャップ環境で再現性高く運用したい場合は、",[39,312,315],{"href":313,"rel":314},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fkubo\u002Fon-premise",[43],"Kubo On-Premise","のような完全自社管理型のマネージドK3s基盤も選択肢になります。",[11,318,320],{"id":319},"_7-まとめ","7. まとめ",[16,322,323,324,327],{},"PodDisruptionBudgetとTopology Spread Constraintsは、どちらも可用性を守る機能ですが、守る対象が異なります。PDBは「削除してよいPod数」を管理し、Topology Spread Constraintsは「Podをどこに置けるか」を管理します。ノードのdrainのように",[34,325,326],{},"クラスタの母数そのものを一時的に変える操作","は、この2つの前提を同時に揺さぶります。",[16,329,330,331,333,334,337],{},"今回のポイントは、PDBの設定ミスを疑う前に「drain中は一時的にノード数が減る」という事実と",[29,332,107],{},"計算を結びつけて考えることでした。この視点があれば、",[29,335,336],{},"kubectl get events","から原因にたどり着くまでの時間を大きく短縮できます。",[16,339,340,341,344,345,350],{},"K3sベースの本番クラスタでこうした検証や運用手順の標準化にかかる工数を減らしたい場合は、",[39,342,44],{"href":41,"rel":343},[43],"のAI-Driven DeploymentとCaptain UIによる可視化が助けになります。気になる方は",[39,346,349],{"href":347,"rel":348},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[43],"お問い合わせ","から相談してみてください。",{"title":352,"searchDepth":353,"depth":353,"links":354},"",2,[355,356,357,358,359,360,361],{"id":13,"depth":353,"text":14},{"id":48,"depth":353,"text":49},{"id":89,"depth":353,"text":90},{"id":169,"depth":353,"text":170},{"id":205,"depth":353,"text":206},{"id":250,"depth":353,"text":251},{"id":319,"depth":353,"text":320},"2026-10-19","K3s本番でノードをdrainしたら一部PodがPendingのまま戻らなかった。原因はTopology Spread ConstraintsのmaxSkewとPodDisruptionBudgetの衝突。drainがノード数不足を自ら作り出す仕組みを公式ドキュメントベースで検証する。","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending\u002Ftitle.webp","ja",{},true,"\u002Fblog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending",{"title":5,"description":363},"blog\u002Fja\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending",[373,374,375,376,377,378],"k3s","kubernetes","topology-spread-constraints","poddisruptionbudget","scheduling","node-drain","E09aFMiWeqD0BgEYzW9i4Y29Vs390hx-XrCBB4S1NJA",[381,390,399,409,421,431],{"path":382,"title":383,"description":384,"date":385,"tags":386},"\u002Fblog\u002Fja\u002Fk3s-vpa-poddisruptionbudget-mass-eviction","K3sで自動リソース最適化を入れたら、全レプリカが同時に退避した。PodDisruptionBudgetという防波堤","K3sでリソース最適化ツールを有効化した直後、全レプリカが同時に退避され503が発生した実例をもとに、Podが一斉に退避されるメカニズムと、PodDisruptionBudgetのminAvailable設定による具体的な防御方法をエンジニア向けに詳しく解説する。","2026-09-18",[373,374,376,387,388,389],"eviction","vertical-scaling","resource-optimization",{"path":391,"title":392,"description":393,"date":394,"tags":395},"\u002Fblog\u002Fja\u002Fbroadcast-redundancy-kubernetes-high-availability-design","FIFAワールドカップの中継は、なぜ絶対に途切れないのか。放送業界の二重伝送に学ぶKubernetesの本当の高可用性設計","Kubernetesの高可用性は「レプリカを増やす」だけでは実現しない。放送業界のSMPTE ST 2022-7二重伝送技術を手がかりに、Topology Spread ConstraintsとマルチAZ設計の本質を解説する。","2026-07-20",[374,373,396,375,397,398],"high-availability","sre","managed-kubernetes",{"path":400,"title":401,"description":402,"date":403,"tags":404},"\u002Fblog\u002Fja\u002Fk3s-hpa-containerresource-sidecar-utilization-dilution","HPAがサイドカーの分まで合算してスケールしていた。KubernetesのContainerResourceという盲点","KubernetesのHPAはPod内の全コンテナの使用量を合算して利用率を計算するため、Istioサイドカーやログエージェントの負荷が判定を薄める。ContainerResourceメトリクスで解決する方法をK3s\u002FKubo文脈で解説する。","2026-10-21",[373,374,405,406,407,408],"hpa","containerresource","autoscaling","service-mesh",{"path":410,"title":411,"description":412,"date":413,"tags":414},"\u002Fblog\u002Fja\u002Fk3s-vs-kubernetes-cni-flannel-default-comparison","K3s vs 標準Kubernetes、CNIの出発点はここまで違う。Flannel標準搭載とCNI選択制が生む差","K3sはFlannelをデフォルト同梱し、標準KubernetesはCNI未選定の状態から始まる。この出発点の違いがNetworkPolicyの可否や通信性能にどう影響するかを、K3s\u002FKubernetes\u002FCalico\u002FCiliumの公式ドキュメントをもとに比較・解説する。","2026-10-20",[373,374,415,416,417,418,419,420],"cni","flannel","calico","cilium","networkpolicy","networking",{"path":422,"title":423,"description":424,"date":425,"tags":426},"\u002Fblog\u002Fja\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass","CELに書き換えたポリシーは、本当に同じ意味なのか。KyvernoからValidatingAdmissionPolicyへの移行で\"黙って素通り\"したリソースの正体","KyvernoのClusterPolicyが廃止へ向かう中、ValidatingAdmissionPolicy（CEL）に移行したら一部リソースだけ検証が素通りしていた。原因のCELランタイムエラーとfailurePolicyの罠、安全な移行手順を解説する。","2026-10-18",[373,374,427,428,429,430],"validatingadmissionpolicy","kyverno","cel","admission-control",{"path":432,"title":433,"description":434,"date":435,"tags":436},"\u002Fblog\u002Fja\u002Fk3s-vpa-inplaceorrecreate-pod-resize-cgroup","VPAをInPlaceOrRecreateにしたのに、Podはまだ再起動していた。K3sでInPlacePodVerticalScalingの\"無停止\"を検証する","VPAのInPlaceOrRecreateモードとIn-Place Pod ResizeでPodは本当に再起動しないのか。K3s環境でQoSクラス制約とcgroup v1の罠を実際の設定例とkubectlコマンドで検証する。","2026-10-17",[373,374,437,407,438],"vpa","cgroup",1792592611530]