Docker

containerd とは?Docker ユーザーのためのコンテナランタイム入門

Flaty

この記事で分かること

  • containerd が解決する問題
  • コンテナランタイムの分類(高レベル/低レベル)
  • Docker と containerd の関係
  • containerd のアーキテクチャ
  • Kubernetes が containerd を使う仕組み(CRI)

Docker を使っていると、「containerd」という名前を見かけることがあります。docker info を実行すると containerd のバージョンが表示されますし、Kubernetes のドキュメントでも「コンテナランタイムとして containerd を使用」といった記述が出てきます。

この記事では、Docker ユーザーの視点から、containerd とは何か、なぜ存在するのかを解説します。

なぜ containerd を知る必要があるのか

Docker だけ知っていれば十分?

ローカル開発で Docker を使うだけなら、containerd を意識する必要はありません。しかし、以下のような場面では containerd の理解が役立ちます。

  • Kubernetes を運用する: Kubernetes v1.24 以降、Docker(dockershim)のサポートが削除され、containerd や CRI-O といったランタイムを直接使用するようになりました
  • コンテナのトラブルシューティング:「コンテナが起動しない」「イメージが pull できない」といった問題の原因が Docker 層ではなく containerd 層にあることがある
  • 軽量なコンテナ環境を構築する: CI/CD 環境や組み込みシステムでは、Docker デーモンなしで containerd を直接使うケースがある

コンテナ技術のレイヤー構造

containerd を理解するには、コンテナ技術がどのようなレイヤーで構成されているかを知る必要があります。

コンテナ技術のレイヤー構造

Docker は「コンテナを便利に使うためのツール群」であり、その内部で containerd と runc を使っています。

コンテナランタイムとは

「コンテナランタイム」という用語は文脈によって指すものが異なります。混乱を避けるため、高レベルと低レベルに分けて整理します。

高レベルランタイム(コンテナマネージャー)

イメージの管理、コンテナのライフサイクル管理、ネットワーク設定などを担当します。

  • containerd: Docker 社が開発し、CNCF に寄贈。現在広く使われている
  • CRI-O: Red Hat 主導で開発。Kubernetes 専用に設計されている
  • Podman: Red Hat が開発するコンテナエンジン。Docker 互換の CLI を持ち、デーモンレスで動作。CRI は実装しておらず、主にローカル開発やスタンドアロン環境向け

低レベルランタイム(OCI ランタイム)

実際にコンテナを作成・起動する処理を担当します。Linux カーネルの機能(namespace、cgroup)を使ってプロセスを隔離します。

  • runc: Docker 社が開発し、OCI に寄贈。containerd のデフォルトランタイム
  • crun: Red Hat が開発。C 言語で書かれ、runc より高速
  • gVisor (runsc): Google が開発。ユーザー空間で動作する独自カーネルがシステムコールを処理し、ホスト OS への攻撃面を削減
  • Kata Containers: 軽量 VM 内でコンテナを実行してセキュリティを強化

高レベルランタイムは、低レベルランタイムを呼び出してコンテナを起動します。containerd はデフォルトで runc を使いますが、設定で他のランタイムに切り替えることも可能です。

Docker と containerd の関係

Docker の歴史と containerd の誕生

Docker は当初、コンテナの起動からイメージ管理、ネットワークまですべてを 1 つのモノリシックなデーモンで処理していました。

しかし、この設計には課題がありました。

  • 再起動の影響: 旧設計では Docker デーモンを再起動すると、実行中のコンテナも停止してしまう
  • 拡張性: 他のツール(Kubernetes など)が Docker の機能の一部だけを使いたくても、全体を組み込む必要がある
  • 標準化: コンテナ技術がエコシステムとして成長するには、特定企業の実装に依存しない標準が必要

これらの課題を解決するため、Docker 社は 2016 年にコンテナのコア機能を containerd として切り出し、2017 年に CNCF(Cloud Native Computing Foundation)に寄贈しました。

現在の Docker アーキテクチャ

現在の Docker は以下のように動作します。

Docker のアーキテクチャ
  1. ユーザーが docker run を実行
  2. Docker CLI が Docker デーモン(dockerd)に API リクエストを送信
  3. dockerd が containerd にコンテナ作成を依頼
  4. containerd が runc を呼び出してコンテナを起動
  5. runc はコンテナを起動した後、終了する(常駐しない)
  6. containerd の shim プロセスがコンテナの親プロセスとして残る

このアーキテクチャにより、shim が生きている限り、containerd を再起動しても実行中のコンテナは概ね影響を受けません(shim が独立して動作するため)。

Docker なしで containerd を使う

containerd は Docker なしでも使えます。containerd に付属する ctr コマンドや、Docker 互換の CLI を持つ nerdctl を使います。

# ctr でイメージを pull してコンテナを実行
ctr image pull docker.io/library/nginx:latest
ctr run -d docker.io/library/nginx:latest my-nginx

# nerdctl(Docker 互換の使い勝手)
nerdctl run -d --name my-nginx nginx:latest

nerdctlcontainerd/nerdctl で開発されており、docker コマンドとほぼ同じ使い方ができます。Docker Compose 互換の nerdctl compose も提供されています。

containerd のアーキテクチャ

containerd は以下のコンポーネントで構成されています。

containerd のアーキテクチャ

コアサービス

  • Content Store: イメージのレイヤー(blob)を保存
  • Metadata Store: コンテナやイメージのメタデータを BoltDB で管理
  • Snapshotter: コンテナのファイルシステムを管理。overlayfs や btrfs などのバックエンドをサポート
  • Task Service: コンテナのライフサイクル(作成、開始、停止)を管理

shim(シム)

containerd と低レベルランタイム(runc など)の間に位置するプロセスです。

shim の主な役割は以下の通りです。

  • プロセスの親として動作: コンテナプロセスの親となり、containerd が再起動してもコンテナは動き続ける
  • stdio の中継: コンテナの標準入出力を containerd に中継
  • 終了ステータスの報告: コンテナ終了時に終了コードを containerd に報告

shim があることで、containerd のアップグレードやクラッシュがコンテナに影響しない設計になっています。

プラグインアーキテクチャ

containerd はプラグインベースで設計されています。ほぼすべての機能がプラグインとして実装されており、必要に応じて拡張や置き換えが可能です。

# containerd のプラグイン一覧を確認
ctr plugins ls

Kubernetes と containerd

CRI(Container Runtime Interface)

Kubernetes はコンテナランタイムを直接呼び出すのではなく、CRI という標準インターフェースを通じて操作します。

Kubernetes と containerd の関係

kubelet が CRI 経由で containerd に指示を出し、containerd が実際のコンテナ操作を行います。 ※ shim の単位は runtime 実装や設定で変わります。本図は io.containerd.runc.v2(Kubernetes + containerd)で Pod 単位になるケースを示しています。

なぜ Docker ではなく containerd を使うのか

Kubernetes v1.20 で「Docker サポートの非推奨」がアナウンスされ、v1.24 で dockershim(Docker を CRI 経由で呼び出すためのアダプター)が削除されました。 ただし、Docker Engine を使い続けたい場合は cri-dockerd を介して利用できます。

この背景には以下の理由があります。

  • オーバーヘッドの削減: kubelet → dockershim → dockerd → containerd という経路が、kubelet → containerd に短縮される
  • メンテナンスの簡素化: dockershim のコードを Kubernetes 本体から削除できる
  • 機能の重複: Kubernetes は Docker のイメージビルド機能やネットワーク管理機能を使わない

ただし、Docker でビルドしたイメージは引き続き使える ことは重要なポイントです。Docker イメージは OCI(Open Container Initiative)の標準と互換であり、containerd や CRI-O でもそのまま実行できます。

crictl コマンド

Kubernetes 環境で containerd を操作するには、crictl コマンドを使います。

使用前にランタイムのエンドポイントを設定しておくとスムーズです。

# /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
# Pod の一覧を表示
crictl pods

# コンテナの一覧を表示
crictl ps

# イメージの一覧を表示
crictl images

# コンテナのログを表示
crictl logs <container-id>

crictl は CRI 互換のランタイムすべてで使えるため、containerd でも CRI-O でも同じコマンドで操作できます。

まとめ

  • containerd は、Docker の内部で使われているコンテナランタイム。CNCF プロジェクトとして独立している
  • コンテナランタイムには高レベル(containerd、CRI-O)と低レベル(runc)の区分がある
  • Docker は containerd を呼び出し、containerd は runc を呼び出してコンテナを起動する
  • Kubernetes v1.24 以降は containerd を直接使用する(Docker を経由しない)
  • Docker でビルドしたイメージは、OCI 標準と互換であるため containerd でもそのまま使える
  • shim プロセスにより、containerd の再起動がコンテナに影響しない設計になっている

参考

ABOUT ME
ぴょい
ぴょい
しがないソフトウェアエンジニアです。 学んだことをメモがわりに書いています。たまに技術以外の話も。 このブログの内容は個人の見解であり、所属組織とは関係ありません。記載内容には誤りがある可能性がありますので、公式ドキュメント等もあわせてご確認ください。
記事URLをコピーしました