コンテナの正体:namespace と cgroup の原理
コンテナが軽量VMではない理由が腑に落ちる。namespaceが「見え方」を隔離し、cgroupが「使える量」を制限する——ホストと同じカーネル上で動くただのプロセスだと原理から理解できる。
- 1.コンテナは独自カーネルを持たない。ホストのカーネル上で動くプロセス群に、namespace と cgroup という2つの制約を被せただけ。
- 2.namespace は資源の見え方を隔離する:PID・NET・MNT・USER 等で「自分専用の世界」を演出。cgroup は使える量を制限する:CPU・メモリ・I/O を上限付きで配る。
- 3.隔離はカーネルの分離ではなく可視性の制御なので、カーネルの脆弱性は全コンテナに波及しうる。これが「軽量VMではない」核心。
「箱」の中身を開けてみる
コンテナを起動すると、その中ではプロセス番号 1 から始まる独自のプロセス表が見え、専用の IP アドレスを持ち、ホストとは別のファイルツリーが広がっています。一見すると小さな OS が動いているようですが、ホスト側で ps を打つと、その「コンテナ内のプロセス」がホストのプロセス一覧にそのまま並んでいます。つまりコンテナは別のマシンでも軽量な仮想マシンでもなく、ホストのカーネルが直接スケジュールしている、ただの Linux プロセスです。
VM が CPU 命令レベルで別のカーネルを動かすのに対し、コンテナにはゲストカーネルが存在しません。違いの詳細は /devops/container-vs-vm/ にありますが、本稿ではその「壁」を作っている2つのカーネル機構——namespace(隔離) と cgroup(制限)——を内部から見ていきます。
namespace:資源の「見え方」を分ける
namespace はカーネルが管理する各種グローバル資源を、プロセスごとに別々のインスタンスに見せかける仕組みです。資源を分割するのではなく、どの集合が見えるか を切り替えます。clone(2) / unshare(2) / setns(2) の3つのシステムコールで作成・参加します。
| namespace | 隔離する対象 | 効果 |
|---|---|---|
| PID | プロセスID空間 | コンテナ内では PID 1 から振り直し。外のプロセスは見えない |
| NET | ネットワークスタック全体 | 独自の NIC・ルーティング表・iptables・ポート空間 |
| MNT | マウントポイント集合 | 独自のファイルシステム階層(ルートを差し替え) |
| UTS | ホスト名・ドメイン名 | コンテナ固有のホスト名を持てる |
| IPC | System V IPC・POSIX メッセージキュー | プロセス間通信オブジェクトを分離 |
| USER | UID/GID のマッピング | コンテナ内 root を外側の非特権ユーザーへ写像 |
| CGROUP | cgroup 階層の見え方 | 自分の cgroup ルートだけを見せる |
要点は、namespace が 入れ子と写像 で成り立っていることです。たとえば PID namespace では、コンテナの「PID 1」はホストから見れば PID 1万番台のごく普通のプロセスです。カーネルは1つのプロセスに対し、所属する各 PID namespace ごとに異なる番号を割り当てて管理しています。
コンテナ内で root(UID 0)に見えるプロセスを、ホストでは UID 100000 などの非特権ユーザーへ写像するのが USER namespace です。これにより「コンテナ内 root」が万一ブレイクアウトしても、ホスト上ではただの一般ユーザー権限しか持ちません。rootless コンテナの基盤であり、特権分離の最後の砦になります。
MNT namespace と組み合わせて使うのが pivot_root(2) です。コンテナイメージを展開したディレクトリを新しいルート / に差し替えることで、ホストのファイルツリーから切り離します(古い chroot の強化版に相当)。NET namespace では、ホスト側に作った仮想イーサネットペア(veth)の片端をコンテナの namespace へ移し、もう片端をブリッジへ繋ぐことで通信経路を構築します。
cgroup v2:資源の「使える量」を配る
namespace が可視性を制御するのに対し、cgroup(control group)は 資源の量 を制御します。v1 は資源ごとに独立した階層を持ち管理が煩雑でしたが、v2 では単一の統一階層 に一本化され、すべてのコントローラが同じツリー構造を共有します。プロセスは末端(リーフ)の cgroup にのみ所属できる、というルールが整合性を保ちます。
cgroup は /sys/fs/cgroup 配下の擬似ファイルシステムとして見え、ディレクトリを mkdir すれば cgroup が1つ生まれ、cgroup.procs に PID を書き込めばそのプロセスが所属します。主要コントローラは次のように働きます。
# メモリ上限を 512MiB に設定(cgroup v2)
echo "536870912" > /sys/fs/cgroup/myapp/memory.max
# CPU を「100ms 周期のうち最大 50ms」= 0.5 コア相当に制限
echo "50000 100000" > /sys/fs/cgroup/myapp/cpu.max
# プロセスをこの cgroup に所属させる
echo $PID > /sys/fs/cgroup/myapp/cgroup.procs
- CPU:
cpu.maxのquota period形式で、各周期に使える時間を上限化(CFS バンド幅制御)。さらにcpu.weightで空き時間の比例配分を決めます。上限到達時はプロセスがスロットリングされ、待たされます。 - メモリ:
memory.maxがハードリミット。これを超えて確保しようとすると、まず回収(reclaim)が走り、それでも足りなければ cgroup 単位の OOM killer が内部のプロセスを停止します。memory.highを併用するとソフトリミット(超過時に積極的に reclaim しつつ即死は避ける)を設定できます。 - I/O:
io.maxでブロックデバイス単位の IOPS・帯域を制限。io.weightで比例配分します。
cpu.max が 50000 100000 のとき意味は「0.5 コア」ですが、実体は周期ごとの実行時間の上限です。マルチスレッドアプリが短時間に複数コアを使い切ると周期序盤で割当を消費し、残りの時間スロットリングされて待ちが発生します。CPU 使用率が上限に達していないのにレイテンシが伸びる典型原因がこれです(cpu.stat の throttled_usec を確認します)。
なぜ「軽量VM」ではないのか
ここまでで核心が見えます。コンテナは 新しいカーネルを起動していません。namespace で見える資源を絞り、cgroup で使える量を絞った、ホストカーネル上の普通のプロセスです。だからこそ起動はミリ秒で、メモリも数十 MB から済みます。一方で代償もあります。
全コンテナがホストの単一カーネルを共有するため、カーネルに権限昇格の脆弱性があれば、1つのコンテナからホストや他コンテナへ波及しうるのが「軽量VMではない」最大の帰結です。VM がハイパーバイザという別レイヤーで命令を隔離するのとは、隔離の強度が原理的に異なります。マルチテナントで強い隔離が要るなら、gVisor(ユーザー空間カーネル)や Firecracker / Kata Containers(軽量 VM)を被せて二重化します。
この前提が、コンテナ運用の作法を規定します。状態を内部に持たず使い捨てにする発想(/devops/immutable-infra/)や、1コンテナ1責務でスケールさせる /devops/microservices/ の設計は、いずれも「コンテナ=制約付きプロセス」という正体から自然に導かれます。
「コンテナはなぜ軽いか」には『独自カーネルを持たずホストカーネルを共有するから』、「何で隔離しているか」には『namespace(可視性)と cgroup(資源量)』と答えます。両者の役割分担——namespace は見え方、cgroup は使える量——を取り違えないことが採点ポイントです。
観測:制約の中身を覗く
コンテナの内部状態は、結局カーネルのインターフェース越しに観測します。cgroup の memory.current / cpu.stat(throttled_usec を含む)/ io.stat を読めば、その瞬間の消費と制限到達状況が分かります。プロセス単位やシステムコール単位でさらに踏み込むなら、カーネルにフックを仕掛ける /devops/ebpf-observability/ が強力です。いずれにせよ、コンテナを「ブラックボックスの小さなマシン」ではなく「カーネルが管理する名前空間と制御群の組」として捉えると、観測も制限もデバッグも一本の筋で理解できます。
DevOps/インフラ Article
コンテナの正体:namespace と cgroup の原理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コンテナ
比較で見る軸
難易度: advanced / カテゴリ: DevOps/インフラ / タグ数: 5
導入後に効く点
namespace は資源の見え方を隔離する:PID・NET・MNT・USER 等で「自分専用の世界」を演出。cgroup は使える量を制限する:CPU・メモリ・I/O を上限付きで配る。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- DevOps/インフラ
- タグ数
- 5
判断チェックリスト
- 自社の用途が「コンテナ / namespace」に近いか確認する。
- 強みである「コンテナは独自カーネルを持たない。ホストのカーネル上で動くプロセス群に、namespace と cgroup という2つの制約を被せただけ。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。