Zigでトイコンテナランタイム開発 - 用語集

本カテゴリの記事に登場した専門用語を、カテゴリ別に整理した用語集です。記事が増えるのにあわせて随時更新します。

Linuxネームスペース基礎

  • namespace(名前空間):プロセスから見える「あるリソースの一覧」をグループ単位で隔離するLinuxカーネルの機能です。ネームスペースの種類ごとに隔離対象のリソースが異なり、PID namespaceならプロセスID空間、Mount namespaceならマウントポイントの一覧が対象になります。コンテナは複数のネームスペースを組み合わせて「隔離されたように見える環境」を作ります
  • PID namespace:プロセスID(PID)の採番を隔離するネームスペースです。新しいPID namespaceの中で最初に起動したプロセスはPID 1として扱われ、そのネームスペース内の他のプロセスの親(あるいは孤児プロセスの引き取り先)としての役割を担います。ホスト側からは通常のPIDとして、コンテナ内からは別のPID(多くの場合1)として同じプロセスが見えます
  • UTS namespace:ホスト名(hostname)とNISドメイン名を隔離するネームスペースです。名前の由来はUNIXのuname構造体(UTS: UNIX Time-sharing System)です。コンテナごとに異なるホスト名を設定できるようにする、比較的単純なネームスペースの1つです
  • Mount namespace:マウントポイントの一覧を隔離するネームスペースです。新しいMount namespaceを作成した直後はホストと同じマウント一覧をコピーした状態から始まり、以後のmount/umountはそのネームスペース内だけに反映されます
  • IPC namespace:SysV IPC(メッセージキュー・セマフォ・共有メモリ)とPOSIX名前付きセマフォの識別子空間を隔離するネームスペースです
  • Network namespace:ネットワークインターフェース、ルーティングテーブル、iptables/nftablesのルール、ポート番号空間を隔離するネームスペースです。新規作成直後はループバック(lo)以外のインターフェースを持たないため、コンテナを外部と通信させるにはvethなどで明示的にインターフェースを用意する必要があります
  • User namespace:UID/GIDの対応関係を隔離するネームスペースです。ホスト側の非特権ユーザーを、User namespace内では擬似的にroot(UID 0)として扱えるようにする(UID mapping)ことで、ホストへの実害を抑えたまま「コンテナ内でrootとして振る舞う」ことを実現します。rootless実行の土台になるネームスペースです
  • cgroup namespace:プロセスから見えるcgroupのルートディレクトリの位置を隔離するネームスペースです。他のネームスペースと異なりリソースそのものを隔離するのではなく、cgroupfsの見え方を隔離する点が特徴です
  • unshare(2) / setns(2) / clone(2)のnamespaceフラグ:ネームスペースを操作する3つの主要なsyscallです。cloneは新しいネームスペースに所属するプロセスを生成しながら同時に作成し、unshareは実行中のプロセスを新しいネームスペースへ切り離し、setnsは既存の(他プロセスが使っている)ネームスペースに参加します
  • /proc/[pid]/ns/:各プロセスが所属するネームスペースをファイルとして参照できる仮想ディレクトリです。同じネームスペースに属するプロセス同士はここが同じinode番号を指します

プロセス生成・syscall関連

  • fork(2):呼び出し元プロセスのコピーを新しいプロセスとして生成するsyscallです。親プロセスのメモリ空間はcopy-on-writeで共有されるため、生成コスト自体は比較的低く抑えられています
  • clone(2)forkの汎用版にあたるsyscallで、生成する子プロセスとどのリソース(メモリ空間、ファイルディスクリプタテーブル、各種ネームスペースなど)を共有するかをフラグで細かく指定できます。コンテナランタイムはCLONE_NEWPIDCLONE_NEWNETのようなフラグを渡すことで、プロセス生成と同時にネームスペースを分離します
  • execve(2):現在のプロセスのメモリイメージを、指定した実行ファイルの内容で完全に置き換えるsyscallです。PIDは変わらないまま実行中のプログラムだけが差し替わります
  • PID 1の責務:あるPID namespace内で最初に動くプロセスは特別な扱いを受け、親のいない孤児プロセスを引き取る役割を持ちます。通常のシグナルハンドラを登録していない場合、SIGTERMなどのデフォルト動作が無視される点も特徴です
  • ゾンビプロセス:終了したがまだ親プロセスにwaitされていないプロセスです。終了ステータスをカーネルが保持し続けるための一時的な状態で、親がwaitを呼ぶことで解消されます。コンテナのPID 1は、自身の子孫すべてのゾンビ化を防ぐためにwait処理を担う必要があります
  • シグナル(SIGCHLD/SIGTERM等):プロセス間で非同期にイベントを通知する仕組みです。子プロセスの終了はSIGCHLDとして親に通知され、コンテナの終了要求は多くの場合SIGTERMとして伝えられます
  • std.os.linux:Zig標準ライブラリが提供する、Linuxのsyscallを直接呼び出すための低レベルなモジュールです。syscallごとに用意されている高レベル関数の充実度にはばらつきがあり、対応する関数が存在しない場合はsyscall番号を指定して直接呼び出す必要があります

マウント・ファイルシステム関連

  • rootfs:プロセスから見たファイルシステムのルート(/)として使われる、コンテナ専用のディレクトリツリーです。コンテナイメージのlayerを展開して作成します
  • chroot(2):プロセスから見たルートディレクトリを変更するsyscallです。Mount namespaceを操作しない古典的な隔離手法で、..を辿るなどの手段でchroot外へ脱出できる既知の弱点があるため、コンテナランタイムでは後述のpivot_rootが使われます
  • pivot_root(2):現在のルートファイルシステムを別の場所に退避しつつ、指定したディレクトリを新しいルートに切り替えるsyscallです。切り替え後に古いルートをumountできるため、chrootと異なりホスト側のファイルシステムツリーへの経路を完全に断ち切れます
  • mount propagation(shared/private/slave):あるマウントポイントに対するmount/umount操作を、他のマウント名前空間へ伝播させるかどうかを制御する設定です。コンテナのMount namespaceを安全に隔離するには、事前にマウントをprivateに設定してホストへの伝播を止める必要があります
  • bind mount:既存のディレクトリやファイルを、別のパスにも同じ実体として見せるマウント方式です。ホスト側のディレクトリをコンテナのrootfs内の特定パスにそのまま持ち込む(ボリュームマウント)際の基本手段です
  • tmpfs:メモリ上に構成される一時的なファイルシステムです。/dev/tmpなど、永続化が不要な領域によく使われます
  • OverlayFS:複数のディレクトリ(lower/upper)を1つに重ね合わせて見せるUnion系ファイルシステムです。イメージのlayerを重ねてrootfsを構成する際、実運用のコンテナランタイムで広く使われています(現時点の本プロジェクトの実装では未対応)

cgroup(リソース制御)関連

  • cgroup(control group):プロセスをグループ化し、CPU・メモリ・I/Oなどのリソース使用量を制限・計測するLinuxカーネルの機能です
  • cgroup v2:単一の階層(unified hierarchy)ですべてのコントローラを扱う、現行世代のcgroup実装です。旧来のcgroup v1がコントローラごとに別々の階層を持っていたのに対し、v2はディレクトリツリーが1つに統一されています
  • cgroupfs:cgroupをディレクトリとファイルの操作として扱えるようにする仮想ファイルシステムです。多くの場合/sys/fs/cgroupにマウントされており、ディレクトリの作成がcgroupの作成に、ファイルへの書き込みが設定の変更に対応します
  • controller(コントローラ):cgroupが管理するリソースの種別ごとの実装単位です。cpumemoryiopidsなどがあり、有効化するコントローラはcgroup.subtree_controlファイルで親から子へ順番に許可していきます
  • cgroup.procs:あるcgroupに所属させたいプロセスのPIDを書き込むファイルです。書き込んだ時点でそのプロセスは該当cgroupの制限下に入ります
  • memory.max / cpu.max:cgroup v2でメモリ使用量の上限、CPU使用時間の上限をそれぞれ設定するファイルです。上限を超えるとメモリはOOM Kill、CPUはスロットリング(一時的な実行停止)の対象になります

ネットワーク関連

  • veth(virtual Ethernet)ペア:2つのインターフェースが対になって生成される仮想ネットワークデバイスです。片方に流したパケットがもう片方にそのまま出てくる、ケーブルでつないだような挙動をします。片方をコンテナのNetwork namespaceに、もう片方をホスト側に残すことで、コンテナとホストの間の通信路を作ります
  • ブリッジ(bridge):複数のネットワークインターフェースを1つのL2セグメントとしてまとめる仮想スイッチです。複数のコンテナのveth片方をブリッジに接続することで、コンテナ同士やホストとの通信を実現します
  • NAT(Network Address Translation)とMASQUERADE:パケットの送信元/宛先アドレスを書き換える技術です。コンテナがプライベートなIPアドレスのままインターネットに出て行けるようにするため、ホストの外向きインターフェースを送信元に見せかけるMASQUERADEというNATの一種がよく使われます
  • iptables / nftables:Linuxカーネルのパケットフィルタリング機構を操作するツールです。NATやポートフォワーディングなど、コンテナのネットワーク周りのルール設定に使われます。nftablesはiptablesの後継にあたる実装です

セキュリティ・権限関連

  • capabilities(Linux capabilities):伝統的にroot(UID 0)にまとめて与えられていた特権を、CAP_NET_ADMIN(ネットワーク設定変更)やCAP_SYS_ADMIN(多数の管理操作)のような単位に分割した仕組みです。プロセスに必要な最小限のcapabilityだけを与えることで、rootとして動かす場合でも権限を絞り込めます
  • seccomp(SECure COMPuting mode):プロセスが呼び出せるsyscallをフィルタリングするLinuxカーネルの機能です。BPFプログラムとしてフィルタルールを記述し、許可されていないsyscallの呼び出しをブロック(あるいはプロセスを強制終了)します
  • UID/GID mapping:User namespace内のUID/GIDと、ホスト側のUID/GIDとの対応関係です。/proc/[pid]/uid_mapgid_mapに書き込むことで設定し、コンテナ内のroot(UID 0)をホスト側の非特権ユーザーに対応付ける、といった使い方をします
  • rootless(コンテナ):ホスト側でroot権限を持たない一般ユーザーのまま、User namespaceを使ってコンテナ内だけroot相当の操作を行えるようにする実行方式です。ホストへの侵害範囲を抑えられる一方、Network namespaceの構築など一部の操作に追加の工夫が必要になります
  • AppArmor / SELinux:Linux Security Modules(LSM)の枠組みで実装される、強制アクセス制御(MAC)の仕組みです。capabilitiesやネームスペースによる隔離を補完する、さらに別レイヤーの制限として使われます(現時点の本プロジェクトの実装では未対応)

OCI仕様関連

  • OCI(Open Container Initiative):コンテナのフォーマットとランタイムを標準化するための業界団体、およびその仕様群の名称です。2015年に設立されました
  • OCI Runtime Spec:コンテナを「どう実行するか」を定めた仕様です。バンドル(後述)のディレクトリ構成や、create/start/kill/deleteといったライフサイクル操作のセマンティクスを規定します
  • bundle(OCIバンドル):OCI Runtime Specが定める、実行に必要な情報一式をまとめたディレクトリです。設定ファイルのconfig.jsonと、rootfsとして使うディレクトリを含みます
  • config.json:OCIバンドルに含まれる設定ファイルです。実行するプロセス(コマンド・環境変数・作業ディレクトリ)、マウント一覧、Linux固有の設定(ネームスペース・cgroup・capabilities等)を1つのJSONにまとめて記述します
  • OCI Image Spec:コンテナイメージの構造を定めた仕様です。イメージがmanifest・config・layerの組み合わせとしてどう表現されるかを規定します
  • OCI Distribution Spec:イメージをレジストリとの間でpush/pullする際のHTTP APIを定めた仕様です
  • manifest:イメージを構成するlayerの一覧と、それぞれのメディアタイプ・サイズ・digestをまとめたJSONです。イメージのpullは、まずこのmanifestを取得することから始まります
  • layer(レイヤー):rootfsに対する差分(tarアーカイブ)として表現される、イメージの構成要素です。複数のlayerを順に重ねて展開することで、最終的なrootfsを構築します
  • digest:manifestやlayerの内容から計算されるハッシュ値(sha256:...の形式)です。レジストリ上のblob(実データ)を一意に参照するための識別子として使われ、内容が変わればdigestも変わるため改ざん検知にも使えます
  • blob:レジストリが保持する、layerやconfigの実データそのものを指す言葉です。digestを指定してAPIから取得します

開発環境・ツール関連

  • runc:OCI Runtime Specのリファレンス実装にあたる、Go製のコンテナランタイムです。Docker/containerdの内部で実際にコンテナを起動する部分を担っています
  • youki:runcと同じくOCI Runtime Spec準拠のコンテナランタイムですが、Rustで実装されています
  • unshare(1) / nsenter(1):それぞれunshare(2)setns(2)をコマンドラインから直接使えるようにしたユーティリティです。ネームスペースの動作確認や、実行中のコンテナへのデバッグ用アタッチに使えます
  • strace:プロセスが発行するsyscallを1件ずつ記録・表示するツールです。自前のsyscallラッパーが意図した引数でsyscallを呼び出せているかを確認する際に使います
  • skopeo / crane:OCI Distribution APIを使ってレジストリとやり取りするコマンドラインツールです。manifestやlayerの中身をコマンドラインから直接確認する際に使えます

参考リンク