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 を実行したときの流れは、おおよそ次のとおりです。
dockerCLIが、dockerdのAPIにコンテナの作成と起動を依頼するdockerdが、ネットワークやボリュームの準備をして、containerdにコンテナの作成を依頼する- containerdが、イメージの層からルートファイルシステムを用意し、コンテナごとに shim(
containerd-shim-runc-v2)を起動する - shimがruncを呼び出し、runcがnamespaceやcgroupsを設定してコンテナのプロセス(nginx)を起動する
- 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を使う
関連記事: