クラスター外に公開したPodのダウンタイムを最小化する
ダウンタイム≒リクエストを受けられるPodがひとつもない
状態を最小化したい
環境
- EKS
- PodはALBやNLBを使って公開
- AWS Load Balancer Controllerを利用
-
target-type: ip
で利用 - TargetGroupBindingを利用(Ingress使っててもあまり変わらないと思われる)
-
Deploymentの更新時のダウンタイムを最小化する
Deploymentの更新時にリクエストを受けられるPodがひとつもない状態を引き起こす要因は2つある。
- ローリングアップデートの進行が速すぎてターゲットグループへの登録が間に合っていない
- Podの終了が速すぎてターゲットグループからの登録解除が間に合っていない
ローリングアップデートの進行が速すぎてターゲットグループへの登録が間に合っていない
Deploymentのspec.strategy.rollingUpdate.maxSurge
とspec.strategy.rollingUpdate.maxUnavailable
は25%なので、レプリカがそう多くはない状態においては
「新Podが1個作られる」→「旧Podが1個消える」 が交互に繰り返されてローリングアップデートが進行する。
デフォルトでは「新Podができた(Readyになった)」の条件に「ターゲットグループに登録完了してhealthyになった」は含まれていないため、
ターゲットグループ側にhealthyなターゲットがいないにもかかわらず旧Podがどんどん削除されていき、healthyなターゲットがひとつも存在しない瞬間=ダウンタイムが発生することがある。
KubernetesにはPodがReadyになったと判断されるための条件を追加する仕組み(Readiness Gates)があり、AWS Load Balancer Controllerはこれに対応している。
Namespaceにラベル付けをすることでWebhookにより自動的にNamespace内のPodに対して有効化できる。
同じNamespace内でもPodによって有効・無効を変えたい場合は、MutatingWebhookConfigurationを変更してobjectSelector
を設定する。
Podの終了が速すぎてターゲットグループからの登録解除が間に合っていない
Podが速やかに終了しすぎるとターゲットグループからの登録解除が間に合わず、「healthyなターゲットだからリクエストを転送したが、Podはもうそこにはいなかった」場合がある。
Podのspec.containers.lifecycle.preStop
でsleepなどさせて終了を先延ばしにすることで、PodがServiceから外れターゲットグループからも登録解除されるまでの時間稼ぎをする。
apiVersion: apps/v1
kind: Deployment
...
spec:
...
template:
...
spec:
containers:
lifecycle:
preStop:
exec:
command: ["sleep", "90"]
ノードメンテナンス時のダウンタイムを最小にする
ノードメンテナンス時≒kubectl drain
が実行されたとき
- drainを削除やメンテ予定のノード全体に実施したら、Podが一斉に終了して誰も残らなかった
- drainを選択的に(1台ずつや1AZずつ)実施したが、Podの配置が偏っていたので結局全員いなくなった
- Podの終了が早すぎてターゲットグループからの登録解除が間に合っていない
- 対処は先ほどと同じなので省略
drainを削除やメンテ予定のノード全体に実施したら、Podが一斉に終了して誰も残らなかった
PodDisruptionBudgetを作って「最低○人残ってほしい」という意思表示をすることで「一斉に終了して誰も残らない」を防ぐ。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: foo-api
namespace: foo
spec:
# 最低ひとりは残ってほしい
minAvailable: "1"
selector:
matchLabels:
app: foo-api
最大限削除されてもいい数をspec.maxUnavailable
で指定することもできる。
minAvailable
とmaxUnavailable
は33%
のように割合で書くこともできる。
drainを選択的に(1台ずつや1AZずつ)実施したが、Podの配置が偏っていたので結局全員いなくなった
このケースも全員一気にいなくなることはPDBで防げる。
が、配置が偏らないようにもしておくと障害の備えにもなり、PDBも守られやすい(PDBを守りながら終了していくために別ノードに新たにPodを作る過程を経るより、最初からPodが散っていて円滑に終了していける方が短時間で済み、タイムリミットがあるdrainも正常終了しやすい)。
Podの配置を散らすためにTopology Spread Constraintsを利用できる。
Topology Spread Constraints
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-api
spec:
...
template:
metadata:
labels:
app: foo-api
spec:
...
topologySpreadConstraints:
# zoneによるpodの偏りを1台までとする
- maxSkew: 1
whenUnsatisfiable: DoNotSchedule
topologyKey: topology.kubernetes.io/zone
labelSelector:
matchLabels:
app: foo-api
Descheduler
Topology Spread Constraintsの制限事項として、「Podの配置時にしか考慮されない」点がある。
例:2つのAZにPodが3つあり、いずれのAZにも1つ以上Podがあった。HPAによりPodが2個に減ったが、その際1つしかないAZのPodが終了した=一方のAZにPodが2個、もう一方には0個に偏った
この点をカバーするためにPodを自動的に削除して再配置を促すDeschedulerが開発されている。
Topology Spread Constraintsに反した状態になっているPodを削除するstrategyを指定するvalues.yaml例
deschedulerPolicy:
strategies:
# https://github.com/kubernetes-sigs/descheduler#removepodsviolatingtopologyspreadconstraint
RemovePodsViolatingTopologySpreadConstraint:
enabled: true
params:
includeSoftConstraints: false
nodeFit: true
Discussion