← logs

Firecracker rootfs の Storage CoW を reflink で検証した

2026年5月15日

HITSC の検証用基盤で ticket ごとの rootfs を reflink clone し、Storage CoW の効果を rootfs provision 時間とディスク使用量の増分で確認した記録

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

HITSC / hitto Troubleshooting Contest は、Firecracker microVM 上に実際の Linux 問題環境を用意するために作っている検証用基盤である。

この基盤では、ticket ごとに Firecracker microVM を起動する。

各 VM には専用の rootfs.ext4 が必要になる。

素朴には、問題テンプレートの rootfs を ticket ごとに full copy すればよい。

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

ただし、rootfs が大きくなると copy 時間とディスク使用量が問題になる。

特に Docker 入り rootfs では数 GiB 単位になる。

そこで HITSC の実装では、Storage CoW の入口として reflink backend を入れた。

backend 設計

現時点の RootFSProvisioner は 3 種類ある。

current:
  既存互換 backend。
  cp --reflink=always を試し、失敗したら cp --reflink=never、
  さらに失敗したら Go copy へ fallback する。

copy:
  明示的な full copy 比較用 backend。
  cp --reflink=never を使う。

reflink:
  明示的な CoW backend。
  cp --reflink=always を使い、CoW clone できなければ失敗する。

重要なのは、currentreflink の違いである。

current は互換性重視なので、reflink できなければ copy へ fallback する。

一方、reflink は検証や運用判断のために、reflink できなければ明確に失敗させる。

つまり、Storage CoW を本当に使えているか確認したい時は、HITSC_ROOTFS_BACKEND=reflink を指定する。

sudo HITSC_ROOTFS_BACKEND=reflink hitsc ticket start <ticket-id>

最初のベンチで見えたこと

まず、docker-iptables-001current / copy / reflink を比較した。

run directory:

current:
  /var/lib/hitsc-firecracker/metadata/benchmarks/bench-rootfs-current-20260602-194422

copy:
  /var/lib/hitsc-firecracker/metadata/benchmarks/bench-rootfs-copy-20260602-194422

reflink:
  /var/lib/hitsc-firecracker/metadata/benchmarks/bench-rootfs-reflink-20260602-194422

条件:

problem: docker-iptables-001
count: 3 tickets
nodes per ticket: server, client1, client2
rootfs operations: 9
parallel: 1
scenario: start
runtime_backend: direct

結果はこうだった。

rootfs_provision mean:
  current:  29.060 ms
  copy:     27.486 ms
  reflink:  26.229 ms

一見すると、copy と reflink の差がほぼない。

events.jsonlrootfs_method はこうだった。

current:
  rootfs_method=reflink 9 回

copy:
  rootfs_method=copy 9 回

reflink:
  rootfs_method=reflink 9 回

ここで分かったことは 2 つある。

1. current backend は、この worker 上では実際に reflink へ落ちていた
2. しかし、このベンチだけでは Storage CoW の効果を強く主張できない

理由は、rootfs copy 時間だけでなく、page cache、sparse file、ファイルシステムの挙動、VM 起動処理などの影響が混ざるためである。

特に Docker rootfs は大きいが、ext4 raw image の内部の使われ方や host 側 copy の sparse 処理によって、単純な「見かけサイズ分の書き込み」にはならない。

したがって、Storage CoW を評価するには、起動時間だけでなく、ディスク使用量の増分を見る必要がある。

disk proof

そこで、smoke-001 を使って copy と reflink のディスク増分を比較した。

run directory:

copy:
  /var/lib/hitsc-firecracker/metadata/benchmarks/bench-smoke-disk-copy-20260602-01

reflink:
  /var/lib/hitsc-firecracker/metadata/benchmarks/bench-smoke-disk-reflink-20260602-01

disk proof:
  /var/lib/hitsc-firecracker/metadata/benchmarks/rootfs-disk-proof-20260602-01/result.tsv

条件:

problem: smoke-001
count: 3 tickets
parallel: 1
scenario: start
runtime_backend: direct

rootfs_provision の結果:

copy:
  mean: 433.569 ms
  p50:  432.758 ms
  p95:  443.119 ms
  max:  444.270 ms

reflink:
  mean:  13.707 ms
  p50:   11.474 ms
  p95:   18.054 ms
  max:   18.785 ms

この条件では、reflink は copy より約 31.6 倍速かった。

ticket_start も短くなった。

copy ticket_start mean:
  813.150 ms

reflink ticket_start mean:
  396.740 ms

rootfs_provision だけでなく、ticket start 全体でも約 2 倍速くなっている。

ディスク使用量

rootfs-disk-proof-20260602-01/result.tsv には、ベンチ前後のディスク使用量と instances 配下の見かけ使用量を記録した。

copy:
  before=6801301504
  after=8478265344
  delta=1676963840
  du_instances=6442496818

reflink:
  before=6801465344
  after=6805004288
  delta=3538944
  du_instances=6442488237

見かけ上、instances 配下にはどちらも約 6.4 GB 相当の rootfs がある。

copy du_instances:
  6442496818 bytes

reflink du_instances:
  6442488237 bytes

しかし、実際の filesystem 使用量の増分はまったく違う。

copy delta:
  1676963840 bytes
  約 1599 MiB

reflink delta:
  3538944 bytes
  約 3.4 MiB

つまり、reflink では ticket ごとの rootfs file は存在しているが、実データ block の大部分は base rootfs と共有されている。

これが Storage CoW の効果である。

なぜ du_instances は同じように見えるのか

ここで少し紛らわしい点がある。

du_instances が copy でも reflink でも約 6.4 GB に見えるため、最初は「reflink でもディスクを食っているのでは」と疑いたくなる。

しかし、reflink clone されたファイルは、見かけ上は独立したファイルである。

ファイルサイズや単純な du の見方だけでは、block 共有の実態を読み違えることがある。

今回の判断では、ベンチ前後の filesystem 使用量の増分を見た。

copy:
  filesystem usage +約 1599 MiB

reflink:
  filesystem usage +約 3.4 MiB

この差により、reflink の Storage CoW が効いていると判断した。

current backend の意味

今回の current backend では、rootfs_method=reflink が記録されていた。

これは、worker の現在の filesystem では reflink が使えていることを意味する。

ただし、current は fallback を持つ。

別の worker や別の filesystem では、同じ current でも rootfs_method=copyrootfs_method=go-copy になる可能性がある。

したがって、Storage CoW の有無を明確にしたい場合は、以下を見る。

events.jsonl:
  rootfs_backend
  rootfs_method

運用で CoW 必須にしたい場合は、current ではなく reflink を指定する。

sudo HITSC_ROOTFS_BACKEND=reflink hitsc ticket start <ticket-id>

これで reflink 不可の worker では失敗する。

失敗する方が、知らないうちに full copy へ fallback してディスクを食い尽くすより安全である。

Storage CoW と Memory CoW の関係

今回の話は Storage CoW である。

Storage CoW:
  rootfs.ext4 の block 共有
  HITSC_ROOTFS_BACKEND=reflink

Memory CoW:
  snapshot.mem の page 共有
  HITSC_RUNTIME_BACKEND=snapshot-restore

両方を合わせると、ticket ごとの VM 発行はかなり軽くなる。

rootfs:
  base rootfs と ticket rootfs が block 共有

memory:
  snapshot.mem 由来の page が VM 間で共有

network:
  ticket ごとに namespace / bridge / TAP を分離

Memory CoW については、別の記事で PSS を使って確認している。

実験 backend の候補

reflink はファイルシステムが対応している場合には非常に扱いやすい。

ただし、全 worker で使えるとは限らない。

そのため、将来候補として以下もメモしている。

LVM thin:
  block layer で thin snapshot を作る

dm-snapshot:
  device mapper snapshot を使う

OverlayFS:
  lowerdir/upperdir で差分管理する

ただし、MVP の主経路にはまだ入れない。

LVM thin や dm-snapshot は pool 管理や device cleanup が難しく、OverlayFS は Firecracker の block device rootfs 管理と相性が難しい。

現時点では、rootfs.base.ext4 + reflink が最も単純で、問題作成フローも変えずに済む。

まとめ

今回確認できたこと:

RootFSProvisioner に current / copy / reflink backend を実装した
current backend はこの worker では rootfs_method=reflink になっていた
copy backend は rootfs_method=copy として明示的に full copy できる
reflink backend は rootfs_method=reflink として明示的に CoW clone できる
smoke-001 では rootfs_provision が copy 平均 433.6 ms、reflink 平均 13.7 ms だった
disk delta は copy 約 1599 MiB、reflink 約 3.4 MiB だった

結論として、この worker では reflink による Storage CoW が効いている。

効果を見るときは、起動時間だけではなく、rootfs_provision の時間と filesystem 使用量の増分を見るのがよい。