TenForward

技術ブログ。はてなダイアリーから移転しました

一般ユーザでの lxc コンテナの実行

(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" ユーザでコンテナを実行するための準備を行っています.

  1. 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
  2. ユーザ名前空間では,ホスト上の uid/gid の範囲を名前空間内の uid/gid の範囲にマッピングしますが,その時に必要な uidmap パッケージをインストールします (uidmap はソースとしては shadow に含まれます).
    sudo apt-get install uidmap
  3. (2013-12-21 追記) 2013-12-17 のコミットで setuid されるようになったのでそれ以降のモノを使う場合はこの手順は不要になりました.lxc-user-nic コマンドを setuid します.lxc パッケージに含まれていますが,セキュリティ上の理由からか現時点では設定されずにインストールされています.これは,コンテナで veth を使いますが,ホスト上でネットワークI/Fを作るには特権が必要なために準備されているコマンドです.
    sudo chmod u+s /usr/bin/lxc-user-nic
  4. (この手順は公開時抜けてたので追記しました!!) lxc-user-nic 用の設定ファイルを作成します."ユーザ名 インターフェースのタイプ アタッチするブリッジ そのユーザに許すインターフェースの数" の順で書きます.
    echo "karma veth lxcbr0 2" | sudo tee -a /etc/lxc/lxc-usernet"
    • (2013-12-21 追記) lxc-user-nic の man が追加されましたので明らかになった部分は取り消し線を入れてます
    • インターフェースのタイプは現時点では "veth" のみです.
    • 「そのユーザに許すインターフェース数」は間違いかも? ちょっと解説を見つけられなかったのでソースを斜め読みして判断したので.veth の場合,ペアでインターフェースが出来ますが,その場合は 1 組で 1 と数えると思います(が間違いかも)
    • 設定ファイルの場所とファイル名はコンパイル時に決まります.デフォルトが /etc/lxc/lxc-usernet になってたはず.
  5. 多分,あるユーザが自身の持つ uid/gid 以外に扱える uid/gid の範囲を指定するためだと思いますが,subuid/subgid というものを指定します.uid/gid ともに 100000 〜 199999 までを karma ユーザに割り当てています.
    sudo usermod -v 100000-199999 -w 100000-199999 karma
  6. 自分用の 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
  7. 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 を使わなくても,一般ユーザで実行するコンテナのツリーを作ることは可能です.

  1. イメージを作成します.
    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
  2. これで準備が整いましたのでスタートできます (cgroup 作成の時に登録した pid で実行されているシェルの上で実行してね).
    lxc-start -n karma01 -P /home/karma/lxc -o log -l DEBUG -d
  3. 動いたかな? 少し変な出力があるけど動いていますね.
    $ 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
  4. コンソールアクセスも可能です
    $ lxc-console -n karma01 -P /home/karma/lxc 

    Connected to tty 1
    Type to 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) で異なっています.

$ 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 が一般ユーザ権限で動くからあまり一般的には何がうれしいかわからんのかもしれんけど ^^;)