LXC

LXC というコンテナ技術について調べている。LXC というのは Docker のように Linux 上で他の Linux を動かすしくみだ。色々調べたが正直 Docker との違いはよく分からなかった。端的には、LXC が init を動かすのに対して

```shell
$ 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 も動く)

```shell
$ 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](https://linuxcontainers.org/ja/lxc/manpages//man1/lxc-create.1.html)

## 様々なコマンド

コンテナのリスト

    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 を指定する)

```shell
# 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 の範囲を確認する。

```shell
$ grep $USER /etc/subuid
tyamamiya:100000:65536
$ grep $USER /etc/subgid
tyamamiya:100000:65536
```

これで、uid / gid ともに 100000 から 65536 個使えるとわかる。この値を使って LXC を設定する。

```shell
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 モードと同じ。

```shell
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

非常に面白い。

## 参考

* [Containers - lxc | Ubuntu](https://ubuntu.com/server/docs/containers-lxc)
* [第16回　Linuxカーネルのコンテナ機能 [6] ─ユーザ名前空間 | gihyo.jp](https://gihyo.jp/admin/serial/01/linux_containers/0016)
* [LXC 1.0: Blog post series [0/10] - Qiita](https://qiita.com/m-shibata/items/ee9b295a2596824da819)
* [Linux Containers - LXC - Man ページ](https://linuxcontainers.org/ja/lxc/manpages//index.html)
* [LXD vs Docker | Ubuntu](https://ubuntu.com/blog/lxd-vs-docker)