Alpine Linux
夢を見た。小用で目が覚め、時刻を確認した、0230Z。0300Z以降でお腹が空いたと言う センサーからの情報が有れば、そのまま今日のスタートになるんだけど、今回は偽で あった。追加寝。
デカになってた。ホシを尾行して、別府の杉乃井ホテルとか指宿の白水館とかへ行く。 決して、青森の酸ヶ湯温泉とか山形の白布温泉じゃない所が、後で考えると可笑しい。
刑事が正確に会社名や部署を宿帳に書くわけにもいかず、嘘を書いた。某ガードマン 会社のうん十年務めたご褒美の一人旅という事にした。偽名はありきたりの目立たない 山口と書いたかな。でも、これって、私文書偽造にならないのか? デカという職業柄 いらん事に気を遣う。
で、ホシはいつの間にか居なくなってたぞ。見張り失敗、デカ失格。そんな事を意に介さない で楽しむのが、亀有のコチカメ風。
のんびりしすぎて、チェックアウト時間ぎりぎり。帳場に行ったら、遠くから女将に 声をかけられた。山口さーん。
はーい、と言ったら、目が覚めた。0430Z。ちょいと寝坊したけど、今日も一日が始まるな。
Alpineを入れてみる
前回ドッカーでアルパインに出会った。
Alpine Linux で Docker イメージを劇的に小さくする を見ると、人気者のようですね。
ならば、単独で入れてみるかな。
AlpineLinuxにデスクトップ環境を導入する(VMwarePlayer)のように頑張っておられる方もいるようだし。
Small. Simple. Secure.の元、何種類かの コースが用意されてる。オイラーは、バニラを選んでみた。
ISOから起動したら、rootでログインして、setup-aplineすると、ネットはどうするとか、アカウントはどうするとか質問が出てくるんで、それに答えていく。diskに入れる時は、sysを選択する事。Alpine setup scriptsが、参考になるぞ。
終了する時は、poweroff とする事。これが、ちょっと 特殊で焦ったぞ。
alpine:~$ uname -a Linux alp 4.9.29-0-hardened #1-Alpine SMP Mon May 22 19:30:59 GMT 2017 x86_64 Linux
dockerじゃないので、自前で用意したカーネルが動いている。なかなか新しめなやつです。 2cpuでちゃんと動いているぞ。
開発環境が、デフォで入らないのは、Windowsと一緒の事で、使う事だけのユーザーが99.9%を 占めているから、しょうがない。
自前でコンパイルをやりたい人は、 Creating an Alpine package あたりを参考にする。要は、
apk add alpine-sdk apk add build-base gcc abuild binutils binutils-doc gcc-doc apk add cmake cmake-doc extra-cmake-modules extra-cmake-modules-doc
ぐらいをやっておけって事だな。
シンプルに
どこかの馬の骨みたいに、悪夢のシステムデーは採用しません。無理に採用するとdebianみたいに、 袂を分かつ事が起きるからです。 initの自由を目指すディストリビューション「Devuan 1.0」が公開
おかげで、/etcの中だけで、完結しています。unixはこうでなくちゃね。
alpine:/etc$ rc-status Runlevel: default chronyd [ started ] acpid [ started ] crond [ started ] sshd [ started ] Dynamic Runlevel: hotplugged Dynamic Runlevel: needed/wanted sysfs [ started ] fsck [ started ] root [ started ] localmount [ started ] klogd [ started ] Dynamic Runlevel: manual
ああ、思い出した。sshdが使うホストキーは、どう作成してるんだろう? ちょいと 調べてみるかな。/etc/init.d/sshd
description="OpenBSD Secure Shell server" description_checkconfig="Verify configuration file" description_reload="Reload configuration" checkconfig() { : if ! yesno "${SSHD_DISABLE_KEYGEN}"; then ssh-keygen -A || return 1 fi : } start() { checkconfig || return 1 ebegin "Starting ${SVCNAME}" start-stop-daemon --start --exec "${SSHD_BINARY}" \ --pidfile "${SSHD_PIDFILE}" \ -- ${SSHD_OPTS} eend $? }
後は、懐かしい inittabと珍しい、rc.conf と conf.d/ それに runlevel/ ですかね。rc.confに組み込みlinuxの 一端が伺える。
# LINUX SPECIFIC OPTIONS # This is the subsystem type. Valid options on Linux: # "" - nothing special # "docker" - Docker container manager # "lxc" - Linux Containers # "openvz" - Linux OpenVZ # "prefix" - Prefix # "rkt" - CoreOS container management system # "uml" - Usermode Linux # "vserver" - Linux vserver # "systemd-nspawn" - Container created by the systemd-nspawn utility # "xen0" - Xen0 Domain # "xenU" - XenU Domain # If this is commented out, automatic detection will be used.
ssh-keygen
上で出て来た、ssh-keygenを元祖 OpenBSDで見ておく。
まずは、マニュアルから、ssh-keygen(1)
-A For each of the key types (rsa1, rsa, dsa, ecdsa and ed25519) for which host keys do not exist, generate the host keys with the default key file path, an empty passphrase, default bits for the key type, and default comment. This is used by /etc/rc to generate new host keys.
まとめて、ホストキーを作ってくれるとな。で、そのソースは、 /usr/src/usr.bin/ssh/ssh-keygen.c に有ったぞ。
static void do_gen_all_hostkeys(struct passwd *pw) { struct { char *key_type; char *key_type_display; char *path; } key_types[] = { #ifdef WITH_OPENSSL #ifdef WITH_SSH1 { "rsa1", "RSA1", _PATH_HOST_KEY_FILE }, #endif /* WITH_SSH1 */ { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, #endif /* WITH_OPENSSL */ { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, { NULL, NULL, NULL } }; :
ふーん、OPENSSLが定義されてると、ed25519ってのも定義されるんか。で、一応作成場所も 当たっておく。場所は、 pathnames.h に、定義されてた。
#define ETCDIR "/etc" #define SSHDIR ETCDIR "/ssh" #define _PATH_SSH_PIDDIR "/var/run" #define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" #define _PATH_HOST_KEY_FILE SSHDIR "/ssh_host_key" #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" #define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" #define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key" #define _PATH_DH_MODULI ETCDIR "/moduli" #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
丸裸にするのって、楽しいね。ああ、丸裸って書くと、ぐぐるの村八分にあうか。面倒な 世の中は嫌いだぞ。
apk index
/etc/apk/world ってファイルに、自分が故意に入れたパッケージ名が記憶されてる。
パッケージにどんなのが有るかWebで眺めるのもいいけど、手元でgrepとかやって、絞り込み たいな。何処かに、その為のインデックスが無いかな? ガサ入れしてみた。 それらしいのが有ったぞ。
alpine:/var/cache/apk$ ls APKINDEX.6e97666e.tar.gz APKINDEX.ed3a7416.tar.gz
2つ有るのは、メインと有志のと別々に管理してるからだな。展開してみると、 APKINDEXの方が求めるものだった。
C:Q1T4CHVvlDM7lBpgvS3XVJD9w6nEw= P:ocaml V:4.04.0-r1 A:x86_64 S:75797210 I:217706496 T:Main implementation of the Caml programming language U:http://caml.inria.fr L:LGPLv2 o:ocaml m:Borys Zhukov <mp5@mp5.im> t:1480778225 c:b6b2655272894633f0ef5a97a140d83d42841401 D:so:libc.musl-x86_64.so.1 so:libncursesw.so.6 C:Q1VRgYwhd85no/XFXclxTJouHIRoM= P:libreoffice-lang-sq :
こんな形式のデータになっていた。P:と o:と、同じようなデータが記載されてるんで、 どちらを取り出すか迷ったけど、Pの方を抜き出して、ソートして眺めている。
alpine:~$ wc APK-index 7961 7961 110239 APK-index alpine:~$ fgrep -v -- -doc APK-index | fgrep -v -- -dev | wc 5292 5292 71198
何も考えずにカウントすると、8000近い数値が出てくるけど、ドキュメントとかヘッダー類も 数えちゃってるので、水増しされてる。それらを取り除くと、約5300個のパッケージに なった。(パッケージによっては、何とかプラグインなんてのも独立させてる場合が有るので、 有効数は更に少なくなる)
じゃ、パッケージになってないのはどうする? その為に、初っ端に開発環境を放り込んだんだろ。有効利用しろよ。
まずはgaucheから
コンパイル環境を整えた後、真っ先にコンパイルしてみるのはgaucheだ。昔はコンパイル環境の テストにつかってたのはrubyだったけど、いつの間にか疎遠になってる。で、やってみると、
make[1]: Leaving directory '/home/sakae/Gauche-0.9.5/lib' make[1]: Entering directory '/home/sakae/Gauche-0.9.5/ext' (cd util; make default) make[2]: Entering directory '/home/sakae/Gauche-0.9.5/ext/util' ../../src/gosh -ftest ../../src/precomp -e -P -o util--match ../../libsrc/util/match.scm make[2]: *** [Makefile:25: util--match.c] Segmentation fault
見事にセグフォだわい。原因は大体予想がつくぞ。クリチカルな事をやってるGCで落ちてるはず。 組み込み用のlibc(libc.musl)じゃケア出来ていないのだろう。
alpine:~/Gauche-0.9.5/src$ ./gosh Segmentation fault
gdbを入れて追ってみる。
(gdb) r main Starting program: /home/sakae/Gauche-0.9.5/src/gosh main Program received signal SIGSEGV, Segmentation fault. 0x00007ffff78b62c2 in GC_find_limit_with_bound (p=0x555555759090 "\001", up=up@entry=0, bound=bound@entry=0x0) at os_dep.c:966 966 GC_noop1((word)(*result)); (gdb) bt #0 0x00007ffff78b62c2 in GC_find_limit_with_bound (p=0x555555759090 "\001", up=up@entry=0, bound=bound@entry=0x0) at os_dep.c:966 #1 0x00007ffff78b635e in GC_find_limit (p=<optimized out>, up=up@entry=0) at os_dep.c:978 #2 0x00007ffff78b63ce in GC_init_linux_data_start () at os_dep.c:463 #3 0x00007ffff78b4df0 in GC_init () at misc.c:1159 #4 0x0000555555555c86 in main (ac=2, av=0x7fffffffebe8) at main.c:592
残念な事に、予想が見事的中してしまった。(予想が的中して嬉しいのは、競馬ファンだけ、かな?) 潔く、撤退しましょ。gaucheの代わりに、chibi-schemeをコンパイルして入れた。こちらは、 自前のGCをやってるんで、素直に動いた。
難物のgdbはどうよ?
クリチカルなやつとしてgdbが有る。パッケージに有るので素直にそれを使えばいいんだけど、 自前でコンパイルする練習のためにやってみる。
make[3]: Leaving directory '/home/sakae/gdb-7.12.1/gdb' g++ -g -O2 -I. -I. -I./common -I./config -DLOCALEDIR="\"/usr/local/share/locale\"" -DHAVE_CONFIG_H -I./../include/opcode -I./../opcodes/.. -I./../readline/.. -I./../zlib -I../bfd -I./../bfd -I./../include -I../libdecnumber -I./../libdecnumber -I./../intl -I./gnulib/import -Ibuild-gnulib/import -Wall -Wpointer-arith -Wno-unused -Wunused-value -Wunused-function -Wno-switch -Wno-char-subscripts -Wempty-body -Wunused-but-set-parameter -Wunused-but-set-variable -Wno-sign-compare -Wno-write-strings -Wno-narrowing -Wformat-nonliteral -c -o amd64-linux-nat.o -MT amd64-linux-nat.o -MMD -MP -MF .deps/amd64-linux-nat.Tpo amd64-linux-nat.c amd64-linux-nat.c:27:23: fatal error: asm/prctl.h: No such file or directory #include <asm/prctl.h> ^ compilation terminated.
やっぱりコンパイルが中止された。ヘッダーが見つからんって、毎度お馴染みのリナ意地悪 攻撃ですよ。何とか-devを見繕って入れなきゃいかんか脳。ヘッダーは別パッケージっている リナの流儀ってどうして生まれたのだろう?
一番に思いつくのは、DISK容量が貴重だったんで、万人が使う事は無いヘッダーはいらないって 事で、別にしたんだな。次に思いつくのは、悪い人が来て、勝手に悪さするプログラムを コンパイル、インストールするのを防ぐため。libcのヘッダーが無いと、ハロワすら表示 出来ませんからな。(この説、ちと根拠に乏しいか。まずは、コンパイル環境が有るって前提が 必要だからね)
でもまあ、コンパイル環境がデフォで入っていないって事からすると、まあ、リナは普通に 用意されたアプリを使うものですっていう、メーカーOSに倣っているんだな。
で、どうしよう? こういう時は、パッケージを入れた時に、どんなものが付属してきたか 調べるのがいいかな。上で挙げた、INDEXに依存情報が記載されてたな。
D:so:libc.musl-x86_64.so.1 so:libexpat.so.1 so:libncursesw.so.6 so:libpython2.7. so.1.0 so:libreadline.so.6 so:libz.so.1
何種類かのライブラリィーが必要との事。対応をもう少し精密に調べてみたいな。Webに載ってるのかしらん? 当たってみよう。
package details (gdb)の、 Git repositoryを辿ると、FreeBSDで言う、Makefile相当が現れた。通称、APKBUILDって言う んだな。なんか、パッチを当ててるよ。それはそうと、目当ては、
makedepends="ncurses-dev expat-dev texinfo readline-dev python2-dev zlib-dev autoconf automake libtool linux-headers"
だな。一番怪しそう(必要と思われる)なのは、linux-headerか。大事になりそうなんで 止めておこう。それより、汎用性のありそうな、ncurses-devとかreadline-dev、zlib-dev ぐらいを入れておこう。(大事な事なので、何度でも言います。リナの意地悪に対抗して生きる術です)
busybox and kernel
アルパインの特徴、小さいを実現してる決定打、busyboxにちょっと対面しておくか。 ソースを取ってきて、コンパイルしてみる。
INSTALLの説明によれば、リナのカーネルを作る過程と似せてあるとな。
The BusyBox build process is similar to the Linux kernel build: make menuconfig # This creates a file called ".config" make # This creates the "busybox" executable make install # or make CONFIG_PREFIX=/path/from/root install
menuconfigの代わりに、make defconfigすると、全部入りが出来るようだ。で、全部入りを やってみる。
alpine:~/busybox-1.21.0$ make CC networking/ifconfig.o In file included from include/libbb.h:40:0, from networking/ifconfig.c:49: /usr/include/sys/poll.h:1:2: warning: #warning redirecting incorrect #include <sys/poll.h> to <poll.h> [-Wcpp] #warning redirecting incorrect #include <sys/poll.h> to <poll.h> ^~~~~~~ networking/ifconfig.c:59:26: fatal error: net/if_slip.h: No such file or directory # include <net/if_slip.h> ^ compilation terminated.
ははは、またヘッダー無いぞエラーだよ。リナでコンパイルする苦労の99%はヘッダー探しに 費やされるという事です。取り合えずなんで、net関係は無しよでコンパイルしたら
/usr/lib/gcc/x86_64-alpine-linux-musl/6.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -ldmalloc collect2: error: ld returned 1 exit status make: *** [Makefile:716: busybox_unstripped] Error 1
今の環境に、dmallocは無いと抜かす。jemallocならapkで入るんだけどね。取り合えず、 dmallocは無しの方向で、コンパイルを完了させた。簡単な試食。
alpine:~/busybox-1.21.0$ ./busybox du -sh 34.4M . alpine:~/busybox-1.21.0$ ./busybox ls -l busybox* -rwxr-xr-x 1 sakae nogroup 702624 May 29 15:50 busybox -rwxr-xr-x 1 sakae nogroup 819448 May 29 15:50 busybox_unstripped -rw-r--r-- 1 sakae nogroup 728261 May 29 15:50 busybox_unstripped.map -rw-r--r-- 1 sakae nogroup 27295 May 29 15:50 busybox_unstripped.out
busyboxは、リナに使われるコマンドの集大成をコンパクトにまとめたもの。手元に置いて、 参照するのがよかろう。余計な機能が省かれているので、見通しが良いぞ。
上でカーネルの作成なんて言葉を聞いたものだから、オイラーもやってみたくなった。 ネットを調べると、専用の道具を使うようだけど、昔ながらの方法も出てたので、やってみた。
今動いてるカーネルの設定をそのまま使う方法が有るとな。/boot/configを持ってきて、 やってみたらエラー。ソースが新しいからかな。で、やっぱりmenuconfigしたよ。GUI(もどき)で、設定してくって苦痛だな。お勧めに従ってやってみたら、perlが必要とか。そんな事、 どの案内にも書いてなかったぞ。
で、20分ほどして、カーネルが出来上がった。今動いてるのよりも随分と太ったものになったぞ。その代わり、/lib/modulesの下に出来たのは小粒だった。
alpine:/lib/modules$ du -sh * 138.1M 4.4.59 320.0K 4.4.68-mine
異常に小さい。このままインストールすると二度と起動しなくなる予感がしたので、ここで止める。もっと練習が必要そうだけど、得る所が無さそうだな。もっと違う事を野郎。
docker in alpine linux
で、悪戯心を発揮して、wikiでdockerなんてのを検索したら、下記を発見。
alpineを知ったのはdockerを経由してだ。前にも書いたけど、alpineはその小ささから注目を 浴び、ドッカー社に目を付けられた。そのお返しに、alpineがドッカーを飲み込んじゃえって 作戦。
ドッカーがパッケージになってるので、一発導入。そして、サービスの登録と起動。
alpine:~# apk add docker (1/6) Installing libmnl (1.0.4-r0) (2/6) Installing libnftnl-libs (1.0.7-r0) (3/6) Installing iptables (1.6.0-r0) (4/6) Installing xz (5.2.2-r1) (5/6) Installing libseccomp (2.3.1-r0) (6/6) Installing docker (1.12.6-r0) Executing docker-1.12.6-r0.pre-install Executing busybox-1.25.1-r0.trigger OK: 972 MiB in 130 packages alpine:~# rc-update add docker boot * service docker added to runlevel boot alpine:~# service docker start * Caching service dependencies ... [ ok ] * /var/log/docker.log: creating file * /var/log/docker.log: correcting mode * /var/log/docker.log: correcting owner * Starting docker ... [ ok ]
自分自身の分身をドッカーへ
alpine:~# docker pull alpine Using default tag: latest latest: Pulling from library/alpine cfc728c1c558: Pull complete Digest: sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96 Status: Downloaded newer image for alpine:latest alpine:~# docker run -it alpine /bin/ash / # df Filesystem 1K-blocks Used Available Use% Mounted on overlay 6593672 2361716 3877296 38% / tmpfs 2021904 0 2021904 0% /dev tmpfs 2021904 0 2021904 0% /sys/fs/cgroup /dev/sda3 6593672 2361716 3877296 38% /etc/resolv.conf /dev/sda3 6593672 2361716 3877296 38% /etc/hostname /dev/sda3 6593672 2361716 3877296 38% /etc/hosts shm 65536 0 65536 0% /dev/shm tmpfs 2021904 0 2021904 0% /proc/latency_stats tmpfs 2021904 0 2021904 0% /proc/timer_list tmpfs 2021904 0 2021904 0% /proc/timer_stats tmpfs 2021904 0 2021904 0% /proc/sched_debug
次はウブをドッカーへ
alpine:~# docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu b6f892c0043b: Pull complete 55010f332b04: Pull complete 2955fb827c94: Pull complete 3deef3fcbd30: Pull complete cf9722e506aa: Pull complete Digest: sha256:382452f82a8bbd34443b2c727650af46aced0f94a44463c62a9848133ecb1aa8 Status: Downloaded newer image for ubuntu:latest alpine:~# docker run -it ubuntu /bin/bash root@9de8c49d509c:/# cat /etc/os-release NAME="Ubuntu" VERSION="16.04.2 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.2 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial root@9de8c49d509c:/#
ウブの長期保守版も、2回の改定が行われているのね。
root@9de8c49d509c:/# df Filesystem 1K-blocks Used Available Use% Mounted on overlay 6593672 2501836 3737176 41% / tmpfs 2021904 0 2021904 0% /dev tmpfs 2021904 0 2021904 0% /sys/fs/cgroup /dev/sda3 6593672 2501836 3737176 41% /etc/hosts shm 65536 0 65536 0% /dev/shm
オーバーレイって所に重ね合わせの法則が適用されるんか。
alpine:~# docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest ebcd9d4fca80 3 days ago 117.9 MB alpine latest 02674b9cb179 8 days ago 3.984 MB
そして、それぞれのイメージの大小比較。圧倒的にalpneが小さいな。ドッカー社が目を 付けるのも頷けるよ。
dot.profile for alpine
alpine:~$ cat .profile alias lv=less alias updatedb='su -c "find / > /var/db/LOCATE"' alias locate='cat /var/db/LOCATE | grep -v docker/overlay | egrep ' tmux
こんな初期ファイルを用意した。他のOSで常用してるlocateが見当たらなかったので、 簡易版を書いた。ドッカー関係の一部を隠蔽してる。他にも/devの下とか/procの下を 除外すべきだろうけど、面倒なので、お茶を濁している。