TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

Reading KubeVirt Source Code 1: Repository Map — Where to Start Reading

Status: Draft (2026-10-17), Source + read-only actual measurement. The baseline is KubeVirt v1.9.0(commit 79d34c17). The "Measured" label indicates values checked solely by reading in a small cluster with v1.9.0 installed. Builds and tests were not run (see "Limitations" below).

Reading KubeVirt Source Code 1: Repository Map

Table of Contents

1. Intuition: Five Programs Attached to Kubernetes

KubeVirt does not modify Kubernetes. It simply adds CRDs (VirtualMachine, VirtualMachineInstance, etc.) and launches a few programs in the cluster that watch those CRDs. Even when reading the repository, you will not lose your way if you divide things based on these five programs.

Program Execution Location One-Line Role
virt-operator Control Plane (Deployment) Installs the rest and sets up the platform
virt-api Control Plane (Deployment) Inspects requests via webhooks and exposes subresources like console and start
virt-controller Control Plane (Deployment, single leader) Watches VMs to create VMIs, and VMIs to create pods
virt-handler Per node (DaemonSet) Aligns the VMI assigned to its node with the actual domain
virt-launcher One per VM (Pod) Converts VMI into a libvirt domain XML and launches QEMU

If Part 2 of "Until a Student VM Starts" (../kubevirt-lab-vm/02-kubevirt-inside.md) explained the roles of these five from a user's perspective, this post looks at which folder in the repository contains each role.

2. Diagram: Folders and Execution Locations

kubevirt/kubevirt 저장소 지도 (v1.9.0) 왼쪽은 폴더, 오른쪽은 그 코드가 실제로 도는 자리 cmd/ 바이너리 입구 — main 만 얇게 pkg/virt-api 웹훅 · 서브리소스 API pkg/virt-controller VM·VMI 컨트롤러 · 파드 템플릿 pkg/virt-operator 설치 · 업그레이드 pkg/virt-handler 노드 데몬 · 장치 · 네트워크 pkg/virt-launcher 도메인 XML · libvirt 호출 pkg/network · storage … 여러 구성요소가 함께 쓰는 부품 staging/…/api 타입 정의 · client-go 와 짝 tests/ 기능 시험(ginkgo, 진짜 클러스터) hack/ · Makefile 빌드 · 코드 생성 · 개발 클러스터 컨트롤 플레인 virt-api ×2 Deployment virt-controller ×2 리더 1 + 대기 1 virt-operator ×2 Deployment 노드마다 virt-handler DaemonSet VM 마다 virt-launcher 파드 virt-launcher-monitor → virt-launcher → QEMU 실측(v1.9.0 소스, 시험·생성 파일 제외한 Go 줄 수) virt-launcher 26.9k virt-handler 24.5k virt-controller 23.3k virt-operator 21.3k virt-api 13.5k api 타입 11.8k
Figure: Folders of kubevirt/kubevirt and where their code runs

3. Folder by Folder

cmd/ — A Thin Entry Point

cmd/ Each folder below has one main , and most are just a few lines long. virt-controller consists entirely of this (cmd/virt-controller/virt-controller.go:26).

func main() {
	watch.Execute()
}

, while the core body is in pkg/virt-controller/watch/application.go of Execute() (:303). It reads flags, creates informers, and once it wins the leader election, it runs controllers for VMs, VMIs, migrations, etc., as goroutines in onStartedLeading() (:609). The exceptions are the two node-side programs. cmd/virt-handler/virt-handler.go 's Run() (:209and cmd/virt-launcher/virt-launcher.go 's main()(:347have long setup code, so there is plenty to read even within cmd/ . We will follow these two in Parts 4 and 5.

cmd/ Also contains many small helpers besides the five: virt-launcher-monitor(PID 1 that wraps the launcher and watches it to the end), virt-chroot (when handler runs commands in a different namespace), virt-tail(a sidecar streaming serial console logs), virtctl(user CLI), and more.

pkg/virt-* — The Core of the Five Components

Folder File to Read First Covered Chapter
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/(Generates CRDs, webhooks, and DaemonSets as code) 2

pkg/ The Rest of pkg/ — Shared Parts Used Together

Packages outside the component folders are easy to understand by asking "Who calls them?". pkg/network is used together by virt-handler (preparing pod networks) and virt-launcher (domain's <interface>). pkg/controller contains controller common tools like informer factories and expectations, pkg/virt-config contains KubeVirt CR settings and feature gates, and pkg/hypervisor handles differences between hypervisors (KVM and the newly added MSHV). Part 3's memory overhead calculation is located in this pkg/hypervisor/kvm . pkg/libvmi is a builder for creating VMIs and is heavily used by test code.

staging/src/kubevirt.io/ — Two Separately Distributed Modules

API types (kubevirt.io/api) and clients (kubevirt.io/client-go) are separate Go modules because other projects also use them. In the same manner as Kubernetes, the root go.mod ties these two via replace into the staging/ folder inside the repository. Therefore, a PR that modifies VM fields modifies staging/src/kubevirt.io/api/core/v1/types.go , regenerates the code, and updates them together (Part 2).

tests/ — Functional Tests Running on a Real Cluster

Unit tests are *_test.go next to the code (Measured: pkg/ 717 below), written in Ginkgo and Gomega, and run with a fake client. tests/ consists of functional tests . They install KubeVirt in a real cluster and launch VMs to verify them. Each test has labels like decorators.SigCompute attached so you can select and run them by SIG.

hack/ and Makefile — Building Inside Containers

Makefile Almost all targets in hack/dockerized go through KUBEVIRT_NO_BAZEL=true . Since builds occur inside a builder container using Bazel (or go if

), your local machine only needs Docker (or Podman) and rsync. Command Location
make Formatting + Bazel build + Manifests Makefile:17
make generate Re-generating all generated code (Part 2) Makefile:53
make test / make go-test Unit tests (Bazel / go) :93 · :90
make cluster-up · cluster-sync Spinning up a dev cluster inside a VM using kubevirtci and deploying my build :173
make functest Functional testing in that cluster :101

To run only a subset of functional tests, pass Ginkgo filters like FUNC_TEST_ARGS='--focus-file=vmi_networking' make functest (Testing section of ). A development cluster spins up nodes as VMs, so you needdocs/getting-started.md a Linux machine that supports nested virtualization nested virtualization. On a Mac, even though you can build, cluster-up is difficult.

4. Measurement: Five programs observed in the cluster

Inspected the kubevirt namespace of a small cluster running v1.9.0 (2026-10-17).

Component Count Image
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 (per worker node) quay.io/kubevirt/virt-handler:v1.9.0
virt-launcher Equal to the number of running VMs (3) of VMI status launcherContainerImageVersion: …virt-launcher:v1.9.0

In the same namespace, virt-template-apiserver·virt-template-controller(v0.2.2) is also visible. This is a separate project that handles VM templates, and it is brought into the repository as manifests only via hack/virt-template/sync.sh . It is not covered in this series.

Size by folder. Go lines of code excluding tests and generated files (v1.9.0, find … ! -name '*_test.go' ! -name '*generated*').

Folder Files Lines Test files Test lines
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 is large with 23,000 lines across 42 files. watch/vm/vm.go alone has 3,640 lines. Conversely, virt-launcher has many small files. It is also worth noting that there are four folders where test lines outnumber main body lines. This is why reviewers frequently say "Show me the tests first" (Part 8).

How to jump from logs to source code. KubeVirt component logs are in JSON, and the "pos" field contains 파일:줄 . Cross-referencing the measured logs' pos with the v1.9.0 source code showed that they all match. For example, the "pos":"vm.go:1459" of virt-handler corresponds to the log line at pkg/virt-handler/vm.go:1459 , and the VMI is in phase: … | Domain status: … of virt-launcher is "pos":"manager.go:1651" . When reading the code for the first time, Domain defined. picking a single log line and tracing backward picking a single log line and tracing backward from its line number is the fastest approach. However, line numbers vary across versions, so make sure to open the tag matching your cluster's version.

5. Where to start reading

We recommend reading in the order of how a single VM starts up. At each step, you only need to grasp "what the input is and what it produces."

  1. staging/src/kubevirt.io/api/core/v1/types.go — VirtualMachine(1942 lines), VirtualMachineInstance(47 lines), and the list of phases (1098 lines). Field names correspond directly to YAML names.
  2. pkg/virt-controller/watch/vm/vm.go of execute()(357 lines) → sync()(3215 lines) → syncRunStrategy()(970 lines). VM → VMI.
  3. pkg/virt-controller/watch/vmi/lifecycle.go 's sync()(66 lines) and services/template.go 's renderLaunchManifest()(395 lines). VMI → Pod.
  4. pkg/virt-handler/vm.go 's execute()(321 lines) → sync()(1433 lines). Pod on node → Domain.
  5. pkg/virt-launcher/virtwrap/manager.go 's SyncVMI()(1380 lines) and converter/converter.go 's Convert_v1_VirtualMachineInstance_To_api_Domain()(967 lines). Domain → QEMU.

docs/components.md is also a good starting point, but its version is a bit outdated. It described the libvirt daemon running inside the pod as libvirtd , but virt-launcher in v1.9.0 launches libvirtd instead of the monolithic virtqemud (cmd/virt-launcher/virt-launcher.go:432). In our actual measurements, the processes inside the pod were virtqemud and virtlogd (Part 5).

6. Limitations

Titi's note. Intimidated by 9,000 files? Don't be. The path a single VM takes involves only five programs. Pick a single log line from pos and read backward from it. The mental map will form in no time!

References

All accessed on 2026-10-17.

Sign in to like

Comments

No comments yet.

Sign in to leave a comment