Low Priority Pod を利用したスパイク対処策が面白かった
はじめに
Kubernetes の Low Priority Pod (LPP) を利用した Azure Kubernetes Service (AKS) のノードスケーリングの迅速化について、とても良いアイデアの記事が公開されていたので、読み解きつつ紹介したいと思います。
なぜ「Low Priority Pod」か?
AKS 等の Managed Kubernetes では、ノードが不足してからオートスケールで新規ノードが起動するまでには一般に 5〜10分 程度のラグが生じることと思います。突発的なトラフィックのスパイク時には、この待ち時間がレイテンシ悪化や応答失敗率の上昇に直結してしまいます。そこで Low Priority Pod を用いて事前の "バッファ" の準備を実現しよう、というお話です。
Low Priority Pod は実際の業務ワークロードは実行しないダミー的な Pod で、リソースを消費してクラスタに空き容量がない状態を作りバッファ的なノードを前もって立ち上げさせる 役割を担います。需要が来た瞬間には、優先度の高い実ワークロードの Pod のために Low Priority Pod を停止させて即時に資源を獲得する。これがコンセプトになっています。
どういう仕組みか?
Priority と Preemption
Kubernetes は Pod に優先度 (PriorityClass) を設定でき、ある Pod がスケジュールできない場合、より低優先度の Pod を Preempt してリソースを確保します。これ自体はクラスタの基本機能で、スケジューラが低優先度を選んで SIGTERM -> Grace Period -> リソース解放という順に実行します。
バッファの補充
追い出された Low Priority Pod は Pending 状態になり、Cluster Autoscaler が Pending を検知して設定されたノード追加を進め、バッファを補充します。
どんなシナリオに向くか?
- SLA が厳しく、スパイクの頻度 / 規模が読みにくいアプリケーションやバックエンド API
- 短い猶予時間でのレスポンスが期待されるバッチ / イベント駆動処理
逆に、負荷が安定していて 10 分程度のノード起動ラグを許容できる環境では、仕組み維持の労力やコストの方が勝ってしまう恐れがあります。
トレードオフ
- コスト増:LPP は「何もしないのに資源を食う」ため、常時、余分なノード代がかかる
- サイズ設計が肝:LPP の requests (CPU / メモリ) を大きくし過ぎると過剰プロビジョニングとなり、一方で小さすぎると十分なバッファが確保できない
- LPP 停止時の動作調整:Grace Period やサイドカーなどへの挙動、ログ出力設計など
- 有効性の監視・調整:発動の頻度 / Pending 継続時間 / ノード追加の遅延等を継続観測し、LPP の replicas と requests を調整
YAML サンプルを分解!
元記事のサンプルを引用しつつ、説明を加えていきます。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: low-priority
value: 0
globalDefault: false
description: "Priority class for buffer pods"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: buffer-pods
namespace: default
spec:
replicas: 3 # バッファ深さ:後述の式で調整
selector:
matchLabels:
app: buffer
template:
metadata:
labels:
app: buffer
spec:
priorityClassName: low-priority
containers:
- name: buffer-container
image: registry.k8s.io/pause:3.9 # 軽量ダミー
resources:
requests:
cpu: "1000m" # 1 vCPUを確保
memory: "2Gi" # CAを確実に動かすため十分に大きく
limits:
cpu: "1000m"
memory: "2Gi"
-
PriorityClass.value: 0- 「低いが、過度に低くない」値を設定します。デフォルトの優先度 (多くの場合 1000 程度) より低いので、実 Pod に負けて追い出されやすいはずです。
- AKS のドキュメントでは PriorityClass が -10 未満の Pod はノードスケールアップのトリガーにならない旨の注意があり、過度に低い優先度を設定することは避けるべきとのことです。
-
replicas: 3- 用意する LPP の数。1 Pod = requests 分の資源を予約します。想定される突発トラフィックの立ち上がり量から決めていきましょう。
-
image: registry.k8s.io/pause:3.9- 何もしない / 起動が迅速な Pod を指定できれば、自前のコンテナイメージでも OK
-
resources.requests- ここが最重要。Pod 1 個あたりがどれだけリソースを確保しておくかを決めます。ノード 1 台を確実に占有したいなら、そのノードの allocatable に近いサイズにしておきます。
なお、下記も知っておくと良い情報でした。
- LPP が特定ノードに偏ってしまっていると、ノード依存している実 Pod が起動できない場合がある。
topologySpreadConstraintsやpreferredDuringSchedulingIgnoredDuringExecutionの node / zone spread で、広げて配置しておくと良い。 - Grace Period のデフォルトは 30 秒。実 Pod の即時性を優先したい場合、LPP 側の
terminationGracePeriodSecondsを短めにしておくと良い。
おわりに
ということで、AKS でも活用できる Low Priority Pod によるスパイク対策の仕組みを紹介しました。
実際の運用では監視なども考慮した十分な設計が必要とは思いますが、突発的な集中アクセスが来るコンシューマ向けアプリなどでは有効な対策になりそうですね!
Discussion