From Scared of the Repo to Mentoring People Into It
---
title: "From Scared of the Repo to Mentoring People Into It"
published: true
published_at: "2026-10-13T14:00:00Z"
description: "Going from scared of a big repo to mentoring people into open source in two years, told through the real merged PRs that built the path."
tags: "opensource, career, community, beginners"
canonical_url: "vandit.dev"
cover_image: "vandit.dev"
---
Everyone's first open source contribution starts the same way: you find a huge, famous project, open the code, and immediately feel like you wandered into the cockpit of a plane mid-flight. Thousands of files. Contributors who clearly do this in their sleep. A CONTRIBUTING.md that assumes you already know eleven things you don't. And a little voice going "you do not belong here, close the tab."
This post is about ignoring that voice, because ignoring it is basically the whole game. I went from "too scared to open a PR" to being a GSoC mentor two years later, and the path was way less mysterious than it looked from the bottom. Here's what actually worked, with the real PRs so you can see it wasn't magic.
Nobody starts by writing the cool part
Here's the thing they don't tell you. Your first contribution to a massive project is almost never the exciting distributed-systems wizardry. Mine, with Jenkins, was documentation and tooling. I helped rebuild the Jenkins docs, read by 11 million+ users, onto a proper versioned stack (GatsbyJS + Antora). That included unglamorous but real work like porting the whole site over and dragging a pile of plugins up to date.
And I remember thinking, at the time, "is docs even a real contribution?" Reader, it is the realest one. To fix the docs you have to understand the thing, understand its build, understand where people actually get confused. You end up learning the project sideways, through its edges, which is honestly a cheat code. By the time you're comfortable in the docs and the CI, the scary code isn't so scary anymore. You've been living next door to it for months.
That was GSoC 2023. I went in as a mentee, fully expecting to be found out as a fraud any day. Never happened. Turns out everyone's just figuring it out at their own layer.
> Note
>
> "Edges" means docs, tests, CI, tooling, build scripts, error messages. The stuff that surrounds the core logic. It's lower stakes, easier to get reviewed, and it teaches you the codebase's shape faster than staring at the hardest file in the repo ever will.
The trick is picking the boring door
If you're staring at kubernetes/kubernetes or prometheus/prometheus wondering how anyone contributes to that, here's my actual advice: don't look for the impressive change. Look for the annoying one.
- A flaky test everyone hates but nobody's fixed.
- A regression that needs a test to lock it down so it never comes back. That's literally what my first merge into core Kubernetes was: a regression test for a negative-index bug in json-patch.
- Some CI orchestration held together with tape. A big chunk of my Kueue and Prometheus test-infra work was exactly this.
These are unglamorous, which is precisely why they're wide open, and why maintainers love the person who takes them. Do enough of them and you start getting handed the real stuff. Eventually I was in the actual query layer of Prometheus, adding a limit parameter to /query and /query_range, and exposing a new metric in memcached_exporter. None of that was the first thing I touched. It was the tenth.
> The move
>
> "Contributing to Kubernetes" sounds like scaling a cliff. Up close it's a hundred small handholds. You never need to make the whole climb at once. You only need to reach the next hold: one flaky test, one missing regression test, one confusing doc.
Then one day you're on the other side
In 2024 I came back to GSoC. Not as a mentee. As a mentor.
Being on the other side of the table rewired how I saw the whole thing. Because now I was watching new contributors do the exact thing I did: freeze up at the size of the repo, apologize for "dumb questions," assume their small PR wasn't worth submitting. And I got to be the person going "no, submit it, it's good, this is literally how it works."
The gap between mentee and mentor felt like a canyon when I was staring up at it. Turns out it's one year and a pile of small, finished contributions. Nobody hands you a badge that says you belong. You just keep showing up until, one day, you're the one telling someone else they belong.
TL;DR
- The scared feeling on your first big-repo PR is normal and semi-permanent. Everyone feels it. Open the PR anyway.
- Start at the edges: docs, tests, tooling, CI. You learn the project sideways, and it's the fastest way in.
- Chase the annoying tasks, not the impressive ones. Flaky tests and regressions are how you get merged into projects like Kubernetes and Prometheus.
- Do enough small finished things and "mentee to mentor" stops looking like a canyon and starts looking like a staircase.
If you've been circling some giant repo waiting to feel ready, this is your sign. You're not going to feel ready. Pick the boring door and walk in.
Go deeper
- My merged PRs across these projects if you want to see the actual trail
- Kubernetes contributor guide and Prometheus contributing docs
- How Google Summer of Code works, if you want to get paid to do your first contributions
---
Fun fact: I still get the "do I belong here" flicker opening an unfamiliar codebase. The difference now is I know it's a liar. Go find one flaky test in a project you admire this week. That's the whole first step.
Mentions
Kubernetes の分散ジョブスケジューラ比較 はじめに この記事は、Slurm と Kubernetes のどちらかが優れていると主張するためのものではありません。両者の違いをできるだけ公平に並べたうえで、Kubernetes の上でジョブのスケジューリングがどう組み立てられているのかを整理することが目的です。 ! 筆者は Slurm も Kubernetes もどっちも好きだにゃーん。(=´ω`;=) Kubernetes に、4 つの Pod からなる分散学習ジョブを投げたとします。2 つは動いたのに、残り 2 つはノードに空きが無く、いつまでも Pending のまま、ということが起こり得ます。クラスタが壊れているわけではありません。 kube-scheduler は Pod を 1 つずつ正しく置くが、4 つ全部が置けるかは誰も確かめない 図のとおり、どの部品も自分の仕事を正しくこなしています。ジョブを扱うコントローラは 4 つの Pod を作り、kube-scheduler は GPU が空いているノードに Pod を 1 つずつ置きます。置けない Pod は Pending のまま待ちます。置かれた 2 つの worker は、そこで止まります。分散学習では、all-reduce のような集合通信を始める前に、全 worker が互いの居場所を確かめ合う rendezvous という集合の段階があります。4 つの GPU で学習するなら、4 つの worker が全員そろうまで、先に起動した worker はこの段階で待ち続けます。残り 2 つは Pending のままなので、全員はいつまでもそろいません。その間、置かれた 2 つは GPU を確保したまま 1 ステップも学習を進められず、設定した待ち時間を過ぎるとタイムアウトのエラーで終了します。再試行の設定によって Pod が作り直されても、空きが増えていなければ同じことを繰り返します。 問題は、4 つがそろうかをジョブ全体として判断する部品が、既定の設定の Kubernetes では動いていないことです。 HPC で広く使われる Slurm では、この状態はふつう起こりません。Slurm にはできて、追加の仕組みを入れていない素の Kubernetes にはできないことを、まず 5 つ並べ、D1 から D5 の ID を付けます。後の節では、各仕組みがどの ID を埋めるのかを、この ID で示します。 D1 から D5 は、どれもジョブ全体か利用者のアカウントを単位にした判断 D1 は、複数ノードのジョブを 1 つの単位として始めることです。Slurm は、要求したノードと GPU がすべてそろってから、それらを 1 つの割り当てとして確保してジョブを始めます。この割り当てを allocation と呼びます。素の Kubernetes は Pod を 1 つずつノードに置くので、一部だけ動いて残りが待つ状態が起こります。 D2 は、上限を超えたジョブを待ち行列で待たせることです。Slurm では、アカウントごとに GPU 数などの合計に上限を設けられます。この上限の 1 つが GrpTRES です。上限を超えるジョブも受け付けたうえで、PENDING のまま待たせます。Kubernetes の ResourceQuota は、上限を超える Pod の作成を断るだけで、順番を持った待ち行列にはなりません。 D3 は、大きなジョブを遅らせずに小さなジョブを先に流す backfill です。Slurm は、各ジョブに指定された実行時間の上限をもとに大きなジョブの開始予定時刻を計算し、それを動かさない範囲でだけ小さなジョブを先に始めます。素の Kubernetes には、ジョブ全体の開始予定時刻という考え方がありません。 D4 は、動いているジョブを止めて、作り直さずに再開することです。Slurm でプリエンプションの方式に suspend を選ぶと、ジョブのプロセスは一時停止され、resume するとそのまま続きから動きます。Kubernetes の Job を suspend すると、動いている Pod は削除されます。 D5 は、過去の使用量から優先度を決めることです。Slurm では、設定すれば fair-share を優先度の計算に入れられます。利用実績の記録をもとに、使いすぎた利用者の優先度を下げる仕組みです。Kubernetes の PriorityClass は、Pod ごとに決まった値を与えるだけです。 Slurm 側の D1 から D5 には、設定で有効にする機能も含まれます。5 つに共通するのは、判断の単位が Pod ではなく、ジョブ全体か利用者のアカウントだという点です。このうち D3 の backfill と D5 の fair-share は製品ごとの差が大きいため、この記事では D1、D2、D4 を中心に比べます。この記事では、Kubernetes の上でこの「ジョブ単位の判断」を後から足す Kueue・Volcano・KAI-Scheduler が、それぞれどこを受け持ち、内部がどうなっているのかを整理します。主張は公式ドキュメントで確認したものに限り、その場で出典を示します。Amazon EKS Distributed AI ワークショップの本に、Kueue を実際に使う章を追加しました。併せて読むと、手を動かして確かめられます。 全体像 Kueue は 2 番目の段を受け持つ。Volcano と KAI は 4 番目の段に入り、それぞれ待ち行列も持つ 名前 受け持ち 置き場所 kube-scheduler Pod を 1 つずつノードに置く Kubernetes の素の機能 Kueue ジョブ単位で、資源の枠に収まるかを決める kube-scheduler の手前 Volcano 対象に指定した Pod の配置を、kube-scheduler の代わりに担当する kube-scheduler と並ぶ選択肢 KAI-Scheduler 上と同じ位置で、GPU の分け合いや階層キューなど GPU クラスタ向けの機能が多い kube-scheduler と並ぶ選択肢 Slurm ノードの確保そのものがジョブの単位になっている Kubernetes とは別の世界 この図は、段ごとにどの製品が受け持つかを示したもので、全部を同時に入れる構成を示したものではありません。Kueue と Volcano・KAI-Scheduler は待ち行列の段で受け持ちが重なるので、この記事では基本的にどちらか一方を選ぶ構成として比べます。併用の余地も含めた関係は、各製品を見たあとの「Kueue と Volcano・KAI の関係」で整理します。 2 つの前提を確認したあと、この表の順に、仕組みと内部の構成を 1 つずつ見ていきます。 そもそも何を決める層なのか 分散ジョブを動かす仕組みは、2 つの違う問いに答えています。1 つは「このジョブを今動かしてよいか」、もう 1 つは「どの Pod をどのノードに置くか」です。この 2 つは別の問いで、同じ機構が両方を受け持つとは限りません。この区別を知らないと、Kueue と Volcano を同じ種類の仕組みだと誤解してしまいます。前者の「動かしてよいか」の判断を、Kueue では admission と呼びます。後に出てくる Volcano の admission webhook は、Kubernetes が API へのリクエストを検証・書き換えする仕組みのことで、Kueue の admission とは別物です。 gang scheduling とは 同期型の分散学習では、決まった数の worker がそろわないと学習が進みません。必要な数に足りないまま一部だけ動かしても、残りを待つ間、その分の資源が占有されるだけです。必要な数のタスクがそろうときだけ始め、そろわなければ始めないという考え方を、gang scheduling と呼びます。 素の Kubernetes の挙動 標準の kube-scheduler は、Pod を 1 つずつ見て置き場所を決めるだけです。ジョブという単位そのものを知らないので、4 つのうち 2 つだけ動いて残り 2 つを待つ状態が、何の異常もなく起こります。これが D1 の欠けている状態です。なお、kube-scheduler 自身にも PodGroup をまとめて置く gang scheduling が入りつつあります。v1.37 で Beta になりましたが既定では無効で、GenericWorkload の feature gate を有効にする必要があります。この記事では、既定の設定の kube-scheduler を前提にします。 どの部品も Pod を単位に動き、ジョブ全体をまとめて置く部品は無い kube-controller-manager の Job コントローラが Pod を作り、kube-scheduler が未配置の Pod を 1 つずつ選んでノードを決め、その結果を API server に書き戻します。kubelet は自分のノードに割り当てられた Pod を実行します。次に見る Kueue・Volcano・KAI-Scheduler は、kube-scheduler の手前に置くか、kube-scheduler の代わりに使うかして、ジョブ単位の扱いを持ち込む仕組みです。 4 つの部品の役割 部品 動く場所 何をするか この記事との関係 API server コントロールプレーン クラスタの状態を読み書きする唯一の窓口です。Job や Pod の定義を受け取って保存し、他の部品はすべてここを通して状態を見たり書いたりします。 Kueue の webhook も、ここに届いた作成のリクエストに割り込んで suspend: true を書き加えます。 kube-controller-manager コントロールプレーン 「あるべき状態」と「いまの状態」の差を埋める controller をまとめて動かすプロセスです。Job controller は、Job に書かれた数だけ Pod を作ります。 Pod を作る数は知っていますが、その Pod が全部置けるかは確かめません。 kube-scheduler コントロールプレーン まだノードが決まっていない Pod を 1 つずつ取り出し、空きと条件に合うノードを選んで、結果を API server に書き戻します。 判断の単位が Pod 1 つなので、D1 の状態が起こります。Volcano・KAI-Scheduler は、この部品の代わりに置き先を決めます。 kubelet 各ノード 自分のノードに割り当てられた Pod を API server から受け取り、コンテナを起動して動かし続けます。 置かれた worker が rendezvous で待ち続けるのは、kubelet が起動したコンテナの中です。 Kueue とは ! ここから先は、ノードを自動で足し引きする Karpenter を使う構成を前提にします。Karpenter は OSS ですが、AWS が開発を始めたもので、この記事では実質的に Amazon EKS を前提にした部品として扱います。そのため以降は、Amazon EKS Distributed AI ワークショップの本で今回実装した EKS 上の構成を前提に解説します。 Kueue が決めるのは、ジョブが Pod を作ってよい時期 Kueue は、ジョブが要求する GPU などの資源量を、quota と呼ぶあらかじめ決めた枠と比べ、枠に収まるジョブだけを通します。収まらないジョブは、Job や TrainJob のように suspend できるものなら、Pod を作らせずに待たせます。通した後にどのノードへ置くかは kube-scheduler が決めますが、Kueue は資源の種類と配置条件を表す ResourceFlavor で、候補のノードを絞れます。 ID で言うと、Kueue が埋めるのは D2 です。上限を超えたジョブを、Pod を作らせないまま待ち行列で待たせます。D1 については、ジョブ全体の資源量を枠と照合するところまでで、各 Pod をどこに置けるかは kube-scheduler が Pod ごとに判断します。D5 は、Admission Fair Sharing を設定すれば、同じ ClusterQueue を使う LocalQueue の間で過去の使用量を数え、通す順番に入れられます。D4 は埋めません。 Topology Aware Scheduling という機能を使うと、設定したラックやノードなどのトポロジーに沿って、Kueue が置き先の範囲を絞れます。waitForPodsReady は既定では無効です。有効にすると、通したジョブの全 Pod が決めた時間内に Ready にならないとき、Kueue はそのジョブを取り下げて待ち行列に戻し、戻すたびに次に通すまでの間隔を延ばします。上限回数を設定すれば、そこで打ち切ります。さらに blockAdmission を有効にすると、先に通したジョブの全 Pod が Ready になるまで、後続のジョブを通さなくなります。ただし、どちらも先のジョブの全 Pod を同時に置けることを保証する機能ではありません。 Kueue はジョブを止め、通すときに nodeSelector などを足すだけで、Trainer・kube-scheduler・Karpenter は変わらない 図は、ワークショップの本で実際に動かしている EKS 上の構成です。先に、図に出てくる Kueue の 3 つのオブジェクトを説明します。 オブジェクト 置き場所 役割 LocalQueue ジョブと同じ namespace 利用者がジョブを投入する窓口です。ジョブは kueue.x-k8s.io/queue-name ラベルで、投入先の LocalQueue の名前を指定します。LocalQueue 自身は枠を持たず、どの ClusterQueue に流すかだけを決めています。 ClusterQueue namespace に属さない、クラスタ全体のオブジェクト GPU などの枠を持ち、通してよいかを判断します。複数の namespace の LocalQueue が、同じ ClusterQueue を共有できます。 ResourceFlavor namespace に属さない、クラスタ全体のオブジェクト 枠の中身がどのノードの資源かを表します。この構成では、Karpenter の NodePool の名前をノードのラベルとして持たせています。 Kueue の実体は kueue-system namespace の kueue-controller-manager という 1 つの Deployment で、webhook とコントローラはこのプロセスの中にあります。CRD と webhook の設定はクラスタ全体に登録されますが、本で使っている Terraform の設定ファイル kueue.tf では、webhook が働く namespace を managed-by-kueue=true のラベルを付けたものだけに絞っています。TrainJob を 1 つ投入すると、図の番号の順に次のことが起きます。主体は動く部品、相手はその部品が手を入れる対象です。 番号 主体 相手 すること その後の状態 1 利用者 EKS の API server kueue.x-k8s.io/queue-name: gpu-ddp のラベルを付けた Kubeflow Trainer の TrainJob を kubectl apply で送る 作成のリクエストが API server に届く 2 Kueue の webhook API server が保存しようとしている TrainJob 作成を止めずに suspend: true を書き加える TrainJob は止まった状態で保存される。Trainer が作る JobSet も止まった状態のままで、Pod は作られない 3 kueue-controller-manager のジョブ用コントローラ 保存された TrainJob 必要な GPU や CPU の量をまとめた Workload を 1 つ作る Kueue が待ち行列で扱う単位ができる 4 kueue-controller-manager のスケジューラ LocalQueue を経て ClusterQueue に並んだ Workload いま使われている量に要求量を足し、ClusterQueue の ResourceFlavor ごとの枠に収まるかを判断する 収まらなければ TrainJob は止まったままで、枠が空くのを待つ 5 kueue-controller-manager 枠に収まった Workload の TrainJob Workload を admitted にし、ResourceFlavor の nodeLabels を nodeSelector として、tolerations があればそれも付けて、suspend を外す Workload は admitted、TrainJob は suspend: false になる 6 Trainer と JobSet・Job のコントローラ suspend が外れた TrainJob Trainer が、suspend の解除と 5 で付いた nodeSelector・tolerations を JobSet に伝え、JobSet と Job のコントローラが Pod を作る Pod ができ、置き先が未定のまま並ぶ 7 kube-scheduler 置き先が未定の Pod nodeSelector に合い、空きのあるノードに bind する。置けない Pod には Unschedulable の印を付ける 置けた Pod は動き出し、残りは Pending のまま 8 Karpenter Unschedulable の印が付いた Pod 条件に合う EC2 ノードを起動する ノードが Ready になると 7 に戻り、kube-scheduler が残りを bind する Kueue が手を入れるのは 2 から 5 までで、6 から 8 の Trainer・kube-scheduler・Karpenter の動き方は変わりません。止まっている TrainJob には Pod が無いので、Karpenter はそのためのノードを起動しません。この構成は、本の Kueue の章でも解説しています。 Volcano とは Volcano は、Kueue と違って配置そのものを担当します。schedulerName で Volcano を指定した Pod について、kube-scheduler の代わりに置き先を決めます。Volcano は、ジョブのタスクを 1 つずつ仮に割り当て、最低人数に届けばまとめて確定します。最低人数は、Volcano の Job である vcjob では minAvailable、Pod のまとまりを表す PodGroup では minMember という名前です。複数チームに資源を割り振る Queue というオブジェクトも持ちます。ID で言うと、Volcano は自分が配置する Pod について D1 と D2 を埋め、D4 は埋めません。D5 は、v1.16.0 で入った fairshare プラグインを設定すれば、同じ Queue を使う namespace の間で過去の使用量を数え、待っているジョブの順番に入れられます。 Volcano はキュー、gang、配置を自分の Pod について一通り持つ Volcano のサーバ側は、主に admission webhook・controller-manager・scheduler の 3 つです。admission webhook がジョブなどのリソースを検証・補完します。vcjob を使う場合は、controller-manager が最低人数を指定した PodGroup と、ジョブの Pod を作ります。scheduler は一定周期でセッションを開き、設定ファイルに書いた順に action を実行します。enqueue は Queue の空きを見て PodGroup をスケジューリング対象に進めてよいかを決め、allocate はタスクを 1 つずつ仮に割り当てます。preempt は優先度で場所を空け、reclaim は Queue の取り分を他の Queue から取り戻します。backfill は資源量を指定しない Pod を隙間に入れる機能で、Slurm の backfill とは別物です。Queue はプロセスではなく、テナントごとの重みと上限を書いたオブジェクトです。gang のプラグインは、最低人数を満たせば確定し、満たさなければ取り消すか、資源が空くのを待つ pipelined という中間状態に置きます。確定した割り当ては、Volcano の scheduler 自身が API server に反映します。 KAI-Scheduler とは KAI-Scheduler も、Volcano と同じく、kube-scheduler の代わりに配置を担当するスケジューラです。特徴は、1 枚の GPU を複数のジョブで分け合う機能を持つことです。チームごとの枠を何段にも入れ子にできる階層キューや、動いているジョブも含めて置き直し、空きをまとまった形にする consolidation も持っています。ID で言うと、D1 と D2 を埋め、D4 は埋めない点は Volcano と同じです。D5 は、Time Based Fairshare を有効にすれば、キューごとの過去の使用量を Prometheus に貯め、各キューの保証量を超えて使う分の取り分に反映できます。 配置を決める Deployment と、それを反映する Deployment が分かれている KAI-Scheduler では、pod-grouper という部品が PyTorchJob や JobSet などのワークロードから KAI-Scheduler 独自の PodGroup を作り、API server に書き込みます。これが gang の単位になります。scheduler は、階層キューの取り分と、Pod をなるべく少ないノードに詰める bin-packing を考えて置き先を決めます。そのうえで書くのは BindRequest というオブジェクトだけで、実際の bind はそれを読んだ別デプロイの binder が行います。この点は、スケジューラのプロセス自身が bind まで行う Volcano とは異なります。GPU を分け合うときは、既定のモードでは共有する GPU を専用の reservation Pod が押さえ、各 Pod が annotation で要求した割合はスケジューラが勘定します。このモードでは GPU メモリは分離されず、HAMi-core を使うモードを選ぶとメモリの上限がかかります。 Kueue と Volcano・KAI の関係 Kueue と Volcano・KAI-Scheduler は、受け持つ段の一部が重なる技術です。重なるのは待ち行列と枠、つまり D2 で、Volcano と KAI-Scheduler も自分の Queue を持ちます。重ならないのは配置で、Kueue は配置をしません。Kueue の公式ドキュメントは、Pod をノードに置くのは kube-scheduler の責任だとし、成熟した部品の機能を作り直さないことを設計の原則に挙げています。 そのため、この記事では代表的な構成として次の 2 通りを比べます。1 つは Kueue と kube-scheduler の組み合わせで、待ち行列を Kueue が、配置を kube-scheduler が受け持ちます。もう 1 つは Volcano か KAI-Scheduler を単体で使う方法で、待ち行列と配置の両方を 1 つの製品が受け持ちます。この 2 通りは同じ問題への別の答えなので、その意味では競合します。後の表で Kueue と Volcano・KAI を横に並べているのは、この 2 通りを比べているためです。 併用できないわけではありません。Kueue はジョブの開始を suspend などで止め、置き先の条件を足しますが、Pod をどのスケジューラが置くかは Pod の schedulerName で決まります。そのため、Kueue で通したジョブの配置を KAI-Scheduler に任せる構成は、仕組みの上では組めそうに見えます。ただし、待ち行列と枠を 2 か所で管理することになり、両者の枠をそろえる必要があります。2026 年 10 月 23 日の時点で、Kueue・Volcano・KAI-Scheduler のどのリポジトリのドキュメントにも、この組み合わせ方の手順は見つかりませんでした。 それでも EKS では、併用を考える理由が 1 つあります。KAI-Scheduler の設計提案によると、KAI-Scheduler は枠を超えて待たせているジョブでも Pod の作成を止めないので、Pod は作られ、Unschedulable の印が付きます。Karpenter はこの印を需要とみなすので、動かないジョブのために GPU ノードが立つことがあると、この文書は指摘しています。同じ文書は、Kueue と Volcano はこの Pod を Karpenter から隠すとも書いています。これを直す提案は、まだ Proposed の段階です。Karpenter と組むなら、ジョブを Pod の手前で止める Kueue を前に置く意味があるかもしれません。ただし Kueue が通したあとに KAI-Scheduler の枠で止まれば同じことが起きるので、両者の枠をそろえられるかが鍵になります。この組み合わせは、ワークショップの本の Advanced として今後確かめます。 Slurm とは Slurm の仕組みは「AWS ParallelCluster ワークショップ」でくわしく解説しているので、ぜひ参照してください。 Slurm は資源をジョブ単位で渡し、Kubernetes は Pod のまとまりを表すオブジェクトを足してそこへ近づける 資源の割り当てが先に 1 回で決まり、タスクはその中で始まる Slurm と Kubernetes 系の違い 表に入る前に 1 つ注意があります。Slurm が「gang scheduling」と呼ぶ機能は、ここまで使ってきた意味と強調点が違います。Slurm の gang scheduling は、同じノードを複数のジョブで時間を分けて使う機能で、1 つのジョブに属するタスクはまとめて一時停止・再開されます。この記事がここまで「全部そろうまで始めない」という起動の話をしてきたのに対し、Slurm のこの機能は「始めたあとの時間の分け方」の話で、D4 の suspend を使って成り立っています。 ID できること Slurm Kueue と kube-scheduler Volcano / KAI の単体 D1 最低人数がそろう場合だけ配置を確定する ◯ ジョブの単位そのもの △ ジョブ全体の資源量を quota と照合するが、各 Pod を置けるかは kube-scheduler が個別に判断する ◯ Volcano の minAvailable、KAI の batch-min-member D2 上限を超えたジョブを待ち行列で待たせる ◯ アカウントごとの上限と QOS ◯ ClusterQueue の quota ◯ Volcano の Queue、KAI の Queue D4 動いているジョブのプロセスを一時停止し、同じ状態で後から再開する ◯ scontrol suspend と resume ✕ Kueue が使う Job の suspend は動いている Pod を削除する ✕ Volcano の vcctl job suspend は Pod を削除し、再開時に Pod を作り直す。KAI は、対応する機能を公式ドキュメントで確認できず D4 の ✕ は、Kubernetes 側が劣っているとだけ読むと誤解します。Slurm の suspend はプロセスに停止のシグナルを送るだけで、止まったジョブはノードのメモリに残ったままだと、slurm.conf の PreemptMode の説明に書かれています。プロセスが生きている以上、GPU 上に確保したメモリも残ると考えられるので、止めても後から来たジョブがその GPU のメモリを使えるとは限りません。また Kubernetes では、spot の中断や Karpenter の縮小でノードそのものが消えるので、その場で止めた状態もノードと一緒に消えます。そのため Kubernetes では、学習コードが途中経過をチェックポイントとして共有のストレージに保存し、Pod を消されたら別のノードで続きから再開する方式が基本になります。Kueue が通したジョブを取り下げると Pod は消えるので、続きから再開できるかは、学習コードが途中経過を保存しているかで決まります。プロセスごと保存する仕組みとしては、kubelet の checkpoint API と、GPU の状態を保存する NVIDIA の cuda-checkpoint があります。ただし cuda-checkpoint は UVM メモリなど一部のメモリに対応していないため、この記事では扱いません。 この表の内容は、各製品のドキュメントを確認した時点のものです。バージョンが進むと変わる可能性があるため、最終判断は参考に挙げた一次資料の最新版で確認してください。 Kueue の最近のアップデート Kueue は、機能を足すマイナー版がおよそ 2 か月ごと、修正のパッチ版がおよそ毎週出ていて、2026 年 10 月 23 日の時点の最新は v0.20.3 です。ここでは、この記事の D1 の弱点に関わる、v0.20.0 の 2 つの機能を挙げます。どちらも alpha で既定は無効なので、使うには feature gate を有効にする必要があります。 機能 何に使えるか KueueDRADeviceFeasibility 通す前に、ノードごとに DRA のデバイスの空きを確かめ、置けないジョブのために quota を確保しないようにする。対象は DRA で GPU を要求するジョブで、device plugin の nvidia.com/gpu で要求するジョブや、CPU・メモリ・taint が原因で置けない場合は対象外 DynamicQuotaOrchestration 見つかった容量を、配下の Cohort と ClusterQueue に nominalQuota の比で配り、status.effectiveQuotas に書く。ノード数が変わるクラスタで、ClusterQueue の枠を実際の容量に追従させる どちらも、「Kueue が通しても、置けるとは限らない」という D1 の弱点に手を入れる変更です。何が困るのかを具体的に書くと、次の 2 つです。 1 つ目は、デバイスが足りないのに通してしまうことです。Topology Aware Scheduling を使わない構成では、Kueue は設定した枠からいま使われている量を引いた残りの数だけを見て、その GPU がどのノードにあるかは見ません。たとえば枠が 8 枚分残っていて、実際の空きも 8 枚あっても、それが 8 台のノードに 1 枚ずつ散らばっていれば、1 台に 8 枚を要求するジョブはどのノードにも置けません。それでも数の上では枠に収まるので、Kueue はジョブを通し、その 8 枚分の quota を確保します。Pod は Pending のまま動かず、確保された quota のせいで、本当は置ける別のジョブが待たされます。KueueDRADeviceFeasibility は、通す前にノードごとの空きを確かめることで、この取り違えを減らします。 2 つ目は、quota の数と実際の容量がずれることです。quota は人が書いた固定の数なので、spot の中断や Karpenter の縮小でノードが減っても、そのまま残ります。実際より大きな枠のまま通したジョブは、置き場所が無く Pending になります。逆にノードを増やしても枠を書き換えなければ、空いた GPU を使えません。DynamicQuotaOrchestration は、見つかった容量に合わせて枠を配り直すので、この書き換えを手でしなくて済むようになります。nominalQuota は、配分の比として引き続き人が決めます。 どちらもまだ alpha なので、本番で頼る前に、自分の Kueue の版と feature gate の状態を確かめてください。 なお、EKS で KueueDRADeviceFeasibility を使い、ノードの追加を Karpenter に任せるなら、Karpenter も DRA の要求を理解する必要があります。Karpenter は既定では DRA の要求を無視します。NVIDIA GPU を DRA で扱う DRANVIDIAGPU の feature gate は、v1.15 で alpha として入ったばかりで既定は無効です。有効にするには --ignore-dra-requests=false の設定も要ります。alpha で既定は無効なので、この記事では本番向けの構成には含めません。DRA を使う構成は、今後ワークショップの本で検証します。 まとめ Kueue は通してよいかだけを決めて D2 を埋め、配置は kube-scheduler に任せます。Volcano と KAI-Scheduler は配置そのものを担当し、D1 と D2 を自分の Pod について埋めます。Kueue と kube-scheduler の組み合わせと、Volcano か KAI-Scheduler の単体は、待ち行列の段で重なる別の選択肢です。ただし Karpenter と組む EKS では、Kueue を前に置いて KAI-Scheduler に配置を任せる併用も検討の余地があり、これは今後確かめます。D5 は、3 つとも設定すれば過去の使用量を優先度に入れられますが、数える単位と効く範囲は製品ごとに違います。今回確認した範囲では、どれも D4 のように止めたプロセスをそのまま再開することはできません。 Ray がどこに位置するのか気になった人もいるかもしれません。Ray は、ここまでの仕組みで資源が確保されてジョブが動き出したあと、その一段上の層で、確保済みの GPU にタスクを割り振る役目を担います。Ray の autoscaler を使う構成では、足りない分の worker Pod を Ray の側から要求することもあります。 参考 Kueue の概要 Kueue の Topology Aware Scheduling waitForPodsReady の設定 Volcano のコア概念 Volcano のアーキテクチャ KAI-Scheduler の README Slurm のクイックスタート Slurm のアーキテクチャ概要 Slurm の Gang Scheduling Slurm のリソース上限 Slurm のスケジューリング設定 (backfill) Slurm の preemption Slurm の Multifactor Priority Kubernetes の ResourceQuota Kubernetes の Job の suspend Kubernetes の Pod の優先度 kube-scheduler の仕組み Amazon EKS Distributed AI ワークショップ - Kueue を実際に使う章を追加しています
From Scared of the Repo to Mentoring People Into It --- title: "From Scared of the Repo to Mentoring People Into It" published: true published_at: "2026-10-13T14:00:00Z" description: "Going from scared of a big repo to mentoring people into open source in two years, told through the real merged PRs that built the path." tags: "opensource, career, community, beginners" canonical_url: "vandit.dev" cover_image: "vandit.dev" --- Everyone's first open source contribution starts the same way: you find a huge, famous project, open the code, and immediately feel like you wandered into the cockpit of a plane mid-flight. Thousands of files. Contributors who clearly do this in their sleep. A CONTRIBUTING.md that assumes you already know eleven things you don't. And a little voice going "you do not belong here, close the tab." This post is about ignoring that voice, because ignoring it is basically the whole game. I went from "too scared to open a PR" to being a GSoC mentor two years later, and the path was way less mysterious than it looked from the bottom. Here's what actually worked, with the real PRs so you can see it wasn't magic. Nobody starts by writing the cool part Here's the thing they don't tell you. Your first contribution to a massive project is almost never the exciting distributed-systems wizardry. Mine, with Jenkins, was documentation and tooling. I helped rebuild the Jenkins docs, read by 11 million+ users, onto a proper versioned stack (GatsbyJS + Antora). That included unglamorous but real work like porting the whole site over and dragging a pile of plugins up to date. And I remember thinking, at the time, "is docs even a real contribution?" Reader, it is the realest one. To fix the docs you have to understand the thing, understand its build, understand where people actually get confused. You end up learning the project sideways, through its edges, which is honestly a cheat code. By the time you're comfortable in the docs and the CI, the scary code isn't so scary anymore. You've been living next door to it for months. That was GSoC 2023. I went in as a mentee, fully expecting to be found out as a fraud any day. Never happened. Turns out everyone's just figuring it out at their own layer. > Note > > "Edges" means docs, tests, CI, tooling, build scripts, error messages. The stuff that surrounds the core logic. It's lower stakes, easier to get reviewed, and it teaches you the codebase's shape faster than staring at the hardest file in the repo ever will. The trick is picking the boring door If you're staring at kubernetes/kubernetes or prometheus/prometheus wondering how anyone contributes to that, here's my actual advice: don't look for the impressive change. Look for the annoying one. - A flaky test everyone hates but nobody's fixed. - A regression that needs a test to lock it down so it never comes back. That's literally what my first merge into core Kubernetes was: a regression test for a negative-index bug in json-patch. - Some CI orchestration held together with tape. A big chunk of my Kueue and Prometheus test-infra work was exactly this. These are unglamorous, which is precisely why they're wide open, and why maintainers love the person who takes them. Do enough of them and you start getting handed the real stuff. Eventually I was in the actual query layer of Prometheus, adding a limit parameter to /query and /query_range, and exposing a new metric in memcached_exporter. None of that was the first thing I touched. It was the tenth. > The move > > "Contributing to Kubernetes" sounds like scaling a cliff. Up close it's a hundred small handholds. You never need to make the whole climb at once. You only need to reach the next hold: one flaky test, one missing regression test, one confusing doc. Then one day you're on the other side In 2024 I came back to GSoC. Not as a mentee. As a mentor. Being on the other side of the table rewired how I saw the whole thing. Because now I was watching new contributors do the exact thing I did: freeze up at the size of the repo, apologize for "dumb questions," assume their small PR wasn't worth submitting. And I got to be the person going "no, submit it, it's good, this is literally how it works." The gap between mentee and mentor felt like a canyon when I was staring up at it. Turns out it's one year and a pile of small, finished contributions. Nobody hands you a badge that says you belong. You just keep showing up until, one day, you're the one telling someone else they belong. TL;DR - The scared feeling on your first big-repo PR is normal and semi-permanent. Everyone feels it. Open the PR anyway. - Start at the edges: docs, tests, tooling, CI. You learn the project sideways, and it's the fastest way in. - Chase the annoying tasks, not the impressive ones. Flaky tests and regressions are how you get merged into projects like Kubernetes and Prometheus. - Do enough small finished things and "mentee to mentor" stops looking like a canyon and starts looking like a staircase. If you've been circling some giant repo waiting to feel ready, this is your sign. You're not going to feel ready. Pick the boring door and walk in. Go deeper - My merged PRs across these projects if you want to see the actual trail - Kubernetes contributor guide and Prometheus contributing docs - How Google Summer of Code works, if you want to get paid to do your first contributions --- Fun fact: I still get the "do I belong here" flicker opening an unfamiliar codebase. The difference now is I know it's a liar. Go find one flaky test in a project you admire this week. That's the whole first step.