ステータス: ドラフト (2026-10-17)、ソース + 読み取り専用の実測。 基準は KubeVirt
v1.9.0(コミット79d34c17)です。「実測」の表示は、v1.9.0 がインストールされた小さなクラスターで閲覧のみにより確認した値です。ビルドとテストは実行していません(下記の「制限」)。
KubeVirt ソースコードリーディング 1: リポジトリマップ
目次
- 1. イントゥスイッション: Kubernetes に紐づく 5 つのプログラム
- 2. 図: フォルダと実行場所
- 3. フォルダを一つずつ
- 4. 実測: クラスターで確認した 5 つのプログラム
- 5. どこから読むべきか
- 6. 制限
- 参考
1. イントゥスイッション: Kubernetes に紐づく 5 つのプログラム
KubeVirt は Kubernetes を改変しません。CRD(VirtualMachine、VirtualMachineInstance など)を追加し、その CRD を監視するプログラムをいくつかクラスター上で起動するだけです。リポジトリを読む際も、この 5 つのプログラムを基準に分類すれば迷子になりません。
| プログラム | 実行場所 | 一行の役割 |
|---|---|---|
| virt-operator | コントロールプレーン (Deployment) | 残りのコンポーネントをインストールし、基盤を立ち上げる |
| virt-api | コントロールプレーン (Deployment) | Webhook でリクエストを検証し、console や start などのサブリソースを提供する |
| virt-controller | コントロールプレーン (Deployment、リーダー 1 つ) | VM を監視して VMI を、VMI を監視して Pod を作成する |
| virt-handler | ノードごと (DaemonSet) | 自ノードに割り当てられた VMI を実際のドメインに合わせる |
| virt-launcher | VM ごとに 1 つ (Pod) | VMI を libvirt ドメイン XML に変換し、QEMU を起動する |
「学生 VM が起動するまで」第 2 編(../kubevirt-lab-vm/02-kubevirt-inside.md)がこれら 5 つの役割をユーザーの視点で説明しているならば、この記事では
その役割がリポジトリのどのフォルダにあるのかを見ていきます。
2. 図: フォルダと実行場所
3. フォルダを一つずつ
cmd/ — エントリポイントは薄い
cmd/ 以下のフォルダにはそれぞれ main が一つずつあり、大部分は数行程度です。virt-controller はこれがすべてです
(cmd/virt-controller/virt-controller.go:26).
func main() {
watch.Execute()
}
本体は pkg/virt-controller/watch/application.go の Execute()
(:303)です。フラグを読み込み、インフォーマーを作成し、リーダー選挙に勝利すると onStartedLeading()
(:609)で VM・VMI・マイグレーションなどのコントローラーをゴルーチンで実行します。例外はノード側の 2 つのプログラムです。 cmd/virt-handler/virt-handler.go の Run()
(:209)と cmd/virt-launcher/virt-launcher.go の
main()(:347)は、セットアップコードが長いため
cmd/ の中にも読みどころが多くあります。第 4・5 編でこれら 2 つを追っていきます。
cmd/ には 5 つ以外にも小さなヘルパーが多くあります。 virt-launcher-monitor(launcher をラップして最後まで見守る PID 1)、 virt-chroot
(handler が別の名前空間でコマンドを実行するとき)、 virt-tail(シリアルコンソールログをストリーミングするサイドカー)、 virtctl(ユーザー CLI)などです。
pkg/virt-* — 5 つのコンポーネントの本体
| フォルダ | 最初に見るファイル | 扱う編 |
|---|---|---|
pkg/virt-api |
api.go, webhooks/validating-webhook/admitters/, rest/ |
2 |
pkg/virt-controller |
watch/vm/vm.go, watch/vmi/lifecycle.go, services/template.go |
3 |
pkg/virt-handler |
vm.go, cache/domain-watcher.go, device-manager/ |
4·7 |
pkg/virt-launcher |
virtwrap/manager.go, virtwrap/converter/converter.go |
5 |
pkg/virt-operator |
resource/generate/components/(CRD・Webhook・DaemonSet をコードで作成する) |
2 |
pkg/ の残りの部分 — 共通で使われる部品
コンポーネントフォルダ以外のパッケージは、「誰が呼び出すか」という観点で見ると分かりやすいです。 pkg/network は virt-handler(Pod ネットワークの準備)と
virt-launcher(ドメインの <interface>)が一緒に使用します。 pkg/controller はインフォーマーファクトリーや期待値(expectations)などのコントローラー共通ツール、 pkg/virt-config は KubeVirt CR の設定とフィーチャーゲート、 pkg/hypervisor はハイパーバイザーごとの差異(KVM と新しく追加された
MSHV)です。第 3 編のメモリオーバーヘッドの計算はこの pkg/hypervisor/kvm にあります。 pkg/libvmi は VMI を作成するビルダーであり、テストコードで多く使用されます。
staging/src/kubevirt.io/ — 別途配布される 2 つのモジュール
API タイプ(kubevirt.io/api)とクライアント(kubevirt.io/client-go)は他のプロジェクトでも流用されるため、独立した Go モジュールになっています。Kubernetes と同様の方法で、ルートの go.mod がこれらを replace としてリポジトリ内の staging/ フォルダにまとめています。そのため、VM のフィールドを修正する PR は staging/src/kubevirt.io/api/core/v1/types.go を修正し、生成コードを再作成して一緒にコミットします(第 2 編)。
tests/ — 実際のクラスターで実行される機能テスト
単体テストはコードの隣にある *_test.go であり(実測: pkg/ 以下 717 個)、Ginkgo・Gomega で記述され、フェイククライアントで実行されます。 tests/ は
機能テスト(functional test) です。実際のクラスターに KubeVirt をインストールし、VM を起動して検証します。テストごとに decorators.SigCompute
のようなラベルが付いており、SIG ごとに選択して実行できます。
hack/ と Makefile — ビルドはコンテナ内で
Makefile のほぼすべてのターゲットが hack/dockerized を経由します。ビルダーコンテナ内で Bazel(または KUBEVIRT_NO_BAZEL=true の場合は go)でビルドするため、手元のマシンには Docker(または Podman)と rsync さえあれば十分です。
| コマンド | 実行内容 | 場所 |
|---|---|---|
make |
フォーマット調整 + Bazel ビルド + マニフェスト | Makefile:17 |
make generate |
生成コードの全面的な再作成(第 2 編) | Makefile:53 |
make test / make go-test |
単体テスト(Bazel / go) | :93 · :90 |
make cluster-up · cluster-sync |
kubevirtci を使用して VM 内に開発用クラスターを立ち上げ、自分のビルドをデプロイする | :173 |
make functest |
そのクラスターでの機能テスト | :101 |
機能テストの一部のみを実行する場合は、 FUNC_TEST_ARGS='--focus-file=vmi_networking' make functest のように Ginkgo のフィルターを渡します
(docs/getting-started.md の Testing セクション)。開発用クラスターはノードを VM として起動するため、 ネストされた仮想化が可能な Linux マシンが必要です。Mac ではビルドはできても、 cluster-up は困難です。
4. 実測: クラスターで確認した 5 つのプログラム
v1.9.0 がインストールされた小さなクラスターの kubevirt 名前空間を読み取りました(2026-10-17)。
| 対象 | 数量 | イメージ |
|---|---|---|
| virt-operator | Deployment 2/2 | quay.io/kubevirt/virt-operator:v1.9.0 |
| virt-api | Deployment 2/2 | quay.io/kubevirt/virt-api:v1.9.0 |
| virt-controller | Deployment 2/2 | quay.io/kubevirt/virt-controller:v1.9.0 |
| virt-exportproxy | Deployment 2/2 | quay.io/kubevirt/virt-exportproxy:v1.9.0 |
| virt-handler | DaemonSet 5/5(各ワーカーノード) | quay.io/kubevirt/virt-handler:v1.9.0 |
| virt-launcher | 稼働中の VM 数と同数(3 個) | VMI status の launcherContainerImageVersion: …virt-launcher:v1.9.0 |
と同じ名前空間に virt-template-apiserver·virt-template-controller(v0.2.2)も見られます。これは VM テンプレートを扱う独立したプロジェクトであり、リポジトリには hack/virt-template/sync.sh としてマニフェストのみが同期されます。このシリーズでは扱いません。
フォルダ別の規模。 テストと生成ファイルを除いた Go の行数です(v1.9.0、 find … ! -name '*_test.go' ! -name '*generated*').
| フォルダ | ファイル | 行数 | テストファイル | テスト行数 |
|---|---|---|---|---|
pkg/virt-launcher |
119 | 26,910 | 128 | 32,700 |
pkg/virt-handler |
74 | 24,520 | 62 | 17,174 |
pkg/virt-controller |
42 | 23,313 | 44 | 34,602 |
pkg/virt-operator |
57 | 21,260 | 48 | 14,596 |
pkg/storage |
45 | 18,007 | 47 | 24,614 |
pkg/virt-api |
49 | 13,514 | 43 | 20,893 |
staging/…/api |
55 | 11,831 | 2 | 52 |
pkg/network |
75 | 9,913 | 74 | 16,213 |
virt-controller は 42 ファイルに 23,000 行とファイルサイズが大きいです。 watch/vm/vm.go の一つは 3,640 行もあります。逆に virt-launcher は小さなファイルが多くなっています。テストの行数が本文よりも多いフォルダが 4 つあることも注目に値します。レビューで「まずテストを見せてほしい」という意見がよく出る理由です(第 8 編)。
ログからソースへジャンプする方法。 KubeVirt コンポーネントのログは JSON であり、 "pos" フィールドに 파일:줄 が含まれています。実測したログの
pos を v1.9.0 のソースと突き合わせたところ、すべて一致していました。例えば virt-handler の "pos":"vm.go:1459" は
pkg/virt-handler/vm.go:1459 の
VMI is in phase: … | Domain status: … のログ行であり、virt-launcher の "pos":"manager.go:1651" は Domain defined. です。
初めてコードを読むときは、 ログを 1 行選び、その行番号から 逆方向にたどるのが最も早いです。ただし、バージョンが異なれば行番号も異なるため、クラスターのバージョンと一致するタグを開いてください。
5. どこから読むべきか
VM が 1 つ起動するフローに沿って読む順序を推奨します。各段階で「入力は何か、そして何を作り出すのか」だけを押さえれば十分です。
staging/src/kubevirt.io/api/core/v1/types.go—VirtualMachine(1942 行)、VirtualMachineInstance(47 行)、phase リスト(1098 行)。 フィールド名がそのまま YAML の名前になります。pkg/virt-controller/watch/vm/vm.goのexecute()(357 行)→sync()(3215 行)→syncRunStrategy()(970 行)。VM → VMI。pkg/virt-controller/watch/vmi/lifecycle.goのsync()(66 行)とservices/template.goのrenderLaunchManifest()(395 行)。VMI → Pod。pkg/virt-handler/vm.goのexecute()(321 行)→sync()(1433 行)。ノードにおける Pod → ドメイン。pkg/virt-launcher/virtwrap/manager.goのSyncVMI()(1380 行)とconverter/converter.goのConvert_v1_VirtualMachineInstance_To_api_Domain()(967 行)。ドメイン → QEMU。
docs/components.md も良い出発点ですが、バージョンが少し古いです。Pod 内で動作する libvirt デーモンを libvirtd と記述していますが、
v1.9.0 の virt-launcher はモノリシックな libvirtd の代わりに virtqemud を起動します
(cmd/virt-launcher/virt-launcher.go:432)。
実測でも Pod 内のプロセスは virtqemud と virtlogd でした(第 5 編)。
6. 制限
- ビルドとテストを実行していません。
makeはビルダーイメージと Bazel の依存関係をダウンロードする必要があり、make cluster-upはネストされた仮想化が可能な Linux マシンが必要です。必要なものは Docker または Podman、rsync、および数十 GB のディスクです(docs/getting-started.mdの Building セクション)。テスト時間とビルド時間は 未実測です。 - 行数は規模を推測するための数字にすぎません。空行とコメントをカウントし、生成ファイルは名前に
generatedが含まれているもののみを除外しました。 - クラスターの実測は特定のクラスター、特定の日付の状態です。Deployment の数(2)はデフォルト値ではなく、そのクラスターの設定である可能性があります。
一言メモ。 9,000 個ものファイルがあるなんて怖じけづきますか? しかし、VM が 1 つ通過するパスには 5 つのプログラムしかありません。ログから
posを 1 行選び、その行から逆向きに読んでみてください。マップがすぐに頭の中に描かれます!
参考
すべて 2026-10-17 に閲覧しました。
- KubeVirt v1.9.0 ソース: https://github.com/kubevirt/kubevirt/tree/v1.9.0
- 開発ガイド(ビルド・テスト・開発用クラスター): https://github.com/kubevirt/kubevirt/blob/v1.9.0/docs/getting-started.md
- コンポーネントドキュメント: https://github.com/kubevirt/kubevirt/blob/v1.9.0/docs/components.md
- 姉妹シリーズ「学生 VM が起動するまで」第 2 編: KubeVirt の内部