同じサーバーで、Web サイトのアプリとデータベースを動かしたい。 でも、設定ファイルが混ざったり、片方がメモリを使い切ったりすると困ります。
コンテナは、こうしたアプリにそれぞれの実行環境を用意するための仕組みです。 アプリごとにファイルや見えるプロセスを分け、使えるメモリなどにも上限を付けられます。 この章では「何が分かれていて、何が共通なのか」を、図と具体例で見ていきます。
GOAL この章のゴール
- コンテナの中と外で、見えるプロセスが違う理由を説明できる
- 「見える範囲を分ける」と「使える量を制限する」を区別できる
- コンテナと VM の違いを、カーネルを共有するかどうかで説明できる
アプリは、サーバーの上で動いている
まず、動いているプログラムのことを プロセス と呼びます。 Web サーバーの nginx も、起動するとプロセスになります。
nginx をコンテナで動かしても、サーバーの上でプロセスが動くことは同じです。 変わるのは、そのプロセスから見えるファイルや、ほかのプロセスの範囲です。 コンテナを動かしている側のマシンを ホスト と呼びます。
たとえば、コンテナの中でプロセス一覧を見ると、nginx とその仲間だけが並びます。 ホストで同じ一覧を見ると、その nginx に加えて、ほかのアプリも並びます。 同じマシンで動いていても、見る場所によって一覧が違うのです。
namespace: 見える世界を分ける
下の図で、左右の nginx: master process を探してみてください。
左では番号が 4231、右では 1 です。
これは別々の nginx ではなく、同じプロセスを、コンテナの外と中から見たものです。
この番号を PID (プロセス ID) と呼びます。
右の一覧には、左にある sshd などのプロセスが出てきません。
コンテナの中から見える範囲を分けているからです。
このように、プロセスから見える範囲を分ける Linux の仕組みが namespaceネームスペースプロセスの一覧やネットワークなど、プロセスから見える範囲を種類ごとに分ける Linux の仕組み。用語集で見る です。
プロセスの番号と見える範囲を分けるものは、PID namespace と呼びます。
新しい PID namespace で最初に起動したプロセスには、内側で 1 番が付きます。
図の nginx のように、その後に増えたプロセスには別の番号が付きます。
分けられるのはプロセスだけではありません。
たとえば network namespace でネットワークを分けると、別々のコンテナがそれぞれ内側で同じ 80 番ポートを使えます。
ポートは通信の受け口の番号です。
別のネットワークに分かれているので、番号が同じでも取り合いになりません。
namespace にはほかにも種類がありますが、まずは「プロセスやネットワークの見える範囲を分けられる」と押さえれば、この先を読めます。 どの範囲を分けるかは設定次第で、複数のコンテナが同じ namespace を共有することもあります。
ファイル: 同じ /etc でも、中身は別
プロセスの一覧が分かれていても、設定ファイルまで共通だと困ります。 そこで、コンテナにはアプリ用のファイル一式を用意します。 アプリ本体やライブラリなどを配布用にまとめたものが コンテナイメージ です。
Linux のファイルは、/ という一番上のディレクトリからたどります。
これを ルートディレクトリ と呼びます。
コンテナでは、イメージから用意したファイル一式を、この / の下に見せます。
だから、ホストとコンテナの両方に /etc があっても、通常は別のディレクトリを指します。
この準備には、ファイルシステムの接続状態を分ける mount namespace や、ルートを入れ替える pivot_root などが使われます。
ホスト自身の / をコンテナのものに置き換えるわけではありません。
実際の手順は Part 3 の「コンテナを手で作る」 で試します。
cgroups: メモリや CPU の使いすぎを防ぐ
次は、アプリがメモリをどんどん使ってしまう場面です。 ほかのアプリが見えなくても、同じホストのメモリを使っていることは変わりません。 プロセスの一覧を分けただけでは、メモリの使いすぎは防げません。
そこで「このコンテナのメモリはここまで」と上限を設定します。 プロセスをグループにまとめ、CPU やメモリなどの使用量を管理する仕組みが cgroupsシーグループスプロセスをグループにまとめ、CPU やメモリなどの使用量を制限・監視する Linux の仕組み。用語集で見る です。
上限に達したときの動きは、資源によって違います。
- CPU: 一定時間内に使える CPU 時間を使い切ると、次の期間まで実行が待たされます。
- メモリ: 上限に達し、不要なメモリを回収しても足りないと、グループ内のプロセスが終了させられることがあります。これがメモリ不足による OOM (Out Of Memory) の一例です。
namespace は「見える範囲」、cgroups は「使える量」を担当します。 コンテナを起動しただけで必ず適切な上限が付くわけではないので、使える量は別途設定します。
VM との違い: カーネルを共有する
ここまでで、プロセス、ファイル、使える量を分けられることが分かりました。 では、コンテナごとに OS も丸ごと起動しているのでしょうか。
通常の Linux コンテナでは、OS の中核である カーネル をホストと共有します。 カーネルは、メモリを管理したり、ファイルやネットワークへのアクセスを処理したりする部分です。 コンテナの中のアプリがファイルを開くときも、ホストのカーネルが処理します。
VM (仮想マシン) は、それぞれが自分のカーネルを起動します。 図では、カーネルの段がいくつあるかを見てください。
| 観点 | VM | 通常の Linux コンテナ |
|---|---|---|
| カーネル | VM ごとに起動する | ホストと共有する |
| アプリを動かす準備 | OS とアプリを起動する | 実行環境を用意してアプリを起動する |
| OS の選択 | VM ごとに別の OS を選べる | ホストのカーネルで動くアプリを使う |
コンテナは、カーネルを毎回起動する必要がないぶん、起動やメモリ使用量の負担を抑えやすくなります。 ただし、共有しているカーネルの不具合が、複数のコンテナに影響する可能性もあります。 より強く分離する gVisor や Kata Containers などの選択肢は、Part 3 の「分離のレベル」 で扱います。
イメージ: 共通のファイルを再利用する
コンテナイメージには、アプリを動かすためのファイルが入っています。 たとえば、同じ Linux のファイル一式を土台にして、Web アプリ用とバッチ処理用のイメージを作れます。 そのたびに土台を丸ごと保存すると、同じファイルが重複します。
そこでイメージは、ファイルの追加・変更・削除を 層 (レイヤー) として持ちます。
同じ保存先に共通の層があれば、それを再利用できます。
docker pull でダウンロード済みの層が飛ばされるのも、このためです。
実行時には、イメージの層にコンテナごとの書き込み層を重ねます。 この重ね合わせによく使われるのが OverlayFSオーバーレイエフエス複数のディレクトリを重ね、1 つのディレクトリに見せる Linux のファイルシステム。用語集で見る です。 アプリからは、層を意識せず普通のファイルとして読み書きできます。
コンテナの中でファイルを変更しても、元のイメージは変わりません。 そのコンテナを削除すると、書き込み層も失われます。 残したいデータをボリュームに保存するのは、このためです。
試す: ホスト側からコンテナを見つける
ここで、自分専用のハンズオン環境を使って確かめます。 探すのは、コンテナの中のアプリが、ホストのプロセス一覧にも出てくることです。
Kubernetes がコンテナを動かすマシンを ノードノードKubernetes がコンテナを動かすマシン。物理マシンの場合も VM の場合もある。用語集で見る と呼びます。
これらのマシンと、それらを管理する仕組みをまとめて クラスタクラスタKubernetes でアプリを動かすノードと、それらを管理する仕組みの集まり。用語集で見る と呼びます。
ノードの名前と台数は、kubectl get nodes で確認できます。
- 画面下の Web ターミナルを開き、環境に接続します。
- 次のコマンドを実行します。ノードの中に一時的な作業場を作り、プロセス一覧を表示します。
- 出力の中に、
corednsやcilium-agentなどのアプリ名を探します。番号や並びは環境によって変わります。
kubectl debug node/$(kubectl get node -o jsonpath='{.items[0].metadata.name}') -it --profile=sysadmin \
--image=busybox:1.37 -- chroot /host \
sh -c 'ps -eo pid,ppid,comm --forest | grep -A2 containerd-shim | head -20'出力例
1204 1 containerd-shim-runc-v2
1250 1204 \_ pause
1290 1204 \_ coredns
--
1320 1 containerd-shim-runc-v2
1366 1320 \_ pause
1402 1320 \_ cilium-agentcontainerd-shim は、コンテナの実行を支えるプロセスです。
細かな親子関係は後の章で扱います。
今は、コンテナ内で動くアプリを、ホスト側の一覧でも見つけられたことを確認できれば十分です。
終わったら、一時的な作業場を削除します。
kubectl delete pods -l app.kubernetes.io/managed-by=kubectl-debugふりかえり
Q図の nginx が、ホストでは PID 4231、コンテナ内では PID 1 でした。なぜでしょう?
同じプロセスでも、ホストとコンテナ内では別の番号で見えます。コンテナ内のすべてのプロセスが 1 番になるわけではありません。
Qコンテナのメモリ使用量に上限を付けるのは、どの仕組み?
cgroups が使える量を管理します。namespace で見えるプロセスを分けても、それだけではメモリの上限は付きません。
Q通常の Linux コンテナ同士が共有しているのはどれ?
アプリ用のファイルや書き込み層が別でも、カーネルは共有します。VM はそれぞれにカーネルを持ちます。
参考
- Linux man-pages「namespaces(7)」: https://man7.org/linux/man-pages/man7/namespaces.7.html
- Linux man-pages「pid_namespaces(7)」: https://man7.org/linux/man-pages/man7/pid_namespaces.7.html
- Linux man-pages「pivot_root(2)」: https://man7.org/linux/man-pages/man2/pivot_root.2.html
- Linux kernel documentation「Control Group v2」: https://docs.kernel.org/admin-guide/cgroup-v2.html
- Docker docs「What is a container?」: https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/
- Docker docs「Understanding the image layers」: https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/
- Kubernetes ドキュメント「Nodes」: https://kubernetes.io/docs/concepts/architecture/nodes/