Help us understand the problem. What is going on with this article?

dockerでbusybox一個だけのファイルを含むイメージを作って動かしたらどうなるか

More than 1 year has passed since last update.

動機

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
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした