[{"data":1,"prerenderedAt":498},["ShallowReactive",2],{"blog-ja-k3s-system-upgrade-controller-plan-stuck-node-drain":3,"blog-related-ja-k3s-system-upgrade-controller-plan-stuck-node-drain":446,"blog-ja-k3s-system-upgrade-controller-plan-stuck-node-drain-alt":436},{"id":4,"title":5,"author":6,"body":7,"date":430,"description":431,"extension":432,"image":433,"locale":434,"meta":435,"navigation":436,"path":437,"seo":438,"stem":439,"tags":440,"__hash__":445},"blog\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain.md","Planは消えずに残っていた。K3sのsystem-upgrade-controllerが止まり続けた理由","Kubo Team",{"type":8,"value":9,"toc":411},"minimark",[10,23,29,34,41,62,76,81,111,119,123,129,138,142,166,197,201,213,232,236,258,267,270,276,279,283,316,320,351,363,372,375,384,387,408],[11,12,13,14,18,19,22],"p",{},"K3sの",[15,16,17],"code",{},"system-upgrade-controller","で自動アップグレードを組んだはずなのに、気づけば",[15,20,21],{},"Plan","のステータスが何日も「進行中」のまま動かなくなっていた——。複数ノードで構成するK3sクラスタを自動運用していると、こうした「止まっているのに誰も気づかない」トラブルに出会うことがあります。",[11,24,25,26,28],{},"今回取り上げるのは、K3s特有の自動アップグレード機構である",[15,27,17],{},"が、特定のノードだけで詰まり続けてフリート全体のアップグレードがブロックされる問題です。原因を整理すると、PodDisruptionBudget（PDB）とtaint、そしてdrainタイムアウトという3つの要因が絡み合っていました。",[30,31,33],"h2",{"id":32},"system-upgrade-controllerはただのcronjobではない","system-upgrade-controllerは「ただのCronJob」ではない",[11,35,36],{},[37,38],"img",{"alt":39,"src":40},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain\u002Fsection01.webp",[11,42,43,44,46,47,49,50,53,54,61],{},"まず前提として、",[15,45,17],{},"がどういう仕組みかを整理します。これはRancher\u002FSUSEが提供するOSSツールで、",[15,48,21],{},"というカスタムリソースでアップグレードポリシーを宣言的に定義し、コントローラがノードを監視してアップグレード用の",[15,51,52],{},"Job","をスケジュールする仕組みです（",[55,56,60],"a",{"href":57,"rel":58},"https:\u002F\u002Fdocs.k3s.io\u002Fupgrades\u002Fautomated",[59],"nofollow","K3s公式ドキュメント","）。",[11,63,64,65,67,68,70,71,61],{},"標準のKubernetesをkubeadmで構築した場合、この仕組みは存在しません。kubeadmにはクラスタ自体を自動でローリングアップグレードする標準機構がなく、アップグレードは基本的に手動またはCI\u002FCDパイプラインに自前で組み込む必要があります。一方K3sは、",[15,66,17],{},"をインストールして",[15,69,21],{},"を適用するだけで、コントロールプレーンノード→エージェントノードの順にローリングアップグレードを自動実行できます（",[55,72,75],{"href":73,"rel":74},"https:\u002F\u002Fdocumentation.suse.com\u002Fcloudnative\u002Fk3s\u002Flatest\u002Fen\u002Fupgrades\u002Fautomated.html",[59],"SUSE公式ドキュメント",[77,78,80],"h3",{"id":79},"planの基本的な動き","Planの基本的な動き",[82,83,84,92,98,108],"ul",{},[85,86,87,91],"li",{},[88,89,90],"strong",{},"ノードセレクタ","でアップグレード対象のノード群を指定する",[85,93,94,97],{},[88,95,96],{},"concurrency","フィールドで同時にアップグレードするノード数を制御する",[85,99,100,103,104,107],{},[88,101,102],{},"channel","（リリースチャンネル）または",[88,105,106],{},"version","（特定バージョン）でアップグレード先を指定する",[85,109,110],{},"サーバノード用Planとエージェントノード用Planを分け、サーバが先に完了してからエージェントが追従する設計が推奨されている",[11,112,113,114,61],{},"便利な反面、この「Plan→Job→ノード選択」という3層構造を理解せずに運用していると、どこで詰まっているのかを特定するのに時間がかかります。実際、GitHub上の公式リポジトリにも、Planの内部でノードごとにJobがどう生成されるかの詳細な仕様がまとめられています（",[55,115,118],{"href":116,"rel":117},"https:\u002F\u002Fgithub.com\u002Francher\u002Fsystem-upgrade-controller",[59],"rancher\u002Fsystem-upgrade-controller",[30,120,122],{"id":121},"planが詰まる3つの原因","Planが詰まる3つの原因",[11,124,125],{},[37,126],{"alt":127,"src":128},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain\u002Fsection02.webp",[11,130,131,133,134,137],{},[15,132,17],{},"のPlanがノードごとに進まなくなる原因は、突き詰めると",[88,135,136],{},"アップグレード対象ノードの「安全な退避（drain）」がどこかで止まっている","ことに尽きます。代表的な3つの要因を整理します。",[77,139,141],{"id":140},"_1-poddisruptionbudgetが退避を許可しない","1. PodDisruptionBudgetが退避を許可しない",[11,143,144,145,148,149,152,153,156,157,160,161,61],{},"Kubernetesの",[15,146,147],{},"PodDisruptionBudget","（PDB）は、意図的な中断（voluntary disruption）の際にアプリケーションの可用性を守るための仕組みです。",[15,150,151],{},"minAvailable","または",[15,154,155],{},"maxUnavailable","を指定すると、クラスタは「現在許可されている中断数（",[15,158,159],{},"disruptionsAllowed","）」を計算します（",[55,162,165],{"href":163,"rel":164},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Ftasks\u002Frun-application\u002Fconfigure-pdb\u002F",[59],"Kubernetes公式ドキュメント",[11,167,168,169,172,173,176,177,182,183,186,187,192,193,196],{},"この値が",[15,170,171],{},"0","になっていると、ノードのdrain処理はPodのeviction要求に対して",[15,174,175],{},"429 Too Many Requests","を返され続け、いつまで経ってもPodが退避できません。これはK3s固有の問題ではなくKubernetesのEviction API全体の仕様で、PDBチェックはAPIサーバーのアドミッション制御段階で行われます（",[55,178,181],{"href":179,"rel":180},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fscheduling-eviction\u002Fapi-eviction\u002F",[59],"Kubernetes公式ドキュメント: API-initiated Eviction","）。Microsoft LearnのAKSトラブルシューティング記事でも、全く同じ「",[15,184,185],{},"ALLOWED DISRUPTIONS","が0のまま詰まる」パターンが報告されています（",[55,188,191],{"href":189,"rel":190},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Ftroubleshoot\u002Fazure\u002Fazure-kubernetes\u002Fcreate-upgrade-delete\u002Ferror-code-poddrainfailure",[59],"Microsoft Learn","）。つまりこれは",[88,194,195],{},"マネージドサービスかセルフホストかを問わない、Kubernetes運用に共通する落とし穴","です。",[77,198,200],{"id":199},"_2-taintのあるノードはデフォルトで対象外になる","2. taintのあるノードはデフォルトで対象外になる",[11,202,203,205,206,209,210,61],{},[15,204,17],{},"が生成するアップグレードJobのPodは、デフォルトでは",[15,207,208],{},"node.kubernetes.io\u002Funschedulable:NoSchedule","という最小限のtolerationしか持ちません。つまり、それ以外のカスタムtaint（例えば専用ワークロード用に付けたtaint）がノードに設定されていると、明示的にtolerationを追加しない限り、そのノードにはアップグレードJob自体がスケジュールされず、Planが「待機中」のまま止まって見えることになります（",[55,211,118],{"href":116,"rel":212},[59],[11,214,215,216,219,220,223,224,61,229,231],{},"taintとtolerationの基本的な仕組み自体は標準Kubernetesの機能で、",[15,217,218],{},"NoSchedule","は新規Podのスケジューリングのみを拒否し、",[15,221,222],{},"NoExecute","は既存Podまで退避させるという違いがあります（",[55,225,228],{"href":226,"rel":227},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fscheduling-eviction\u002Ftaint-and-toleration\u002F",[59],"Kubernetes公式ドキュメント: Taints and Tolerations",[15,230,17],{},"の文脈では、この挙動を知らないまま「なぜかこのノードだけアップグレードされない」と見過ごされがちです。",[77,233,235],{"id":234},"_3-drainタイムアウトでplanが待ち続ける","3. drainタイムアウトでPlanが待ち続ける",[11,237,238,239,242,243,246,247,250,251,254,255,61],{},"PDBやtaintの問題がなくても、特定のPodが何らかの理由で退避に時間がかかっている場合、drain処理はタイムアウトまで待ち続けます。Planの",[15,240,241],{},"drain","仕様には",[15,244,245],{},"force","や",[15,248,249],{},"ignoreDaemonSets","、",[15,252,253],{},"skipWaitForDeleteTimeout","といったオプションがあり、これらを事前に設定していないと、本番環境で想定より長く待機してしまうケースがあります（",[55,256,118],{"href":116,"rel":257},[59],[11,259,260,261,266],{},"なお、本記事で扱う3要因のうち、PodDisruptionBudget単体の挙動については、",[55,262,265],{"href":263,"rel":264},"https:\u002F\u002Fkubo.hexabase.io\u002Fblog\u002Fja\u002Fk3s-vpa-poddisruptionbudget-mass-eviction",[59],"K3sでPodDisruptionBudgetが引き起こす大量退避の問題","でも別の角度から取り上げている。今回はあくまで「system-upgrade-controllerのPlanを止める一要因」として扱う。",[30,268,269],{"id":269},"復旧と予防策",[11,271,272],{},[37,273],{"alt":274,"src":275},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain\u002Fsection03.webp",[11,277,278],{},"Planが実際に詰まってしまった場合の緊急対応と、再発防止のための事前設定を分けて整理します。",[77,280,282],{"id":281},"緊急対応詰まった場合","緊急対応（詰まった場合）",[284,285,286,296,309],"ol",{},[85,287,288,291,292,295],{},[15,289,290],{},"kubectl get plan -n system-upgrade","と",[15,293,294],{},"kubectl get jobs -n system-upgrade","で、どのノードのJobが止まっているかを確認する",[85,297,298,299,302,303,305,306,308],{},"原因がPDBであれば、",[15,300,301],{},"kubectl get pdb -A","で",[15,304,185],{},"列を確認し、",[15,307,171],{},"になっているPDBを一時的に調整する",[85,310,311,312,315],{},"それでも進まない場合は、対象のPlanを一度削除し、cordon状態のままになっているノードを",[15,313,314],{},"kubectl uncordon","で手動復旧してから、設定を直したPlanを再適用する",[77,317,319],{"id":318},"予防策事前にやっておくこと","予防策（事前にやっておくこと）",[82,321,322,333,336,345],{},[85,323,324,325,327,328,246,330,332],{},"Planの",[15,326,241],{},"設定に",[15,329,245],{},[15,331,249],{},"を明示的に指定し、デフォルト挙動に任せない",[85,334,335],{},"アップグレード対象ノードに付与しているカスタムtaintを棚卸しし、必要であればPlanのJob側にtolerationを追加する",[85,337,338,339,341,342,344],{},"PDBの",[15,340,151],{},"\u002F",[15,343,155],{},"がレプリカ数に対して厳しすぎないか、アップグレード前にレビューする",[85,346,347,348,350],{},"Planの進行状況を監視できるよう、",[15,349,21],{},"リソースのステータスをモニタリングに組み込む",[11,352,13,353,355,356,358,359,362],{},[15,354,17],{},"そのものは、インストールして",[15,357,21],{},"を書くだけで動き出すお手軽さが魅力です（",[55,360,60],{"href":57,"rel":361},[59],"）。しかしこの「お手軽さ」の裏には、PDBやtaint、drainタイムアウトという3つの落とし穴が隠れていることを理解しておく必要があります。",[11,364,365,366,371],{},"こうした自動化の仕組みを運用し続ける負担自体を減らしたい場合、マネージドK3sサービスを使えば、アップグレード運用の設計・監視を含めて任せることができます。特にオフラインや断続的な接続しかないエッジ環境では、Planが詰まっていることに気づくまでのタイムラグが大きなリスクになります。",[55,367,370],{"href":368,"rel":369},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fkubo\u002Fon-premise",[59],"Kubo On-Premise","はAir-Gapped環境にも対応しているため、閉域ネットワークのエッジノード群でもこうした自動化の設計・監視を外部委託しやすくなります。",[30,373,374],{"id":374},"クラスタ構成そのものを見直すタイミングかもしれない",[11,376,377,378,383],{},"アップグレード運用でPDBやtaintの設定ミスが頻発するようであれば、それはクラスタ全体の設計を見直すサインでもあります。",[55,379,382],{"href":380,"rel":381},"https:\u002F\u002Fkubo.hexabase.io\u002F",[59],"Kubo Cloud","のAI-Driven DeploymentとCaptain UIを使えば、Planの進行状況やノードの状態を可視化しながら、ブラックボックスにならない形でK3sクラスタを運用できます。標準Kubernetes互換であるためベンダーロックインもなく、既存のAWS\u002FAzureの知識や資産をそのまま活かせる点も、アップグレード自動化の設計を見直す際のハードルを下げてくれます。",[30,385,386],{"id":386},"まとめ",[82,388,389,394,397,400],{},[85,390,13,391,393],{},[15,392,17],{},"は、kubeadmベースの標準Kubernetesには存在しない、K3s固有のローリングアップグレード機構である",[85,395,396],{},"Planが特定ノードで止まる原因は、主に①PodDisruptionBudgetによる退避拒否、②taintによるJobスケジュール対象外、③drainタイムアウトの3つに集約される",[85,398,399],{},"緊急時はPlanの削除とノードのuncordon、恒常対策としてはdrain設定の明示化とPDB\u002Ftaintの事前レビューが有効",[85,401,402,403,407],{},"エッジ環境など「止まっていることに気づきにくい」構成では、",[55,404,406],{"href":380,"rel":405},[59],"Kubo","のようなマネージドサービスでアップグレード運用自体を委譲する選択肢も検討する価値がある",[11,409,410],{},"自動化は仕組みを理解してこそ安心して任せられます。次にPlanを適用する前に、今回紹介した3つのチェックポイントを確認してみてください。",{"title":412,"searchDepth":413,"depth":413,"links":414},"",2,[415,419,424,428,429],{"id":32,"depth":413,"text":33,"children":416},[417],{"id":79,"depth":418,"text":80},3,{"id":121,"depth":413,"text":122,"children":420},[421,422,423],{"id":140,"depth":418,"text":141},{"id":199,"depth":418,"text":200},{"id":234,"depth":418,"text":235},{"id":269,"depth":413,"text":269,"children":425},[426,427],{"id":281,"depth":418,"text":282},{"id":318,"depth":418,"text":319},{"id":374,"depth":413,"text":374},{"id":386,"depth":413,"text":386},"2026-10-31","K3sのsystem-upgrade-controllerで自動アップグレードを組んだら、特定ノードだけPlanが進まなくなった。原因はPodDisruptionBudgetとtaintの盲点。仕組みと復旧手順、Kuboでの解決策を解説する。","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain\u002Ftitle.webp","ja",{},true,"\u002Fblog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain",{"title":5,"description":431},"blog\u002Fja\u002Fk3s-system-upgrade-controller-plan-stuck-node-drain",[441,442,17,443,444],"k3s","kubernetes","rolling-upgrade","edge-computing","7ZwN4UsiD1A7fpnI-OfSAJtflQmuW71cu-9p2OCYUoM",[447,455,464,472,481,489],{"path":448,"title":449,"description":450,"date":451,"tags":452},"\u002Fblog\u002Fja\u002Fk3s-prometheus-agent-mode-stale-marker-edge-monitoring","K3sエッジのPrometheusをAgent modeにしたら、ターゲットが消えた瞬間にメトリクスも消えていた","K3sエッジクラスタでPrometheus Agent modeを使うと、ターゲット消失時のstale marker処理次第で中央監視に古い値が残り続けることがある。K3s edge環境の監視設計とつまずきポイントを解説する。","2026-11-03",[441,442,453,444,454],"prometheus","observability",{"path":456,"title":457,"description":458,"date":459,"tags":460},"\u002Fblog\u002Fja\u002Fk3s-spegel-embedded-registry-mirror-airgap-node-join","K3sの組み込みレジストリミラーが効いているはずなのに、新しいエッジノードだけimage pullに失敗した","K3sの組み込みレジストリミラー（Spegel）はクラスタ全体で有効なはずが、新規追加したエッジノードだけimage pullに失敗した。原因はregistries.yamlの未配布と認証ドリフト、containerd 2.0の既知バグ。エアギャップ運用の正しい配布手順を解説する。","2026-10-30",[441,442,461,462,463,444],"spegel","registry-mirror","airgap",{"path":465,"title":466,"description":467,"date":468,"tags":469},"\u002Fblog\u002Fja\u002Fk3s-microshift-etcd-ostree-edge-comparison","エッジでetcdを固定されるのは罠か前提か。K3sとMicroShift、OSTree自動ロールバックの代償を比較する","K3sとMicroShiftはどちらも「軽量Kubernetes」と語られるが、データストアとOS更新モデルの前提が根本的に違う。etcd固定とOSTree自動ロールバックの実態を比較し、エッジ本番運用でどちらを選ぶべきかを整理する。","2026-10-29",[441,470,442,444,471],"microshift","redhat",{"path":473,"title":474,"description":475,"date":476,"tags":477},"\u002Fblog\u002Fja\u002Fk3s-vs-microk8s-ha-etcd-dqlite-edge-production","K3sとMicroK8s、同じ\"軽量\"でも本番で勝つのはどちらか。dqliteとetcdが分けるHA構成の分岐点","K3sとMicroK8s、同じ軽量Kubernetesでも本番運用の明暗を分けるのはHA構成だ。K3sのembedded etcdとMicroK8sのdqlite、それぞれの最小RAM要件・アーキテクチャの違いを公式ドキュメントのデータで比較し、選定基準を解説する。","2026-10-25",[441,478,442,479,444,480],"microk8s","high-availability","comparison",{"path":482,"title":483,"description":484,"date":485,"tags":486},"\u002Fblog\u002Fja\u002Fedge-k3s-hardware-selection-beyond-appliance","家庭用ルーターを自作PCに置き換える動きが加速。エッジKubernetesも「専用アプライアンス」依存を見直す時期だ","ISP支給ルーターを自作PC+OSSに置き換える動きが個人の間で広がっている。工場や店舗のエッジKubernetesも同じ理由で専用アプライアンス依存を見直すべき理由と、ハードウェア選定の具体的なチェックリスト、GitOpsによる拠点運用の標準化までを解説する。","2026-08-29",[441,442,444,487,488],"hardware-selection","network-appliance",{"path":490,"title":491,"description":492,"date":493,"tags":494},"\u002Fblog\u002Fja\u002Fk3s-baremetal-loadbalancer-bgp-metallb-cilium","ベアメタルK3sのLoadBalancer、ARPだけでは限界が来る。BGPで外部ルーティングを設計する","オンプレ・エッジのK3sクラスタでクラウドLBに頼れないとき、Layer2(ARP)方式のロードバランシングはなぜ限界に達するのか。MetalLB BGPモードとCilium BGP Control Planeの違い、移行の判断基準を解説する。","2026-08-26",[441,442,495,496,497,444],"metallb","bgp","load-balancer",1793798696588]