← logs

HITSCで学ぶ仮想化の地図

2026年6月7日

HITSC を題材に、Proxmox、KVM、QEMU、Firecracker、microVM、TAP、network namespace、snapshot、CoW の役割を仮想化初学者向けに整理した地図

tags: HITSC, Virtualization, Firecracker, KVM, Proxmox

この記事に出てくる HITSC 関連の用語、構成名、設計上の呼び方は、執筆時点の検証用基盤に関するものです。実装や設計の進行に伴い、現在は変更されている場合があります。

HITSC / hitto Troubleshooting Contest を作っていると、いろいろな単語が出てくる。

KVM
QEMU
Proxmox
Firecracker
microVM
kernel
rootfs
TAP
bridge
network namespace
snapshot
CoW

仮想化に慣れていないと、この時点でかなりつらい。

しかも、これらは全部「仮想マシンを起動する技術」と雑にまとめられがちだが、実際には担当している場所が違う。

この記事では、HITSCを題材にして、仮想化初学者向けに全体像を整理する。

まず全体像

HITSCの現在の構成は、大きく見るとこうである。

physical server
  |
  | Proxmox
  v
worker VM: hitsc-worker-01
  |
  | Firecracker + KVM
  v
ticketごとのmicroVM
  |
  | SSH
  v
参加者がトラブルシューティングするLinux環境

物理サーバに直接Firecrackerを入れるのではなく、Proxmox上にworker VMを作り、その中でFirecracker microVMを動かしている。

つまり、HITSCでは仮想化が2段になっている。

第1段:
  Proxmox VM
  物理サーバ上にworker VMを作る

第2段:
  Firecracker microVM
  worker VM内に問題環境を作る

この構成を初めて見ると「VMの中でVMを動かしているの?」となる。

そうである。 いわゆるnested virtualizationである。

HITSCでは、Proxmoxは大きなVM管理レイヤ、Firecrackerは問題環境を大量に発行する小さなVM実行エンジンとして使っている。

VMとは何か

VMは、ざっくり言うと「OSから見ると本物のコンピュータっぽく見える箱」である。

普通のLinuxサーバには、次のような部品がある。

CPU
memory
disk
NIC
serial console
kernel
filesystem
process

VMでは、このうちCPU、memory、disk、NICなどを仮想的に用意する。

guest OSは、それを本物の機械だと思って起動する。

host machine
  |
  +-- virtual CPU
  +-- virtual memory
  +-- virtual disk
  +-- virtual NIC
        |
        v
      guest OS

HITSCで重要なのは、参加者が入る問題環境が「コンテナ」ではなく「VM」だという点である。

参加者はrootでログインする。 systemctl を見たり、journalctl を見たり、Dockerやiptablesを壊したり直したりする。

この用途では、host kernelを共有するコンテナより、guest kernelを持つVMの方が自然である。

コンテナとVMの違い

かなり大雑把に言うと、違いはkernelを共有するかどうかである。

container:
  host kernelを共有する
  processやfilesystemを分離する

VM:
  guest kernelを起動する
  OSごと別のmachineとして動く

図にするとこう。

container:

host Linux kernel
  ├── container A process
  ├── container B process
  └── container C process

VM:

host Linux kernel
  ├── guest VM A kernel -> process
  ├── guest VM B kernel -> process
  └── guest VM C kernel -> process

コンテナは軽い。 ただし、host kernelを共有する。

VMは重くなりやすい。 ただし、guest kernelを持つので、より本物のサーバに近い。

HITSCはトラブルシューティング練習基盤なので、次のような体験を重視している。

SSHでログインする
systemd PID 1が動く
systemctlが使える
journalctlが使える
iptablesやDockerも問題にできる
rootで壊してもticket単位で隔離される

このため、HITSCではmicroVMを採用している。

KVMとは何か

KVMは、Linux kernelが持っている仮想化機能である。

正確な説明はLinux kernelのKVM documentationにあるが、HITSCを理解するうえでは、まず次の理解でよい。

KVM:
  Linux kernel側でCPU仮想化を支える仕組み

KVMを使うと、Intel VT-xやAMD-VのようなCPUの仮想化支援機能を使って、guest OSを高速に動かせる。

HITSC workerで確認した /dev/kvm は、ユーザー空間のVMMがKVMへアクセスするための入口である。

ls -l /dev/kvm
lsmod | grep kvm
egrep -c '(vmx|svm)' /proc/cpuinfo

FirecrackerはKVMを使う。 QEMUもKVMを使える。 ProxmoxのVMも基本的にQEMU/KVMで動く。

つまりKVMは、HITSCの下の方でかなり重要な土台になっている。

QEMUとは何か

QEMUは、広い意味ではmachine emulator / virtualizerである。

QEMUのsystem emulationは、CPU、memory、deviceを持つ仮想machineを作り、guest OSを動かす。

KVMと組み合わせると、CPU実行の重い部分をKVMに任せ、QEMUはmachine全体の構成やdevice emulationを担当する。

よく見る言葉として、QEMU/KVMがある。

KVM:
  CPU仮想化をLinux kernel側で支える

QEMU:
  仮想machine、device、disk、NICなどを扱う

QEMU/KVM:
  QEMUがKVMを使って高速にVMを動かす構成

Proxmoxは、VM実行の土台としてQEMU/KVMを使う。

HITSCでは、Proxmox上のworker VMを作るところでQEMU/KVMにお世話になっている。

Firecrackerとは何か

Firecrackerは、KVMを使ってmicroVMを作るVMMである。

VMMはVirtual Machine Monitorの略で、仮想マシンを作って動かすプログラムだと思えばよい。

Firecrackerの特徴は、汎用PCっぽいVMを作るのではなく、必要なdeviceをかなり絞ったmicroVMを作ることにある。

QEMU:
  汎用的
  多くのmachine/deviceを扱える
  とても柔軟

Firecracker:
  microVM向け
  device modelを小さくする
  速い起動と小さい攻撃面を狙う

HITSCでは、参加者ごと、ticketごとに問題環境を発行したい。

tkt-000001 -> microVM
tkt-000002 -> microVM
tkt-000003 -> microVM

この用途では、全部を重い汎用VMとして起動するより、Firecracker microVMの方が相性がよい。

microVMは「小さいVM」

microVMはコンテナではない。 VMである。

ただし、普通の汎用VMより小さく、シンプルに作られている。

HITSCでのmicroVMは、だいたいこういう部品で動く。

Firecracker process
  |
  +-- guest kernel: vmlinux-6.1-firecracker
  +-- rootfs: rootfs.ext4
  +-- virtual block device: /dev/vda
  +-- virtual network device: eth0
  +-- API socket
  +-- stdout/stderr log

参加者から見ると、普通のLinuxサーバにSSHしているように見える。

ssh root@10.41.0.2
systemctl status nginx
journalctl -xeu nginx

しかし、HITSC worker側から見ると、それはFirecracker processが動かしているmicroVMである。

kernelとrootfs

FirecrackerでLinux guestを起動するには、最低限kernelとrootfsが必要になる。

kernel:
  Linux kernel本体
  例: /var/lib/hitsc-firecracker/kernels/vmlinux-6.1-firecracker

rootfs:
  guest OSのfilesystem
  例: /var/lib/hitsc-firecracker/problems/nginx-001/rootfs.base.ext4

kernelは「OSの中核」である。 rootfsは「/etc や /usr や /var が入っているdisk image」である。

Firecrackerには、普通のPCのBIOS画面やinstallerを使うような雰囲気はない。 HITSCでは、あらかじめ作ったkernelとrootfsを渡して起動する。

{
  "boot-source": {
    "kernel_image_path": "/var/lib/hitsc-firecracker/kernels/vmlinux-6.1-firecracker",
    "boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
  },
  "drives": [
    {
      "drive_id": "rootfs",
      "path_on_host": "/var/lib/hitsc-firecracker/instances/tkt-xxx/rootfs.ext4",
      "is_root_device": true,
      "is_read_only": false
    }
  ]
}

HITSCでUbuntu 24.04 rootfsを作ったとき、最初は debootstrap --variant=minbase のrootfsに udevkmod が足りず、guestのNICが出ない問題に当たった。

この経験から分かるのは、Firecrackerが動くだけでは十分ではないということである。

Firecracker:
  仮想machineを起動する

guest kernel:
  virtio-net / virtio-blkなどを認識する

rootfs:
  systemd, udev, sshd, networkdなどを持つ

この3つが揃って、ようやくSSHできる問題環境になる。

TAPとは何か

VMにネットワークを持たせるには、host側にも接続口が必要になる。

HITSCでは、Firecrackerのguest NICとhost側のTAP deviceをつなぐ。

guest microVM
  eth0: 10.41.0.2
    |
    | virtio-net
    |
host / namespace
  tapxxxx: 10.41.0.1

TAPは、Linux上に作る仮想的なL2 network interfaceである。

Firecrackerはguest側に仮想NICを見せる。 host側では、その通信がTAP deviceに出てくる。

そのため、HITSCの起動処理では次のようなことをする。

ip tuntap add dev tapxxxx mode tap
ip addr add 10.41.0.1/24 dev tapxxxx
ip link set tapxxxx up

実際には現在のHITSCではticketごとのnetwork namespace内にTAPやbridgeを作る。

network namespaceとは何か

network namespaceは、Linuxのネットワークスタックを分ける仕組みである。

namespaceを分けると、interface、route、iptablesなどを別々に持てる。

root namespace:
  workerの通常ネットワーク

ticket A namespace:
  br/tap/route/iptables

ticket B namespace:
  br/tap/route/iptables

HITSCではticketごとにnetwork namespaceを作る。

これにより、複数ticketで同じguest IPを使い回せる。

ticket A:
  guest: 10.41.0.2

ticket B:
  guest: 10.41.0.2

ticket C:
  guest: 10.41.0.2

root namespaceだけでTAPを作ると、同じIPを同時に使い回せない。

ticketごとにnamespaceを切ることで、HITSCは「同じ問題環境を何個も複製する」ことができる。

bridgeとは何か

複数VM問題では、1つのticket内に複数のmicroVMを起動する。

たとえば docker-iptables-001 は、server、client1、client2の3 VM構成である。

ticket namespace
  mgmt bridge
    ├── server eth0
    ├── client1 eth0
    └── client2 eth0

  lan bridge
    ├── server eth1: 192.168.36.1
    ├── client1 eth1: 192.168.36.2
    └── client2 eth1: 192.168.36.3

bridgeは、ざっくり言うと仮想スイッチである。

複数のTAPを同じbridgeにつなぐことで、microVM同士が同じL2 network上にいるようにできる。

これにより、HITSCでは次のような問題を作れる。

client1からserverへcurlする
client2からは遮断する
server上のiptablesを調査する
DockerのDOCKER-USER chainを直す

これはコンテナ単体の演習では出しにくい、かなり物理っぽいトラブルシューティングになる。

ticketとは何か

HITSCでは、問題環境の発行単位をticketと呼ぶ。

Problemはテンプレートである。

Problem:
  nginx-001
  docker-iptables-001
  smoke-001

Ticketは、Problemから発行された1回分の環境である。

Problem: nginx-001
  |
  +-- Ticket: tkt-20260605-103227-9ce797
        |
        +-- Instance: Firecracker microVM

同じuserが同じProblemを何度も解けるように、HITSCでは user + problem を主キーにしていない。

user + problem:
  同じ問題を1回しか持てない

ticket:
  同じ問題を何度でも発行できる

この考え方は、HITSCの設計でかなり重要である。

HITSCのticket startで何が起きるか

hitsc ticket start <ticket-id> を実行すると、内部では多くのことが起きる。

単純化するとこう。

1. problem.yamlを読む
2. ticketに対応するinstance directoryを作る
3. rootfs.base.ext4からticket用rootfs.ext4を作る
4. network namespaceを作る
5. bridge/TAP/vethを作る
6. rootfsへIP設定やcredentialを注入する
7. firecracker.jsonを生成する
8. Firecracker processを起動する
9. API socketへmachine/drives/network設定を入れる
10. InstanceStartする
11. SSH readinessを確認する
12. SQLiteとstate.jsonへ状態を書く

ユーザーから見ると1コマンドだが、実際にはLinux、Firecracker、filesystem、network、SQLiteが全部動く。

だからHITSCのバグ調査では、どの層で壊れているかを分けて見る必要がある。

Firecracker processは起動しているか
guest kernelはbootしているか
eth0はguest内にあるか
TAPにpacketは出ているか
namespaceのrouteは正しいか
sshdは起動しているか
SQLiteの状態だけ壊れていないか

この分解ができると、HITSCのログがかなり読みやすくなる。

snapshotとは何か

snapshotは、ある時点のVM状態を保存する仕組みである。

HITSCではFirecracker snapshot restoreを使って、起動済みVMに近い状態から復元する検証をしている。

direct boot:
  kernel boot
  systemd boot
  sshd起動
  SSH ready

snapshot restore:
  保存済みsnapshotをload
  途中状態から再開
  SSH readyまで短縮

snapshot restoreは速い。 実測でもdirect bootよりかなり短くなった。

ただし、snapshotには注意点がある。

VMのmemory stateを保存するので、guest内の一意値も複製されうる。

/etc/machine-id
SSH host key
random seed
application token
userspace PRNG state

そのため、HITSCではsnapshotを単なる高速化機能としてだけでなく、clone後の一意性再生成もセットで考えている。

ここで出てくるのが、snapshot-fastsnapshot-reuniquesnapshot-strict のようなprofileである。

CoWとは何か

CoWはCopy-on-Writeの略である。

最初は同じデータを共有し、書き込みが起きた部分だけコピーする、という考え方である。

HITSCでは、2種類のCoWを考えている。

Storage CoW:
  rootfs.ext4をreflinkなどで共有する
  書き込まれたblockだけ別物になる

Memory CoW:
  snapshot.memを複数VMで共有する
  書き込まれたmemory pageだけ別物になる

Storage CoWは、ticketごとのrootfs作成を速くし、ディスク使用量を減らす。

rootfs.base.ext4
  -> ticket-a/rootfs.ext4
  -> ticket-b/rootfs.ext4
  -> ticket-c/rootfs.ext4

Memory CoWは、snapshot restoreした複数VMのmemory使用量を減らす。

ただし、CoWは高速化・効率化の話であって、安全なclone運用そのものではない。 一意性再生成やsnapshot取得位置の設計は別に必要である。

HITSCでよく出るファイル

HITSCを触っていると、以下のpathをよく見る。

/var/lib/hitsc-firecracker/
  kernels/
    vmlinux-6.1-firecracker

  rootfs/
    ubuntu-24.04-base.ext4

  problems/
    nginx-001/
      problem.yaml
      rootfs.base.ext4
      scripts/check.sh

  tickets/
    tkt-...
      ticket.json

  instances/
    tkt-.../
      rootfs.ext4
      firecracker.json
      firecracker.socket
      stdout.log
      stderr.log
      state.json

  metadata/
    hitsc.sqlite3

それぞれの役割はこう。

kernels:
  guest kernel置き場

rootfs:
  共通rootfs置き場

problems:
  問題テンプレート

tickets:
  発行された問題環境の管理情報

instances:
  実際に起動するVMごとのartifact

metadata:
  SQLiteやbenchmark/load-test log

この対応関係が分かると、HITSCのdebugがだいぶ楽になる。

よくある勘違い

Firecrackerは問題管理システムではない

FirecrackerはmicroVMを起動するVMMである。

HITSCのような問題管理、ticket管理、score管理はFirecrackerの責務ではない。

Firecracker:
  microVMを起動する

HITSC:
  problem.yamlを読む
  rootfsを作る
  networkを作る
  Firecrackerを起動する
  scoreする
  reset/destroyする

microVMはコンテナではない

Firecracker microVMはVMである。

guest kernelがある。

このため、systemdやjournalctl、Dockerなどを含む「実サーバっぽい問題」を作りやすい。

速いVMでもSSH readyまでは時間がかかる

Firecracker processの起動自体は軽い。

しかし、HITSCの ticket start で見ている時間には、以下が含まれる。

rootfs copy / reflink
network namespace作成
TAP/bridge作成
Firecracker起動
Linux kernel boot
systemd boot
sshd起動
SSH readiness確認
SQLite/state.json更新

そのため、「Firecrackerは速い」と「HITSCの問題環境がSSH可能になるまで1秒未満」は同じ意味ではない。

snapshot restoreやStorage CoWを入れる理由は、この差を縮めるためである。

localhost-onlyでもnetwork ruleが甘いと漏れる

HITSCではpublished portをworker localhost限定にしている。

しかし、実際にpacketを転送するiptables ruleが甘いと、別ticket namespaceから到達できる可能性があった。

これは別の記事にまとめた。

ここから分かるのは、仮想化基盤では「設定値」だけでなく、「packetが実際に通る経路」を見る必要があるということだ。

HITSCを読む順番

仮想化初学者がHITSCを読むなら、次の順番がよいと思う。

1. このblog
   仮想化の地図を掴む

2. docs/architecture/
   HITSC全体の構造を読む

3. docs/problem-authoring/
   問題作成方法を読む

4. docs/design/current/
   現在の設計判断を読む

5. docs/blog/
   失敗ログと調査記事を読む

6. internal/
   実装コードを読む

コードリーディングなら、最初はこのあたりがよい。

internal/model/
  Ticket, Instance, Problemのデータ構造

internal/ticket/
  ticket issue/start/score/closeの流れ

internal/instance/
  rootfs, network, runtime起動の流れ

internal/network/
  namespace, bridge, TAP, NAT, port forward

internal/runtime/
  Firecracker起動

internal/store/
  SQLiteとstate.json

まとめ

HITSCは、単にFirecrackerを起動するCLIではない。

HITSCは、Firecracker microVMを使って、トラブルシューティング用のLinux問題環境をticket単位で発行する基盤である。

そのため、いくつもの層が重なっている。

Proxmox:
  worker VMを管理する

KVM:
  Linux kernel側で仮想化を支える

Firecracker:
  microVMを起動する

rootfs:
  guest OSの中身

network namespace:
  ticketごとのnetworkを分離する

SQLite/state.json:
  HITSCの状態を保存する

ticket:
  問題環境の発行単位

初学者にとって大事なのは、全部を一度に理解しようとしないことである。

まずは、どの技術がどの層を担当しているかを見る。 その地図があると、No route to hosteth0がないdatabase is lockednginxが起動しない のような問題を、どの層から調べるべきか判断しやすくなる。

HITSCの実装はまだ育っている途中だが、この「層を分けて考える」感覚は、仮想化基盤を作るうえでかなり大事だと思う。

参考

関連ログ