chrootとunshareを使い、シェル上でコマンド7つで簡易コンテナ
コンテナ型仮想化であるDockerは、様々な名前空間の分離やcgroupなどのLinuxの機能を使用して実現されています。
ここでは、簡易的に機能を「ルートディレクトリとPID名前空間も分離されている」と限定し、シェル上で7つのコマンドでコンテナを立ち上げてみます。
最終的な目標は、立ち上げたコンテナ上でpsを実行し、以下のようにPIDが1から割り振られていることを確認することです。
bash-4.3# ps PID TTY TIME CMD 1 pts/1 00:00:00 bash 7 pts/1 00:00:00 ps bash-4.3#
結論: 7つのコマンド
先に書いてしまうと、7つのコマンドは以下のとおりです。
まず、簡易コンテナの作成から、コンテナ内のbash起動まで。
$ mkdir test_container $ cd test_container $ cp -r /bin /lib /lib64 . $ sudo unshare -pf chroot . bash
新たにbashが立ち上がりますので、
psを実行するためにprocとdevptsのマウント。
mkdir -p /proc /dev/pts mount -t proc proc /proc mount -t devpts devpts /dev/pts
以上で準備完了です。
psを実行してみると、PIDが新たに1から割り振られていることを確認できます。
bash-4.3# ps PID TTY TIME CMD 1 pts/1 00:00:00 bash 5 pts/1 00:00:00 ps
また、"ls /"を試すと、"test_container"ディレクトリ内で作成したものしか無く、
ルートディレクトリを"test_container"へ変更できていることを確認できます。
bash-4.3# ls /
bin dev lib lib64 proc
説明: chrootコマンドとunshareコマンド
今回重要となるコマンドは、4つ目で実行したchrootコマンドとunshareコマンドです。
chrootコマンドは、ルートディレクトリを変更するコマンドで、以下のように使用します。
# chroot 新たにルートとするディレクトリ ルートディレクトリ変更後に実行するコマンド
chrootを実行すると、第1引数のディレクトリを新たなルートディレクトリ(/)として、第2引数のコマンドを実行します。
なお、ルートディレクトリを変えたあと第2引数のコマンドを実行するので、第2引数のコマンドの実行バイナリ自体と、実行バイナリが参照する共有ライブラリは、第1引数のディレクトリ内に存在する必要があります。(そのため、3つ目のコマンドで/binと/lib・/lib64をコンテナ内へコピーしていました。)
また、unshareコマンドは、様々な名前空間の分離を行うコマンドで、以下のように使用します。
# unshare [オプション] コマンド [コマンドの引数]
オプションでは「何の名前空間を分離するか」を指定します。
今回は、以下の2つのオプションを使用しました。
p | PID名前空間の分離 |
f | 指定したプログラムを直接実行するのではなくunshareの子プロセスとしてfork |
なお、chrootとunshare共に、同名のシステムコールが存在し、コマンドはシステムコールをシェル上から呼び出すものとなっています。