2026年6月25日

2026年6月25日

containerdとruncの仕組みとDockerとの違い|コンテナランタイムの役割分担

docker run でコンテナを起動するとき、実際にコンテナを作っているのはDocker本体(dockerd)ではなく、その下で動く containerd と runc です。containerdとruncはDockerから独立したソフトウェアで、KubernetesなどDocker以外のツールからも使われています。

この記事では、それぞれの役割と、Dockerとの関係を説明します。内容は2026年10月時点の各プロジェクトの公式ドキュメント(containerd.io、github.com/opencontainers/runc、docs.docker.com)に合わせています。

3つの役割分担

ソフトウェア 分類 主な役割
Docker(docker CLI と dockerd) コンテナの開発・運用ツール イメージのビルド、ネットワークとボリュームの管理、Compose、使いやすいCLIとAPI
containerd 高レベルのコンテナランタイム イメージの取得と保管、スナップショット(ファイルシステムの層)の管理、コンテナとタスクのライフサイクル管理
runc 低レベルのコンテナランタイム OCIの仕様どおりに、Linuxのnamespace・cgroupsなどを設定してコンテナのプロセスを起動する

runc は、OCI(Open Container Initiative)のランタイム仕様に沿ってLinux上でコンテナを起動するCLIツールです。OCI仕様の参照実装で、「設定ファイル(config.json)とルートファイルシステムを受け取り、隔離されたプロセスを起動する」ことだけを行います。イメージのダウンロードやネットワークの管理は行いません。runcの公式の説明でも、エンドユーザーが直接使うのではなく、上位のソフトウェアから使われることを想定した低レベルのツールとされています。

containerd は、イメージをレジストリから取得してディスクに展開し、runcに渡せる形(OCIバンドル)を用意して、コンテナの起動・停止・削除を管理するデーモンです。CNCF(Cloud Native Computing Foundation)のプロジェクトで、もともとDockerから切り出されたものです。

Docker は、そのさらに上で、Dockerfile からのイメージのビルド、ポートの公開やDNSを含むネットワーク、ボリューム、Composeなど、開発と運用に必要な機能をまとめて提供します。

docker run からコンテナが起動するまで

docker run nginx を実行したときの流れは、おおよそ次のとおりです。

  1. docker CLIが、dockerd のAPIにコンテナの作成と起動を依頼する
  2. dockerd が、ネットワークやボリュームの準備をして、containerdにコンテナの作成を依頼する
  3. containerdが、イメージの層からルートファイルシステムを用意し、コンテナごとに shim(containerd-shim-runc-v2)を起動する
  4. shimがruncを呼び出し、runcがnamespaceやcgroupsを設定してコンテナのプロセス(nginx)を起動する
  5. runcは起動が終わると終了し、以後はshimがコンテナのプロセスの親として残り、終了コードや標準入出力を扱う

shimが間に入るので、containerdを再起動しても、動いているコンテナのプロセスは止まりません。dockerd は、既定では停止するときにコンテナも止めますが、/etc/docker/daemon.json で "live-restore": true を設定すると、dockerd の再起動中もコンテナを動かしたままにできます。

サーバーで次のコマンドを実行すると、この構成を確かめられます。

docker info --format '{{.DefaultRuntime}}'
ps -ef | grep -E 'dockerd|containerd' | grep -v grep

1つ目は runc と表示されます。2つ目では、dockerd、containerd、コンテナの数だけの containerd-shim-runc-v2 が並びます。shimの引数に -namespace moby とあるのは、Dockerのコンテナがcontainerdの moby 名前空間で管理されているためです(名前空間は後で説明します)。

Docker 29 での変化

Docker Engine 29では、新しくインストールした環境で、イメージの保管にcontainerdのイメージストアが既定で使われるようになりました。それ以前は、イメージはDocker独自の仕組み(graph driver)で保管していて、containerdはコンテナの実行だけを担当していました。以前からの環境をアップグレードした場合は、もとの保管方式がそのまま使われます。

Kubernetes との関係

Kubernetesのkubeletは、CRI(Container Runtime Interface)という仕様でコンテナランタイムとやり取りします。containerdはCRIのプラグインを持っているので、kubeletから直接使えます。

Kubernetesは、Dockerを使うための中継部品(dockershim)をバージョン1.24で削除しました。現在のKubernetesのノードでは、Dockerを介さずにcontainerdやCRI-Oを使うのが一般的です。Dockerでビルドしたイメージは、OCIの形式なのでcontainerdでもそのまま動きます。

containerd を操作するコマンド

containerdには、用途の違う3つのCLIがあります。

コマンド 提供元 用途
ctr containerdに同梱 デバッグ用。機能は最低限で、Dockerのような使い勝手は想定していない
nerdctl containerdのプロジェクト(本体とは別に配布) Dockerと互換のCLI。nerdctl run など、docker とほぼ同じ書き方で使える
crictl KubernetesのSIG Node CRI経由でcontainerdやCRI-Oを操作する。Kubernetesのノードのデバッグ用

containerdには「名前空間(namespace)」があり、使うツールごとにイメージやコンテナが分かれています。Dockerは moby、Kubernetesは k8s.io、ctr は既定で default を使います。

# 名前空間の一覧
sudo ctr namespaces ls

# Docker が作ったコンテナを containerd の側から見る
sudo ctr -n moby containers ls

ctr でイメージを取得して、コンテナを動かす例です。イメージ名はレジストリから完全な形で書きます。

sudo ctr images pull docker.io/library/alpine:latest
sudo ctr run --rm -t docker.io/library/alpine:latest test1 sh

ctr で作ったコンテナは default 名前空間にあるので、docker ps には表示されません。逆に、Dockerのコンテナを ctr で操作すると、Dockerの管理情報と食い違うので、ctr -n moby は確認だけにとどめます。

containerd を単体でインストールする

Dockerを使わずにcontainerdだけを使う場合(Kubernetesのノードなど)の入れ方です。

  • ディストリビューションのパッケージ: UbuntuやDebianでは sudo apt install containerd で入る。runcも依存関係として入る
  • Dockerのリポジトリの containerd.io パッケージ: Dockerが配布しているもので、runcを含む。Dockerを入れると一緒に入るのもこのパッケージ
  • 公式のバイナリ: GitHubのリリースからダウンロードし、/usr/local に展開する。runc、CNIプラグイン、systemdのユニットファイルは別に入れる

設定ファイルは /etc/containerd/config.toml です。無い場合は、既定値を書き出してから編集します。

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo systemctl restart containerd
sudo systemctl status containerd

ログは sudo journalctl -u containerd で確認できます。

Kubernetesのノードとして使う場合は、cgroupのドライバーをkubeletと合わせる(systemdを使う)設定などが必要です。設定項目の書き方はcontainerdのバージョン(1.x系と2.x系)で異なるので、使っているバージョンの公式ドキュメントとKubernetesのドキュメントを確認してください。

runc を単体で動かしてみる

runcの公式のREADMEにある手順で、runcが受け取るもの(OCIバンドル)を確かめられます。ルートファイルシステムを作るためにDockerを使います。

mkdir -p ~/mycontainer/rootfs
cd ~/mycontainer

# busybox イメージの中身を rootfs に展開する
docker export $(docker create busybox) | tar -C rootfs -xf -

# 設定ファイル config.json のひな形を作る
runc spec

# コンテナを作成・起動する(終了すると削除される)
sudo runc run mycontainerid

runc spec が作る config.json には、起動するコマンド(既定は sh)、マウント、namespace、cgroupsの設定などが書かれています。containerdは、イメージの情報からこのファイルとrootfsを組み立ててruncに渡しています。

別の端末から、runcが管理しているコンテナを一覧できます。

sudo runc list

runc run の代わりに runc create と runc start を分けて実行すると、作成と起動の間にネットワークの設定などを挟めます。上位のランタイムは、この仕組みを使っています。終わったら sudo runc delete mycontainerid で削除します。

なお、runc list に表示されるのはrunc自身が状態を保存しているコンテナだけです。Dockerやcontainerdが起動したコンテナは別の場所で管理されるため、引数なしの runc list には表示されません。

どれを使えばよいか

  • アプリの開発、イメージのビルド、1台のサーバーでの運用: Docker(またはDocker互換のPodman)
  • Kubernetesのノード: containerd(またはCRI-O)。Dockerは不要
  • Dockerを入れずにDockerと同じ書き方で使いたい: containerd と nerdctl
  • runcを直接使う場面は、コンテナランタイムの開発や学習に限られる

まとめ

  • runcはOCI仕様に沿ってコンテナのプロセスを起動する低レベルのランタイム
  • containerdはイメージの管理とコンテナのライフサイクルを受け持つ高レベルのランタイムで、shimを介してruncを呼ぶ
  • Dockerはcontainerdとruncの上に、ビルド・ネットワーク・ボリューム・Composeなどの機能を載せたもの
  • Kubernetesは1.24でdockershimを削除し、CRI経由でcontainerdなどを直接使う
  • containerdの操作は、デバッグなら ctr、Docker互換なら nerdctl、Kubernetesなら crictl を使う

関連記事:

お気軽にご相談ください

お見積りへ お問い合わせへ
▲