インフラ

gVisorとは?コンテナを隔離するサンドボックスの仕組みとrunc・Kata比較・導入判断

gVisorは、Googleが開発しオープンソースで公開しているコンテナサンドボックスです。標準的なコンテナはホストOSのカーネルを共有するため、コンテナ内のプロセスがカーネルの脆弱性を突くとホスト全体へ影響が及ぶ余地が残ります。gVisorはコンテナとホストカーネルの間に「ユーザー空間で動くカーネル」を差し込み、この共有を断ち切ることで隔離を強めるのが基本設計です。この記事では、SentryとGoferによる仕組み、システムコールを傍受するプラットフォームの選択肢、runcやKata Containersとの違い、Docker/Kubernetesでの導入手順、そして採用すべき場面と見送るべき場面までを実装者向けに整理します。

まとめ(先に結論)

  • 正体:gVisorはGo言語で書かれたアプリケーションカーネル兼コンテナサンドボックス。OCIランタイム runsc として runc と置き換えて使える。
  • 仕組み:コンテナのシステムコールをユーザー空間の Sentry が代行し、ファイルアクセスは Gofer が仲介する。コンテナからホストカーネルへ直接届くシステムコールを絞り、攻撃対象領域を縮小する。
  • 代償:Sentryを経由するぶんシステムコール発行やI/Oが多い処理では速度低下が出やすく、一部のシステムコールや特殊機能は未対応のことがある。
  • 向く場面:他者が持ち込むコードを動かすマルチテナント環境、CI/CDのビルド隔離、サーバーレスやユーザー投稿コードの実行など「信頼境界をコンテナ単位で引きたい」ケース。
  • 見送る場面:低レイテンシや高スループットのI/Oが要件の中心で、かつコード提供元を自社で統制できている場合は、runcのまま他の防御層を足すほうが費用対効果が見合うことが多い。

gVisorとは何か:ユーザー空間で動くカーネルという設計思想

コンテナは仮想マシンと違い、ホストのLinuxカーネルを全コンテナで共有します(前提となる仕組みはコンテナとは?仮想マシンとの違いからDocker・企業の導入判断まで解説で整理しています)。この共有はコンテナが軽量である理由そのものですが、同時に「カーネルという一枚岩を全員で使う」という信頼境界の弱さにもなります。あるコンテナがカーネルの欠陥を突けば、隣のコンテナやホストへ横断される経路が理屈上は開いてしまうためです。

gVisorはこの前提を作り替えます。コンテナのプロセスが発行するシステムコールは、ホストのLinuxカーネルへそのまま渡りません。代わりに、gVisorが用意するユーザー空間のカーネル実装が受け止めて解釈します。コンテナから見ればLinuxのシステムコール群がそこにあるように振る舞いますが、その実体はホストカーネルではなくgVisorの中にあります。ホストカーネルへ届くのは、gVisorが厳密に絞り込んだごく一部だけです。

SentryとGofer:システムコールとファイルで役割を分ける

gVisorのサンドボックスは、主に2種類のプロセスで構成されます。

  • Sentry:コンテナのシステムコールを受け取り、ファイルやプロセス、シグナル、ネットワークといったカーネル相当の機能を代行するユーザー空間カーネル。Linuxの多数のシステムコールをgVisor自身のGoコードで再実装しており、コンテナはSentryとだけ会話する。
  • Gofer:コンテナがアクセスするファイルシステムを供給する仲介プロセス。Sentryはホストのファイルへ直接触れず、Gofer経由でファイル操作を要求する。SentryとGoferの間はプロトコル(LISAFSなど)で通信し、ここでもホストへの直接アクセスを一段はさむ。

この分業により、たとえコンテナ内のコードがSentryの想定外の挙動を試みても、影響はサンドボックスの内側にとどまりやすくなります。Sentry自身も後述のプラットフォームによってホストカーネルとの接点を制限された状態で動くため、「二重の壁」でホストを守る構図です。

runsc:runcと置き換えできるOCIランタイムの位置づけ

実運用でユーザーが触れるのは runsc というバイナリです。runscはOCI(Open Container Initiative)準拠のランタイムで、Dockerの既定ランタイムである runc と入れ替えて使えます。つまりコンテナイメージやDockerfile、Kubernetesのマニフェストはそのままに、「どのランタイムで起動するか」を切り替えるだけでgVisor化できるのが設計上の狙いです。ランタイムを差し替える発想は、デーモンレス・rootlessで注目されるPodmanとdockerの違いとも通じる観点で、コンテナの実行層は用途に応じて選べる領域になっています。

gVisorはどうやってコンテナを隔離するのか:システムコール傍受

gVisorの中核は「コンテナが出したシステムコールをどうやってSentryへ届けるか」です。ここを担うのがプラットフォームと呼ばれる層で、2026年時点では主に次の方式があります。

  • systrap:シグナルベースでシステムコールを捕捉する方式。従来のptrace方式より発行のたびのコストを抑える狙いで導入され、現在の既定プラットフォーム系として案内されている。
  • KVM:ハードウェア仮想化機構(KVM)を使い、Sentryをより低いオーバーヘッドで動かす方式。ベアメタルや入れ子仮想化が使える環境で選ばれる。
  • ptraceptrace でシステムコールを傍受する古くからの方式。互換性は広いがシステムコールごとの往復コストが大きい。

いずれの方式でも、コンテナのシステムコールはホストカーネルへ素通りせず、必ずSentryが介在します。ホストカーネルの視点では、動いているのはgVisor(Sentry)というひとつのプロセス群であり、コンテナ内の何百というシステムコールの種類を直接さばく必要がありません。これが「攻撃対象領域を縮小する」という表現の実体です。カーネル層で挙動を観測・制御する技術としてはeBPFとは?Linuxカーネルを拡張する仕組みもありますが、eBPFがホストカーネル内で動くのに対し、gVisorはカーネル機能そのものをユーザー空間へ持ち出す点で狙いが異なります。

ファイルシステムとネットワークをサンドボックス内でどう扱うか

ファイルアクセスは前述のGoferが仲介し、Sentryがホストのファイルを直接開くことはありません。ネットワークについては、gVisor自身がユーザー空間にTCP/IPスタック(netstack)を持ち、パケット処理もサンドボックス内で完結させる構成を取れる点が特徴です。ホストのネットワークスタックを共有しないぶん隔離は強まりますが、独自スタックゆえに一部のネットワーク機能やパフォーマンス特性がホストカーネルのそれと差が出る場合があります。要件次第でホストネットワークを使う設定も選べます。

runc・Kata Containersとの違いを3方式で比較

コンテナの隔離を強める選択肢はgVisorだけではありません。代表的な3方式を、隔離の仕組みと代償の観点で並べます。

方式 隔離の仕組み 強み 代償・注意点
runc(標準) ホストカーネルを共有しseccomp等で制限 最速・最軽量で互換性が最も広い カーネル共有ゆえ境界はその堅牢性に依存
gVisor(runsc) ユーザー空間カーネルがsyscallを代行 VMより軽く攻撃対象領域を縮小 I/O・syscall多用時に速度低下や機能非対応
Kata Containers コンテナごとに軽量VMを起動し隔離 VMレベルの強い境界でカーネルも別 VM起動ぶんの起動時間とメモリが増える

大づかみには「runc=速いがカーネル共有」「Kata=強いが重いVM」で、gVisorはその中間に位置づけられます。ユーザー空間カーネルという方式ゆえに、VMほどの起動コストをかけずにカーネル共有を避けたい、という要件に噛み合います。

Docker/KubernetesへgVisorを組み込む手順

runscはOCIランタイムなので、既存のコンテナ基盤へ「別ランタイムの追加」という形で組み込みます。ここでは要点だけを示します(実際の導入では対象ノードのカーネルやアーキテクチャの前提を必ず確認してください)。

Dockerのdaemon設定にruntimeとしてrunscを登録する

runscをインストールしたうえで、Dockerのデーモン設定にランタイムとして登録します。

# /etc/docker/daemon.json にruntimesとしてrunscを追加
{
  "runtimes": {
    "runsc": {
      "path": "/usr/local/bin/runsc"
    }
  }
}
# 設定反映後、--runtime を指定して起動
docker run --rm --runtime=runsc hello-world

コンテナ内で dmesg を実行するとgVisor由来のカーネル表記が返るため、runc起動との違いを確認できます。

KubernetesでRuntimeClassを定義しPod単位で割り当てる

Kubernetesでは、ノードのコンテナランタイム(containerdなど)にrunscのハンドラを設定したうえで、RuntimeClass を定義してPod単位で割り当てます。全PodをgVisor化するのではなく、隔離を強めたいワークロードだけに指定できるのが利点です。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
metadata:
  name: sandboxed-app
spec:
  runtimeClassName: gvisor   # このPodだけgVisorで起動
  containers:
  - name: app
    image: your-app:latest

マネージドKubernetesではGKE Sandboxのように、ノードプール設定でgVisorを有効化する形で提供されるものもあります。どこまでを内製で組み、どこからマネージドに寄せるかは、運用体制と隔離要件の兼ね合いで決める領域です。この設計をクラウド横断で詰めたい場合は、インフラ構築(AWS・Google Cloud・Azure)のように環境選定から実装まで支援できる体制を使うと、ランタイム選定と周辺の権限・ネットワーク設計を一体で進められます。どのワークロードにどの隔離を割り当てるかという上位の設計は、コンテナオーケストレーションとは?Kubernetesの役割と自社に必要かの判断とあわせて検討すると全体像が描きやすくなります。

gVisorを採用すべき場面と見送るべき場面を分ける判断基準

gVisorは「万能の防御」ではなく、隔離と引き換えに性能や互換性のコストを払う道具です。導入可否は次の条件で切り分けると判断がぶれません。

gVisorの採用がコストに見合う典型的なワークロードの条件

  • 信頼できないコードを動かす:他者が持ち込むビルド、ユーザー投稿の関数、共同利用のCI/CDジョブなど、コードの中身を自社で保証できないワークロードを同一クラスタに同居させる場合。カーネル共有を避けたい要求が明確なほど費用対効果が出る。
  • マルチテナントで境界を強めたい:テナントごとに情報や権限を確実に分けたいが、テナント数が多くVMを1つずつ立てるのは重い、という中間解として。
  • 性能要件がシステムコール多用でない:計算主体・メモリ主体のワークロードは、I/O多用のものよりgVisorのオーバーヘッドが表面化しにくい。事前にベンチマークで許容範囲を確かめられる場合。

gVisorの導入を見送るか慎重な事前検証を要する代表的な場面

  • 低レイテンシ・高スループットのI/Oが中心:データベースや大量のファイル・ネットワーク処理が主役のワークロードは、Sentry経由の往復が効いて速度低下が目立ちやすい。ここは実測で線を引く。
  • 特殊なシステムコールやデバイスに依存:一部のシステムコールや、GPU・特殊デバイスへの直接アクセスなど、gVisorが未対応または制約付きの機能に依存する場合は、事前検証で動作を確かめてから判断する。
  • コード提供元を自社で統制できている:動かすものがすべて自社管理で信頼できるなら、runcのままseccompやAppArmor、権限最小化、そしてCNAPとは?クラウドネイティブ保護を1つに束ねる仕組みのような統合的な保護を積むほうが、性能を落とさず守りを固められることが多い。

言い切ると、gVisorは「信頼境界をコンテナ単位で引き直したい」ときに効く道具であり、逆に信頼が確保できている環境で性能を犠牲にしてまで入れる対象ではありません。まず「何を信頼していないのか」を言語化し、そのうえで対象ワークロードだけにrunsc(RuntimeClass)を割り当てるのが、無理のない導入の形です。

よくある質問

Q. gVisorを入れるとコンテナはどれくらい遅くなりますか?

ワークロード次第で幅があります。CPUやメモリ主体の処理では差が小さい一方、システムコールやI/Oを大量に出す処理ではオーバーヘッドが表面化します。プラットフォーム(systrap/KVM/ptrace)の選択でも変わるため、本番相当のベンチマークで自分の処理に対する数値を測ってから判断するのが確実です。

Q. 既存のDockerイメージをそのまま使えますか?

基本的にはイメージを作り直さず、起動時のランタイムを runsc に切り替えるだけで動かせます。ただしgVisorが未対応のシステムコールや機能に依存するアプリは例外的に動作差が出るため、移行前に対象コンテナで動作確認をおこないます。

Q. gVisorとKata Containersはどちらを選ぶべきですか?

VMレベルの強い境界とカーネル分離を最優先し、起動時間やメモリ増を許容できるならKata、VMほどの重さを避けつつカーネル共有を断ちたいならgVisor、という切り分けが目安です。両者は排他ではなく、ワークロードごとに使い分ける構成も取れます。

Q. Kubernetesで一部のPodだけgVisorにできますか?

できます。RuntimeClass を定義し、隔離したいPodの runtimeClassName に指定すれば、そのPodだけがrunscで起動します。クラスタ全体を切り替える必要はありません。

Q. gVisorはセキュリティ対策として単体で十分ですか?

gVisorはカーネル共有に由来するリスクを下げる一枚ですが、権限最小化・ネットワーク制御・イメージの脆弱性管理など他の層と組み合わせて初めて全体の守りになります。単体の銀の弾丸ではなく、多層防御の一要素として位置づけるのが適切です。

関連記事

資料請求

RELATED POSTS 関連記事