JA EN
体系OSと実行環境
·★ 会員·15分で読めます

コンテナを1から — namespaceとcgroupの正体

コンテナは軽い仮想マシンではなく、見えるものを制限されたただのプロセスです。namespace(見え方の隔離)・cgroup(使用量の上限)・イメージ(層の積み重ね)の3つに分解し、比喩から overlayfs・cpu.max・exit 137 の読み方まで、前提知識ゼロで積み上げます。

対象textタスクsystems

「自分の環境では動く」を終わらせた道具

新しく入ったメンバーがコードを動かすまでに半日かかる。手元では通ったテストが本番で落ちる。原因を追うと、言語のバージョンが違った、Cライブラリが古かった、環境変数が1つ足りなかった——といった話に行き着きます。ソフトウェアはコードだけでは動きません。その周りにある大量の前提とセットで、はじめて動きます。

コンテナは、その前提ごと丸ごと配れるようにした道具です。「同じイメージなら、ノートPCでもCIでも本番でも同じものが立ち上がる」という約束が、いまのサーバ運用の土台になっています。

ここで最初のつまずきが来ます。コンテナは軽い仮想マシンではありません。仮想マシンのように振る舞って見えるものの正体は、あなたのマシンで動いているごく普通のプロセスです。この記事は、その正体を namespace・cgroup・イメージの3つに分解して、最後まで積み上げます。

比喩: 一戸建てを建てるか、同じ建物に壁を立てるか

仮想マシン(VM)は、土地を借りて家をもう一軒建てるやり方です。基礎から建てるので、水道もガスも玄関も全部その家の専用になります。ソフトウェアで言えば、CPUやディスクの「ふり」をする仮想ハードウェアを用意し、その上でゲストOSのカーネルを丸ごと起動します。だから中でLinuxを動かすことも、Windowsを動かすこともできます。強い隔離が手に入る代わりに、家を1軒建てるだけの手間と資材がかかります。

コンテナは、すでにある建物の中に壁を立てて部屋を仕切るやり方です。基礎も水道管も建物と共有し、専用に用意するのは壁と鍵だけ。共有されているものがカーネルです。カーネルとは、OSの中核でハードウェアを直接扱う部分のこと(この役割はプロセスとメモリを1からで扱いました)。コンテナはこのカーネルを新しく起動しません。ホストのカーネルにそのまま頼みごとをします。

ここから性質がすべて導けます。カーネルを起動しないので立ち上がりはプロセスの起動と同じ手順で済みます。カーネルが1つなので同じ機械に載る数が多い。そして、カーネルを共有するがゆえにLinuxのコンテナは基本的にLinuxの上でしか動かないし、隔離の強さもVMほどではありません。軽さと引き換えに、壁1枚ぶん薄いのです。

どちらが優れている、という話ではありません。素性の分からないコードを他人から預かって動かすなら、壁は厚いほうがいい。自分たちが書いたアプリを、同じ形で何十個も並べて回したいなら、部屋を仕切るほうが速くて安い。だからクラウドの実際の姿は二階建てで、事業者が用意したVMの中で、あなたのコンテナが動いています。「VMかコンテナか」ではなく、境界を何段用意するかの設計です。

直感: 「コンテナ」という機能はLinuxに存在しない

意外に思われますが、Linuxカーネルに create_container() のような命令はありません。あるのは、普通のプロセスに次の3つを重ねがけする仕組みだけです。

  1. namespace(名前空間) — そのプロセスから見えるものを差し替える。他のプロセス、ネットワーク、ファイルツリーが、外の世界と切り離される
  2. cgroup(コントロールグループ) — そのプロセスが使える量に上限をかける。CPU・メモリ・I/O・プロセス数
  3. イメージ — そのプロセスに見せるファイル一式を差し替える。読み取り専用の層を積み重ねて作られる

「コンテナを起動する」とは、この3つを設定してからプログラムを実行することの言い換えにすぎません。ホスト側で ps を打てば、コンテナの中のプロセスは普通に一覧に出てきます。VMの中身がホストから見えないのとは対照的です。

docker run と打ったときに起きることを順に並べると、こうなります。ランタイムはまずイメージの層をディスク上にそろえ、それらを重ねた1つのファイルツリーを組み立てる。次に namespace を一式こしらえて、これから作るプロセスの「見える世界」を差し替える。続いて cgroup のディレクトリを作り、上限を書き込み、そこにプロセスを登録する。最後に根っこを先ほどのファイルツリーへ切り替えてから、イメージに記録された起動コマンドを実行する。ここまでが「起動」で、走り出したあとは、あなたが端末から立ち上げたプログラムとまったく同じ資格でCPUの順番待ちに並びます。

裏を返せば、「コンテナが落ちた」の中身はたいてい中のプログラムが終了しただけです。OSのシャットダウン処理は挟まらず、プロセスが消えれば namespace も一緒に片付きます。停止が速い理由も、落ちたときに手がかりが最後のログ1行しか残らない理由も、同じところから来ています。

3つ目のイメージだけ、先に効き目を見ておきます。イメージは読み取り専用の層でできていて、同じ層はホスト上に1回だけ置けば全員で使い回せます。100個のコンテナを同じイメージから起動しても、イメージ本体のコピーが100個できるわけではありません。増えるのは、それぞれが書き込んだ差分だけです。VMのようにディスクイメージを複製する方式との差は、ここで桁になります。

FIG 1横軸 n を「同じイメージから起動したインスタンスの数」、縦軸を「ディスクの消費量」と読み替えてください。VMのようにOS一式を複製すると青い O(n) の直線を登りますが、層を共有するコンテナはイメージ本体ぶんが O(1)(ほぼ水平の線)で、そこに書き込みぶんの差分が乗るだけです。n を右へ動かすと、この2本の開き方がそのまま「1台に何個載るか」の差になります

namespace は「このプロセスには、この範囲だけ見せる」という仕切りです。今のLinuxには8種類あり、それぞれ別の対象を隔離します。

この先にあるもの

§

ここから先は会員限定です

解説記事371本・教科書26章・学生モード48単元・論文精読6本が、月額¥490ですべて読み放題になります。新しい解説は毎日3本ずつ増えます。いつでも解約でき、解約後も期間の終わりまで読めます。

会員の方はログインすると続きが表示されます

コメント

コメントにはログインが必要です