PodDisruptionBudget の仕組み
v1.37 時点(2026年10月26日 確認)
目次
この記事ではノードの保守のような計画した作業で同時に止まる Pod の数を抑える PodDisruptionBudget(以下、PDB)の仕組みを、図で説明します。PDB が抑えられる中断と抑えられない中断の違いと、退避(eviction)を頼まれたときに PDB に照らして許すか断るかを誰が決めるのかがわかります。ノードから Pod を退かす kubectl drain と、ノードに新しい Pod を置かせない kubectl cordon の動きもわかります。後半では kind のマルチノードのクラスタで、drain が PDB に断られて待ちながら Pod を 1 つずつ退避させる様子を観察する手順を示します。
Deployment が ReplicaSet を通して Pod の数を保つ仕組みを扱った第12回の内容を前提にしています。PDB で守る対象は Deployment のように Pod の数を保つワークロードです。まだの方は先にこちらをどうぞ。
Deployment とローリングアップデートの仕組みReplicaSet を持ち、Pod を宣言的に更新する Deployment の仕組みを図で説明します。ひな形を変えたときに新しい ReplicaSet へ Pod を少しずつ移すローリングアップデート、pod-template-hash の役割、リビジョンと切り戻しを押さえ、kind のクラスタでイメージを更新して rollout undo で戻す手順を紹介します。以下の図は、kubectl drain が Pod の退避を頼んだときに、どのコンポーネントが何をするかを表しています。後半の手順で作る Deployment web の Pod と、PDB web-pdb を例にしています。
drain は Pod を直接消すのではなく、Eviction API で退避を頼みます。頼まれた kube-apiserver は、その Pod を選ぶ PDB に照らして、許すなら Pod に削除の印を付け(200 OK)、許さないなら断ります(429 Too Many Requests)。PDB の status には今いくつまで止めてよいかが書かれていて、それを主に書くのは kube-controller-manager の中の disruption-controller です(例外は「status と disruption-controller」で説明します)。Pod を実際に止めるのは、これまでの回と同じくノードの kubelet です。図では、kube-controller-manager が kube-apiserver を通して Pod を見る線と、kube-apiserver が status に書き込むと読める線を省き、Pod へ向かう線は Pod 1 つずつではなくノードの枠に向けて引いています。
参考:API-initiated Eviction | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes、PodDisruptionBudget | Kubernetes API Reference、kube-controller-manager | Kubernetes
PodDisruptionBudget とは
PodDisruptionBudget は、複数の Pod で動くアプリケーションについて、自発的な中断(voluntary disruption)で同時に止まる Pod の数を抑えるリソース です。中断(disruption)は、ここでは Pod が止められたり消えたりすることを指します。自発的な中断は、ノードの drain のように、人やツールがわざと起こす中断です(次の節で詳しく見ます)。PDB は v1.21 から安定版(Stable)で、API のグループとバージョンは policy/v1 です。
参考:Disruptions | Kubernetes、PodDisruptionBudget | Kubernetes API Reference
公式ドキュメントは PDB を、アプリケーションの持ち主がアプリケーションごとに作れるものとして説明しています。たとえば、多数決(quorum)で動くアプリケーションなら、動いている Pod の数が多数決に要る数を下回らないようにしたい、Web のフロントエンドなら、要求を受ける Pod の数が全体の一定の割合を下回らないようにしたい、という使い方です。一方、クラスタの管理者やホスティングの事業者は、Pod や Deployment を直接消すのではなく、Eviction API を呼んで PDB を尊重するツールを使うべき(should)だとされています。kubectl drain はそうしたツールの 1 つです。
drain は保守の前にノードから Pod を退かす作業です。公式ドキュメントではカーネルの更新やハードウェアの保守の前に、ノードのすべての Pod を安全に退避させるのに kubectl drain を使える、と説明しています。
なぜ PodDisruptionBudget が必要なのか
第12回で見たとおり、Deployment は spec.replicas に書いた数の Pod を保ちます。Pod が消えれば ReplicaSet が代わりを作りますが、代わりの Pod が起動して Ready(第34回)になるまでには時間がかかります。drain はノードのすべての Pod を退避させようとするので、同じ Deployment の Pod が 1 つのノードに集まっていると、それらが一度に止まり、代わりが動き出すまで要求を受けられる Pod が足りなくなることが考えられます。第33回の Topology Spread で Pod を散らしておいても、複数のノードを続けて drain すれば、同じことが起こり得ると考えられます。
PDB を作っておくと、Eviction API はその PDB が許す数までしか同時に退避を許しません。公式ドキュメントの例では、.spec.replicas: 5 の Deployment に、使える Pod が 4 つ以上残ることを求める PDB(4 つあればよいとする PDB)があると、5 − 4 で、Eviction API は 1 つずつ(2 つ同時ではなく)自発的な中断を許します。drain は断られた退避を頼み直しながら進みます(「kubectl cordon と kubectl drain」で説明します)。
ただし、どのクラスタでも PDB が要るわけではありません。公式ドキュメントによると、基本的な Kubernetes のクラスタには、自動で起きる自発的な中断はありません(利用者が起こすものだけです)。管理者やホスティングの事業者が、ノードのソフトウェアの更新やノードの自動スケーリングのように、自発的な中断を起こすサービスを動かしていることがあります(may)。自分のクラスタでそうした中断が起きるかを管理者などに確かめ、何も有効でなければ PDB を作らなくてもよい、と書かれています。
PodDisruptionBudget の仕組み
自発的な中断と非自発的な中断
公式ドキュメントによると、Pod は、誰か(人かコントローラー)が消すか、避けられないハードウェアやシステムのソフトウェアのエラーが起きない限り、消えません。避けられない場合を非自発的な中断(involuntary disruption)、それ以外を自発的な中断と呼びます。公式ドキュメントが挙げる例を次の表にまとめます。
| 種類 | 公式ドキュメントの例 |
|---|---|
| 非自発的な中断 | ノードの物理マシンの故障、管理者による VM の誤った削除、クラウドプロバイダーやハイパーバイザーの障害、カーネルパニック、ネットワークの分断でノードがクラスタから消えること、ノードの資源不足による Pod の退避 |
| 自発的な中断(アプリケーションの持ち主) | Pod を管理する Deployment などの削除、Pod のひな形の更新による入れ替え、Pod の直接の削除(誤って消した場合など) |
| 自発的な中断(クラスタの管理者) | 修理や更新のためのノードの drain、クラスタを縮めるためのノードの drain、別の Pod を置くためにノードから Pod を取り除くこと |
非自発的な中断の例の多くは、ノードのマシンや、ノードを動かす仮想マシン(VM)とその基盤(ハイパーバイザー)、OS の中心部(カーネル)の故障や停止です。
非自発的な中断の「ノードの資源不足による退避」は、第29回で見た kubelet の node-pressure eviction です。第29回で見たとおり、node-pressure eviction では kubelet は PDB を守りません。「ネットワークの分断でノードが消える」は、第4回で見た、ノードと連絡が取れなくなる場面にあたると考えられます。公式ドキュメントによると、PDB は非自発的な中断を防げません。ただし、非自発的な中断で止まった Pod も、PDB の数に入ります(原文は count against the budget)。止まった Pod は健全な Pod に数えられないので、そのあいだは disruptionsAllowed が減り、同じときに drain すると退避が断られやすくなると考えられます。
参考:Disruptions | Kubernetes、Node-pressure Eviction | Kubernetes
自発的な中断でも、すべてが PDB に抑えられるわけではありません。公式ドキュメントは注意として、Deployment や Pod を削除すると PDB は迂回される(bypass) と書いています。PDB を尊重するのは、次の節の Eviction API を通した退避です。kubectl delete のような直接の削除は PDB を迂回します。
参考:Disruptions | Kubernetes、API-initiated Eviction | Kubernetes
ひな形の更新による入れ替え(ローリングアップデート)の扱いも決まっています。ローリングアップデートで消えたり使えなくなったりした Pod は PDB の数に入りますが(そのあいだ disruptionsAllowed が減ると考えられます)、Deployment や StatefulSet のローリングアップデートそのものは PDB で制限されません。更新中の失敗の扱いはそれぞれのワークロードの spec で決める、とされています。Deployment で、更新中に同時に使えなくなる Pod の数を決めるのは、第13回で見た maxUnavailable と maxSurge です。
Eviction API で退避を頼む
API で頼む退避(API-initiated eviction)は、Eviction API で Eviction オブジェクトを作り、Pod の穏やかな終了(graceful termination)を起こすこと です。Eviction API を直接呼ぶか、kubectl drain のような kube-apiserver のクライアントから頼みます。公式ドキュメントは Pod の Eviction を作ることを、ポリシーで制御された Pod の DELETE のようなもの、と説明しています。このときは PDB と、Pod の terminationGracePeriodSeconds(第8回で見た終了までの猶予の時間。既定では 30 秒)が守られます。
参考:API-initiated Eviction | Kubernetes、Pod Lifecycle | Kubernetes
以下は公式ドキュメントが示す、Pod quux の退避を頼む Eviction の例です。そのまま引用しています。policy/v1 の Eviction は v1.22 から使えます。
{
"apiVersion": "policy/v1",
"kind": "Eviction",
"metadata": {
"name": "quux",
"namespace": "default"
}
}公式ドキュメントによると、これを Pod の eviction というサブリソース(第35回で見た scale と同じく、オブジェクトに付いた別のパス)に POST すると、kube-apiserver はアドミッションの検査をして、次のどれかで応答します。
| 応答 | 意味 |
|---|---|
200 OK | 退避が許された。Eviction が作られ、Pod の URL に DELETE を送ったときと同じように Pod が削除される |
429 Too Many Requests | 設定された PDB のため今は退避が許されず、あとで頼み直せることがある。API の流量の制限(rate limiting)でこの応答になることもある |
500 Internal Server Error | 設定の誤りで退避が許されない。たとえば、複数の PDB が同じ Pod を選んでいる場合 |
退避したい Pod が PDB を持つワークロードに属していなければ、kube-apiserver は必ず 200 OK を返して退避を許します。
退避が許されたあとの流れは、第8回で見た、Pod を kubectl delete で消したときの流れとよく似ています。kube-apiserver が Pod に削除の時刻(deletion timestamp)と猶予の時間を記録し、Pod のノードの kubelet がそれに気付いて Pod を穏やかに止め始めます。そのあいだに、コントロールプレーンは Pod を EndpointSlice(第21回)から外します。猶予の時間が過ぎると kubelet が Pod を強制的に終了させ、最後に kube-apiserver が Pod を削除します。
PodDisruptionBudget のフィールド
PDB にはどの Pod を守るかを決める spec.selector と、spec.minAvailable か spec.maxUnavailable のどちらか一方を書きます。以下は公式ドキュメントの、app: zookeeper のラベルの Pod を守る PDB の例です。そのまま引用しています。ZooKeeper は、公式ドキュメントが etcd と並べて挙げる、複数の Pod の多数決(quorum)で動く分散アプリケーションで、例の zk はその略だと考えられます。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: zookeeper参考:Specifying a Disruption Budget for your Application | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes
フィールドの意味を、公式ドキュメントと API リファレンスに沿って次の表にまとめます。
| フィールド | 意味 |
|---|---|
selector | 退避を PDB で管理する Pod を選ぶラベルセレクタ(第11回)で、タスクのページは必須としている。空({})だと、Namespace のすべての Pod を選ぶ |
minAvailable | 退避させても、選んだ Pod のうち残りの使える Pod がこの数以上なら、退避を許す。数か割合で書く |
maxUnavailable | 退避したあと、選んだ Pod のうち使えない Pod がこの数までなら、退避を許す。数か割合で書く |
unhealthyPodEvictionPolicy | Ready でない Pod を、どんなときに退避の対象にするか(「Ready でない Pod の扱い」で説明) |
参考:Specifying a Disruption Budget for your Application | Kubernetes、PodDisruptionBudget | Kubernetes API Reference
minAvailable と maxUnavailable は 1 つの PDB にどちらか一方しか書けません。公式ドキュメントは組み込みのコントローラー(Deployment、ReplicationController、ReplicaSet、StatefulSet)の Pod を守るときは、コントローラーの .spec.selector と同じセレクタを PDB の .spec.selector に書くよう説明しています。PDB の例が 3 つの Pod の StatefulSet を選ぶなら、minAvailable: 2 と maxUnavailable: 1 はまったく同じ意味で、公式ドキュメントはコントローラーの数の変化に自動で合わせられる maxUnavailable を勧めています。なお、PDB の maxUnavailable は、第13回で見た Deployment の maxUnavailable(更新のときの数)とは名前が同じだけの別のフィールドです。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes、Deployments | Kubernetes
公式ドキュメントによると、割合("50%" のような文字列)で書くと、Pod の数は切り上げで求めます。たとえば Pod が 7 つで minAvailable が "50%" なら、使える状態で残すべき数は 4 です。maxUnavailable では止めてよい数を切り上げるので、止まる数が書いた割合を超えることがあります。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes
maxUnavailable を 0 か 0% にするか、minAvailable を 100% か Pod の数と同じにすると、自発的な退避を 1 つも許さないことになります。公式ドキュメントによると、そうした Pod が動いているノードは drain に成功できず、drain は終わりません。これは PDB の決まりのとおりの動きだとされています。公式ドキュメントは 1 つだけで動く状態のあるアプリケーションの守り方の候補として、maxUnavailable を 0 にしておき、止めてよいときに PDB を消して、あとで作り直すやり方を挙げています。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes
Deployment などの組み込みのワークロード以外が管理する Pod や、コントローラーの無い Pod にも PDB は使えます。ただし、その場合は minAvailable の整数しか使えず、maxUnavailable や割合は使えません。対応する持ち主のリソースが無いと、Kubernetes は Pod の全体の数を求められないからです。maxUnavailable は、選んだ Pod がすべて同じコントローラーに管理されているときだけ使えます。また、セレクタが重なって 1 つの Pod を複数の PDB が選ぶと、その Pod の退避は Eviction API が許さないので、公式ドキュメントは重なりを避けたほうがよいとしています。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes
status と disruption-controller
PDB の status には、選んだ Pod を数えた結果が入ります。公式ドキュメントの例では、zk-pdb の status は次のとおりです(ほかの部分は省略しています)。
status:
currentHealthy: 3
desiredHealthy: 2
disruptionsAllowed: 1
expectedPods: 3
observedGeneration: 1参考:Specifying a Disruption Budget for your Application | Kubernetes
各フィールドの意味を、API リファレンスに沿って次の表にまとめます。
| フィールド | 意味 |
|---|---|
currentHealthy | 今の健全な(healthy)Pod の数 |
desiredHealthy | 望ましい、健全な Pod の最小の数 |
disruptionsAllowed | 今許されている Pod の中断の数 |
expectedPods | この PDB が数えた Pod の全体の数 |
observedGeneration | status を更新したときに見た PDB の世代。PDB の metadata.generation と同じときだけ、disruptionsAllowed などが正しい |
conditions | DisruptionAllowed の状態。理由(reason)は SufficientPods、InsufficientPods、SyncFailed など |
v1.37 時点の公式ドキュメントでは、今の実装での健全な Pod は .status.conditions に type="Ready" で status="True" の項目を持つ Pod、つまり Ready の Pod です。望ましい数(公式ドキュメントの「intended」の数)は、Pod を管理するワークロードのリソースの .spec.replicas から求めます。コントロールプレーンは Pod の .metadata.ownerReferences(第6回の持ち主の記録)をたどって、持ち主のリソースを見つけます。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Disruptions | Kubernetes
status を主に書くのは、kube-controller-manager の中の disruption-controller です(既定で有効)。disruptionsAllowed が 0 でないことは、disruption-controller が Pod を数えて status を更新したことを表す、と説明されています。API リファレンスの PodDisruptionBudget のページには disruption controller と PodDisruptionBudget controller の 2 つの呼び名が出てきますが、どちらもこのコントローラーを指すと考えられます。
参考:Specifying a Disruption Budget for your Application | Kubernetes、kube-controller-manager | Kubernetes、PodDisruptionBudget | Kubernetes API Reference
退避を許すか断るかを決めるのは、kube-apiserver です。API リファレンスによると、status の disruptedPods には、kube-apiserver の eviction のサブリソースが退避の頼みを処理した Pod が入ります。入っているのは、kube-apiserver が処理した時点から、PDB のコントローラーが削除の印を確かめるまで(またはタイムアウトまで)です。kube-apiserver が処理した時点から入るとあるので、この記録を書くのは kube-apiserver だと読めます。コマ送り図の(1/4)で disruptionsAllowed がすぐ 0 になるのは、kube-apiserver が退避を処理したときにこの記録を残し、コントローラーが数え直す前に次の頼みが来ても、許す退避の数を多く見積もらないようにするためだと考えられます。
Ready でない Pod の扱い(unhealthyPodEvictionPolicy)
PDB は健全な Pod の退避を断ることで、currentHealthy が desiredHealthy を下回らないようにします。では、動いている(Running)が Ready でない Pod はどうでしょうか。それを決めるのが unhealthyPodEvictionPolicy で、v1.31 から安定版です(機能ゲートはすでに外され、切り替えられません)。2 つの値を、公式ドキュメントに沿って次の表にまとめます。
| 値 | Running で Ready でない Pod の扱い | 公式ドキュメントが挙げる影響 |
|---|---|---|
IfHealthyBudget(v1.37 時点で、書かないときの既定) | 守っているアプリケーションが中断していない(currentHealthy が desiredHealthy 以上の)ときだけ退避できる | すでに中断しているアプリケーションの Pod に、健全になる機会を最大限に与える。その代わり、CrashLoopBackOff の Pod などがあると drain が止まることがある |
AlwaysAllow | PDB の条件を満たすかどうかにかかわらず、中断しているとみなして退避できる | 健全になりかけた Pod が、その機会を得られないことがある。管理者は、うまく動かないアプリケーションを退避させやすくなる |
参考:Specifying a Disruption Budget for your Application | Kubernetes、PodDisruptionBudget | Kubernetes API Reference
どちらの値でも、健全な Pod の退避は PDB に従います。Pending、Succeeded、Failed のフェーズの Pod はいつでも退避の対象になります。公式ドキュメントの Disruptions のページと Safely Drain a Node のページは、drain のときにうまく動かないアプリケーションを退避させられるよう、PDB に AlwaysAllow を設定することを勧めています(recommended)。既定の動きでは、drain はアプリケーションの Pod が健全になるのを待ちます。
参考:Specifying a Disruption Budget for your Application | Kubernetes、Disruptions | Kubernetes、Safely Drain a Node | Kubernetes
kubectl cordon と kubectl drain
kubectl cordon は、ノードをスケジュールできない状態(unschedulable)にするコマンドです。ノードの spec.unschedulable は新しい Pod をそのノードに置けるかを決めるフィールドで、既定ではスケジュールできます。公式ドキュメントによると、スケジュールできない印を付けると、スケジューラーはそのノードに新しい Pod を置かなくなりますが、すでに動いている Pod には影響しません。ノードの再起動などの保守の準備に役立つ、と書かれています。元に戻すのは kubectl uncordon です。
参考:Nodes | Kubernetes、Node | Kubernetes API Reference、kubectl cordon | Kubernetes、kubectl uncordon | Kubernetes
Taints and Tolerations のページは、ノードがスケジュールできないときに node controller が自動で付ける組み込みの Taint として、node.kubernetes.io/unschedulable を挙げています(第32回)。Safely Drain a Node のページは、新しい Pod がこの Taint を許容していると、drain したノードに置かれることがある(might)と注意し、DaemonSet 以外でこの Taint を許容するのは避けるよう書いています(Avoid)。第15回で見たとおり、daemonset-controller は DaemonSet の Pod にこの Taint の Toleration を自動で足すので、DaemonSet の Pod はスケジュールできないノードでも動きます。
同じページによると、自分やほかの API の利用者が Pod の nodeName(第31回で見た、ノードの名前を直接指定するフィールド)を直接書くと、スケジューラーを迂回するので、その Pod は drain してスケジュールできない印を付けたノードにも割り当てられて動きます。
参考:Taints and Tolerations | Kubernetes、Taints and Tolerations | Kubernetes、Safely Drain a Node | Kubernetes
kubectl drain は、保守の準備としてノードを空けるコマンドです。v1.37 時点のリファレンスに沿って、drain がすることを次の表にまとめます。
| 対象 | drain の扱い |
|---|---|
| ノード | まずスケジュールできない印を付けて、新しい Pod が来ないようにする(cordon と同じ) |
| ふつうの Pod | kube-apiserver が対応していれば Eviction API で退避させ、対応していなければふつうの DELETE で消す |
| mirror Pod(第4回) | kube-apiserver から消せないので、対象にしない |
| DaemonSet の Pod | --ignore-daemonsets が無いと drain は進まない。付けても消さない |
| コントローラーの無い Pod | ReplicationController、ReplicaSet、DaemonSet、StatefulSet、Job のどれにも管理されていない Pod があると、--force が無ければどの Pod も消さない |
| emptyDir(第26回)を使う Pod | --delete-emptydir-data を付けると、それでも続ける(ノードの emptyDir のデータは消える) |
DaemonSet の Pod を消さないのは、消しても daemonset-controller がすぐに同じノードへ作り直すからです。Safely Drain a Node のページによると、DaemonSet の Pod の多くは、ネットワークやストレージ、ログの収集のように、ノードが動いているあいだはそのノードに残る必要がある、ノードごとのサービスです。--ignore-daemonsets はそれらを飛ばして drain を終わらせるための指定で、DaemonSet の Pod は保守のあいだも動き続け、ノードをクラスタから外したときに消えます。
参考:Safely Drain a Node | Kubernetes、kubectl drain | Kubernetes
drain は Pod の穏やかな終了を待ちます。リファレンスは、コマンドが終わるまでマシンを操作しないよう(should not)書いています。kubectl drain が正常に終われば、対象にしない Pod を除くすべての Pod が、猶予の時間と PDB を守って退避し終えています。そのあとは物理マシンの電源を切るか、クラウドなら仮想マシンを消してかまいません。保守のあいだノードをクラスタに残しておいた場合は、作業のあとに kubectl uncordon を実行して、新しい Pod を置けるように戻す必要があります。
参考:kubectl drain | Kubernetes、Safely Drain a Node | Kubernetes
PDB に断られた退避について、公式ドキュメントは次のように説明しています。kubectl が代わりに送る退避の頼みは一時的に断られることがあるので、drain は、対象のノードのすべての Pod が終わるか、設定できるタイムアウト(--timeout。0 は無限)に達するまで、失敗した頼みを定期的にすべて頼み直します。
drain と PDB が組み合わさる流れ
公式ドキュメントの Disruptions のページには、3 つのノードと、少なくとも 3 つのうち 2 つが使える状態であることを求める PDB を持つ Deployment の例があります。1 つ目のノードを drain すると、その上の Pod の退避はすぐに成功します。代わりの Pod が起動し終わる前に、待ちきれない管理者が 2 つ目のノードを drain しようとすると、使える Pod が 2 つしかないので drain は止まります(block)。代わりの Pod が使えるようになると、2 つ目のノードの drain を進められます。代わりの Pod を置く資源がクラスタに無ければ、代わりの Pod は Pending のままで、drain は再び止まります。その場合は、ノードをクラスタに戻さないと作業を進められません。
公式ドキュメントはこの例から、中断が起きる速さは、アプリケーションに要る Pod の数、穏やかな終了にかかる時間、新しい Pod の起動にかかる時間、コントローラーの種類、クラスタの資源の量で変わる、とまとめています。
以下の図は、後半の手順と同じく、3 つの Pod がすべて 1 つ目のワーカーにある Deployment web と、minAvailable: 2 の PDB web-pdb があるときに、1 つ目のワーカーを drain する流れをコマ送りで表しています。図の disruptionsAllowed の値は公式ドキュメントの説明から考えたもので、手元で確かめた値ではありません。Pod の名前は見分けるために 1〜6 の番号にしています(実際は web- に続けてハッシュと文字列が付きます)。kube-apiserver と ReplicaSet は省き、退避の頼みは利用者からノードの枠への線に、断りは PDB から利用者への線にまとめています。
(1/4)〜(3/4)では、同時に止まる web の Pod は 1 つまでに抑えられています。drain はノードの 3 つの Pod をどれも退避させようとしますが、PDB が 2 つ同時の退避を許さないからです。代わりの Pod の置き場所があるので、待つ時間は代わりの Pod が Ready になるまでの時間だと考えられます。
手元で試す
ここまでの内容を、手元の kind のクラスタで試す手順を示します。公式のタスク(Specifying a Disruption Budget for your Application と Safely Drain a Node)に沿って、Deployment web と PDB web-pdb を作り、ワーカーを drain します。まず、drain が PDB に断られながら web の Pod を 1 つずつ退避させる様子を見ます。次に、代わりの Pod の置き場所が無いと drain が待ち続ける様子を見ます。
各段で何が表示されるかは、どれも公式ドキュメントから予想した結果で、手元で実行して確かめたものではありません。以降の手順では、この予想を「はずです」と書きます。公式ドキュメントで確かめられなかった点(ノードの名前や、kubectl が出す行の文面など)は、その都度「考えられます」と断ります。引用する出力の例は公式ドキュメントに載っている、ほかのクラスタで取ったもので、名前や数などの値は手元とは違います。各段の最後に、成功かどうかを見分ける目安を書きます。
コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.37.0(kind v0.33.0 の既定のノードイメージ) |
| ノード構成 | マルチノード(クラスタ名 multi-node):コントロールプレーン 1、ワーカー 2 |
| 必要なもの | Docker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)3 つ |
参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes
kind でクラスタを作る手順は、以下の記事で解説しています。この回は第31〜33回と同じマルチノードのクラスタを使います。1 つ目のワーカーを drain したときに、代わりの Pod を置ける 2 つ目のワーカーが要るからです。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。この回では端末を 3 つ使います。第31〜33回と同じく 1 つ目を監視に、2 つ目を操作に使い、さらに 3 つ目を足して PDB を監視します。drain は終わるまで端末を占めるので、PDB の数の変化は別の端末で追います。役割を次の表にまとめます。
| 端末 | 役割 | 開くタイミングと注意 |
|---|---|---|
| 1 つ目 | default の Namespace の Pod の監視 | 「監視を始める」の節で開く。監視だけなので cd は要らない |
| 2 つ目 | 操作(マニフェストを渡す、cordon、drain など) | 最初に開き、マニフェストを保存するディレクトリに cd する。drain のあいだは占有される |
| 3 つ目 | PDB の監視 | 「監視を始める」の節で開く。監視だけなので cd は要らない |
マルチノードのクラスタを用意する
第2回・第31回と同じ次の設定ファイルを使います。公式ドキュメントの例をそのまま引用しています。kind-multi-node.yaml という名前で、コマンドを実行するディレクトリに保存します。前の回までに保存したファイルが残っていれば、それを使えます。
# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker2 つ目の端末を開き、cd でマニフェストを保存するディレクトリに移動してから、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
multi-node が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。前の回の kind が表示されても、この回では使いません。手元のマシンの資源(メモリなど)に余裕がなければ、kind delete cluster --name kind で消してから進めてください。
$ kind create cluster --name multi-node --config kind-multi-node.yaml
$ kind get clusters
kind get clusters に multi-node が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-multi-node を付けて、操作するクラスタをはっきりさせます。続けて、ノードを一覧します。
$ kubectl get nodes --context kind-multi-node
ノードが 3 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。
次に、第31回と同じく、node-role.kubernetes.io/control-plane のラベルを持たないノード、つまりワーカーのノードだけを一覧します。
$ kubectl get nodes -l '!node-role.kubernetes.io/control-plane' --context kind-multi-node
参考:Labels and Selectors | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes
ノードが 2 つ表示されるはずです。名前は multi-node-worker や multi-node-worker2 のような形になると考えられます(公式ドキュメントでは確かめられなかった推測です)。ここからは表示された順に「1 つ目のワーカー」「2 つ目のワーカー」と呼びます。2 つの名前をメモしておいてください。このあとのコマンドの <1 つ目のワーカーの名前> と <2 つ目のワーカーの名前> は、< と > も含めて、メモした名前に置き換えます。
各ノードがスケジュールできるかと、Taint のキーを表示します。第32回で使った kubectl のクイックリファレンスのコマンドを、spec.unschedulable の列を足して短くした形です。[*] をシェルに解釈させないよう、指定全体を一重引用符で囲みます。
$ kubectl get nodes -o 'custom-columns=NAME:.metadata.name,UNSCHEDULABLE:.spec.unschedulable,TAINTS:.spec.taints[*].key' --context kind-multi-node
参考:kubectl Quick Reference | Kubernetes、Node | Kubernetes API Reference
UNSCHEDULABLE 列は、どのノードも値が無いことを表す表示になるはずです。spec.unschedulable は書かれていなければスケジュールできる、という意味だからです。TAINTS 列は、コントロールプレーンのノードが node-role.kubernetes.io/control-plane で、ワーカーの 2 つは値が無いことを表す表示になるはずです(第32回)。どちらかのワーカーの UNSCHEDULABLE 列が true なら、前にこの回を試した名残なので、kubectl uncordon <そのワーカーの名前> --context kind-multi-node で戻してから進めます。
前の回のクラスタを使い続けている場合は、default の Namespace に Pod と PDB が残っていないことも確かめます。
$ kubectl get pods,pdb --context kind-multi-node
pdb は poddisruptionbudgets の短い名前(shortname)です。見つからないことを知らせる 1 行が出れば、そのまま進めます。残っていたら、自分で作ったものかを確かめてから消します。
監視を始める
ここで 1 つ目と 3 つ目の端末を開きます。1 つ目の端末で、Pod が置かれたノードの名前(NODE 列)も表示しながら Pod を監視します。この監視は片付けの前まで止めずに続けます。
$ kubectl get pods --watch -o wide --context kind-multi-node
3 つ目の端末で、PDB を監視します。PDB はまだ無いので、名前を指定せずに種類全体を監視します。この監視も片付けの前まで続けます。
$ kubectl get pdb --watch --context kind-multi-node
どちらの端末も、見つからないことを知らせる 1 行が出たあと、止まったように見えると考えられます。
Pod を 1 つ目のワーカーに集める
drain で Pod が 1 つずつ退避する様子をはっきり見るため、web の 3 つの Pod を、最初は 1 つ目のワーカーに集めておきます。Pod をどのノードに置くかは kube-scheduler が決めるので(第31回)、そのままでは 2 つのワーカーに散らばるかもしれません。そこで 2 つ目のワーカーを cordon してから Deployment を作り、作り終えたら uncordon します。
2 つ目の端末で、2 つ目のワーカーを cordon します。
$ kubectl cordon <2 つ目のワーカーの名前> --context kind-multi-node
ノードに印を付けたことを知らせる 1 行が出るはずです。公式のチュートリアルの drain の例では、ノードに印を付けたときの行は node "kubernetes-node-pb41" cordoned の形でした。もう一度、さきほどの custom-columns のコマンドを実行します。
$ kubectl get nodes -o 'custom-columns=NAME:.metadata.name,UNSCHEDULABLE:.spec.unschedulable,TAINTS:.spec.taints[*].key' --context kind-multi-node
参考:Running ZooKeeper, A Distributed System Coordinator | Kubernetes、Node | Kubernetes API Reference
2 つ目のワーカーの行の UNSCHEDULABLE 列が true になるはずです。「kubectl cordon と kubectl drain」で見たとおり、スケジュールできないノードには node controller が node.kubernetes.io/unschedulable の Taint を付けるので、TAINTS 列にもこのキーが現れるはずです。
以下は nginx の Pod を 3 つ動かす Deployment のマニフェストです。第33回と同じく、イメージは nginx で、ラベルは app: web にしています。web.yaml という名前で保存します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent2 つ目の端末で、マニフェストを渡します。
$ kubectl apply -f web.yaml --context kind-multi-node
deployment.apps/web が作られたことを知らせる 1 行が出るはずです。1 つ目の端末には web の Pod の行が 3 つ書き足されていき、STATUS 列は Pending、ContainerCreating を経て Running になるはずです。NODE 列は 3 つとも 1 つ目のワーカーの名前になるはずです。2 つ目のワーカーはスケジュールできず、コントロールプレーンのノードは NoSchedule の Taint で外れる(第32回)ので、置けるノードは 1 つ目のワーカーだけだからです。クラスタを作ったばかりなら、イメージの取得に時間がかかるので、ContainerCreating のまましばらく止まって見えることがあります(第9回)。
3 つとも Running になったら、2 つ目のワーカーを uncordon します。
$ kubectl uncordon <2 つ目のワーカーの名前> --context kind-multi-node
公式のチュートリアルの例では、出力は次のとおりです。手元ではノードの名前が 2 つ目のワーカーの名前になり、書式も例と違うことがあると考えられます。
参考:Running ZooKeeper, A Distributed System Coordinator | Kubernetes、kubectl uncordon | Kubernetes
uncordon しても、すでに動いている Pod は動かないはずです。スケジュールできるかどうかは新しい Pod を置くときに見るもので、動いている Pod には影響しないからです。
この段では、次の 2 点が合っていれば成功です。
- 1 つ目の端末で、
webの 3 つの Pod がREADY列1/1、STATUS列Runningで、NODE列がすべて 1 つ目のワーカーの名前になっている kubectl uncordonが印を外したことを知らせる 1 行を出して終わる
PodDisruptionBudget を作る
以下は web の Pod のうち少なくとも 2 つが使える状態で残るように退避を抑える PDB のマニフェストです。公式ドキュメントの zk-pdb の例をもとに、名前とセレクタを web に合わせ、「Ready でない Pod の扱い」で見た推奨の unhealthyPodEvictionPolicy: AlwaysAllow を足しています。web-pdb.yaml という名前で保存します。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
unhealthyPodEvictionPolicy: AlwaysAllow参考:Specifying a Disruption Budget for your Application | Kubernetes、PodDisruptionBudget | Kubernetes API Reference
selector は Deployment web の .spec.selector と同じ app: web です。「PodDisruptionBudget のフィールド」で見た StatefulSet の例と同じく、3 つの Pod に対する minAvailable: 2 は maxUnavailable: 1 と同じ意味で、同時に止めてよいのは 1 つまでのはずです。minAvailable にしたのは、引用する公式ドキュメントの出力の例と見比べやすくするためです。どちらの drain でも、退避の対象になるノードの web の Pod は drain を始めた時点でどれも Ready なので、AlwaysAllow は結果を変えないと考えられます。
2 つ目の端末で、マニフェストを渡します。
$ kubectl apply -f web-pdb.yaml --context kind-multi-node
poddisruptionbudget.policy/web-pdb が作られたことを知らせる 1 行が出るはずです。3 つ目の端末には web-pdb の行が現れるはずです。公式ドキュメントの例では、合う Pod が 3 つあるときの kubectl get poddisruptionbudgets の表示は次のとおりです。
参考:Specifying a Disruption Budget for your Application | Kubernetes
手元では名前が web-pdb になり、AGE 列の値も手元のものになるはずです。--watch で追っているので、変化のたびに行が書き足されます。作った直後は、disruption-controller がまだ Pod を数えていないため、ALLOWED DISRUPTIONS 列が 0 の行が先に出ることもあると考えられます。公式ドキュメントにも、合う Pod が無いときは 0 になる例があります。
続けて、2 つ目の端末で status を表示します。
$ kubectl get pdb web-pdb -o yaml --context kind-multi-node
末尾の status: に、「status と disruption-controller」で引用した例と同じ currentHealthy: 3、desiredHealthy: 2、disruptionsAllowed: 1、expectedPods: 3 が並ぶはずです。手元では conditions も表示されるはずです。
この段では、次の 2 点が合っていれば成功です。
- 3 つ目の端末の
web-pdbの行で、MIN AVAILABLE列が2、ALLOWED DISRUPTIONS列が1になる statusのcurrentHealthyが3、disruptionsAllowedが1になっている
1 つ目のワーカーを drain する
Safely Drain a Node のページの手順で、1 つ目のワーカーを drain します。kind のノードでは、第4回で見た kube-proxy と kindnet の DaemonSet の Pod が動いているので、--ignore-daemonsets を付けます。付けないと、drain は進みません。2 つ目の端末で、次のコマンドを実行します。
$ kubectl drain --ignore-daemonsets <1 つ目のワーカーの名前> --context kind-multi-node
参考:Safely Drain a Node | Kubernetes、kubectl drain | Kubernetes
drain のあいだは、1 つ目の端末の READY 列に注目します。第34回で見たとおり、READY 列の 1/1 は Pod のコンテナが Ready になったことを表します。この手順の Pod はコンテナが 1 つなので、1/1 なら Pod も Ready です。PDB が健全な Pod として数えるのは、この Ready の Pod です。drain のあいだに起こるはずのことを、端末ごとに次の表にまとめます。表の変化はコマ送り図の(1/4)〜(4/4)にあたります。
| 端末 | 表示の変化 |
|---|---|
| 1 つ目(Pod) | web の Pod の 1 つが Terminating になり、新しい web の Pod が 2 つ目のワーカーに現れて、READY 列が 0/1 から 1/1 になる。これが 3 回繰り返される |
| 2 つ目(drain) | ノードに印を付けたことを知らせる行のあと、Pod の退避を知らせる行が並び、最後に drain が終わったことを知らせる行が出る |
| 3 つ目(PDB) | ALLOWED DISRUPTIONS 列が 0 と 1 を行き来する |
公式のチュートリアルの例では、drain の出力は次のとおりです。この例は --force と --delete-emptydir-data も付けて、別のクラスタで実行したものです。
参考:Running ZooKeeper, A Distributed System Coordinator | Kubernetes
この例で見るべき行は、ノードに印を付けた cordoned の行、Pod を消した deleted の行、drain が終わった drained の行の 3 種類です。WARNING の行は例で付けた --force などにかかわる警告です。手元では --force を付けていないので、コントローラーの無い Pod を消すことを知らせる警告は出ないと考えられます。一方、--ignore-daemonsets で飛ばした DaemonSet の Pod(第4回で見た kube-proxy と kindnet)を知らせる警告は、手元でも出ると考えられます。この警告は失敗を表すものではないと考えられます。例の出力では kube-proxy が DaemonSet に管理されていない Pod として並んでいますが、例のクラスタは kind と構成が違い、kube-proxy が DaemonSet で動いていなかったと考えられます。kind では kube-proxy が DaemonSet で動いています(第4回)。手元では、ノードの名前は 1 つ目のワーカーの名前に、Pod の名前は web- で始まる名前になり、今の kubectl では行の文面や語が例と違うこともあると考えられます。
1 つ目の端末で、web の Pod が Terminating になるのは一度に 1 つずつのはずです。「drain と PDB が組み合わさる流れ」で見たとおり、2 つ目の退避は、1 つ目の代わりの Pod が Ready になるまで断られるからです。代わりの Pod が 2 つ目のワーカーに置かれるのは、1 つ目のワーカーが cordon されていて、コントロールプレーンのノードには Taint があるからです。クラスタを作り直した場合は、2 つ目のワーカーではまだ nginx のイメージを取得していないので、最初の代わりの Pod は ContainerCreating が長めになり、そのぶん drain も長く待つと考えられます。
drain が終わるまでの時間は、主に代わりの Pod が Ready になるまでの時間の 3 回分だと考えられ、数分かかることもあります。5 分ほどたっても終わらなければ、1 つ目の端末で Pending のまま残っている Pod が無いかを確かめます。見つかったら、2 つ目の端末で Ctrl+C を押して drain を止め、kubectl describe pod <Pending の Pod の名前> --context kind-multi-node を実行して、出力の Events: の欄で理由を見ます(第32回)。理由がわからなければ、片付けへ進みます。
1 つ目のワーカーで、kube-system などほかの Namespace の Pod(第4回で見た CoreDNS など)が動いていれば、それらも退避させられると考えられます。drain は mirror Pod と DaemonSet の Pod を除くすべての Pod を対象にするからです。第4回でソースコードから読んだとおり、CoreDNS の Pod は既定で 2 つあり、退避されても ReplicaSet が代わりをほかのノードに作ると考えられます。1 つ目の端末は default の Namespace だけを監視しているので、それらは表示されません。drain が emptyDir を使う Pod があることを知らせて止まった場合は、そのような Pod が 1 つ目のワーカーにあると考えられます。この手順の web の Pod は emptyDir を使っていないので、その Pod が何かを確かめてから、--delete-emptydir-data を付けるかどうかを決めます。
drain は、1 つ目のワーカーの Pod が消えれば終わります。最後の代わりの Pod(コマ送り図の web-6)は、drain が終わった直後にはまだ起動中のことがあります。1 つ目の端末でその Pod の READY 列が 1/1 になるまで少し待ってから、2 つ目の端末で web の Pod の置き場所を確かめます。
$ kubectl get pods -l app=web -o wide --context kind-multi-node
web の Pod が 3 つ表示され、どれも READY 列が 1/1 で、NODE 列がすべて 2 つ目のワーカーの名前になっているはずです。1 つ目のワーカーは cordon されたままです。custom-columns のコマンドを実行すると、1 つ目のワーカーの UNSCHEDULABLE 列が true になっているはずです。
この段では、次の 3 点が合っていれば成功です。
kubectl drainがエラーを出さずに終わる(例と同じなら、最後にdrainedの語を含む行が出る)- 1 つ目の端末で、
webの Pod がTerminatingになったあと、その代わりの Pod がREADY列1/1になるまで、次のwebの Pod のTerminatingの行が出ない webの 3 つの Pod がすべて 2 つ目のワーカーでREADY列1/1になる
置き場所が無いと drain が待ち続ける
次に、公式ドキュメントの例の後半と同じく、代わりの Pod の置き場所が無いときに drain が止まる様子を見ます。1 つ目のワーカーは cordon されたままなので、ここで 2 つ目のワーカーを drain すると、代わりの Pod を置けるノードがありません。2 つ目の端末で、次のコマンドを実行します。
$ kubectl drain --ignore-daemonsets <2 つ目のワーカーの名前> --context kind-multi-node
web の Pod の 1 つは退避を許され、Terminating になるはずです。その代わりの Pod は、1 つ目の端末で STATUS 列が Pending のまま、NODE 列にもノードの名前が入らないはずです。2 つのワーカーはどちらもスケジュールできず、コントロールプレーンのノードには Taint があるからです。代わりの Pod が Ready にならないので、残りの 2 つの退避は断られ続け、3 つ目の端末の ALLOWED DISRUPTIONS 列は 0 のままのはずです。--timeout を付けていないので、drain は頼み直しを続け、2 つ目の端末のコマンドは終わらないと考えられます。公式のチュートリアルでも、PDB に断られて止まった drain は Ctrl+C で止めています(“Use CTRL-C to terminate kubectl.")。
公式のチュートリアルの例では、PDB に断られて止まった drain の出力は次のとおりです。見るべき行は Cannot evict pod as it would violate the pod's disruption budget. を含む行で、退避が PDB に反するので断られたことを表します。最後の行は退避できない Pod を指していると読めます。WARNING の行の意味は、上の例と同じです。
参考:Running ZooKeeper, A Distributed System Coordinator | Kubernetes
手元でも、disruption budget という語を含む、退避が PDB に反することを知らせる行が出ると考えられますが、文面と出るタイミングは例と違うことがあります。1 分ほど待って、1 つ目の端末の代わりの Pod が Pending のままで、3 つ目の端末の ALLOWED DISRUPTIONS 列が 0 のままなら、PDB が退避を止めていると判断できます。公式のチュートリアルと同じく、2 つ目の端末で Ctrl+C を押して drain を止めます。チュートリアルによると、drain できなかったノードも cordon されたまま残ります。
続けて、1 つ目のワーカーを uncordon して、代わりの Pod の置き場所を作ります。
$ kubectl uncordon <1 つ目のワーカーの名前> --context kind-multi-node
1 つ目の端末で、Pending だった web の Pod の NODE 列に 1 つ目のワーカーの名前が入り、やがて READY 列が 1/1 になるはずです。公式のチュートリアルでも、ノードを uncordon すると、Pending だった Pod がそのノードに置き直されています。3 つ目の端末の ALLOWED DISRUPTIONS 列は、その Pod が Ready になると 1 に戻るはずです。
参考:Running ZooKeeper, A Distributed System Coordinator | Kubernetes、Disruptions | Kubernetes
この段では、次の 3 点が合っていれば成功です。
- drain のあいだ、
webの代わりの Pod がPendingのままで、ALLOWED DISRUPTIONS列が0のまま - Ctrl+C を押すまで、
kubectl drainが終わらない - 1 つ目のワーカーを uncordon すると、
Pendingだった Pod が 1 つ目のワーカーでREADY列1/1になる
片付け
1 つ目の端末と 3 つ目の端末で Ctrl+C を押して、監視を止めます。
2 つ目の端末で、cordon されたままの 2 つ目のワーカーを uncordon します。前の段を飛ばした場合は、1 つ目のワーカーも uncordon します。
$ kubectl uncordon <2 つ目のワーカーの名前> --context kind-multi-node
続けて、作った PDB と Deployment を、マニフェストを指定して削除します。Deployment を消すと、その ReplicaSet と Pod も消えます(第6回)。
$ kubectl delete -f web-pdb.yaml --context kind-multi-node
$ kubectl delete -f web.yaml --context kind-multi-node
参考:kubectl uncordon | Kubernetes、kubectl delete | Kubernetes
少し待ってから、残っていないかを確かめます。マニフェストでは Deployment と PDB 自身の metadata にラベルを付けていないので、-l app=web では見つかりません。そこで PDB と Deployment は種類全体で、Pod はラベルで調べます。ノードがすべてスケジュールできる状態に戻ったかも確かめます。
$ kubectl get pdb,deployments --context kind-multi-node
$ kubectl get pods -l app=web --context kind-multi-node
$ kubectl get nodes -o 'custom-columns=NAME:.metadata.name,UNSCHEDULABLE:.spec.unschedulable,TAINTS:.spec.taints[*].key' --context kind-multi-node
Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。1 つ目と 2 つ目のコマンドで見つからないことを知らせる 1 行が出て、3 つ目のコマンドでどのノードの UNSCHEDULABLE 列にも true が無く、ワーカーの TAINTS 列に node.kubernetes.io/unschedulable が無ければ、片付けは終わっています。
最後に、クラスタを削除し、消えたことを確かめます。続けて次の回を試す場合は、クラスタを残しておいてもかまいません。
$ kind delete cluster --name multi-node
$ kind get clusters
kind get clusters に multi-node が表示されなくなったことを確かめます。kubeconfig に kind-multi-node のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-multi-node で消します。
注意点
PDB と drain でつまずきやすい点と、バージョンで変わる点を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
| 直接の削除は PDB を迂回する | Deployment や Pod を削除すると PDB は効かない。PDB が抑えるのは Eviction API を通した退避 |
| ローリングアップデートは PDB で制限されない | 更新で使えなくなった Pod は PDB の数に入るが、更新の速さは Deployment の maxUnavailable などで決める(第13回) |
| 非自発的な中断は防げない | ノードの故障や資源不足による退避(第29回)は PDB では防げない。ただし PDB の数には入る |
| 退避を 1 つも許さないと drain が終わらない | maxUnavailable: 0 や、minAvailable を 100% か Pod の数にすると、その Pod のノードの drain は終わらない |
unhealthyPodEvictionPolicy の既定は IfHealthyBudget | v1.37 時点の既定で、アプリケーションがすでに中断している(currentHealthy が desiredHealthy を下回る)ときは、Running で Ready でない Pod があると drain が待つことがある。公式ドキュメントは AlwaysAllow を勧めている |
| PDB のセレクタを重ねない | 複数の PDB が選ぶ Pod の退避は許されず、Eviction API は 500 を返す |
| drain のあとは uncordon が要る | 中断した drain でも、ノードはスケジュールできないまま残る。ノードをクラスタに残すなら kubectl uncordon で戻す |
| drain したノードにも置かれる Pod がある | node.kubernetes.io/unschedulable を許容する Pod と、nodeName を直接書いた Pod(第31回)。DaemonSet 以外でこの Taint を許容するのは避けるよう書かれている |
参考:Disruptions | Kubernetes、Specifying a Disruption Budget for your Application | Kubernetes、API-initiated Eviction | Kubernetes、Safely Drain a Node | Kubernetes、Running ZooKeeper, A Distributed System Coordinator | Kubernetes
関連記事
次の回では PDB や Deployment が属する Namespace で、分けられるものと分けられないものを扱います。PDB のセレクタが空のときに選ぶのも、同じ Namespace の Pod です。
Namespace で分けられるもの、分けられないもの1 つのクラスタの中でリソースをグループに分ける Namespace の仕組みを図で説明します。名前の範囲になること、Namespace に属さないクラスタ全体のリソース、既定では Namespace をまたいで届く通信、Namespace を消すと中身も消える流れを押さえ、kind のクラスタで確かめる手順を紹介します。PDB では抑えられない、ローリングアップデートで同時に止まる Pod の数を決める maxUnavailable と maxSurge については、以下の記事で解説しています。
drain したノードに付く node.kubernetes.io/unschedulable を含む、ノードの側から Pod を遠ざける Taint と Toleration については、以下の記事で解説しています。