UnleashZealUpliftPersevereOvercomeDrive

元々Mac BookでDocker Desktopを利用していたのですが、Windows PCを購入したことを機に、Rancher Desktopを導入してみました。
ローカル開発における、Docker ComposeからKubernetesへの移行で経験したRancher Desktopのランタイム切替をもとに、Docker(moby)、containerd、nerdctl、Docker CLIの内部処理に焦点を当てて情報を整理しました。

1. 各コンポーネントの役割と関係性

<全体構成図>

Docker CLI  ⇔  Docker Engine (moby)  ⇔  containerd  ⇔  runc  ⇔  Linux Kernel (namespaces, cgroups)
---
nerdctl     ⇔  containerd            ⇔  runc
  • Docker CLI: ユーザー操作の窓口(CLIコマンド)
  • Docker Engine (moby): Dockerの主要機能を統合したプラットフォーム
  • containerd: コンテナのライフサイクル管理を行うランタイム
  • runc: OCI仕様に準拠したコンテナ実行ツール(ランタイム)
  • nerdctl: containerdを操作するCLI

2. Docker(moby)の内部処理

コマンド実行からコンテナ起動までの流れ

docker run nginx

処理の流れ:

  1. Docker CLIがリクエスト(run nginx)をDocker Engineに送信(REST API経由)
  2. Docker Engineがコンテナのイメージを確認・取得(docker pull)
  3. Docker Engineがcontainerdにコンテナ起動を依頼
  4. containerdがruncに依頼して、Linuxのnamespaceやcgroupsを使いプロセスを分離・起動
  5. コンテナ起動後、Docker Engineが状態管理(ログ・ネットワーク設定)を担当

<内部処理の特徴>

  • Docker(moby)はcontainerdにビルド・実行などの作業を委譲
  • ネットワーク・ストレージ・ボリュームの管理はDocker Engineが直接担当

3. containerdの内部処理

軽量ランタイムとしてのアーキテクチャ

処理の流れ(Kubernetes):

  1. KubeletがCRI(Container Runtime Interface)経由でcontainerdにPod起動要求
  2. containerdは、イメージ管理・スナップショット・ネットワーク設定を分担(各プラグイン使用)
  3. runcを呼び出し、Linuxカーネルのnamespaceやcgroupsを使ってプロセスを起動

<プラグイン>

  • shim(中間プロセス): 親プロセスと独立してコンテナを管理
  • snapshotter: ファイルシステムのレイヤー管理(OverlayFS対応)
  • runtime: OCIランタイム(runcなど)を利用してコンテナを起動

3-1. shim(中間プロセス)

<役割>
コンテナのライフサイクル管理を担当し、親プロセス(containerd)とコンテナプロセスの間を仲介する。

<特徴>
親プロセス(containerd)がダウンしても、コンテナはshim経由で動作を維持できる。 各コンテナごとにshimプロセスが生成され、標準入出力(stdin/stdout/stderr)やリソース管理も担当。 デフォルトではcontainerd-shimが利用されるが、Kata Containersなどの別ランタイムに置き換え可能。

3-2. snapshotter

<役割>
コンテナイメージのレイヤーをファイルシステムとしてマウント・管理する。

<特徴>
**コピーオンライト(CoW)**を利用して、効率的にファイルシステムのレイヤーを扱う。 OverlayFS、Btrfs、ZFS、Windows(Windowsではnaiveスナップショッター)など、複数のファイルシステムをサポート。
イメージのキャッシュや差分管理を最適化して高速なコンテナ起動を実現。

3-3. runtime

<役割>
OCI仕様に基づいて、コンテナプロセスを実際に起動・停止する。

<特徴>
デフォルトはrunc(軽量なコンテナランタイム)を利用。 セキュリティや仮想化が必要な場合は、runsc(gVisor)やKata Containersなどの他のOCIランタイムも選択可能。 コンテナのnamespaceやcgroupsなど、Linuxカーネルの機能を直接操作。

</details>

<特徴>

  • プラグインベースで拡張性が高い
  • Kubernetes環境ではCRI経由で直接利用可能

4. nerdctlの内部処理

Dockerのような操作感とcontainerd連携

コマンド例:

nerdctl run nginx

処理の流れ:

  1. nerdctlがcontainerdのgRPC APIに直接リクエスト
  2. containerdがイメージの取得・管理を担当
  3. containerdがruncでコンテナを起動

<内部構造の違い>

  • Docker CLIはREST API、nerdctlはgRPC経由で高速通信
  • Docker Engineが不要な分、オーバーヘッドが少ない

<特徴>

  • Dockerと同じようにcomposeやbuildもサポート(要プラグイン)
  • Kubernetesと相性が良く、軽量で高速

5. Docker CLIの内部処理

REST APIを使った柔軟な設計

コマンド例:

docker ps

処理の流れ:

  1. Docker CLIがローカルのDocker Engine API(REST)にリクエスト
  2. Docker Engineがコンテナ状態をcontainerdから取得
  3. CLIにコンテナ情報を表示

<特徴>

  • REST APIを通じて外部ツールやGUIからの操作が可能
  • システムの拡張性が高いが、オーバーヘッドも増える

6. Docker Composeはnerdctlおよびcontainerdでサポートされている?

⇒ サポートされている!

<詳細>
Docker(moby)は、Kubernetes環境ではオーバーヘッドが大きいため、より軽量でシンプルなランタイムが求められた背景がある。 一方、containerdはKubernetesとの親和性が高く、軽量かつ高速ですが、単体でDocker Composeのような構成管理を持たない課題があった。
⇒nerdctlがcomposeサブコマンドをサポートすることで、containerd畳でもComposeによる複数コンテナの管理が可能となった

また、DockerイメージやランタイムはOCI(Open Container Initiative)に準拠しており、containerdもOCI仕様をサポートしている。
つまり、containerdでもDockerと互換性があるイメージ、コンテナを利用できる。

7. まとめ

  • Docker (moby): オールインワンプラットフォームだがオーバーヘッドが大きく、開発用途に最適。
  • containerd: 軽量・高速でKubernetesに最適。さらにプラグインで柔軟に拡張可能。
  • nerdctl: Docker CLIと同等の操作感でcontainerdを直接操作可能。
  • Docker CLI: REST API経由での柔軟なコンテナ管理が可能。

<利用パターン>

  • 開発・デバッグ → Docker(moby)
  • Kubernetes運用 → containerd + nerdctl