この記事は、
VyOS Advent Calendar 2014の記事です。
---
VyOSは、ネットワーク機器のように利用できるLinux distribution。Ciscoルータのようにコマンドで設定し、コンフィグを保持して動作する(JunOSと似ているといわれているが、使ったことがないのでわからない)。Brocadeに買収され、公開されなくなってしまったVyattaをコミュニティで保守している。
ファイルシステム階層
このVyOSは、アップグレード等がしやすいように、ディスクにインストールする際も、イメージでインストール(install image)することが推奨されている。install imageでインストールすると、通常の/とswapがあって、必要に応じて/usrや/homeが切れらてて、といったようなパーティションの分割はされず、CDブートするような形になっている。そのため、構造がわからなかったので、追いかけてまとめたのが、以下の図である。
VyOSのファイルシステム階層のまとめ
まとめると、以下のようになる。
- Read-Onlyのsda1:/boot/1.1.0/1.1.0.squashfsをベースに、Copy-On-Writeでsda1:/boot/1.1.0/live-rwの領域を使うoverlayfsのマウントが基本になっている。
- 揮発性の/opt/vyatta/configと、永続化される/opt/vyatta/etc/config (/config)がある。
- /live以下は、このoverlayfsを構成・管理するためにマウントされた領域で、通常は気にしなくて良い。
- /boot以下もほぼ同様に、live環境と通常環境のつじつまを合わせるためにマウントされている。
各領域の解説
各領域を個別に解説していくと、以下の通りになる。/sysとか/procとかは他と一緒なので省略する。
- /live
- overlayfsによってCopy-On-Writeが構成されている前の生の領域をつつくためのディレクトリをマウントするディレクトリ。
- /live/image
- Copy-On-Write領域を含むパーティションをマウントするところ。
- /live/cow
- Copy-On-Write領域の生のファイル群。
- /opt/vyatta/config
- 揮発性のVyOSの設定を書き込むところ。Ciscoでいうrunning-configのイメージ。
- /opt/vyatta/etc/config (=/config)
- 永続化されるVyOSの設定を書き込むところ。Ciscoでいうstartup-configのイメージ。結果的に/configと同じ。ただし、どの環境でも/configを使えるように、というようなことを言っているので、/configを使うほうがベターか?
- /boot
- /bootはCopy-On-Writeの構成の前のタイミングから必要なので、別枠で管理される。そのため、個別にマウントされている。
- /boot/grub
- /bootはイメージ名別だが、grubはブート単位で共通なのでさらにマウントされている。
Debian Liveをベースにしているようなので、おそらくDebian Liveと近いのではないかと思う。
ブートプロセス
上述の説明を作るのに、/proc/mountsを読めばわかるかなーと思っていたが、読んでもわからなかったので、ブートプロセスを追うことになってしまった。もしかしたら参考になる人がいるかもしれないので、マウントに関連する部分について読んだメモを残しておく。
(分割されていないメモのほうが見やすいかもしれないので
ここに置いておく)
ブートプロセスのマウント関連部分の概略
- Grubからinitrdが立ち上がる。
- initrd内で必要なイメージや領域を探し出し、いくつか「/なんとか」にマウント後、本ブートに必要な分を「/root」以下にぶら下げる(このため、ブート後には見えない謎の/cowとか/live-rw-backingが残る)。
- 通常のsysvrcのinit処理に移り、/opt/vyatta/configなど、後からでも間に合う分をマウントする。
以下は各処理。
Grubでブート
まずGrubでブートする。/boot/grub/grub.cfgのデフォルトエントリが以下の通りになっているので、initrd=/boot/1.1.0/initrd.imgが起動する。「boot=live」「vyatta-union=/boot/1.1.0」がポイント。
menuentry "VyOS 1.1.0 linux (KVM console)" {
linux /boot/1.1.0/vmlinuz boot=live quiet vyatta-union=/boot/1.1.0 console=ttyS0,9600 console=tty0
initrd /boot/1.1.0/initrd.img
}
initrdのinit起動
initrd内の
initスクリプトが起動する。カーネルパラメータbootがBOOT環境変数に入っているため、
. /scripts/${BOOT} #L215
ではscripts/liveがdot sourceされ、
mountroot #L218
で、mountroot関数が呼ばれる。
必要なイメージを探したりしながら、ブートに必要な部分がマウントされていく。カーネルパラメータvyatta-union=の分岐で必要なパラメータがセットされているのがポイント。
説明しているとしんどいので呼んだと考えられるコマンドと、呼んだ箇所のメモを記載する。
#Lxxxは特に記述がなければscripts/liveのもの。括弧書きはブート後の/proc/mountsの対応行。
mount -t ext4 -o ro,noatime /dev/sda1 /live/image
-> livefs_root=$(find_livefs ${i})
# $i: 0...60 (timeoutカウンタ)
-> check_dev "${dev}" # L1546
# $dev: "/dev/sda1" #L1531,L1533,L1544の結果からおそらく
-> mount -t ${fstype} -o ro,noatime "${devname}" ${mountpoint} #L1449
# $fstype: "ext4" #L1445のget_fstypeの結果からおそらく
# $devname: "/dev/sda1" #L1423の結果からおそらく
# $mountpoint: "/live/image" #L10
===> mount -t ext4 -o ro,noatime /dev/sda1 /live/image (後の/dev/sda1 /live/image ext4 rw,relatime,data=ordered 0 0)
# livefs_rootには/live/imageが入る
mount -t squashfs -o ro,noatime /dev/loop0 /1.1.0.squashfs
-> mount_images_in_directory "${livefs_root}" "${rootmnt}" "${mac}" # L1700
-> setup_unionfs "${directory}/${LIVE_MEDIA_PATH}" "${rootmnt}" "${adddirectory}" # L609
# $directory: $livefs_root
# $LIVE_MEDIA_PATH: "live" #L11
-> mount -t "${fstype}" -o ro,noatime "${backdev}" "${croot}/${imagename}" # L1178
# $backdev: backdev=$(get_backing_device "${image}" "-r") #L1161
# $(setup_loop "${1}" "loop" "/sys/block/loop*" '0' "${LIVE_MEDIA_ENCRYPTION}" "${2}") #L569
# $croot: "/" #L1071
# $imagename: imagename=$(basename "${image}") #L1142
===> mount -t squashfs -o ro,noatime /dev/loop0 /1.1.0.squashfs (/dev/loop0 /1.1.0.squashfs squashfs ro,noatime 0 0)
mount -o remount,rw /dev/sda1 /live/image
-> # cowprobe=$(find_cow_device "${root_persistence}") #L1223
# $root_persistence: "live-rw" #L13
-> find_cow_device #scripts/live-helpers:L333
-> if ! try_mount "${devname}" "${cow_backing}" "rw"
# $cow_backing: cow_backing="/${pers_label}-backing" #script/live-helpers:L339
# $pers_label: $root_persistence #script/live-helpers:L338
# つまり、$cow_backingは"/live-rw-backing"
# $devname: 探索の結果から/dev/sda1 (のはず)
-> mount -o remount,"${opts}" "${dev}" "${old_mountp}"
# $opts: "rw" #L291
# $dev: "/dev/sda" #L289
# $old_mountp: "/live/image"
===> mount -o remount,rw /dev/sda1 /live/image (後の/dev/sda1 /live/cow ext4 rw,relatime,data=ordered 0 0)
mount -o bind /live/image /live-rw-backing
-> mount -o bind "${old_mountp}" "${mountp}" #script/live-helpers:L314
# $old_mountp: おそらく"/live/image" #L294
# $mountp: /live-rw-backing
===> mount -o bind /live/image /live-rw-backing
(/dev/sda1 /live-rw-backing ext4 rw,relatime,data=ordered 0 0)
## find_cow_deviceはecho "${pers_fpath}"を返す #script/live-helpers:L389
# $pers_fpath: ${cow_backing}/${PERSISTENT_PATH}/${pers_label} #script/live-helpers:L346
# $PERSISTENT_PATH: "$LIVE_MEDIA_PATH" #L482
# $LIVE_MEDIA_PATH: "${ARGUMENT#vyatta-union=}" #L481
# grubのvyatta-union=は/boot/1.1.0
# すなわち、$pers_fpathは"/live-rw-backing/boot/1.1.0/live-rw"
# find_cow_deviceは${cow_backing}/${PERSISTENT_PATH}/${pers_label}である
# "/live-rw-backing/boot/1.1.0/live-rw" を返している
mount -o bind /live-rw-backing/boot/1.1.0/live-rw /cow
-> mount -o bind ${cowdevice} /cow #L1297
===> mount -o bind /live-rw-backing/boot/1.1.0/live-rw /cow (後の/dev/sda1 /live/cow ext4 rw,relatime,data=ordered 0 0)
# $cowdevice: ${cowprobe} #L1249
mount -t overlayfs -o noatime,lowerdir=//1.1.0.squashfs,upperdir=/cow overlayfs /root
-> mount -t ${UNIONTYPE} -o noatime,lowerdir=${rofsstring},upperdir=/cow overlayfs "${rootmnt}" #L1345
# $UNIONTYPE: "overlayfs" #/etc/live.conf
# $rofsstring: "${croot}/${imagename}${roopt}:${rofsstring}" #L1178
# $croot: "/" #L1071
# $imagename: imagename=$(basename "${image}") #L1142
# $roopt: "" #L1087
# $rofsstring: "" #L1074
# rofsstring=${rofsstring%:} #L1183
# $rootmnt: "/root" #L62, #init:L51
==> mount -t overlayfs -o noatime,lowerdir=//1.1.0.squashfs,upperdir=/cow overlayfs /root
(overlayfs / overlayfs rw,relatime,lowerdir=//1.1.0.squashfs,upperdir=/cow 0 0)
mount -t tmpfs tmpfs /root/live
-> mount -t tmpfs tmpfs ${rootmnt}/live #L1364
==> mount -t tmpfs tmpfs /root/live (tmpfs /live tmpfs rw,relatime 0 0)
mount -o move /cow /root/live/cow
-> mount -o move /cow "${rootmnt}/live/cow" #L1411
==> mount -o move /cow /root/live/cow (/dev/sda1 /live/cow ext4 rw,relatime,data=ordered 0 0)
mount --move /live/image /root/live/image
run_scripts /scripts/live-bottom #L1708
-> live-bottom/05mountpoints https://github.com/vyos/live-initramfs/blob/47cb65a9b94ca48696e8e0255c921167ddcfb49b/scripts/live-bottom/05mountpoints
-> mount --move /live/image /root/live/image #scripts/live-bottom/05mountpoints:L33
===> mount --move /live/image /root/live/image (/dev/sda1 /live/image ext4 rw,relatime,data=ordered 0 0)
mount -o bind /root/live/cow/config /root/opt/vyatta/etc/config
-> live-bottom/50vyatta
https://github.com/vyos/build-iso/blob/4ddda254c76d2bfb807ed7c2c5ea992037638bf7/livecd/config.vyatta/chroot_local-includes/usr/share/initramfs-tools/scripts/live-bottom/50vyatta
-> mount -o bind /root/live/cow/config /root/opt/vyatta/etc/config #L51
===> mount -o bind /root/live/cow/config /root/opt/vyatta/etc/config (/dev/sda1 /opt/vyatta/etc/config ext4 rw,relatime,data=ordered 0 0)
あとは、以下で通常のinit処理が起動する。
exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console #L303
ここでもマウント処理がある。
mount -o nosuid,nodev,mode=775,nr_inodes=0 -t tmpfs none /opt/vyatta/config
-> start #L159
-> mount -o $tmpfs_opts -t tmpfs none ${vyatta_configdir} #L169
# $tmpfs_opts: nosuid,nodev,mode=775,nr_inodes=0 #L168
# $vyatta_configdir: "/opt/vyatta/config" # /etc/default/vyattaより
===> mount -o nosuid,nodev,mode=775,nr_inodes=0 -t tmpfs none /opt/vyatta/config (none /opt/vyatta/config tmpfs rw,nosuid,nodev,relatime,nr_inodes=0,mode=775 0 0)
(mount --bind /opt/vyatta/etc/config /config)
if文で結果的に走ってないのだけど、VyOS的に大事な行なので。
-> mount_slashconfig #L175
-> mount --bind /opt/vyatta/etc/config /config #L154
===> このbind mountはlive環境でもそうでないときでも/configが存在するようにという配慮らしい(L139)
mount --bind /live/image/boot/$image_name /boot
-> bind_mount_boot #L193 #ツール類が正しく操作できるようにという配慮(L112)
-> mount --bind /live/image/boot/$image_name /boot #L123
===> mount --bind /live/image/boot/1.1.0 /boot (/dev/sda1 /boot ext4 rw,relatime,data=ordered 0 0)
mount --bind /live/image/boot/grub /boot/grub
-> mount --bind /live/image/boot/grub /boot/grub #L130
===> mount --bind /live/image/boot/grub /boot/grub (/dev/sda1 /boot/grub ext4 rw,relatime,data=ordered 0 0)
感想
/proc/mountsと/etc/mtabを追えばわかるだろうなと思って始めたけど、それだけじゃ全然わからなかった。ディスクを読むのに使ってた
SystemRescueCdも似たような/livemntがあるので、LiveCD系は似たような構成なんだろうなと思う。そういう意味でLiveブートの構造がわかったのはよかった。