動機
dockerのようなコンテナでは通常のLinuxシステムではinitなどがやってくれる処理をdocker自体が面倒を見てくれる。実際にどこまでやってくれるのかを調べるために、busybox一個だけを含むイメージを作って動かして、どんなファイルが自動生成されているのか調べた。
使用したdockerは、Ubuntu 16.04 で sudo apt install docker.io でインストールしたもの。
$ docker version
Client:
Version: 1.13.1
API version: 1.26
Go version: go1.6.2
Git commit: 092cba3
Built: Thu Nov 2 20:40:23 2017
OS/Arch: linux/amd64
Server:
Version: 1.13.1
API version: 1.26 (minimum version 1.12)
Go version: go1.6.2
Git commit: 092cba3
Built: Thu Nov 2 20:40:23 2017
OS/Arch: linux/amd64
Experimental: false
busyboxのビルド
libcとしてuclibcを使ってbusyboxをstatic linkで作成する。
これをする一番簡単な方法はbuildrootを使うこと。
ちなみに、glibcではうまくいかない。これについては後述。
docker のイメージの作成
~/work/amd64/busybox にあるファイルを1個だけ使ってdockerにイメージを作る場合。
$ mkdir /tmp/a
$ cd /tmp/a
$ cp ~/work/amd64/busybox .
$ file ./busybox
./busybox: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$ tar cf - . | docker import - busyboxonly:latest
sha256:1b97070bb9fdf71d3f72777886f1b8a4ff38c77948d0a8452093142590e100c0
確認。
$ docker images |grep busyboxonly
busyboxonly latest 1b97070bb9fd 12 seconds ago 797 kB
実行
docker run -it busyboxonly /busybox sh
イメージに入れたのは/busybox 一個だけなので、それ以外のファイルはdockerが自動生成したもの。
/dev, /etc, /proc, /sys のディレクトリが自動生成されている。
/ # /busybox ls -l
total 784
-rwxr-xr-x 1 1001 1002 797176 Apr 12 05:09 busybox
drwxr-xr-x 5 0 0 360 Apr 12 05:13 dev
drwxr-xr-x 2 0 0 4096 Apr 12 05:13 etc
dr-xr-xr-x 103 0 0 0 Apr 12 05:13 proc
dr-xr-xr-x 13 0 0 0 Apr 12 05:13 sys
自動でmountされているディレクトリは以下の通り。
/ # /busybox mount
none on / type aufs (rw,relatime,si=1c98a1c72fd2dcb8,dio,dirperm1)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/rdma type cgroup (ro,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
/dev/sda1 on /etc/resolv.conf type ext4 (rw,relatime,data=ordered)
/dev/sda1 on /etc/hostname type ext4 (rw,relatime,data=ordered)
/dev/sda1 on /etc/hosts type ext4 (rw,relatime,data=ordered)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
devpts on /dev/console type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,mode=755)
tmpfs on /sys/firmware type tmpfs (ro,relatime)
/etcの4つのファイルが自動生成されている。
/ # /busybox ls -l /etc
total 12
-rw-r--r-- 1 0 0 13 Apr 12 05:13 hostname
-rw-r--r-- 1 0 0 174 Apr 12 05:13 hosts
lrwxrwxrwx 1 0 0 12 Apr 12 05:13 mtab -> /proc/mounts
-rw-r--r-- 1 0 0 237 Apr 12 05:13 resolv.conf
/ #
現在時刻は設定されている。(1970年1月1日とかではない)
/ # /busybox date
Thu Apr 12 05:18:50 UTC 2018
ネットワークは使えるし、ホスト名も解決できるようになっている。
/ # /busybox ping -c 5 twitter.com
PING twitter.com (104.244.42.193): 56 data bytes
64 bytes from 104.244.42.193: seq=0 ttl=53 time=35.088 ms
64 bytes from 104.244.42.193: seq=1 ttl=53 time=34.797 ms
64 bytes from 104.244.42.193: seq=2 ttl=53 time=34.845 ms
64 bytes from 104.244.42.193: seq=3 ttl=53 time=34.876 ms
64 bytes from 104.244.42.193: seq=4 ttl=53 time=34.925 ms
--- twitter.com ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 34.797/34.906/35.088 ms
まとめ
dockerでは以下のことは自動で行われる。
- /dev, /proc, /sys はカーネルの生成するファイルシステムにマウントされる。
- /tmp は作成されないので注意。/var も。
- ネットワーク利用可能になっている。/etcの設定ファイルが自動生成される。
- /etc/hostname, /etc/hosts, /etc/resolv.conf
- 現在時刻は設定されている。(UTCのみ)
- ローカルタイムや、タイムゾーンの情報は無し。
glibc を使ったbusybox ではダメだった理由
最初は先日の記事でビルドしたbusyboxを使った。これだとビルドしている環境のlibc、つまりglibcがリンクされる。
これで試したが、ネットワークのホスト名の解決ができない。
その理由は、glibcはstatic link したとしてもホスト名の解決に関してはlibnss_files.so, libnss_dns.so などをダイナミックにロードして使用しているため。つまりbusybox単体では足りないということ。
(root権限で)
# strace -o /tmp/strace.log ./busybox ping twitter.com
# grep open /tmp/strace.log
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 3
ついでの話。pingコマンドにはSUIDがついている。
ビルドしたばかりのbusyboxを使って通常ユーザーの権限で ping を実行するとpermission denied のエラーになる。
$ ls -l ./busybox
-rwxrwxr-x 1 koba koba 2630040 Apr 12 01:17 ./busybox
koba@instance-1:~/work/busybox/busybox-1.28.3$ ./busybox ping twitter.com
PING twitter.com (104.244.42.193): 56 data bytes
ping: permission denied (are you root?)
実は通常のping コマンドにはSUIDビットが立っていて、通常ユーザが実行してもルート権限で実行されるようになっている。
$ ls -l /bin/ping
-rwsr-xr-x 1 root root 44168 May 7 2014 /bin/ping
これにならって、busyboxにもSUIDビットを立ててみる。
$ sudo chown root:root ./busybox
$ sudo chmod u+s ./busybox
$ ls -l ./busybox
-rwsrwxr-x 1 root root 2630040 Apr 12 01:17 ./busybox
今度はpermission deniedにならずに実行できる。
$ ./busybox ping twitter.com
PING twitter.com (104.244.42.129): 56 data bytes
64 bytes from 104.244.42.129: seq=0 ttl=54 time=35.598 ms
64 bytes from 104.244.42.129: seq=1 ttl=54 time=34.738 ms
64 bytes from 104.244.42.129: seq=2 ttl=54 time=34.703 ms
64 bytes from 104.244.42.129: seq=3 ttl=54 time=34.739 ms
^C
--- twitter.com ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 34.703/34.944/35.598 ms