(2014-04-19 追記) このエントリは古い情報ですので,Ubuntu 14.04 LTS での非特権コンテナ - TenForwardの日記 をご覧ください.
カーネル/VM Advent Calendar 2013 - Qiita のために書いたエントリです.2 回も書く気はなかったけど,折角コンテナ関係のことを書いたので,エントリしてみました :-)
従来,lxc (に限らないけど) コンテナの実行には,色々な特権 (capability) が必要で,基本的には root でコンテナを実行していました.ところが 3.8 で導入されたユーザ名前空間 (User Namespace) により,ホスト上では非特権ユーザであっても,作成したユーザ名前空間内では root になれるようになりました.詳しくはこの辺りから続くエントリを参照してください.
ただ,カーネル側の実装は 3.8 で完了したとは言え,色々な周辺の実装も関係してくるため,3.8 カーネルを準備しただけでは,コンテナの実行までは実現していませんでした.まあ,色々やればこんな感じで実行は可能でしたけど :-)
lxc-1.0 に向けて,色々 lxc 側でも修正が機能追加が行われてきた結果,一般ユーザの権限でもコンテナが動かせるようになりましたので,試してみました.
いつもの通り,色々な環境を揃えるのが Ubuntu 上が一番楽ですので Ubuntu trusy の開発版上で試しています.
$ lsb_release -r Release: 14.04 $ uname -r 3.12.0-7-generic
準備
ここでは "karma" ユーザでコンテナを実行するための準備を行っています.
- lxc の最新を使うため,ubuntu-lxc のデイリービルドのパッケージを使っています.
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:ubuntu-lxc/daily
sudo apt-get update
sudo apt-get install lxc - ユーザ名前空間では,ホスト上の uid/gid の範囲を名前空間内の uid/gid の範囲にマッピングしますが,その時に必要な uidmap パッケージをインストールします (uidmap はソースとしては shadow に含まれます).
sudo apt-get install uidmap
- (2013-12-21 追記) 2013-12-17 のコミットで setuid されるようになったのでそれ以降のモノを使う場合はこの手順は不要になりました.lxc-user-nic コマンドを setuid します.lxc パッケージに含まれていますが,セキュリティ上の理由からか現時点では設定されずにインストールされています.これは,コンテナで veth を使いますが,ホスト上でネットワークI/Fを作るには特権が必要なために準備されているコマンドです.
sudo chmod u+s /usr/bin/lxc-user-nic
- (この手順は公開時抜けてたので追記しました!!) lxc-user-nic 用の設定ファイルを作成します."ユーザ名 インターフェースのタイプ アタッチするブリッジ そのユーザに許すインターフェースの数" の順で書きます.
echo "karma veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet"
- 多分,あるユーザが自身の持つ uid/gid 以外に扱える uid/gid の範囲を指定するためだと思いますが,subuid/subgid というものを指定します.uid/gid ともに 100000 〜 199999 までを karma ユーザに割り当てています.
sudo usermod -v 100000-199999 -w 100000-199999 karma
- 自分用の cgroup を作成します.単に所有者が自分であるグループを作るだけですが,例えばこんな感じですね ($PID はコンテナを実行する元のシェルの pid を入れたり).
#!/bin/bash
for c in /sys/fs/cgroup/*
do
sudo mkdir $c/karma
sudo chown -R karma: $c/karma
if [ `basename $c` = "cpuset" ]; then
echo 0 > $c/karma/cpuset.cpus
echo 0 > $c/karma/cpuset.mems
fi
echo $PID > $c/karma/tasks
done - lxc-create に与えるコンテナ設定ファイルのベースを作成します.別にこれは後から設定ファイルに足しても OK です.
cat > ~/lxc.conf << EOF
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.id_map = u 0 100000 10000
lxc.id_map = g 0 100000 10000
EOF
コンテナ実行
現時点で lxc-create に与えるテンプレートで非特権ユーザコンテナに対応しているのは ubuntu-cloud 用のテンプレートだけです.もちろん,普通に作ったコンテナのツリーを一般ユーザが使えるように uid/gid を変更して,自分で設定ファイルを作成すれば lxc-create を使わなくても,一般ユーザで実行するコンテナのツリーを作ることは可能です.
- イメージを作成します.
mkdir ~/lxc
lxc-create -n karma01 -P /home/karma/lxc -f /home/karma/lxc.conf -t ubuntu-cloud -- -r saucy
:(snip)
Container karma01 created.
$ ls -l lxc
total 4
drwxr-xr-x 3 karma karma 4096 Dec 12 08:08 karma01 - これで準備が整いましたのでスタートできます (cgroup 作成の時に登録した pid で実行されているシェルの上で実行してね).
lxc-start -n karma01 -P /home/karma/lxc -o log -l DEBUG -d
- 動いたかな? 少し変な出力があるけど動いていますね.
$ lxc-info -n karma01 -P /home/karma/lxc
Name: karma01
State: RUNNING
PID: 5489
lxc_container: Operation not permitted - failed to setns
lxc_container: Operation not permitted - failed to setns
lxc_container: Bad file descriptor - failed to setns
CPU use: 2.92 seconds
BlkIO use: 20.46 MiB
Memory use: 25.07 MiB - コンソールアクセスも可能です
$ lxc-console -n karma01 -P /home/karma/lxc
Connected to tty 1
Typeto exit the console, to enter Ctrl+a itself
Ubuntu 13.10 karma01 tty1
karma01 login:
色々確認
lxc-create で作成された config ファイルはこんな.
$ cat lxc/karma01/config # Template used to create this container: /usr/share/lxc/templates/lxc-ubuntu-cloud # Parameters passed to the template: -r saucy # Template script checksum (SHA-1): 6d51b3930a950b3367fab39b656e4d0297968ff8 lxc.network.type = veth lxc.network.hwaddr = 00:16:3e:27:45:f0 lxc.network.flags = up lxc.network.link = lxcbr0 lxc.id_map = u 0 100000 10000 lxc.id_map = g 0 100000 10000 lxc.rootfs = /home/karma/lxc/karma01/rootfs lxc.mount = /home/karma/lxc/karma01/fstab lxc.pivotdir = lxc_putold lxc.devttydir = lxc.tty = 4 lxc.pts = 1024 lxc.utsname = karma01 lxc.arch = amd64 lxc.cap.drop = sys_module mac_admin mac_override sys_time # When using LXC with apparmor, uncomment the next line to run unconfined: #lxc.aa_profile = unconfined # To support container nesting on an Ubuntu host, uncomment next two lines: #lxc.aa_profile = lxc-container-default-with-nesting #lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups lxc.hook.clone = /usr/share/lxc/hooks/ubuntu-cloud-prep
別のユーザ名前空間で実行されているのを確認してみます.user のリンク先が pid:1(ホストのinit) と pid:6848(コンテナの init) で異なっています.
- この辺りは Linux 3.8 で改良された Namespace 機能と lxc-attach コマンド - TenForwardの日記 参照
- コンテナの名前空間を表すファイルの所有者も一般ユーザ(100000/100000)になってますね
$ lxc-info -P ~/lxc -n karma01 Name: karma01 State: RUNNING PID: 6848 lxc_container: Operation not permitted - failed to setns lxc_container: Operation not permitted - failed to setns lxc_container: Bad file descriptor - failed to setns CPU use: 3.16 seconds BlkIO use: 424.00 KiB Memory use: 8.68 MiB $ sudo ls -l /proc/6848/ns total 0 lrwxrwxrwx 1 100000 100000 0 Dec 12 08:30 ipc -> ipc:[4026532167] lrwxrwxrwx 1 100000 100000 0 Dec 12 08:30 mnt -> mnt:[4026532165] lrwxrwxrwx 1 100000 100000 0 Dec 12 08:21 net -> net:[4026532170] lrwxrwxrwx 1 100000 100000 0 Dec 12 08:30 pid -> pid:[4026532168] lrwxrwxrwx 1 100000 100000 0 Dec 12 08:30 user -> user:[4026532164] lrwxrwxrwx 1 100000 100000 0 Dec 12 08:30 uts -> uts:[4026532166] $ sudo ls -l /proc/1/ns total 0 lrwxrwxrwx 1 root root 0 Dec 12 08:32 ipc -> ipc:[4026531839] lrwxrwxrwx 1 root root 0 Dec 12 08:32 mnt -> mnt:[4026531840] lrwxrwxrwx 1 root root 0 Dec 12 08:32 net -> net:[4026531956] lrwxrwxrwx 1 root root 0 Dec 12 08:32 pid -> pid:[4026531836] lrwxrwxrwx 1 root root 0 Dec 12 08:32 user -> user:[4026531837] lrwxrwxrwx 1 root root 0 Dec 12 08:32 uts -> uts:[4026531838]
ホストから見たコンテナ関連プロセスの様子は
karma 6823 0.0 0.1 48360 1416 ? Ss 08:21 0:00 lxc-start -n karma01 -P /home/karma/lxc -o log -l DEBUG -d 100000 6848 0.1 0.2 26680 2444 pts/7 Ss+ 08:21 0:00 \_ /sbin/init 100000 6901 0.0 0.1 36880 1236 ? S 08:21 0:00 \_ @sbin/plymouthd --mode=boot --attach-to-session 100000 7041 0.0 0.0 4388 632 ? Ss 08:21 0:00 \_ ifup --allow auto eth0 100000 7065 0.0 0.0 4440 628 ? S 08:21 0:00 | \_ /bin/sh -c dhclient -1 -v -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases eth0 ? 100000 7068 0.0 0.3 10232 3500 ? S 08:21 0:00 | \_ dhclient -1 -v -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases eth0 100000 7098 0.0 0.0 17320 376 ? S 08:21 0:00 \_ upstart-udev-bridge --daemon 100000 7108 0.0 0.1 40544 1200 ? Ss 08:21 0:00 \_ /lib/systemd/systemd-udevd --daemon 100000 7133 0.0 0.0 15260 656 ? S 08:21 0:00 \_ upstart-socket-bridge --daemon 100000 7256 0.0 0.0 4440 628 ? Ss 08:23 0:00 \_ /bin/sh -e /proc/self/fd/9 100000 7371 0.0 0.0 4352 368 ? S 08:24 0:00 | \_ sleep 59 100102 7284 0.0 0.0 30380 744 ? Ss 08:23 0:00 \_ dbus-daemon --system --fork 100101 7306 0.0 0.1 173740 1428 ? Sl 08:23 0:00 \_ rsyslogd -c5 100000 7322 0.0 0.0 15404 684 ? S 08:23 0:00 \_ upstart-file-bridge --daemon
(実行例は何度か実行して採取してますので pid とか違ってます)
問題
コンテナではネットワークインターフェースは起動していますが,なぜか dhcp でアドレスがもらえてませんでした.まだそういうものなのか,何かまずかったのかまでは調べてません.
container $ ifconfig eth0 Link encap:Ethernet HWaddr 3e:4d:a7:8e:8a:91 inet6 addr: fe80::3c4d:a7ff:fe8e:8a91/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:56 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:17064 (17.0 KB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:32 errors:0 dropped:0 overruns:0 frame:0 TX packets:32 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2368 (2.3 KB) TX bytes:2368 (2.3 KB)
まだまだ問題もあったり,準備に色々特権が必要だったりしますが,今後が楽しみですね! (docker が一般ユーザ権限で動くからあまり一般的には何がうれしいかわからんのかもしれんけど ^^;)