LXC というコンテナ技術について調べている。LXC というのは Docker のように Linux 上で他の Linux を動かすしくみだ。色々調べたが正直 Docker との違いはよく分からなかった。端的には、LXC が init を動かすのに対して
$ sudo lxc-create -t download -n u1 -- -d ubuntu -r bionic -a amd64 --keyserver hkp://keyserver.ubuntu.com:80
$ sudo lxc-execute -n u1 -- ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1312 4 ? Ss 07:08 0:00 init
root 22 0.0 0.0 36704 3060 ? Rs+ 07:08 0:00 ps -aux
docker では init が無いのが違う。(--init
を付加すれば init も動く)
$ docker run -it --rm ubuntu ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 1.0 0.0 7060 1584 pts/0 Rs+ 07:09 0:00 ps -aux
ここでは、たまたま手元にあった Ubuntu-18 を利用して動作を確認する。
インストール
sudo apt install -y lxc
LXC には2つの動作モードがある。
- privileged: root ユーザで lxc を動かす
- unprivileged: 一般ユーザで lxc を動かす
- コンテナ内の root UID は non-root UID に割り当てられるため安全。
privileged モード (root ユーザ) でコンテナの作成
privileged でインタラクティブにコンテナ作成。
sudo lxc-create --template download --name u1 -- --keyserver hkp://keyserver.ubuntu.com:80
またはノンインタラクティブに作成
sudo lxc-create -t download -n u1 -- -d ubuntu -r bionic -a amd64 --keyserver hkp://keyserver.ubuntu.com:80
-
--template
,-t
: でテンプレートというコンテナの作り方を指定する。- テンプレートの実態は
/usr/share/lxc/templates/
にあるシェルスクリプト。
- テンプレートの実態は
-
--name
,-n
: コンテナにつける名前。 -
--
: テンプレートに渡すオプション。-
lxc-create -t TEMPLATE -h
でヘルプが出る。 -
--keyserver
:-t download
に渡す GPG keyserver。事情がありデフォルト値が使えないのでhkp://keyserver.ubuntu.com:80
と指定する。
-
busybox のコンテナを作ってみる。ls -l /bin
すれば分かるが busybox しか無い素晴らしく小さなコンテナができる。
sudo lxc-create --template busybox --name bbox
参考: Linux Containers - LXC - Man ページ - lxc-create.1
様々なコマンド
コンテナのリスト
sudo lxc-ls --fancy
コンテナを起動
sudo lxc-start --name u1
起動したコンテナに入る
sudo lxc-attach --name u1
コンテナを停止
sudo lxc-stop --name u1
コンテナを起動してコマンドを実行して停止
sudo lxc-execute --name u1 -- bash
コンテナを破棄
sudo lxc-destroy --name u1
コンテナの静的構造
/var/lib/lxc/名前/
以下にコンテナの実態が作られる。
-
rootfs
: コンテナから見えるファイル -
config
: 設定ファイル-
lxc.mount.entry
:rootfs
にあるファイルの他、ここで指定したディレクトリをマウントできる。
-
例えば、先程作った busybox コンテナ bbox に pstree コマンドを入れたい時は /var/lib/lxc/bbox/rootfs/
にコピーすれば良い。
sudo cp /usr/bin/pstree /var/lib/lxc/bbox/rootfs/usr/bin/
コンテナの動的構造
lxc-attach
で起動中のコンテナに入れる。先程コピーした pstree
で見ると、init
と /bin/sh
の親プロセス ID が 0 である事がわかる。(引数なしで実行すると init(1) を指定した事になるので、sh や pstreeが表示されないため、明示的に 0 を指定する)
# pstree -p 0
?()─┬─init(1)─┬─getty(10)
│ ├─init(11)
│ ├─syslogd(4)
│ └─udhcpc(9)
└─sh(28)───pstree(75)
一方でコンテナの外に出て観察すると、init
の親が lxc-start
で /bin/sh
の親が lxc-attach
である事がわかる。
Subordinate UID / GID
Unprivileged モードを利用する前に、Linux の仮想環境でユーザを仮想化する方法を確認する。ユーザ仮想化の確認には unshare --user
コマンドを使う。
$ unshare --user
$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
$ echo $$
11839
この時点では uid=65534(nobody) にみえる。$$
は現在の PID なので、この番号 11839
を覚えておく。
別のシェルを開けて、このプロセスの UID を変更する。
$ echo '0 1000 1' > /proc/11839/uid_map
$ echo '0 1000 1' | sudo tee /proc/11839/gid_map
ここで、'0 1000 1' の意味は
- 0: 名前空間内の最初の ID
- 1000: 名前空間外の最初のID つまり自分の ID
- 1: 範囲
となる。ここで unshare --user
を実行したシェルに戻る。
$ id
uid=0(root) gid=0(root) groups=0(root)
このように自分が root になったように見える。他にも色々あるが省略。
Unprivileged モード (一般ユーザ) で動かす。
(注意: Ubuntu 18 では X ターミナルから実行しないと動きませんでした。詳細は https://github.com/lxc/lxc/issues/3002)
まず、自分が使える UID と GID の範囲を確認する。
$ grep $USER /etc/subuid
tyamamiya:100000:65536
$ grep $USER /etc/subgid
tyamamiya:100000:65536
これで、uid / gid ともに 100000 から 65536 個使えるとわかる。この値を使って LXC を設定する。
cat <<EOT > ~/.config/lxc/default.conf
lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
EOT
echo "$USER veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet
Unprivileged モードでコンテナを作成する。
lxc-create -t download -n u1 -- -d ubuntu -r bionic -a amd64 -- --keyserver hkp://keyserver.ubuntu.com
あとは Privileged モードと同じ。
lxc-start --name u1
lxc-attach --name u1
touch hello.txt
作ったファイルは .local/share/lxc/u1/rootfs にできている。
ls -l .local/share/lxc/u1/rootfs/hello.txt
-rw-rw-r-- 1 100000 100000 0 Nov 9 21:24 .local/share/lxc/u1/rootfs/hello.txt
非常に面白い。