この記事では、「仮想環境とコンテナを絶対に理解したい」で使用した主なコマンドを紹介します。
ROOTFS=$(mktemp -d)
何をするか
一時ディレクトリ(空のディレクトリ)を作り、そのパスを ROOTFS 環境変数に格納する。ここにコンテナのファイルシステムを展開する。
その後の意味
以降の操作はこのディレクトリを「疑似ルート」 (/) として使うための準備です。
CID=$(sudo docker container create bash)
sudo docker container export $CID | tar -x -C $ROOTFS
ln -s /usr/local/bin/bash $ROOTFS/bin/bash
sudo docker container rm $CID
何をするか
イメージ bash を元にコンテナを作成する(ただし起動はしない)。戻り値(コンテナ ID)を CID に格納する。
作成したコンテナのルートファイルシステムを tar ストリームで出力し、tar -x -C $ROOTFS で $ROOTFS に展開する。
ホスト上の /usr/local/bin/bash(システム上の bash 実行ファイル)を $ROOTFS/bin/bash というシンボリックリンクとして作る。
先ほど作った一時的なコンテナを削除。ファイルシステムは既に展開済みなので不要となる。
UUID=$(uuidgen)
sudo cgcreate -t $(id -un):$(id -gn) -a $(id -un):$(id -gn) -g cpu,memory:$UUID
ls -l /sys/fs/cgroup/$UUID
cgset -r memory.max="10000000" $UUID
cgset -r cpu.max="300000 1000000" $UUID
何をするか
ユニークな識別子(UUID)を作り、UUID 変数に入れる。
cgcreate(cgroup-tools)で cpu と memory の cgroup(名前は $UUID)を作る。-t と -a はタスク所有者とアクセス権の指定。
作成した cgroup のディレクトリが存在するかを確認するために ls する。
cgroup にメモリ上限を設定する。
CPU 制限を設定。ここでは quota=300000 と period=1000000 → 0.3 → 理論上 30%。
CMD="/bin/sh"
sudo cgexec -g cpu,memory:$UUID \
unshare -muinpf /bin/sh -c "
set -e
# /proc
mkdir -p $ROOTFS/proc
mount -t proc proc $ROOTFS/proc
# /dev/pts
mkdir -p $ROOTFS/dev/pts
mount -t devpts devpts $ROOTFS/dev/pts
mkdir -p $ROOTFS/dev
touch $ROOTFS/dev/ptmx
mount --bind /dev/pts/ptmx $ROOTFS/dev/pts/ptmx
ln -sf /dev/pts/ptmx $ROOTFS/dev/ptmx
# /dev/null
touch $ROOTFS/dev/null
mount --bind /dev/null $ROOTFS/dev/null
# hostname
hostname $UUID
# chroot + CMD
exec capsh --chroot=$ROOTFS --drop=cap_sys_chroot -- -c 'exec $CMD'
"
何をするか
CMD 変数に /bin/sh を格納する。
cgexec で先に作った cgroup($UUID)にプロセスを所属させ、unshare で作るプロセス群をメモリ・CPU 制限の対象にする。
set -e でエラー時にシェルを終了させる。
mkdir -p $ROOTFS/proc
mount -t proc proc $ROOTFS/proc
新しい名前空間内で $ROOTFS/proc に proc 仮想ファイルシステムをマウントします。プロセス情報(/proc)がないと ps やプロセス関連のユーティリティが動かないため必要です。
mkdir -p $ROOTFS/dev/pts
mount -t devpts devpts $ROOTFS/dev/pts
mkdir -p $ROOTFS/dev
touch $ROOTFS/dev/ptmx
mount --bind /dev/pts/ptmx $ROOTFS/dev/pts/ptmx
ln -sf /dev/pts/ptmx $ROOTFS/dev/ptmx
擬似端末 (pty/pts) を $ROOTFS の中に作り、ptmx へのバインドやシンボリックリンクを作成してターミナル(tty)が使えるようにしている
touch $ROOTFS/dev/null
mount --bind /dev/null $ROOTFS/dev/null
/dev/null を作ってホストの /dev/null をバインドすることで $ROOTFS 内での /dev/null を確保
hostname $UUID
新しい名前空間内でホスト名を UUID に設定
exec capsh --chroot=$ROOTFS --drop=cap_sys_chroot -- -c 'exec $CMD'
chroot でルートディレクトリを $ROOTFS に変更し、capsh で cap_sys_chroot 権限を剥奪してから CMD(/bin/sh)を実行する。これにより、コンテナ内シェルが起動する。