インフラ

runcとは?OCIランタイムの仕組み・containerdとの関係と実装での使いどころを解説

DockerやKubernetesでコンテナを動かしていても、コンテナのプロセスを実際に起動しているのが何なのかまで意識する機会は多くありません。その最後のひと押しを担うのが runc です。runcはOCI(Open Container Initiative)ランタイム仕様の参照実装で、Dockerとcontainerdのどちらもデフォルトでこのruncを土台にコンテナを起動します。本記事では、runcが名前空間やcgroupをどう束ねてコンテナを立ち上げるのか、containerdやcontainerd-shimとどう連携するのか、crun・youki・Kata Containersといった選択肢とどこが違うのかを技術者の実装目線で整理し、現場で直接触るべき場面と触らないほうがよい場面まで言い切ります。

まとめ:runcは「バンドルからプロセス起動まで」を担う低レベルランタイム

先に要点を示します。詳細は各章で掘り下げます。

  • runcの正体: OCIランタイム仕様の参照実装。Go言語製の単一バイナリで、ルートファイルシステムと config.json から成るOCIバンドルを受け取り、名前空間・cgroup・capability・seccompを設定してコンテナプロセスを起動する。
  • 階層上の位置: containerdやCRI-Oが「高レベルランタイム」、runcは「低レベルランタイム」。イメージ取得やネットワークは高レベル側が受け持ち、runcは「バンドルがある」状態から「プロセスが走っている」状態までの最後の一区間だけを担当する。
  • 呼び出し経路: containerdはruncを直接叩かず、containerd-shim を挟む。shimがruncを実行してコンテナを生成し、runc自身は起動後に終了する(常駐しない)。
  • 代替実装: C言語製で軽量なcrun、Rust製のyouki、VMで隔離するKata Containers、ユーザー空間カーネルのgVisorなど。いずれもOCIランタイム仕様に沿うため、多くは差し替えて使える。
  • 版番号: 1.5系が公開中(2026年7月時点。v1.5.1が2026年7月14日リリース)。破壊的変更は少なく、パッチはカーネル互換やセキュリティ周りの調整が中心。
  • 実装での距離感: 通常の開発ではruncを直接叩かない。トラブルシュートやランタイム差し替え、隔離強化を検討する場面で「その下で何が起きているか」を知る道具として効いてくる。

runcとは何か:OCIランタイム仕様の参照実装としての位置づけ

runcは、コンテナを生成・実行するためのコマンドラインツールです。Go言語で書かれた単一バイナリで、OCIが定める「ランタイム仕様(runtime-spec)」に準拠します。もともとDockerのコンテナ実行部分を切り出して標準化したもので、現在はOCIプロジェクトとして開発が続いています。

ここで押さえたいのは、runcが扱う入力が「イメージ」ではなく「OCIバンドル」だという点です。OCIバンドルは、展開済みのルートファイルシステム(rootfs)と、コンテナの実行条件を記述した config.json の組み合わせです。config.json には、起動するプロセス、環境変数、マウント、使用する名前空間、cgroupの制限値、seccompプロファイル、capabilityの許可リストなどが宣言的に並びます。runcはこの1ファイルを読み取り、そのとおりにLinuxカーネルのAPIを呼び出してコンテナを組み立てます。

つまりruncは、DockerイメージをレジストリからpullしたりレイヤーをUnionFSで重ねたりはしません。それらは上位の役割です。runcの守備範囲は「バンドルは用意できた。あとはこの条件でプロセスを隔離して走らせてくれ」という一区間に絞られています。この責務の狭さこそがruncの設計思想であり、OCIによって役割が分割されている理由でもあるのです。コンテナ全体の位置づけを整理したい場合は、判断ハブとしてコンテナとは?仮想マシンとの違いからDocker・企業の導入判断まで解説もあわせて参照すると全体像がつかめます。

高レベルランタイムと低レベルランタイムの分業と役割分担の考え方

コンテナランタイムは、担う仕事の抽象度で「高レベル」と「低レベル」に分けて考えると整理しやすくなります。

  • 高レベルランタイム(containerd・CRI-Oなど): イメージのpull・管理、スナップショット、ネットワーク、ボリューム、コンテナのライフサイクル管理といった、運用に必要な幅広い機能を受け持ちます。KubernetesのkubeletはCRI(Container Runtime Interface)経由でこの層に指示を出します。
  • 低レベルランタイム(runc・crun・youkiなど): OCIバンドルを受け取り、名前空間・cgroup・セキュリティ設定を適用してプロセスを起動する、実行の最終工程だけを担います。

この分業により、上位の運用機能と下位の実行機構を独立して差し替えられます。たとえばcontainerdはそのままに、低レベルランタイムだけをruncからcrunへ、あるいはKata Containersへ切り替える、といった構成変更が可能になります。containerd側の詳しい仕組みはcontainerdとは?Dockerとの関係・runc/CRIの仕組みとKubernetesでの役割を解説で掘り下げているので、runcを呼び出す側の視点はそちらで補完してください。

runcがコンテナを起動する仕組みを名前空間とcgroupから理解する

runcが config.json を読んでから、隔離されたプロセスが走り出すまでに何をしているのかを分解します。ここが低レベルランタイムの核心です。

名前空間(namespace)でプロセスから見える世界を区切る

Linuxの名前空間は、プロセスから見える資源の範囲を分離する機構です。runcはコンテナごとに、PID名前空間(プロセスID空間の分離)、mount名前空間(マウントツリーの分離)、network名前空間(ネットワークスタックの分離)、UTS名前空間(ホスト名の分離)、IPC名前空間、そして設定によってはuser名前空間(UID/GIDのマッピング)を作成します。user名前空間を用いると、コンテナ内のroot(UID 0)をホスト上の非特権ユーザーへ対応づけられるため、権限分離の観点で有効です。

cgroupでCPU・メモリ・PID数などの資源使用量を制限する

名前空間が「見える範囲」を区切るのに対し、cgroup(control groups)は「使える量」を制限します。runcは config.json の指定に従い、CPU時間・メモリ上限・PID数・ブロックI/Oなどのリミットをcgroup階層に書き込みます。現在の環境ではcgroup v2が標準となっており、runcもこれに対応済みです。メモリ上限を超えたコンテナがOOM Killerで停止する、といった挙動はこの層で決まります。

seccompやcapabilityなどセキュリティ機構を適用する

runcは隔離だけでなく、コンテナプロセスの権限を絞り込む設定も適用します。具体的には、Linux capabilityの許可リスト付与、seccompによるシステムコールのフィルタリング、必要に応じてSELinuxやAppArmorのラベル付与、そして no_new_privs フラグの設定などです。これらは config.json に宣言されており、runcがコンテナプロセスの生成直前にカーネルへ反映します。標準では危険度の高いシステムコールがseccompでブロックされるため、既定のプロファイルのまま運用しても一定の防御が効きます。

コンテナの初期プロセスを起動してrunc自身は役目を終えて退場する

上記の設定を終えると、runcはコンテナの初期プロセス(config.jsonprocess.args)を実行します。実装上は、runcが自分自身を再実行して初期化専用のプロセスを立て、名前空間へ入り、必要な準備を済ませてから目的のコマンドへ切り替える、という二段構えになっています。コンテナが動き出すと、runcプロセス自体は役目を終えて終了し、常駐はしません。コンテナのプロセスはホストから見れば通常のプロセスの一つとして動き続けます。

containerdとcontainerd-shim、runcの呼び出し経路

実運用では、runcを人が直接叩く場面はまれで、多くはcontainerd経由で呼び出されます。ただしcontainerdはruncを直接実行せず、あいだに containerd-shim というプロセスを挟む構成です。この仲介役には明確な理由があります。

  • 親プロセスからの独立: shimがコンテナの親となることで、containerd本体を再起動・アップグレードしてもコンテナは走り続けます。runcが起動後に終了しても、shimがコンテナプロセスを引き取って管理を継続します。
  • 終了コードとI/Oの保持: コンテナの標準入出力や終了ステータスをshimが保持し、containerdへ伝えます。
  • デーモンレスな実体: 各コンテナに対応するshimが個別に存在するため、単一の巨大デーモンにすべてをぶら下げない構成になります。

まとめると、KubernetesのkubeletがCRI経由でcontainerdへ「このPodを起動せよ」と指示し、containerdがOCIバンドルを用意してshimを起動、shimがruncを実行してコンテナを生成する、という流れです。runcはこの連鎖の末端で、実際にカーネルを操作する実働部隊を務めます。なお、デーモンを持たない実行モデル自体に関心があればPodmanとdockerの違いは?デーモンレス・rootlessの仕組みと移行判断も参考になります。

runcを手元で触る:createとstartなど主要サブコマンド

runcはCLIとして直接操作もできます。動作を理解するうえで、代表的なサブコマンドを把握しておくと役立ちます。実運用でここまで降りる機会は限られますが、挙動確認やトラブルシュートでは有効な手立てです。

  • runc spec:ひな形の config.json を生成する。ここを起点にバンドルを組み立てられる。
  • runc create <id>:バンドルからコンテナを作成する(プロセスはまだ開始しない状態)。
  • runc start <id>:作成済みコンテナの初期プロセスを開始する。
  • runc run <id>:createとstartをまとめて実行する。
  • runc list / runc state <id>:稼働中コンテナの一覧・状態を確認する。
  • runc exec <id> <cmd>:稼働中コンテナ内でコマンドを実行する。
  • runc kill <id> / runc delete <id>:シグナル送出・コンテナ削除を行う。

createとstartが分かれている点は、単なる冗長ではありません。この分離があるおかげで、コンテナを生成した直後・プロセス開始前に、ネットワーク設定などのセットアップフックを差し込めます。OCIランタイム仕様がライフサイクルを段階に区切っているからこそ、上位ツールが各段階に処理を挿入できる、という設計です。

crun・youki・Kata Containersとの違い

runcはOCIランタイムの参照実装ですが、同じ仕様に沿う実装は複数あります。仕様準拠のため多くは差し替えて運用でき、要件に応じて選び分けられます。

  • crun: C言語製の低レベルランタイム。Go製のruncよりバイナリが小さく起動が速い傾向があり、cgroup v2との親和性も高いとされます。短命なコンテナを大量に立てる用途で効いてきます。
  • youki: Rust製の低レベルランタイム。メモリ安全性を言語レベルで担保しつつ、runc互換を目指す実装です。
  • Kata Containers: 軽量なVMでコンテナを包む方式の低レベルランタイム。名前空間による隔離より強い境界を、ホストカーネル共有を避ける形で得られます。マルチテナントで隔離を強めたい場面が主戦場です。詳細はKata Containersとは?軽量VMで隔離するセキュアコンテナの仕組みとgVisor・runc比較・導入判断で掘り下げています。
  • gVisor(runsc): ユーザー空間で動くカーネルがシステムコールを代理受けし、ホストカーネルへの露出を絞る方式。互換性と隔離のトレードオフを持ちます。

runcとcrun・youkiはいずれも「ホストカーネルを共有し、名前空間とcgroupで区切る」同系統です。一方、Kata ContainersやgVisorは「隔離境界そのものを引き直す」異なる系統です。差し替えの容易さと隔離強度は別軸で捉えるのが要点になります。

【実装判断】runcに直接向き合うべき場面・触らないほうがよい場面

ここまでの仕組みを踏まえ、受託開発やインフラ構築の現場でruncとどう距離を取るべきかを条件付きで言い切ります。

runcのレイヤーへ直接向き合う価値が出てくる実装現場の場面

  • コンテナが起動しない原因を切り分けるとき: 上位ツールのエラーが漠然としている場合、runcレイヤーのログやcgroup・seccompの設定を確認すると、権限やカーネル互換に起因する失敗の切り分けが進みます。「どの層で落ちているか」を特定する軸としてrunc理解が効きます。
  • 隔離強度の要件が厳しいとき: マルチテナントや外部コード実行など、名前空間による分離だけでは要件を満たしにくい場合、低レベルランタイムをKata ContainersやgVisorへ差し替える選択が視野に入ります。判断には、runcが標準で何を区切り何を区切らないかの理解が前提になります。
  • 起動性能・密度を詰めたいとき: 短命コンテナを高頻度で立てるワークロードでは、crunへの差し替えで起動オーバーヘッドを削減できる余地があります。効果は環境依存のため、実測して判断するのが妥当です。
  • セキュリティ設定を設計するとき: seccompプロファイルやcapabilityの絞り込みを自社要件に合わせて設計する場合、それらを最終適用するのがruncである以上、config.json のどの項目がどう効くかを押さえる必要があります。

runcのCLIを本番で直接は触らないほうがよい通常運用の場面

  • 通常のアプリ開発・CI/CDでコンテナを使うだけのとき: DockerやKubernetesが抽象化してくれる範囲で完結するなら、runcを個別に操作する理由はありません。抽象を無理にはがすと運用の複雑さが増します。
  • 標準の隔離で要件を満たせるとき: ランタイム差し替えは構成の検証コストを伴います。既定のruncで隔離・性能が足りているなら、差し替えは目的を明確にしてから検討すべきです。
  • 本番環境で場当たり的に runc コマンドを叩くとき: containerdやshimが管理しているコンテナへ、状態管理の外からrunc CLIで介入すると整合が崩れる恐れがあります。学習や検証は隔離した環境で行うのが安全です。

総じて、runcは「常に直接触る道具」ではなく「その下で何が起きているかを理解し、いざというときに降りていける層」として位置づけるのが実務的です。コンテナ基盤の設計・構築・運用を含むインフラ構築の相談は、インフラ構築(AWS・Google Cloud・Azure)で承っています。ランタイムの選定から隔離要件の整理まで、実装判断をふまえてご提案します。

よくある質問

Q1. runcとDockerはどう違いますか?

Dockerはイメージのビルド・配布・管理からコンテナ実行までを含む包括的なプラットフォームで、runcはそのうち「コンテナプロセスを起動する」最下層だけを担う低レベルランタイムです。Dockerは内部でcontainerdを経由し、最終的にrunc(既定の場合)を呼び出してコンテナを起動します。両者は競合ではなく、階層の異なる部品です。

Q2. runcとcontainerdの関係を一言で言うと?

containerdが「高レベルランタイム」でイメージやネットワークを含む運用全般を管理し、runcが「低レベルランタイム」でプロセス起動の最終工程を担います。containerdはruncを直接ではなくcontainerd-shim経由で呼び出します。

Q3. runcを別のランタイムに置き換えられますか?

置き換えられます。crun・youki・Kata Containers・gVisorなどはいずれもOCIランタイム仕様に沿うため、containerdなどの設定で低レベルランタイムを差し替えられます。ただし互換性や隔離強度、性能特性が異なるため、目的を定めて検証したうえで採用するのが妥当です。

Q4. runcはKubernetesでもデフォルトのランタイムですか?

Kubernetes自体はCRI経由でcontainerdやCRI-Oといった高レベルランタイムと連携し、その低レベルランタイムとして既定でruncが用いられる構成が一般的です。Kubernetesが直接runcを呼ぶわけではなく、CRIランタイムの下でruncが動く、という関係です。

Q5. runcの現在のバージョンはどれくらいですか?

1.5系が公開されています(2026年7月時点。v1.5.1が2026年7月14日にリリース)。破壊的変更は抑えられており、パッチはカーネル互換やセキュリティの調整が中心です。正確な最新版は公式のリリースページで都度確認してください。

関連記事

資料請求

RELATED POSTS 関連記事