maxSurge と maxUnavailable の違い
v1.37 時点(2026年10月25日 確認)
目次
この記事では、ローリングアップデートで Pod を入れ替える速さと安全性を決める 2 つの設定である maxSurge と maxUnavailable の仕組みを、図で説明します。maxSurge が更新中の Pod の合計の上限を決め、maxUnavailable が使える Pod の数の下限を決めることがわかります。割合で書いた値を数に直すときの丸め方と、2 つの値の組み合わせで「新しい Pod を先に作るか、古い Pod を先に減らすか」が変わることもわかります。後半では kind のクラスタで値を変えながら Deployment を更新し、各 ReplicaSet の望ましい数の変わり方を比べる手順を示します。
Deployment と、既定の値で進むローリングアップデートを扱った第12回の内容を前提にしています。まだの方は先にこちらをどうぞ。
Deployment とローリングアップデートの仕組みReplicaSet を持ち、Pod を宣言的に更新する Deployment の仕組みを図で説明します。ひな形を変えたときに新しい ReplicaSet へ Pod を少しずつ移すローリングアップデート、pod-template-hash の役割、リビジョンと切り戻しを押さえ、kind のクラスタでイメージを更新して rollout undo で戻す手順を紹介します。以下の図は replicas: 4 の Deployment を既定の値(maxSurge と maxUnavailable がどちらも 25%)で更新するときに、Pod の数がどの範囲に保たれるかを表しています。
4 の 25% はちょうど 1 なので、maxSurge も maxUnavailable も 1 になります。そのため更新中は、Pod の合計が 4 + 1 = 5 まで増えてもよく、使える Pod が 4 − 1 = 3 まで減ってもよいことになります。図の「使えなくてもよい(maxUnavailable)」のグループは、使える Pod が望ましい 4 つから 1 つ減ってもよいという意味です。公式ドキュメントも replicas が 4 の Deployment では Pod の数が 3 から 5 のあいだ(between 3 and 5)になると説明しています。3 は使える Pod の下限、5 は Pod の合計の上限にあたると読めます。
maxSurge と maxUnavailable とは
maxSurge と maxUnavailable は、Deployment の spec.strategy.rollingUpdate に書くフィールドです。第12回で見たとおり、spec.strategy.type が既定の RollingUpdate のとき、Deployment は古い ReplicaSet を少しずつ減らしながら、新しい ReplicaSet を増やします。公式ドキュメントはこの 2 つを ローリングアップデートの進め方を制御するための値 として挙げています。v1.37 時点の公式ドキュメントと API リファレンスに沿って、次の表にまとめます。
| フィールド | 意味 | 既定値 | 割合を数に直すとき |
|---|---|---|---|
maxSurge | 更新中に、望ましい数(replicas)を超えて作ってよい Pod の数の上限 | 25% | 切り上げる |
maxUnavailable | 更新中に、使えなくてもよい Pod の数の上限 | 25% | 切り捨てる |
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
どちらも書かなくてよいフィールドで、書かなければ既定値の 25% が使われます。値は数(例:5)か、望ましい数に対する割合(例:10%)で書けます。また、片方を 0 にしたら、もう片方は 0 にできません。この決まりがあるのは、両方が 0 だと Pod を 1 つも増やせず、1 つも減らせないので、入れ替えが進まないからだと考えられます。
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
以下は 2 つのフィールドを既定値のまま書いた例です。Deployment のマニフェストの spec のうち、この回にかかわる部分だけを抜き出しています。
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%API リファレンスによると、rollingUpdate は type が RollingUpdate のときだけ置くフィールドです。第12回で見たもう 1 つの戦略 Recreate では、新しい Pod を作る前に今ある Pod をすべて終了させるので、この 2 つの値は使いません。
参考:Deployment | Kubernetes API Reference、Deployments | Kubernetes
maxUnavailable の「使えない(unavailable)」は、第6回で見た「使える(available)」の反対です。使える Pod とは Ready になってから spec.minReadySeconds の秒数がたち、そのあいだコンテナが落ちなかった Pod のことです。minReadySeconds の既定は 0 なので、既定では Ready になった時点で使える Pod とみなされます。Pod がいつ Ready になるかは、Probe の回(第34回)で扱います。
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
API リファレンスによると、Deployment の status.unavailableReplicas は、100% の数の Pod が使えるようになるまでに、まだ足りない Pod の数です。足りない分には動いているがまだ使える状態になっていない Pod と、まだ作られていない Pod の両方が入ります。
maxUnavailable が決めるのは、使える Pod が replicas からいくつまで減ってよいか、つまり 使える Pod の下限(replicas − maxUnavailable)です。公式ドキュメントも maxUnavailable を 30% にすると更新のあいだずっと、使える Pod が望ましい数の少なくとも 70% に保たれると説明しています。そのため、上乗せした新しい Pod がまだ使えなくても、使える Pod がこの下限以上あれば、古い Pod を減らすことは制約に触れません。
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
なぜ maxSurge と maxUnavailable が必要なのか
第12回では、既定の値のまま Deployment を更新しました。公式ドキュメントの replicas が 3 の例では、使える Pod は少なくとも 3 つ、Pod の合計は多くても 4 つに保たれていました。この「少なくとも」と「多くても」を決めているのが、maxUnavailable と maxSurge です。
Pod を入れ替えるときに、気にすることは 2 つあると考えられます。1 つは、更新のあいだも十分な数の Pod が要求を受けられることです。第12回で引用したとおり、ReplicationController のページは、理想的なローリングアップデートは要求を受けられる Pod の数を十分に保つものだ、と書いています。もう 1 つは、更新のあいだに Pod が増えすぎないことです。Pod が増えれば、その分だけノードの CPU やメモリを使うと考えられます。
この 2 つは両立しにくいと考えられます。使える Pod の数を減らしたくなければ、新しい Pod を先に作って増やす必要があります。Pod を増やしたくなければ、古い Pod を先に減らす必要があります。どちらを優先するか、一度に何個ずつ入れ替えるかは、アプリケーションやクラスタの事情によって変わります。maxSurge と maxUnavailable は、その釣り合いを Deployment ごとに決めるための値です。
maxSurge と maxUnavailable の仕組み
2 つの値が決める上限と下限
2 つの値は更新中の Pod の数に上限と下限を作ります。公式ドキュメントの説明をもとに、次の表にまとめます。この回の「Pod の合計」は、第12回と同じく、終了中のものを除いた Pod の合計です。
| 決まるもの | 決め方 | replicas: 4、既定値のとき |
|---|---|---|
| Pod の合計の上限 | replicas + maxSurge | 4 + 1 = 5 |
| 使える Pod の下限 | replicas − maxUnavailable | 4 − 1 = 3 |
公式ドキュメントは 2 つの値を 30% にした例で、それぞれの働きを次のように説明しています。
| 値 | 更新が始まった直後 | そのあと |
|---|---|---|
| maxSurge が 30% | 古い Pod と新しい Pod の合計が望ましい数の 130% を超えない範囲(上乗せは maxSurge の分まで)で、すぐに新しい ReplicaSet を増やせる | 古い Pod が終了したら、新しい ReplicaSet をさらに増やせる |
| maxUnavailable が 30% | すぐに古い ReplicaSet を望ましい数の 70% まで減らせる | 新しい Pod の準備ができたら(ready)、古い ReplicaSet をさらに減らし、続けて新しい ReplicaSet を増やせる |
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
表の「準備ができたら(ready)」は、公式ドキュメントの語をそのまま訳したものです。公式ドキュメントは同じ説明の終わりで、更新のあいだ使える(available)Pod が望ましい数の少なくとも 70% に保たれる、と書いています。下限と比べるのは、この記事でいう「使える」Pod の数です。
ここから、maxSurge は「古い Pod を減らす前に、先に作ってよい新しい Pod の数」、maxUnavailable は「新しい Pod が使えるようになる前に、先に減らしてよい古い Pod の数」と読めます。
割合から数への丸め
割合で書いた値は Deployment の望ましい数をもとに数に直されます。公式ドキュメントによると、maxSurge は切り上げ、maxUnavailable は切り捨て です。既定の 25% が replicas ごとに何個になるかを、この決まりで計算すると次のようになります。
replicas | maxSurge(25% を切り上げ) | maxUnavailable(25% を切り捨て) | Pod の合計の上限 | 使える Pod の下限 |
|---|---|---|---|---|
| 1 | 1(0.25 → 1) | 0(0.25 → 0) | 2 | 1 |
| 3 | 1(0.75 → 1) | 0(0.75 → 0) | 4 | 3 |
| 4 | 1(1 → 1) | 1(1 → 1) | 5 | 3 |
| 10 | 3(2.5 → 3) | 2(2.5 → 2) | 13 | 8 |
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
replicas が 3 と 4 の行は、公式ドキュメントの例の数(3 なら少なくとも 3 つで多くても 4 つ、4 なら 3 から 5 のあいだ)と合っています。表のとおり、replicas が 3 以下のとき、既定の maxUnavailable は 0 になります。この場合は、使える Pod の数を減らさずに、Pod を 1 つ上乗せして入れ替えることになります。第12回の例が新しい Pod を 1 つ作ってから古い Pod を 1 つ消していたのは、このためだと考えられます。
先に増やすか、先に減らすか
maxSurge と maxUnavailable のどちらかを 0 にすると、入れ替えの順番がはっきり分かれます。ここでは replicas: 4 の Deployment で、次の 2 つの設定を比べます。
| 設定 | Pod の合計の上限 | 使える Pod の下限 | 入れ替えの順番 |
|---|---|---|---|
maxSurge: 1、maxUnavailable: 0 | 5 | 4 | 新しい Pod を先に作り、使えるようになってから古い Pod を減らす |
maxSurge: 0、maxUnavailable: 1 | 4 | 3 | 古い Pod を先に減らし、空いた分だけ新しい Pod を作る |
以下の 2 つの図は、それぞれの設定で Pod が入れ替わる様子をコマ送りで表しています。公式ドキュメントにこの 2 つの設定の例は無いため、上限と下限の説明に沿って描いた流れです。実際には、1 つのコマに描いた 2 つの変化が少しずれて起きたり、新しい Pod が使えるようになる時刻がずれたりして、途中の数が図と違うことがあると考えられます。図の A〜H は Pod の名前の略です。「動いている」は使える(available)Pod、「作られた」は作られたばかりで、まだ使える状態になっていない Pod を指します。「終了中」の Pod は Pod の合計にも使える Pod にも数えません。第12回の図と同じく、ReplicaSet の数を変えるのは deployment-controller で、Pod を実際に作ったり消したりするのは replicaset-controller です。
まず、maxSurge: 1、maxUnavailable: 0 の場合です。
どのコマでも、使える Pod は 4 つを下回りません。その代わり、更新のあいだは Pod の合計が 5 つになるので、Pod 1 つ分の資源がノードに余分に要ると考えられます。新しい Pod が使えるようになるまで古い Pod を減らせないので、1 つずつしか進みません。
続いて、maxSurge: 0、maxUnavailable: 1 の場合です。
こちらは、Pod の合計が 4 つを超えません。その代わり、更新のあいだは使える Pod が 3 つに減ります。2 つ目の図の(1/4)には矢印が 2 本描いてありますが、古い ReplicaSet を減らすのが先で、新しい ReplicaSet を増やすのはそのあとです。maxSurge が 0 なので、合計が 4 を超えないように、新しい ReplicaSet は古い ReplicaSet が減ってからでないと増やせません。公式ドキュメントも maxUnavailable の分だけ古い ReplicaSet を更新の始めにすぐ減らせることと、新しい Pod の準備ができたあとは古い ReplicaSet を減らすことに「続けて(followed by)」新しい ReplicaSet を増やすことを説明しています。2 つの設定は上乗せする Pod 1 つ分の資源と、使える Pod 1 つ分の減りのどちらを受け入れるかの違いだといえます。
図の「Pod の合計」は終了中の Pod を除いた数です。公式ドキュメントによると、Kubernetes は使える Pod の数(availableReplicas)を計算するとき、終了中の Pod を数えません。そのため、Deployment は終了中の Pod が消えきるのを待たずに、新しい Pod を作り進めると考えられます。終了中の Pod が消えきるまでは、kubectl get pods の一覧に、図の「Pod の合計」より多くの Pod が並ぶことがあります。公式ドキュメントも、更新中に思ったより多くの Pod があることに気付くかもしれず、Deployment が使う資源も replicas + maxSurge の分より多くなることがある、と書いています。
組み合わせによる違い
replicas: 4 の Deployment で、よく考えられる組み合わせを次の表にまとめます。上限と下限は公式ドキュメントの決まりから計算したもので、入れ替え方は「2 つの値が決める上限と下限」の説明から読み取ったものです。
| 設定(maxSurge / maxUnavailable) | Pod の合計の上限 | 使える Pod の下限 | 入れ替え方 |
|---|---|---|---|
| 25% / 25%(既定値。数にすると 1 / 1) | 5 | 3 | 新しい Pod を 1 つ作ることと、古い Pod を 1 つ減らすことを、始めから同時に進められる |
| 1 / 0 | 5 | 4 | 新しい Pod を先に作る。使える Pod を減らさない |
| 0 / 1 | 4 | 3 | 古い Pod を先に減らす。Pod を上乗せしない |
| 100% / 0(数にすると 4 / 0) | 8 | 4 | 新しい Pod を 4 つ、始めにまとめて作れる。そのあいだ Pod の合計は 2 倍になる |
| 0 / 0 | 設定できない | 設定できない | 片方を 0 にしたら、もう片方は 0 にできない |
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
一般に、2 つの値を大きくするほど一度に入れ替えられる Pod が増えるので、更新は早く終わると考えられます。その代わり、maxSurge を大きくすると Pod の合計が増え、maxUnavailable を大きくすると使える Pod が減ります。速さと安全性の釣り合いを決めるのが、この 2 つの値です。
更新が止まったときの違い
第12回では、イメージを打ち間違えて更新が止まる例を見ました。公式ドキュメントによると、Deployment のコントローラーはこうした悪い展開を自動で止め、新しい ReplicaSet を増やすのをやめます。どこで止まるかは rollingUpdate の値、特に maxUnavailable によります。
公式ドキュメントの Proportional scaling の節に、replicas: 10、maxSurge: 3、maxUnavailable: 2 の Deployment を、取得できないイメージで更新した例があります。そのときの kubectl get rs の出力を引用します。
nginx-deployment-1989198191 が新しい ReplicaSet、nginx-deployment-618515232 が古い ReplicaSet です。公式ドキュメントは、新しい展開が maxUnavailable の決まりのために止まっていると説明しています。8 と 5 という数の理由は公式ドキュメントには書かれていませんが、上限と下限の決め方に当てはめると、古い ReplicaSet は使える Pod の下限の 10 − 2 = 8 まで減らされ、新しい ReplicaSet は Pod の合計が上限の 10 + 3 = 13 になる 5 まで増やされた、と読めます。新しい Pod は READY 列が 0 のまま、1 つも使える状態になっていません。
この例からわかるとおり、maxUnavailable を 0 より大きくすると、更新が止まったあいだ、使える Pod はその分だけ減ったままになります。第12回の例(replicas: 3、既定値)では maxUnavailable が 0 だったので、古い Pod 3 つが動き続け、新しい Pod は 1 つで止まっていました。
手元で試す
ここまでの内容を手元の kind のクラスタで試す手順を示します。replicas: 4 の Deployment を作り、maxSurge と maxUnavailable の値を変えながらイメージを更新して、各 ReplicaSet の望ましい数(DESIRED)の変わり方を比べます。コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.37.0(kind v0.33.0 の既定のノードイメージ) |
| ノード構成 | シングルノード(クラスタ名 kind):コントロールプレーン 1 |
| 必要なもの | Docker、kubectl(v1.36〜v1.38)、テキストエディタ、端末(ターミナル)2 つ |
参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes
kind でクラスタを作る手順は、以下の記事で解説しています。この回ではシングルノードのクラスタを使います。この回で見る各 ReplicaSet の望ましい数の変わり方は、ノードが 1 つでも確かめられるからです。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。シングルノードのクラスタを用意する
まず、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
kind が表示されたら、そのまま使えるので、次のクラスタを作るコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。
$ kind create cluster
$ kind get clusters
kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。
続けて、ノードを一覧します。
$ kubectl get nodes --context kind-kind
ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。
Deployment を作る
以下はこの回で使う Deployment のマニフェストです。第12回のマニフェストから、replicas を 4 に変え、minReadySeconds と strategy を加えています。strategy にはまず既定値と同じ 25% を書いておきます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 4
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
minReadySeconds: 10 は、入れ替わりの様子を目で追いやすくするための設定です。新しい Pod は Ready になってから 10 秒たつまで使える Pod とみなされません。maxUnavailable は使える Pod の数で決まるので、1 段ずつ進むたびに少なくとも 10 秒ほど待つことになると考えられます。replicas を 4 にしたのは、既定値の 25% が maxSurge でも maxUnavailable でも 1 になり、2 つの値の違いが表に出やすいからです(replicas が 3 だと maxUnavailable は 0 になります)。
このマニフェストを nginx-deployment.yaml という名前で、コマンドを実行するディレクトリに保存します。第12回で保存した同じ名前のファイルが残っていたら、上の内容で上書きします。このファイルはあとでエディタで何度か書き換えます。
前の回のクラスタを使い続けている場合は、先に nginx-deployment が無いことを確かめます。
$ kubectl get deployments --context kind-kind
nginx-deployment が表示されたら、kubectl delete deployment nginx-deployment --context kind-kind で消してから進みます。
Deployment を作り、展開が終わるまで見ます。
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
$ kubectl rollout status deployment/nginx-deployment --context kind-kind
Waiting for rollout to finish で始まる行がいくつか出たあと、successfully rolled out で終わる行が出て、コマンドが終わるはずです。minReadySeconds を 10 にしたので、第12回より少し時間がかかると考えられます。
Deployment の設定を詳しく表示して、値を確かめます。
$ kubectl describe deployment nginx-deployment --context kind-kind
公式ドキュメントの例では、表示の上の方に戦略にかかわる次の 3 行があります(例は minReadySeconds を書いていない Deployment のものです)。
手元では MinReadySeconds: が 10 になり、RollingUpdateStrategy: は例と同じく 25% の値になるはずです。Replicas: の行が 4 desired で始まることも確かめてください。
入れ替わりを見る準備をする
ここからは端末を 2 つ使います。第11回と同じく、1 つ目の端末で ReplicaSet の変化を追い続け、2 つ目の端末でマニフェストを書き換えて渡します。
1 つ目の端末で、ReplicaSet を監視します。第3回で見たとおり、--watch を付けると、一覧を表示したあとも変化を追い続けます。公式ドキュメントでも、展開の様子を見るのに kubectl get rs --watch を使っています。
$ kubectl get rs --watch --context kind-kind
nginx-deployment- で始まる ReplicaSet が 1 つ表示され、DESIRED 列は 4 になっているはずです。この名前を控えておきます。そのあとは、変化が起きるまで止まったように見えるはずです。見る列は次の 3 つです。
| 列 | 内容 |
|---|---|
DESIRED | ReplicaSet の望ましい数。deployment-controller が書き換える数 |
CURRENT | ReplicaSet の Pod のうち、今ある数 |
READY | ReplicaSet の Pod のうち、要求を受けられる状態の数 |
第11回で見たとおり、--watch の表示はすでに表示した行を消さずに、変化を受け取るたびに行を書き足します。公式ドキュメントにある、一時停止した展開を再開する例では、kubectl get rs --watch の出力は次のとおりです。この例の Deployment は名前も数もこの回のものと違い、Pod の数は 3 です。この例は --watch の表示の読み方を見るためのもので、数はこの記事で説明した上限と下限に当てはまらないところがあります。
例のとおり、同じ ReplicaSet の行が同じ値のまま何度も出ることもあります。それぞれの ReplicaSet について、いちばん新しい行の値を読みます。例の nginx-2142116321 1 2 2 の行のように、DESIRED が先に変わり、CURRENT があとから追いつく様子も読み取れます。deployment-controller が ReplicaSet の数を書き換えたあと、replicaset-controller が Pod を作ったり消したりするからだと考えられます。監視はこのあとの 3 つの更新のあいだ続けたままにしておきます。
既定値(25%)のまま更新する
2 つ目の端末で、nginx-deployment.yaml の image: の行を nginx:1.16.1 に書き換えて保存します。ほかの行は変えません。
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80書き換えたマニフェストを渡し、展開が終わるまで見ます。
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
$ kubectl rollout status deployment/nginx-deployment --context kind-kind
1 つ目の端末に、新しい名前の ReplicaSet の行が現れるはずです。replicas が 4 で既定値のときは、Pod の合計は多くても 5、使える Pod は少なくとも 3 です。「2 つの値が決める上限と下限」の説明どおりなら、更新が始まってすぐに、新しい ReplicaSet の DESIRED が 1 になり、古い ReplicaSet の DESIRED が 3 になると考えられます。古い ReplicaSet が 3 に減ると、2 つの ReplicaSet の DESIRED の合計は 3 + 1 = 4 です。上限の 5 まであと 1 つ作れるので、続けて新しい ReplicaSet の DESIRED が 2 になり、新しい ReplicaSet 2、古い ReplicaSet 3 の、合計 5 の状態が見られると考えられます。
ここで見ておくのは、新しい Pod が使えるようになるのを待たずに、古い ReplicaSet の DESIRED が 3 に減ると考えられる点です。maxUnavailable が 1 なので、使える Pod を 1 つ減らしてよいからです。このあとの 2 つの更新と比べるときに、この点を使います。
そのあとは、新しい Pod が使えるようになるたびに、古い ReplicaSet の DESIRED が減り、新しい ReplicaSet の DESIRED が増えていくはずです。最後は新しい ReplicaSet の DESIRED が 4、古い ReplicaSet が 0 になります。どの時点でも、2 つの ReplicaSet の DESIRED の合計は 5 を超えないはずです。2 つ目の端末で successfully rolled out で終わる行が出たら、この更新は終わりです。
maxSurge: 1、maxUnavailable: 0 で更新する
この更新では、strategy の値とイメージの 2 か所を書き換えます。公式ドキュメントによると、Deployment の展開が始まるのは Pod のひな形(spec.template)が変わったときだけです。イメージも書き換えるのはこのためです。strategy はひな形の外にあるので、strategy だけを変えても Pod は入れ替わりません。
イメージは最初と同じ nginx:1.14.2 に戻します。ひな形が最初と同じに戻るので、第12回の切り戻しと同じく、新しい ReplicaSet は作られず、最初の ReplicaSet(控えておいた名前)がもう一度使われると考えられます。その場合、DESIRED が 0 から増えていくのは最初の ReplicaSet で、減っていくのは 1 つ前の更新で作られた ReplicaSet です。
参考:Deployments | Kubernetes、Deployments | Kubernetes、Deployments | Kubernetes
このように、手元では新しく作られずに 0 から増える ReplicaSet もあります。そこでここからは、数が増えていく ReplicaSet を「増える側」、減っていく ReplicaSet を「減る側」と呼びます。図の「新しい ReplicaSet」が増える側、「古い ReplicaSet」が減る側にあたります。3 つ目の ReplicaSet が現れたときは、その ReplicaSet を増える側として読みます。
2 つ目の端末で、nginx-deployment.yaml を 2 か所書き換えて保存します。1 か所目は strategy の値で、maxSurge を 1、maxUnavailable を 0 にします。
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 02 か所目は image: の行で、nginx:1.14.2 に戻します。
image: nginx:1.14.2書き換えたマニフェストを渡し、展開が終わるまで見ます。
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
$ kubectl rollout status deployment/nginx-deployment --context kind-kind
1 つ目の端末では、コマ送りの 1 つ目の図のとおり、増える側の DESIRED が先に 1 になるはずです。既定値のときは、更新が始まってすぐに減る側の DESIRED が 3 に減ると考えられました。今度は、減る側の DESIRED がしばらく 4 のままのはずです。増える側の新しい Pod が使える状態になってから(Ready のあと minReadySeconds の 10 秒がたってから)、減る側の DESIRED が 3 に減ると考えられます。そのあとは、減る側が 1 つ減り、増える側が 1 つ増えることを、1 つずつ繰り返すはずです。1 つずつしか進まないので、既定値のときより終わるまでに時間がかかると考えられます。
maxSurge: 0、maxUnavailable: 1 で更新する
2 つ目の端末で、nginx-deployment.yaml の strategy の値を、maxSurge を 0、maxUnavailable を 1 に書き換えます。
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1image: の行も、もう一度 nginx:1.16.1 に書き換えて保存します。
image: nginx:1.16.1書き換えたマニフェストを渡し、展開が終わるまで見ます。
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
$ kubectl rollout status deployment/nginx-deployment --context kind-kind
今度は 1 つ前の更新と逆に、nginx:1.16.1 の ReplicaSet が増える側、nginx:1.14.2 の ReplicaSet が減る側になると考えられます。
1 つ目の端末では、コマ送りの 2 つ目の図のとおり、減る側の DESIRED が先に 3 になり、そのあとで増える側の DESIRED が 1 になるはずです。2 つの行はほぼ同時に出ると考えられるので、順番は行の上下で確かめます。--watch は変化を受け取るたびに行を書き足すので、減る側の DESIRED が 3 の行が、増える側の DESIRED が 1 の行より上にあれば、減らすのが先だったと読めると考えられます。maxSurge が 0 なので、2 つの ReplicaSet の DESIRED の合計は 4 を超えないと考えられます。そのあとは、増える側の新しい Pod が使えるようになるたびに、減る側が 1 つ減り、増える側が 1 つ増えるはずです。1 つ前の更新との違いは、最初に動くのが増える側か減る側か、という点です。
ここまでで、1 つ目の端末の監視は Ctrl+C で止めてかまいません。
両方を 0 にしてみる
最後に、受け付けられない値を試します。2 つ目の端末で、nginx-deployment.yaml の strategy の値を、両方とも 0 に書き換えて保存します。image: の行は nginx:1.16.1 のままにします。
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 0書き換えたマニフェストを渡します。
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
公式ドキュメントと API リファレンスによると、片方が 0 のとき、もう片方は 0 にできません。そのため、API がこのマニフェストを受け付けず、kubectl apply はエラーを表示して終わると考えられます。エラーの文言の例は公式ドキュメントに無いので、ここでは載せません。目印は、それまでの kubectl apply のあとに出ていた行(configured のような語で終わる行)が出ずに、エラーを表す行が出ることだと考えられます。
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference
Deployment が変わっていないことを確かめます。
$ kubectl describe deployment nginx-deployment --context kind-kind
RollingUpdateStrategy: の行が、1 つ前の更新の値(maxUnavailable が 1、maxSurge が 0)のままになっているはずです。「Deployment を作る」で引用した例(25% max unavailable, 25% max surge)にならうと、1 max unavailable, 0 max surge の形で表示されると考えられます。
片付け
作った Deployment を、マニフェストを指定して削除します。公式リファレンスにはファイル(pod.json)に書かれた種類と名前で消す相手を決める kubectl delete -f ./pod.json の例があります。そのため、strategy の値が受け付けられないままのこのファイルでも、そのまま使えると考えられます。
$ kubectl delete -f nginx-deployment.yaml --context kind-kind
もし削除できなかったら、kubectl delete deployment nginx-deployment --context kind-kind で名前を指定して消します。第6回で見たとおり、Deployment を消すと、既定ではガベージコレクターが従属物を消すので、ReplicaSet と Pod も消えます。少し待ってから、残っていないかを確かめます。
$ kubectl get deployments,rs,pods --context kind-kind
Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。nginx-deployment の Deployment、ReplicaSet、Pod がどれも表示されなければ、削除できています。
最後に、クラスタを削除し、消えたことを確かめます。
$ kind delete cluster
$ kind get clusters
kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。
注意点
maxSurge と maxUnavailable でつまずきやすい点を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
| 丸め方が 2 つで違う | maxSurge は切り上げ、maxUnavailable は切り捨て。replicas が 3 以下だと、既定の maxUnavailable は 0 になる |
| 両方を 0 にできない | 片方を 0 にしたら、もう片方は 0 より大きくする |
| 数えるのは「使える」Pod | maxUnavailable は使える(available)Pod で数える。minReadySeconds を長くすると、1 段ごとの待ち時間も延びると考えられる |
strategy だけ変えても入れ替わらない | 展開が始まるのは spec.template が変わったときだけ。値は次の展開から使われると考えられる |
| 止まった更新では、使える Pod が減ったままになる | 止まる位置は maxUnavailable による。0 より大きいと、その分だけ使える Pod が減った状態で止まる |
| 更新中は Pod が多く見えることがある | 終了中の Pod は数に入らないので、Pod の数や使う資源が replicas + maxSurge より多くなることがある |
type: Recreate では使わない | rollingUpdate は type が RollingUpdate のときだけ置くフィールド |
| PodDisruptionBudget では抑えられない | PodDisruptionBudget(第36回)は、ノードの drain のような計画した作業で同時に止まる Pod の数を抑える仕組み。ローリングアップデートで使えなくなった Pod はその数に入るが、ローリングアップデート自体は制限されないので、更新で減ってよい数は Deployment の maxUnavailable で決める |
参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference、Disruptions | Kubernetes
maxSurge を 0 より大きくするときは、上乗せする Pod の分の空きがノードに要ると考えられます。空きが無いと上乗せした Pod が動き出せず、maxUnavailable が 0 のときは古い Pod も減らせないので、更新が進まなくなるおそれがあります。その場合は、第12回で見たとおり、spec.progressDeadlineSeconds を過ぎたところで ProgressDeadlineExceeded が報告されると考えられます。Pod がノードに置かれる仕組みは、第6部(スケジューリングとリソース)で扱います。
関連記事
Pod に名前と順番を持たせて、1 つずつ作り、1 つずつ止めるワークロード StatefulSet については、以下の記事で解説しています。
StatefulSet を動かして理解するPod に番号付きの固定の名前を与え、番号の順に作って止める StatefulSet の仕組みを図で説明します。web-0 から順に作り、番号の大きい順に止める決まり、headless Service が受け持つ名前、Deployment との違いを押さえ、kind のクラスタで Pod を消しても同じ名前で戻ることと、起動と停止の順番を確かめる手順を紹介します。Pod がいつ Ready になり、要求を受けられるとみなされるかを決める Probe については、以下の記事で解説しています。
liveness・readiness・startup Probe の違いkubelet がコンテナを定期的に確かめる Probe の 3 種類、liveness・readiness・startup の違いを図で説明します。失敗したときに kubelet がすること、確かめ方と設定のフィールド、readiness Probe と Service の転送先との関係を押さえ、kind のクラスタで readiness が失敗した Pod が転送先から外れることを確かめる手順を紹介します。ノードを止めるときに、同時に止めてよい Pod の数を抑える PodDisruptionBudget については、以下の記事で解説しています。
PodDisruptionBudget の仕組みノードの drain のような自発的な中断で、同時に止まる Pod の数を抑える PodDisruptionBudget の仕組みを図で説明します。Eviction API による退避、minAvailable と maxUnavailable、kubectl drain と cordon を押さえ、kind のクラスタで drain が PDB に断られながら Pod を 1 つずつ退避させる様子を観察します。