こんにちは。最近は引っ越しの準備に追われている@sanposhihoです。
先日開催された CloudNative Days Tokyo 2021 に登壇しました。これはトークの内容やその振り返りに関する記事です。
登壇内容
Kubernetes のコンポーネントの内、Pod をどの Node で起動するかという事を決定する scheduler というものが存在します。このトークでは実際に動作する小規模な scheduler を段階的に実装していく様子から、scheduler はどのように Pod をスケジューリングしているのか、どのように拡張性を提供しているのか等を内部実装の理解を通して学んでいきます。対象者は、(簡単にでも)Kubernetes の使用をした経験のある方としますが、scheduler に関する知識は不問です。
ということで Kubernetes Scheduler の内部実装をテーマにしてお話ししました。アーカイブをみることができるので気になる方は ↑ からぜひ。
内部の仕組みを解説する方法は多く存在します。
まず、挙がる手段としては内部実装を直接解説していく方法です。これは非常に有効的であり、確実な方法だと思います。
しかし、今回の Kubernetes scheduler を含め、規模の大きなものに限って言うと、その全体像を元のコードから直接紹介するのは、かなり複雑な説明となり、うまくやらないと時間も大きく要することになります。
そのため、この手の「内部の仕組みの紹介」というのはコードを読んでいき、直接そこから解説するというよりは、抽象化し、図をベースにして話すと言う方法が取られることが多い感じがします。
そして、今回僕は内部実装を図を中心に抽象化して説明するのではなく、“実際のコードを簡略化したコードから説明する”という手段を選んでみました。
それがタイトルにもある「自作して学ぶ」です。 このアイデア自体は今年の GW に流行った KOBA789 さんの「作って学ぶ RDBMS の仕組み」から得ています。
WEB+DB PRESS Vol.122 に特集「Rust で実装!作って学ぶ RDBMS のしくみ」を書いた
また、自作した Kubernetes Scheduler の実装を紹介していくだけだと単調なトークになりそうだったので、機能ごとに段階を踏んで説明をしていき、その時点の Scheduler をお試しで動作させることができる環境を整えて、みている方もトークの最中/後などに手を動かせるような形式を目指しました。(これも RDBMS 自作本にならっています)
というわけで題材のリポジトリがこちらです。
sanposhiho/mini-kube-scheduler
READMEをみていただければわかりますが、ブランチごとに一つの機能を追加していくような形で実装が分かれています。一部のブランチにはそのブランチの実装の解説が載っているので合わせて参考にしてみると良い復習になるかもしれません。
また、scenario という形でその時点での Scheduler のスケジュールの結果を実際に Pod や Node を作成してみてお試しできるような環境が整っています。scenario では以下の例のように client-go から API を叩けるようになっています。
func scenario(client clientset.Interface) error {
ctx := context.Background()
// create node0 ~ node9, but all nodes are unschedulable
for i := 0; i < 9; i++ {
suffix := strconv.Itoa(i)
_, err := client.CoreV1().Nodes().Create(ctx, &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node" + suffix,
},
Spec: v1.NodeSpec{
Unschedulable: true,
},
}, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("create node: %w", err)
}
}
klog.Info("scenario: all nodes created")
_, err := client.CoreV1().Pods("default").Create(ctx, &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod1"},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container1",
Image: "k8s.gcr.io/pause:3.5",
},
},
},
}, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("create pod: %w", err)
}
klog.Info("scenario: pod1 created")
// wait to schedule
time.Sleep(3 * time.Second)
pod1, err := client.CoreV1().Pods("default").Get(ctx, "pod1", metav1.GetOptions{})
if err != nil {
return fmt.Errorf("get pod: %w", err)
}
klog.Info("scenario: pod1 is bound to " + pod1.Spec.NodeName)
return nil
}
make start
を打つと Scheduler を起動し、記載した scenario が実行されます。
トークをしてみての感想
結果として、時間があっっっとう的に足りませんでした。元々計画していた分でもかなり内容を絞っていたつもりだったのですが、実装を行いスライドを書いて、試しに話してみると 1.5 時間以上かかりました。 そのため、色々削り、圧縮した発表になったため、かなり駆け足となってしまいました。
今のところ
— さんぽし/sanposhiho (@sanpo_shiho) October 16, 2021
「自作して学ぶKubernetes scheduler入門 ~スライド150枚越え、1時間半コース~ 」
になってしまっているけど内容を削るのは勿体無いという気持ちが
最後のQueue+EventHandlerの章まではどうしても説明したい気持ちがあって、そこまで行こうとした時の最小構成のつもりで話していた
— さんぽし/sanposhiho (@sanpo_shiho) November 5, 2021
(その目標がそもそも時間的に無理があったのかもしれない)(「ギリギリ時間内に"詰め込める"くらいまで内容を削る」みたいな考え方になったのが反省点)
という感じで、個人的に最低限、Scheduler と呼べるような機能を揃えるための最低限のラインを攻めるつもりで内容を絞りました。説明しきれなかった部分は例えば以下になります。
- Preemption
- Permit Plugin / wait on permit
- Reserve/UnReserve Plugin
- QueueSort Plugin
- Extender 全般
特に Preemption と Permit に関しては、元々しゃべるつもりのテーマであり、仕組みも面白い部分なので話せなかったことは少し残念です。Permit に関しては実装がリポジトリに載っています。(Preemption に関しては元々簡単な説明のみに留めるつもりだったため、実装はありません)
また、時間的な問題とは別の問題として、この「本家の実装を要約した実装を元に解説する」手法だと、結局コードを元に説明する必要があったため、(書籍や技術記事などと異なり、)スライドをベースに説明を行うトークとはそもそも相性が良くはないのかなとも感じました。
もう少し図などを織り交ぜればこの辺りは改善できたと思っています。
また、そもそも自分の説明力というかまとめ力みたいなものが足りていないことを実感したので修行していきたいと感じました。
終わりに
学生のうちにこう言った大きな素晴らしい舞台でトークをさせていただくことは、光栄の限りであり、イベント参加者としてはもちろん、自分のトークを通して多くの学びを得ることができました。
運営の皆様、トークを聞いてくださった皆様、ありがとうございました。