dockerコマンドを使わずにコンテナを作る - 1
概要
dockerコマンドを使わないでシェルコマンドだけでコンテナを作ることでどのようにしてコンテナが作られているのかの理解を深めたい.
参照のコンテナ技術入門を見れば大体分かるが, 詳細を少し省いて自分なりにわかりやすいように理解を深める形でメモを取る.
ちなみに参照先のコンテナ技術入門ではcgroup v1を使っているが, 現在のubuntuのバージョン(20.04)だとデフォルトでv2のみになっている.
イメージとしては非常にシンプルな以下のような構成のコンテナを作る.
環境
Ubuntu 22.04 LTS(EC2無料枠)
準備
apt update
apt install apt-transport-https ca-certificates curl software-properties-common jq
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update
apt install -y docker-ce
apt install -y cgdb
apt install -y cgroup-tools
apt install -y make gcc
git clone git://git.kernel.org/pub/scm/linux/kernel/git/morgan/libcap.git /usr/src/libcap
(cd /usr/src/libcap && make && make install)
実践
1. 基点となるルートフォルダの作成
ここでdockerコマンドを使ってしまっているが, これは構造化されたファイルシステムを楽に取ってくるために使用している. 他の方法でもできるがこのやり方が楽だと思われる.
# 後にchrootするためのルートフォルダ. rootユーザで作成する.
ROOTFS=$(sudo mktemp -d)
# コンテナを作り, 作ったコンテナのファイルシステムを$ROOTFSに展開する
# ただし全てext4になるため, 後で必要最低限のファイルシステムをマウントする必要がある
CID=$(sudo docker container create bash)
sudo docker container export $CID | sudo tar -x -C $ROOTFS
sudo docker rm $CID
2. cgroupの作成
sudo cgcreate -a ubuntu -t ubuntu -g cpu,memory:chemimotty
# cpuを30%までに制限する
echo "30000 100000" > /sys/fs/cgroup/chemimotty/cpu.max
3. namespace内でプロセス実行
namespaceを切り, 内部で$ROOTFSをルートとしてbashを起動する.
起動したbashプロセスは作成したcgroup内でリソース制限を行う.これでかなり簡単なコンテナっぽいものができる.
unshareのrオプションでrootユーザを新しいnamespaceにマッピングする. これにより新しいnamespace内でもrootユーザで実行できる
sudo cgexec -g cpu,memory:chemimotty /bin/bash -c "unshare -pfumr chroot $ROOTFS /usr/local/bin/bash"
4. ファイルシステムのマウント
procのマウント. この後にexitでnamespaceを抜けても$ROOTFS/procはext4でマウントされている. これはunshareコマンドのmオプションによりmountも分離されているため.
# 作成したコンテナ内にて
mount -t proc proc /proc
# unshareの-pオプションを使っていることにより, pidの分離ができている(1からインクリメントされている)
ps
PID USER TIME COMMAND
1 root 0:00 /usr/local/bin/bash
3 root 0:00 ps
5. hostname変更
hostname変更後にexitでnamespaceを抜けてもhostnameは変更されていない. これはunshareコマンドのuオプションによりutsが分離されているため.
# 作成したコンテナ内にて
hostname chemimotty
uname -n
chemimotty
実験
リソース制限
CPUリソースについて. cpu使用率が大体30%に制限されていることが分かる.
chemimottyというcgroupに所属していおり, ここでのcpu使用率が30%までに設定されているためこうなる.
# コンテナ内において
while true; do : ; done
# 別スクリーンにて, $pidは独立したコンテナ上で動いているひたすら無限ループしているbashの子プロセス
top -p $pid
top - 01:23:53 up 5 days, 1:14, 6 users, load average: 0.26, 0.13, 0.04
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 32.4 us, 0.3 sy, 0.0 ni, 67.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 966.2 total, 64.1 free, 259.3 used, 642.8 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 547.6 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
32824 root 20 0 2812 2452 1932 R 30.3 0.2 0:09.87 bash
所感
相当勉強になった.
chrootを使うと脱獄できてしまい, セキュリティ的に問題があるのでpivot_rootを使っている(runcでは--no-pivotオプションを指定すればchrootで作られる).
誤解を恐れずに言えばchrootとpivot_rootの挙動は違うがルートを変更するという考え方自体は近く, どちらでもコンテナの挙動を実現できると考えたため今回はchrootの方で試すことにした.
参照
コンテナ技術入門
LXCで学ぶコンテナ入門
pivot_root できる条件
【コンテナ要素技術】pivot_rootについて例をまじえて説明します
次回
コンテナの触り程度の機能しかないため, overlayfsおよびnetwork namespaceにも触れていく(それでも足りないであろうが).
Discussion