maxSurge と maxUnavailable の不思議
はじめに
Kubernetes の Deployment にはローリングアップデートの挙動を制御するために maxSurge
と maxUnavailable
と言うフィールドがある。
ご存知の通り、maxSurge
はローリングアップデートの最中に一時的に replicas
より増えていい Pod の数を、maxUnavailable
はローリングアップデートの最中に一時的に replicas
より減っていい Pod の数を指定するものだが、こいつらがちょっと不思議だったので調べてみた。
0
にすることはできない
両方同時に API Reference にもあるように、maxUnavailable
と maxSurge
を両方同時に 0
にすることはできない。maxSurge
側には "This can not be 0 if MaxUnavailable is 0."、maxUnavailable
側には "This can not be 0 if MaxSurge is 0." と書いてある。
実際、最近話題の nginx で試してみると、
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 0
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.4-bookworm
以下のようなエラーが出る。
$ kubectl apply -f ンギンクス.yaml --server-side
The Deployment "nginx" is invalid: spec.strategy.rollingUpdate.maxUnavailable: Invalid value: intstr.IntOrString{Type:0, IntVal:0, StrVal:""}: may not be 0 when `maxSurge` is 0
まぁローリングアップデートする時に Pod の数を増やしても減らしてもダメって言われたら「どないせいっちゅうねん!」ってなるから当たり前ではある。
パーセント指定でもダメ
これらのフィールドには、Pod 数そのものではなくパーセントを指定することもできる。指定できるって言うかむしろこちらの方がメジャーな気もするし、デフォルトも両方とも 25%
である。
で、「両方同時に 0
はダメ」と言うのは 0%
でも同じで、
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 0%
maxUnavailable: 0%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.4-bookworm
以下のようなエラーが出る。
$ kubectl apply -f ンギンクス%.yaml --server-side
The Deployment "nginx" is invalid: spec.strategy.rollingUpdate.maxUnavailable: Invalid value: intstr.IntOrString{Type:1, IntVal:0, StrVal:"0%"}: may not be 0 when `maxSurge` is 0
もちろん、0
と 0%
の組み合わせでもダメだ。
maxSurge
は切り上げ、maxUnavailable
は切り捨て
パーセント指定の場合、実際の Pod 数は replicas
の数と掛け合わせて計算されるのだが、その際 maxSurge
は切り上げで、maxUnavailable
は切り捨てで計算される。
つまり、replicas
が 1 の場合 maxSurge: 1%
は 1 Pod 増えて良くて、maxUnavailable: 99%
は 1 Pod も増えてはいけない。
maxSurge
が切り上げなのは「0%
じゃないってことは、ローリングアップデートの時は Pod 数増えてもいいと思ってるんだから、切り上げとけばいいよね」ってことなんだと思う。
一方で、maxUnavailable
が切り捨てなのは「0%
じゃないってことは、ローリングアップデートの時は Pod 数減ってもいいとは思ってるんだとは思うけど、せっかく Deployment にしてるんだからきっと生きてる Pod が 0 個にはなって欲しくないよね」ってことなんじゃないかな?知らんけど…[1]
0
になる?
計算した結果両方 さて、ここまでは前提知識である。
今回不思議に思ったのは、「パーセント指定で計算の結果両方 0
になる場合でもエラーにならないんだけどどういうことなの?」と言うものだ。
例えばこんなヤツは、
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 0%
maxUnavailable: 1%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.4-bookworm
特にエラーもなく反映できる。
$ kubectl apply -f ンギンクス0.yaml --server-side
deployment.apps/nginx serverside-applied
見て分かる通り、maxSurge
は明らかに 0
、maxUnavailable
は replicas
がデフォルトの 1
なので計算すると 0
になり、両方同時に 0
になるはずなのであるが、特にエラーが出ない。
おいおい、これじゃローリングアップデートできなくね?と思って試しに bookworm
を alpine
に変えて適用してみると、何のエラーもなく適用できる。
$ kubectl apply -f ンギンクスa.yaml --server-side
deployment.apps/nginx serverside-applied
どういうこと?と思って裏で Pod の状態を眺めてみると、
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
nginx-9fc65c5b6-q57k2 1/1 Running 0 4s
nginx-9fc65c5b6-q57k2 1/1 Terminating 0 17s
nginx-6985cd5669-4h84g 0/1 Pending 0 0s
nginx-6985cd5669-4h84g 0/1 Pending 0 0s
nginx-6985cd5669-4h84g 0/1 ContainerCreating 0 0s
nginx-9fc65c5b6-q57k2 0/1 Terminating 0 17s
nginx-9fc65c5b6-q57k2 0/1 Terminating 0 18s
nginx-9fc65c5b6-q57k2 0/1 Terminating 0 18s
nginx-9fc65c5b6-q57k2 0/1 Terminating 0 18s
nginx-6985cd5669-4h84g 1/1 Running 0 3s
普通に Pod が減ってやがる!両方 0
じゃないんかい!
特別ルール
気になって夜しか寝れないので、ソースを調べてみた。
計算の結果両方 0
になった時のために特別ルールがある!
コメントをざっくり訳すとこんな感じ。
バリデーションがあるからユーザが明示的に両方
0
にはできないけど、maxUnavailable
は切り捨てだから結果的に両方0
になっちゃう時もあるよね。
そんな時はmaxUnavailable
を1
にしちゃうよ。
だって、リソースクォータのせいで Pod を増やせないかもしれないからね。
つまり、計算の結果両方 0
になった時は、maxUnavailable
は 1
にすると言う訳だ。マジか!
ドキュメント is どこ…
この挙動、kubernetes を普段使いしてる人から見れば「何を当たり前の事を…」と思うのかもしれないが、エアプなので全く知らなかった。
そして、軽くドキュメントを探してみたものの、この挙動を説明する箇所を見つけられなかった。
できればどこかに書いておいて欲しいんだが…[2]
あと、パーセント指定でもバリデーション時にチェックしてエラーにすることはできると思うんだが、なんでやらないのかも良く分からんな。
何となく、HPA があるから今は両方 0
になっても実際にローリングアップデートする時にも両方 0
とは限らないから、なのかと思ったりもするが…[3]
Discussion