Dockerを数ヶ月以上使っていても、そう感じているエンジニアは少なくありません。コマンドが動けばそれでいい——と言いたいところですが、Kubernetesへの移行・containerd停止時のトラブルシュート・rootlessモードの設計、どれを突き詰めようとしても、Dockerの内部構造を知らないと手が止まります。
この記事では、Dockerのコンテナランタイムがdockerd・containerd・runcの3層で構成される理由、OCI(Open Container Initiative)仕様との関係、コンテナが起動するまでの内部シーケンスを実機出力を交えて解説します。nerdctlやctrコマンドを使ってcontainerdを直接確認する手順、実務での活用場面(Kubernetes理解・障害切り分け)まで一通り整理します。
動作確認環境: RHEL 9.4 / Ubuntu 24.04 LTS、Docker Engine 26.1.x
この記事のポイント
・DockerはdockerD・containerd・runcの3層でコンテナを起動している
・OCI仕様により、DockerとKubernetesで同じruncランタイムを共用できる
・containerdはKubernetes環境ではDockerなしで直接使われる
・ctr・nerdctlコマンドでcontainerdを実機から直接操作して確認できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜDockerの内部構造を知る必要があるのか
Dockerを「コマンドを叩けば動く便利ツール」として使い続けることはできます。ただ、次のような場面で、内部構造の知識がないと明確に詰まります。・Kubernetesへの移行時: KubernetesはDockerを使わずcontainerdを直接使います。「なぜdockerコマンドが効かないのか」が分からないまま進むと、ノード上でのデバッグで手が止まります
・containerd停止時: 「dockerdは動いているのにコンテナが起動できない」——containerdとdockerdの違いを知らないとログの意味すら読めません
・rootlessモードの設計: ユーザー権限でDockerを動かす仕組みは、コンテナランタイムの分離構造が前提知識になります
一度整理しておくと、これらすべての場面で原因の見当が格段につきやすくなります。
OCI仕様とは何か(Dockerを超えた共通規格)
OCI(Open Container Initiative)とは、コンテナランタイムとイメージの仕様を標準化するために2015年に設立された団体です。Docker・Red Hat・Googleらが参加し、現在は3つの仕様を策定しています。・Runtime Specification(runtime-spec): コンテナの起動・停止・削除の手順を規定。runcはこの仕様の参照実装
・Image Specification(image-spec): コンテナイメージのフォーマットを規定。Docker Hub・GCR・ECRなどすべてのレジストリがこの仕様に準拠
・Distribution Specification(distribution-spec): イメージのプッシュ・プルの通信プロトコルを規定
OCI仕様があるおかげで、Dockerで作ったイメージをKubernetesでそのまま動かせます。コンテナをどのエンジンで動かしても「同じ動作をする」ことが保証されるのはこの規格のおかげです。
2024年以降、主要クラウド(AWS・Azure・GCP)はすべてOCI準拠のコンテナイメージを前提としています。Dockerという特定のツールに依存するのではなく、「OCI準拠かどうか」で設計を考える習慣が実務では重要です。
dockerd・containerd・runcの役割と3層構造
Dockerのコンテナ起動は、以下の3つのプロセスが順番に連携して実現されます。1. runc — コンテナの実行エンジン(OCI runtime)
runcはOCI runtime-specの参照実装です。Linux名前空間(PID・NET・MNT・IPC・UTS・USER)とcgroupsを使ってコンテナプロセスを起動する、最も下層のコンポーネントです。runcはコンテナを1つ起動したら役割を終えます。起動後は常駐しません。渡されたコンテナ設定ファイル(config.json)に従ってプロセスを立ち上げるだけで、ネットワーク管理やイメージ管理は行いません。責務の絞り込みが徹底されています。
runcは同等の役割を担う代替実装(crun・youki・gVisorなど)とも入れ替え可能です。OCI仕様に準拠している限り、コンテナの動作は同一になります。
2. containerd — コンテナのライフサイクル管理
containerdはDockerから独立した常駐デーモンです。runcを呼び出してコンテナの起動・停止・削除を管理し、イメージのpull・push、スナップショット管理、ログストリームの処理も担います。Kubernetes 1.24以降、Dockerサポートは廃止されcontainerdがデフォルトのコンテナランタイムになりました。containerd自体はCNCF(Cloud Native Computing Foundation)のプロジェクトとして独立しており、DockerもKubernetesも「containerdという共通の土台の上に乗っている」構図です。
3. dockerd — クライアントAPIを処理するデーモン
dockerdはDocker CLI(dockerコマンド)からのリクエストをREST API(/var/run/docker.sock)で受け取り、containerdに転送するデーモンです。ネットワーク設定(iptables/nftables)やボリューム管理、コンテナイメージのビルド処理もdockerdが担当します。Kubernetesがdockerを使わなくなった理由の一つは、「kubeletがdockerdを経由しなくてもcontainerd直接で十分」と判断したためです。余分な中間デーモンをなくすことで、構成の複雑さとレイテンシを削減できます。
コンテナ起動の内部シーケンス
1. docker run コマンド発行から起動まで
docker run nginx を実行したとき、内部では以下の順序で処理が進みます。1. docker CLI → dockerd(/var/run/docker.sockのUNIX socket経由)
2. dockerd → containerd(/run/containerd/containerd.sock経由)
3. containerd → runc(コンテナ起動の実行を依頼)
4. runc → コンテナプロセス(PID 1として起動後、runcは終了)
5. containerd-shim-runc-v2がコンテナのIO・シグナルを仲介して残る
runcが終了した後は、containerd-shim-runc-v2 というシムプロセスがコンテナのIO・シグナルを仲介します。このシムがあることで、dockerdが停止してもコンテナは動き続けられます(--live-restoreオプションの原理はここにあります)。
2. プロセスツリーで確認する
実際にコンテナを起動して pstree で確認すると、3層構造が見えます。# ubuntu-server-01 (Ubuntu 24.04 LTS) で確認 $ docker run -d --name testnginx nginx d3f2e4a1b9c7f805d2a19e40c3b87f6a10c92d3b $ pstree -p $(pgrep containerd | head -1) containerd(1234)─┬─containerd-shim(3456)─┬─nginx(3789) │ └─nginx(3790) ├─{containerd}(1235) └─{containerd}(1240) $ pstree -p $(pgrep dockerd | head -1) dockerd(2001)─┬─docker-proxy(5678) ├─{dockerd}(2002) └─{dockerd}(2010)
実機で確認する(docker info・ctr・nerdctl)
1. docker info でコンテナランタイムを確認する
# RHEL 9.4 サーバーでの出力例 $ docker info | grep -E 'Runtimes|Default Runtime|containerd|runc' Runtimes: io.containerd.runc.v2 runc Default Runtime: runc containerd version: 659457f06d5034b823af428e9f8f4a9f5c43bfdf runc version: v1.1.12-0-g51d5e946
io.containerd.runc.v2がcontainerd経由のrunc実行を示しています。これがDockerが使うデフォルトのランタイムです。runcと並列に表示されているのは、古いシムなしの直接起動モードとの互換性のためです。2. ctr コマンドでcontainerdを直接操作する
ctrはcontainerdに直接アクセスする低レベルCLIです。sudoが必要で、名前空間の概念があります。# containerdの名前空間一覧を確認 $ sudo ctr namespaces list NAME LABELS moby k8s.io # Dockerのコンテナはmobyネームスペースにあることを確認 $ sudo ctr --namespace moby containers list CONTAINER IMAGE RUNTIME d3f2e4a1b9c7f805d2a19e40c3b87f6a10c92d3b - io.containerd.runc.v2 # イメージ一覧(Dockerでpullしたイメージ) $ sudo ctr --namespace moby images list | head -3 REF TYPE DIGEST SIZE PLATFORMS docker.io/library/nginx:latest ... sha256:4c0fdaa8... 67.3 MiB linux/amd64
moby です。Kubernetesが使う場合は k8s.io になります。同じcontainerdを両者が名前空間で分けて共有しています。3. nerdctlでDockerに近い操作感でcontainerdを使う
nerdctlはcontainerdをDockerライクなCLIで操作できるツールです。Kubernetesノード上やrootless環境で、dockerコマンドなしでcontainerdを直接扱いたい場面で重宝します。# nerdctlのインストール(Ubuntu 24.04 LTS) $ wget -q https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gz $ sudo tar Cxzvf /usr/local/bin nerdctl-1.7.6-linux-amd64.tar.gz # docker runと同じ操作感でコンテナを起動 $ sudo nerdctl run -d --name nerdtest nginx a9f3b2c1d5e87420f16a03c2b9d5e4a7b1c8e340 $ sudo nerdctl ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a9f3b2c1d5e8 docker.io/library/nginx:... "/docker-entrypoint.…" 2 seconds ago Up nerdtest
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、OCI仕様・containerd直接操作・Dockerfile最適化まで含めた実践的なDocker講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
実務での活用場面
1. Kubernetesへの移行時のデバッグ(crictl)
KubernetesはDockerShimを1.24で完全廃止し、containerdまたはCRI-Oをデフォルトランタイムとして使います。Kubernetesノード上でコンテナをデバッグするには、dockerの代わりにcrictl を使います。# Kubernetesノード(k8s-worker-01)上での確認 $ sudo crictl ps CONTAINER IMAGE CREATED STATE NAME ATTEMPT a1b2c3d4e5f6 sha256:4c0fdaa8... 2 hours ago Running nginx 0 # ポッド一覧確認 $ sudo crictl pods POD ID CREATED STATE NAME NAMESPACE ATTEMPT f9e8d7c6b5a4 2 hours ago Ready nginx-pod default 0 # コンテナのログを確認 $ sudo crictl logs a1b2c3d4e5f6 2026/09/02 00:00:01 [notice] 1#1: start worker processes
crictl の操作感はdockerとほぼ同じです。Kubernetesクラスターで「コンテナが起動しない」トラブルを調査する際の第一歩になります。containerd/runcの知識があれば、なぜcrictlで同じことができるかが自然と理解できます。2. containerd停止時のトラブルシュート
「dockerdは動いているのにコンテナが起動できない」場合、containerdが落ちている可能性が高いです。# 症状: docker runがタイムアウトしてエラーになる $ docker run hello-world docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: ... # containerdの状態を確認 $ systemctl status containerd * containerd.service - containerd container runtime Loaded: loaded (/usr/lib/systemd/system/containerd.service) Active: failed (Result: exit-code) since 2026-09-02 09:45:10 JST # containerdを再起動して解消 $ sudo systemctl restart containerd $ systemctl is-active containerd active
shim task や containerd が含まれていたら、まずcontainerdの状態を確認する習慣をつけてください。3. rootlessモードの仕組み
Docker rootlessモードでは、dockerdとcontainerdがroot権限なしでユーザー権限で動作します。cgroup v2とuser namespaceを組み合わせることで、ホストのrootを取られるリスクを大幅に低減できます。rootlessモードの内部では、
slirp4netns がネットワークアドレス変換を担い、rootful構成でdockerdが担っていたiptables操作の代替を行います。この設計も、containerdとruncが明確に役割分担されているからこそ実現できます。本記事のまとめ
Dockerのコンテナ起動は3層のコンポーネントが連携して実現されます。| コンポーネント | 主な役割 | 停止時の症状 |
|---|---|---|
| runc | OCIに基づくコンテナプロセス起動(常駐しない) | コンテナが起動できない(OCI runtime create failed) |
| containerd | コンテナのライフサイクル・イメージ・スナップショット管理 | docker runがshim taskエラーで失敗 |
| dockerd | Docker CLIのAPI処理・ネットワーク・ボリューム管理 | dockerコマンド自体が応答しない |
この理解があると、Kubernetes移行時やcontainerd停止時のトラブルシュートで「今どこで何が起きているか」を迷わず追跡できるようになります。
次に読む記事
・コンテナとは何か|Dockerで理解する仮想マシンとの違いと利点・DockerからKubernetesへ進む最初の一歩|minikubeとkindで学ぶコンテナオーケストレーション入門
現場のDocker設計・コンテナランタイムの仕組み・Kubernetes移行を体系的に学びたい方へ、ハンズオン形式でまとめた実践的なDocker講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Dockerイメージを署名して検証する方法|cosignとDocker Content Trustでサプライチェーン攻撃を防ぐ実践手順
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます