Linux の名前空間と cgroup: コンテナの真の基盤

最終更新: 2月24の2026
  • Linux のコンテナは、プロセスが参照できるものを分離するために名前空間に依存し、プロセスが使用できるリソースの数を制限するために cgroup に依存しています。
  • さまざまなタイプの名前空間 (PID、NET、MNT、UTS、IPC、USER、cgroup、時間) と cgroup コントローラー (CPU、メモリ、I/O、pid) があり、ケースに応じて組み合わせられます。
  • Docker、Kubernetes、systemd はこれらのカーネル プリミティブを利用して、分離された環境を作成し、リソース ポリシーを適用し、サービスを均一に管理します。
  • cgroup メトリックを監視し、名前空間トポロジを理解することは、OOM やパフォーマンスの問題を診断し、本番環境での確実な分離を確保するために不可欠です。

Linuxコンテナ cgroups 名前空間

Docker、 Kubernetes 、あるいはその他のコンテナプラットフォームを扱う場合、遅かれ早かれシステムの内部構造を深く理解する必要が出てきます。コンテナを数秒で起動できる「魔法」の裏側には、Linuxカーネルの2つの重要なコンポーネント、ネームスペースとcgroupsが存在します。これらは、分離と精密なリソース管理を担う2つの要素です。これらを理解することで、単にツールを使うことと、インフラストラクチャを設計することの大きな違いが生まれます。

この記事では、その仕組みについてあまり詳しく説明せずに、冷静に考えてみましょう。 名前空間(プロセスが参照できるもの)と cgroup(プロセスが使用できるもの)DockerとKubernetesがどのようにそれらを活用するか、次のようなコマンドを使って低レベルでそれらをいじくり回す方法 unshare, ip netns または直接書き込む /sys/fs/cgroupまた、実稼働環境でのパフォーマンス、セキュリティ、トラブルシューティングに役立つトリックについても説明します。

Linux ではコンテナは実際に何を行うのでしょうか?

次のようなものを実行すると docker run魔法の仮想マシンは作成されません。 得られるのは、強力な分離と限られたリソースを備えた Linux プロセス (またはプロセス グループ) です。この「トリック」は、次の 4 つのカーネル メカニズムに基づいています。

  • 名前空間: プロセスが認識する内容 (PID、ネットワーク、ファイル システム、ホスト名など) を制御します。
  • cgroupsプロセスが使用できる量(CPU、RAM、I/O、PID など)を制御します。
  • chroot / rootfs 分離: プロセスに独自のファイルシステムを提供します。
  • 通常のLinuxプロセス結局、すべてはカーネルによって管理されるホスト PID に帰着します。

つまり、コンテナとは、1つ以上のcgroup内にネストされた一連の新しい名前空間と、独自に構築されたファイルシステムを使用して実行されるプロセスに過ぎません。残りの処理は、自動化と使いやすいAPIを備えたツール(Docker、containerd、runcなど)によって行われます。

Linux名前空間: 各プロセスが参照できるものを分離する

名前空間は、プロセス群ごとに独立した「世界」を作成するカーネルの抽象化レイヤーです。プロセスは自身の名前空間内のリソースのみを認識し、システムの残りの部分は存在していますが、プロセスからは見えません。

Linuxには現在、システム内の特定の部分に特化した複数の名前空間が存在する。これらを組み合わせることで、コンテナはホストとカーネルを共有しているにもかかわらず、独立したオペレーティングシステムとして認識されるようになる。

名前空間の主な種類

それぞれの名前空間は異なる次元を分離します。「本格的な」コンテナでは、複数の名前空間を同時に使用するのが一般的です。最も重要なものは以下のとおりです。

  • PID名前空間: プロセス識別子空間を分離します。 コンテナ内では、メインプロセスは通常PID 1です。ホストが異なる番号を持つ場合でも、同じPIDを持つことができます。異なる名前空間のプロセスは、互いに干渉することなく同じPIDを持つことができ、ホストから明示的に許可されない限り、互いにシグナルを送信することはできません。
  • ネットワーク (NET) 名前空間: 独自のインターフェース、ルート、ファイアウォール ルール、ソケットなどを備えた独立したネットワーク スタックを作成します。 各コンテナには独自のループバック、独自のIP、独自のルーティングがありますこの方法により、競合することなく異なる名前空間で同じ IP アドレスを再利用し、分離された iptables ルールを適用できます。
  • マウント(MNT)名前空間: ビューをマウントポイントから分離します。 プロセスは自身の名前空間のファイル階層のみを参照しますバインドマウント、共有ボリューム、オーバーレイなどがその下に存在するにもかかわらず、コンテナはホストの実際のルートディレクトリに触れることなく、独自の「/」を持っているように見えるのです。
  • UTS名前空間: ホスト名とNISドメインを制御します。UTS名前空間内では、 ホストシステムに影響を与えずにホスト名を変更できますテスト環境でコンテナを区別したり、ノードをシミュレートしたりするのに非常に便利です。
  • IPC名前空間: System V メッセージ キュー、セマフォ、および共有メモリを分離します。 異なるコンテナプロセスがIPCに干渉するのを防ぐ 事故または設計不良によるもの。
  • ユーザー名前空間おそらく最も繊細です。 内部名前空間の UID と GID を他のホスト ID にマッピングするこれにより、コンテナ内の「ルート」をホスト上の権限のないユーザーにマッピングすることが可能となり、権限昇格のリスクが大幅に軽減され、 コンテナセキュリティ.
  • Cgroup名前空間: 仮想化されたビューを提供します /proc/self/cgroup および cgroup 階層。 コンテナ内からは、コンテナ自体のグループのみが表示されます。これにより、ホストの実際の構造が隠され、分離が強化されます。
  • 時間名前空間: 提供を許可する 時間の認識が異なる(CLOCK_MONOTONIC や CLOCK_BOOTTIME などのクロック) 異なるプロセスグループに対して。シナリオのテストや非常に細かい分離を行う際に役立ちます。
  Contpaq: 特徴と利点

これらの名前空間はすべて /proc/<pid>/ns/次のようなツールで作成したり組み合わせたりすることができます。 unshare y nsenter名前空間に属する最後のプロセスが終了すると、その名前空間は自動的に破棄されます。

コマンドラインから名前空間を試す

コマンド unshare これは実験の基本の 1 つです。 1つ以上の新しい名前空間を持つ子プロセスを作成するたとえば、分離された PID 名前空間をテストするには、次のようにします。

unshare --pid --fork --mount-proc bash
ps aux
echo $$

内部では、その名前空間のプロセスのみが表示され、ホスト上では通常どおりすべてのプロセスが実行されているにもかかわらず、シェルはPID 1として表示されます。これは基本的に、コンテナ起動時にOCIランタイムが行う処理の骨組みです。

ネットワーク名前空間の詳細

ネットワーク機能は最も汎用性の高い機能の一つです。NET名前空間を作成する場合、 ip netns addループバックスタック以外の空のスタックが作成されます。そこから以下の操作を実行できます。

  • その名前空間をホストに接続する ペアで veth (論理ケーブルで結合された 2 つの仮想インターフェイス)。
  • 橋を作る ティポ br0 そして、複数の名前空間をそのブリッジに接続し、プライベート IP を使用して相互に認識できるようにします。
  • NATと転送 iptablesを使ってこれらの「自家製コンテナ」にインターネットアクセスを与えるには /proc/sys/net/ipv4/ip_forward.

そうです、非常に低いレベルでは、Dockerはブリッジでそれを行います docker0 およびデフォルトのネットワーク: ネットワーク名前空間、veth ピア、ブリッジ、および NAT ルールを設定します。 コンテナ同士が通信して外に出ることができるようになります。

ルートファイルシステムとセキュリティのためのマウントとユーザー名前空間

`mount namespace` を使えば、Docker を使わずにコンテナファイルシステムの作成を試すことができます。典型的なパターンは以下のとおりです。

  • 組み立てたり準備したりする 最小ルートファイルシステム (例えば、Alpine minirootfsをダウンロードした wget ディレクトリに抽出されます。
  • マウント名前空間を作成し、 pivot_root o chroot そのディレクトリに。
  • モンタル /proc, /sys および新しいルート内のその他の疑似システム。

そうすれば、シェルはそのファイルシステムだけを見ることになりますが、 実際の PID を持ち、管理者によって制御可能な通常のホスト プロセスのままになります。ユーザー名前空間も使用する場合 --map-root-user権限のないユーザー以外に、内部に「root」を置くこともできます。

cgroups: 各プロセスが消費できるリソースの量

名前空間はプロセスが参照できるリソースを決定する一方、cgroupsはプロセスグループが消費できるシステムリソースの量を制限します。これらはカーネル2.6.24で導入され、現在ではあらゆる最新のコンテナプラットフォームに不可欠なものとなっています。

概念レベルでは、cgroupは ディレクトリ内 /sys/fs/cgroup 制限、優先順位、およびメトリックを記述したファイル適切なファイルにPIDを追加すると(たとえば、 cgroup.procs)の場合、そのプロセスはそれらのルールに従います。

cgroup内のリソースコントローラー

cgroupは、特定のリソースを操作するコントローラー(サブシステム)で構成されています。最も一般的に使用されるコントローラーには以下のものがあります。

  • CPU: 割り当てられたCPU時間を制御します。 株式 相対的な優先順位を定義する。 クォータ ハードリミット(例:100ミリ秒ごとに50ミリ秒のCPUタイムアウト) CPUセット プロセスを特定のコアに固定します。
  • メモリ: RAMとスワップを制限します。 ハードリミットとソフトリミット、OOMキラー設定、統計、メモリ圧力サービスがサーバー全体を消費するのを防ぐのに非常に役立ちます。
  • ブロックI/O(blkio / io): デバイスあたりの帯域幅とIOPSを制限します。 比例した重みを割り当てるか、バイト/秒またはオペレーション/秒で正確な制限を設定します。.
  • ネットワーク帯域幅とパケットを制限するメカニズムは存在しますが、サポートがまだ成熟しておらず、通常は tc および QoS と組み合わせて使用​​されます。
  • PID: マーク プロセス/スレッドの最大数 これによりグループが作成され、スレッドをトリガーするフォーク爆弾やコード エラーが軽減されます。

実際の仕組みは非常にシンプルです。アプリケーションをcgroupに配置し、CPU、メモリ、I/O、およびPIDの最大値を指定します。あとはカーネルがルールを適用してくれます。

cgroups v1 と cgroups v2

実稼働環境では、2 種類の cgroup が見つかります。どちらの領域を踏んでいるのかを明確にすることが重要です。

  • cgroup v1各コントローラーはそれぞれ独立した階層を持ちます。次のようなルートがあります。 /sys/fs/cgroup/cpu/..., /sys/fs/cgroup/memory/..., etc. 柔軟性はありますが、大規模な管理は複雑です。多くの従来のディストリビューションやプラットフォームでは、互換性のためにこれをまだ使用またはサポートしています。
  • cgroup v2: すべてを1つにまとめる 統一された階層 en /sys/fs/cgroup制限は次のようなファイルで設定されます。 cpu.max, memory.max, io.max 等が挙げられる。 管理を簡素化し、リソースの微調整を改善します最近のディストリビューションでは、デフォルトで v2 が使用される傾向があり、ハイブリッド モードが使用されることもあります。
  Docker Swarm:実環境向けコンテナオーケストレーション

En /proc/cgroups 有効になっているコントローラ、アクティブなcgroupの数、v1とv2のどちらにいるかを確認できます(v2ではID 0の統合階層が表示されます)。コマンド mount | grep cgroup また、ファイルシステムの種類も表示されます(cgroup2fs v2を意味します、 tmpfs (通常は v1 を指します)。

cgroup の手動作成と一般的な制限

Dockerが内部で何をしているのかを完全に理解するには、cgroupを手動で作成してみる価値があります。一般的なパターンは次のとおりです。

  • cgroup ディレクトリを作成します。
  • 制限/優先度ファイルの設定 (CPU、メモリ、I/O、PIDなど)。
  • 次のように入力してcgroupにPIDを追加します。 cgroup.procs o tasks (v1 の場合)。

たとえば、cgroups v2 では、CPU とメモリに対して次のようになります。

mkdir /sys/fs/cgroup/test_app
echo "+cpu +memory +io +pids" > /sys/fs/cgroup/cgroup.subtree_control
echo "50000 100000" > /sys/fs/cgroup/test_app/cpu.max
echo "512M" > /sys/fs/cgroup/test_app/memory.max
echo $$ > /sys/fs/cgroup/test_app/cgroup.procs

これにより、現在のプロセスは コアの50%と512 MBのRAM何か集中的なことを始めると( dd (例えば/dev/nullに)変更しても、システムがおかしくなることはありません。cgroups v1では仕組みは似ていますが、以下のようなファイルがあります。 cpu.shares, cpu.cfs_quota_us, memory.limit_in_bytesそれぞれ独自の階層構造になっています。

Docker、Kubernetes、カーネルスタックの仕組み

名前空間とcgroupの上には、カーネルに到達するまで処理をやり取りする複数のソフトウェア層が存在します。ツール名が変わっても、最終的な結果は常に同じです。カーネルが名前空間の作成、cgroupの設定、ファイルシステムのマウントを呼び出します。

今日の典型的なコンテナ「スタック」は次のようになります。

  • オーケストレーション層Kubernetes、Docker Swarm、Nomad…これらはポッド/サービスのライフサイクル、スケジューリング、スケーリング、サポートを処理します マイクロサービスアーキテクチャ.
  • 高レベルコンテナランタイム: Docker エンジン、containerd、CRI-O。 彼らはオーケストレーターの命令を受け取り、それを具体的な行動に変えます。 (イメージのダウンロード、コンテナの作成、スナップショットの管理など)。
  • 低レベルOCIランタイムrunc、crun、および同様のツールは OCI 仕様を実装しており、その実装を直接担当します。 彼らは呼ぶ clone(), unshare(), mount() と会社 コンテナを具体化します。
  • Linuxカーネル名前空間、cgroup、OverlayFS、その他すべての基本機能を提供します。まさにここで「魔法」が起こります。

投げるとき docker runパスは、CLIクライアント → dockerd → containerd → runc → カーネルコールです。このパスに沿って、名前空間が作成され、cgroupが割り当てられ、ルートファイルシステムがOverlayFSとボリュームでマウントされ、ネットワークインターフェースとブリッジが接続されます。 カーネルにとって「コンテナ」と見えるものは、特定のフラグと特別なパスを持つ少数のプロセスです。.

システムの要件と事前チェック

サーバー上で名前空間やcgroupをいじり始める前に、ハードウェアとカーネルが妥当な最小要件を満たしていることを確認しておくことをお勧めします。特に、本番環境に近いものを構築する予定がある場合はなおさらです。

参考までに、少なくとも2つの論理CPU、4GBのRAM(8GB以上が望ましい)、約20GBの空きディスク容量、そしてカーネル3.10以上(cgroups v2のすべての機能と最新の機能を利用するには、4.xまたは5.xが理想的)が一般的に推奨されます。

カーネルが名前空間と cgroup をサポートしていることを確認するには、使用中のバージョンの設定ファイルを確認するだけです。

grep -E "CONFIG_.*_NS" /boot/config-$(uname -r)
grep -E "CONFIG_CGROUP" /boot/config-$(uname -r)

そこにこのようなオプションが表示されるはずです CONFIG_NAMESPACES、CONFIG_PID_NS、CONFIG_NET_NS、CONFIG_CGROUPS、CONFIG_MEMCG、CONFIG_CGROUP_SCHED としてマーク =yそれがなければ、ゲームはあまり楽しめません。

もう一つの実際的な詳細は、システムが ルート権限なしでユーザー名前空間を作成するこれはテストやツールに非常に便利です。sysctl で制御されます。 kernel.unprivileged_userns_clone0 の場合は、アクティブ化する必要があります (または、あらゆる場所で汗を流す必要があります)。

「実際の」マシン上の cgroup と名前空間の調査

これらの概念を理解するための比較的簡単な方法は、システム自体とコンテナがそれらをどのように使用しているかを確認することです。役立つヒントをいくつかご紹介します。

  • ls /proc/self/ns/ 現在のシェルが属する名前空間 (cgroup、ipc、mnt、net、pid、user、uts など) が表示されます。
  • systemd-cgtop これはcgroupごとのCPU、メモリ、I/O使用量を表示します。 top ただし、サービスとコンテナごとにグループ化されます。
  • En /sys/fs/cgroup DockerまたはPodmanが各コンテナに作成するcgroupは、通常パス内のコンテナIDを使って見つけることができます。 cgroup.procs 関連するPIDを一覧表示する.
  • /proc/<pid>/cgroup 特定のプロセスがどの cgroup に割り当てられているかがわかるので、問題のあるアプリを追跡するのに最適です。
  NVMe、コントローラー、キャッシング:究極のパフォーマンスを実現する鍵となる組み合わせ

コンテナは、CPU、メモリ、blkioなどの制限を持つcgroups階層の単なる別のブランチであり、手動で作成する場合と同様です。Dockerの優れた点は、これを自動的に、かつユーザーフレンドリーなAPIで実現してくれることです。

パフォーマンス、NUMA、アフィニティ戦略

高負荷シナリオ(大規模データベース、集中的な処理、ストリーミングなど)では、単に「コンテナを起動する」だけでは不十分です。各サービスへのCPUとメモリの割り当て方が、パフォーマンスに大きく影響します。

cpusetドライバを使用すると、プロセスやサービスを特定のCPUとNUMAノードのセットに割り当てることができます。手順は以下のとおりです。

mkdir /sys/fs/cgroup/cpuset/dedicated_cpus
echo "4-7" > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.cpus
echo "0" > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.mems
echo 1 > /sys/fs/cgroup/cpuset/dedicated_cpus/cpuset.cpu_exclusive

これにより、特定のサービス用にコア群を予約し、システムの他の部分からの干渉を防ぐことができます。これは、全員が同じCPUを競合する際に発生しやすいノイズ、スレッド移行、およびレイテンシの問題を軽減します。

メモリレベルでは、numactlとcgroupsを組み合わせる(または、利用可能な場合はメモリコントローラのNUMA設定を直接使用する)ことで、データを使用するコアに最も近い物理ノード上にデータを保持することができます。これは、レイテンシに敏感なアプリケーションで特に顕著です。

cgroup と名前空間の監視と観測可能性

リソース制限を設定するのは良いことですが、 何が起きているのかを測定しなければ、気づかないうちに自ら足を撃ってしまう可能性があります。cgroup v2自体はログにかなり多くの有用なメトリクスを提供する *.stat, *.events y *.pressure.

いくつかの実際的な例:

  • cpu.stat グループが使用した CPU 時間、調整された回数などがわかります。
  • memory.current y memory.max 電流消費量と設定された制限が表示されます。
  • memory.events OOM、ソフト制限の到達、その他の脅威を記録します。
  • io.stat デバイスごとに読み取り/書き込みを蓄積します。

簡単に概要を説明すると、 systemd-cgtop これは非常に実用的で、より工業的なものを望むなら、 cアドバイザー Docker/containerd cgroupメトリクスをPrometheusにエクスポートすることで、Grafanaで完全なダッシュボードをあまり手間をかけずに構築できます。 高度なシステムモニター.

名前空間の問題をデバッグする場合、 /proc/<pid>/ns/ シンボリックリンクを比較すると、 どのプロセスが全く同じ名前空間を共有しているかを知る、またはスタックした「失われた」ネットワーク名前空間を見つけます。

cgroup マネージャーとして systemd を使用する

ほとんどの最新のディストリビューションでは、systemd はサービス、スライス、スコープの管理に cgroup を多用しています。リソース境界を持つユニットを定義すると、systemd は実際にそのサービス用の cgroup を作成し、ハンドラを設定します。

次のようなディレクティブで宣言されたサービス:

CPUQuota=50%
CPUWeight=500
MemoryMax=512M
IOReadBandwidthMax=/dev/sda 10M

これは、そのユニットのプロセスに対するcgroupのCPU、メモリ、およびI/Oの制限を意味します。これにより、同じ基盤となるカーネルサブシステムを使用して、「従来型」のサービス管理をコンテナモデルに合わせることができます。systemdとその要件の詳細については、systemdの変更点を参照してください。

従来のサービスとコンテナを組み合わせて使用​​するチーム(非常に一般的)にとって、systemd を利用して重要なデーモンに制限を設定することは、コンテナ化された環境内のポッドによってリソースが強制終了されるのを防ぐための洗練された方法です。

データベースから監視エージェント、カスタムアプリケーションまで、複数のワークロードが存在する環境では、名前空間とcgroupを使いこなすことで、強力な分離ポリシーを定義し、サービス品質を調整し、各コンポーネントがCPUとメモリを適切に割り当てられるようにすることができます。Docker、Kubernetes、systemdがこれらのカーネル機能をどのように活用しているかを理解することで、より安全なマルチテナントインフラストラクチャを設計し、複雑な問題(メモリ不足エラーからI/Oボトルネックまで)をデバッグし、安定性を損なうことなくハードウェアを最大限に活用できるようになります。

musl の systemd 259 サポート
関連記事:
systemd 259: musl、セキュリティ、キー変更のサポート