kakudooo docs

Preemption と k8s

k8s でシステムを運用する際、スケジューリングした Pod を安定稼働させたいことがある。 Evictionに加えてPreemptionという仕組みがあることを知って、調べていたところ、どうやら k8s 特有の機能ではなくシステム一般的にマルチタスクを扱うための手法の一つであることが分かった。 理解を深めるためにPreemptionという用語をまず整理した上で、k8s のPreemptionについて調査した内容をまとめておくことにする。

Preemption について

マルチタスクのコンピュータシステムが実行中のタスクを一時的に中断する動作であり、基本的にそのタスク自体の協力は不要で、後でそのタスクを再実行するという意味も含む。このような動作をコンテキストスイッチと呼ぶ。通常、保護されたタスクか、システムの一部であるプリエンプティブスケジューラが行う。それらは、システム内の他のタスクに割り込み、後でそれらタスクを再開させることができる。「プリエンプト」とは「先取りする、差し替える」の意。

引用: wikipedia

マルチタスクのコンピューターシステムにおいて、実行中のタスクをスケジューリングする仕組みの一つ。 実行中のタスク(プログラム)を強制的に一時停止し、他のプログラムの実行に切り替えるもの。 マルチタスクを扱うシステムの具体例としては、OS が挙げられる。OS のタスクスケジューラーは実行中のタスク(プログラム)に対して上記を行う。

参考: プリエンプション

Preemptive と Non-Preemptive

いずれも一つの計算リソース(例: CPU)で複数の処理を進めるマルチタスクを管理する方式のこと。

Preemptive

計算リソースを OS が管理する方式で、OS が計算リソースを実行中のプログラムへ順番に割り当てて実行させる。 OS のカーネルに組み込まれたタスクスケジューラーにより、実行の順番が制御される。 一般的な OS では、プログラムの実行について優先度を設定することで、実行時間に優劣をつけることができる。

参考: プリエンプティブ

Non-Preemptive

OS が計算リソースを管理せず、実行中のタスク(プログラム)が任意に実行権を受け渡す方式。

計算リソースの解放は、タスク(プログラム)の実装に依存する。

というメリットはあるが、タスク(プログラム)の実装によっては、不具合や処理の遅延などが発生する余地があるというデメリットもある。

現在は、preemptive なマルチタスク管理が主流になっている。

参考: ノンプリエンプティブマルチタスク

k8s と Preemption

k8s にも Preemption の仕組みが実装されている。

k8s におけるスケジューリングプロセスとは、kube-scheduler が対象の Pod を配置すべき最適な Node を決定する処理のことである。(これにより、各 Node 上の kubelet が Pod を起動できるようになる。)

Pod の配置や継続的な実行に関わる重要な概念として、以下の 2 つがある。(今回、Eviction については割愛)

ここで言う Preemption とは、ある Node に優先度の高い Pod の配置がスケジュールされた際、同じ Node の優先度の低い Pod を停止させた上で優先度の高い Pod を配置するプロセスのことを指す。

参考: Scheduling Eviction

運用上の注意点

Node に配置した Pod を安定稼働するにあたって、Preemption されると困ることがある。 例えば、実行時間のかかる CronJob やシステムクリティカルな CronJob である。

Eviction と同じく、Preemption にも Pod の予期せぬ Terminate を回避する方法が用意されている。

  1. PriorityClass を作成する
  2. 作成する Pod に priorityClassName 属性を定義する

ことで、

についての挙動を設定できる。

以下は、Pod 自体の優先度を設定する例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

以下は、他の Pod を Preemption するかどうかの設定例

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "This priority class will not cause other pods to be preempted."
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
  priorityClassName: high-priority-nonpreempting

参照: Pod Priority and Preemption

まとめ

k8s のスケジューリング機能もシステム一般的なタスクスケジューリングの仕組みを元に、実装されていることが分かった。

OS のタスクスケジューリングでは

するのに対して k8s のスケジューリングでは

するという対応関係で理解をした。

優先度の細かい管理方法については、以下の記事が参考になりそう。

https://qiita.com/everpeace/items/6152db29e2f609464800