Vine Linux Magazine
ループバックマウントとchrootで作るなんちゃって仮想マシン

筆者: kenta
発行日: 2011,03,03

1台のPCで複数のOSを使い分けるソリューションとして、KVM や VMWare, VirtualBoxのような仮想マシンは今や常識となっています。 しかし、Linuxの上で別のLinux環境を実現するのであればchrootを使っても簡易仮想マシンの様な環境を作ることができます。 Vine Linuxでは、vbootstrap (Vine Linux の基本システムを作成するためのスクリプト)や debootstrap (Debian GNU/Linux bootstrapper)、rinse (Fedora や CentOS 等の chroot 環境を構築するツール)などの便利なパッケージが用意されており、容易に他ディストリビューションをVine Linuxの上で飼うことができます。 ですがここでは、既存のOSがインストール済みのディスクをそのまま活かして、Vine Linux 5.2の上にchrootを使ったなんちゃって仮想マシンのLinux環境を実現する方法を紹介していきます。

  • ディスクのイメージをループバックマウントする方法
    • dd でダンプしたディスクのイメージをマウントする
    • VMWare の VMDK ファイル形式ディスクをマウントする
  • chrootを使ったなんちゃって仮想マシン環境を構築する方法

chrootとVMWare Playerのベンチマーク比較

本題に入る前にまず、chrootを使った場合のパフォーマンス面での利点をみていきます。

chrootの中のプロセスはホストのkernel上で直接実行されます。そのため、VMWare Playerを初めとする仮想マシン環境と較べると次のようなメリットがあります。

  • パフォーマンス・ペナルティがない
  • VMWareの上限である8CPU以上のSMP環境が使える

ここでは、試しにVine Linux 5.2 (x86 32bit) をインストールしたPC上にVMWare Playerをインストールして、VMWare Player上のVine Linux 4.2 (i386) と、chrootの上に構築した Vine Linux 4.2 (i386) の環境での性能を比較してみました。

VMWare Player と chroot 環境でベンチマーク比較:BeeCrypt Benchmark

まずベンチマークプログラムにBeeCrypt Benchmarkを使って、次のそれぞれの環境で性能を比較してみました。

VMWare
Vine Linux 5.2にインストールしたVMWare Player 3.1.3上のVine Linux 4.2環境で実行
chroot
Vine Linux 5.2の上でchrootを使って構築したVine Linux 4.2環境で実行
5.2 native
Vine Linux 5.2の上でそのまま実行

BeeCrypt Benchmark

総じてchroot上でのベンチマーク結果はVMWare Playerよりも高速で、Vine Linux 5.2の上での実行結果と遜色がないパフォーマンスを示しています。BeeCryptのベンチマークは演算が中心となっており、ディスクやメモリアクセスの性能差がでにくい傾向にあるのですが、それでも約 5%程度の差が出ました。

VMWare Player と chroot 環境でベンチマーク比較:カーネルビルド

先ほどと同じくVMWare Player上の Vine Linux 4.2環境と、Vine Linux 5.2のchroot上に構築したVine Linux 4.2環境で、それぞれカーネルビルドに掛かった時間を比較した結果が次のグラフです。

積み重ねグラフはsysとuser時間を、折れ線グラフはreal時間を表しており、4コアのマシン上でビルドしていたためにsysとuser時間の合計よりもreal時間が大幅に短くなるケースが見られます。なお、それぞれ 6回実行しています。特にreal時間にはばらつきが大きいのでどちらが高速かを一概には言えない面もありますが、全体的にchroot環境の方がsys時間が短く、高速であることが見て取れます。

Kernel Build

このように、chroot上で構築したなんちゃって仮想マシンはパフォーマンスの面では一般的な仮想マシンより優位性があるとわかると思います。

事例 1:古い Vine Linux 4.2 の環境を Vine Linux 5.2 にお引っ越し

Vine Linux 5.2をお使いの方が多いと思います。 しかし、どうしても以前使っていたVine Linux 4.2の環境で使っていた特定のアプリケーションがVine Linux 5.2で使えないからなかなか移行できないという方もおられるかと思います。 また、メインではVine Linux 5.2を使っているけど、一部の商用アプリケーションを使用するために別にRed Had ELやSLESのマシンを用意している方もおられるかもしれません。 ここでは、仮想マシンを使用することなくVine Linux 5.2の上で既存のVine Linux 4.2の実行環境を構築する方法を紹介します。

次の手順で進めていきます。

  1. 古いマシンのディスクをVine Linux 5.2上にマウントしてアクセスできるようにする
  2. chrootコマンドを使って、古いマシンのディスクを"/"としてアクセスできるようにする
  3. 一般ユーザーがsshでリモート接続できる設定
なお、途中で詳細な説明を省いているところがあります。不明な点がありましたらご指摘をいただければ可能な範囲で補足説明します。

dd したディスクイメージをループバックマウント

まず、旧マシンのディスクをVine Linux 5.2の環境からアクセスできるようにしておきます。 ここでは、古いマシンのディスクが /dev/hdX に繋がっているものと想定しています。

# dd if=/dev/hdX of=/opt/Vine42.dd.img

とddコマンドを使って、ディスクのイメージを丸ごとファイルにダンプし、この以前のディスクの環境をVine Linux 5.2の上で使えるようにします。 なお、ここではいくつかのコマンドを説明する目的もあるため、若干無駄な操作も入っています。 「初めからルートパーティションだけddすればいいじゃん」などといった野暮なつっこみは厳に御容赦願います。。。

でも、このままループバックマウントのオプション"-o loop"を使ってディスクのイメージをマウントすることはできません。

# mkdir /opt/Vine42
# mount -o loop /opt/Vine42.dd.img /opt/Vine42
mount: ファイルシステムタイプを指定する必要があります

ここでは、パーティションの情報を確認し、オフセットを指定しながらディスクのイメージをループバックマウントします。

# fdisk -lu /opt/Vine42.dd.img 
設定する必要があります シリンダ数.
あなたは特別機能メニューからこれを行なうことができます

Disk /opt/Vine42.dd.img: 0 MB, 0 bytes
255 heads, 63 sectors/track, 0 cylinders, total 0 sectors
Units = セクタ数 of 1 * 512 = 512 bytes
Disk identifier: 0x000a0970

       デバイス Boot      Start         End      Blocks   Id  System
/opt/Vine42.dd.img1   *          63      208844      104391   83  Linux
/opt/Vine42.dd.img2          208845    15727634     7759395   83  Linux
/opt/Vine42.dd.img3        15727635    16771859      522112+  82  Linux swap / Solaris
領域 3 は異なった物理/論理終点になっています:
     物理=(1023, 254, 63) 論理=(1043, 254, 63)

Startセクタ * 512バイト目がパーティションの始まりになるので、1番目のパーティションは 63*512 = 32,256バイト目からのオフセットを指定することでマウントできます。

# mkdir /opt/Vine42
# mount -o loop,offset=`expr 63 \* 512` /opt/Vine42.dd.img /opt/Vine42
$ ls /opt/Vine42/
System.map@                 initrd-2.6.16-0vl76.27.img  vmlinuz@
System.map-2.6.16-0vl76.27  initrd.img@                 vmlinuz-2.6.16-0vl76.27
System.map.old@             initrd.old.img@             vmlinuz.old@
config-2.6.16-0vl76.27      kernel.h
grub/                       lost+found/

こっちは /boot パーティションですね。 一旦アンマウントして、/ に対応する 2番目のセクタをマウント。 ついでに、必要はありませんが/bootもマウントします。

# umount /opt/Vine42
# umount /opt/Vine42
# mount -o loop,offset=`expr 208845 \* 512` /opt/Vine42.dd.img /opt/Vine42
# mount -o loop,offset=`expr 63 \* 512` /opt/Vine42.dd.img /opt/Vine42/boot 
$ df
ファイルシステム     1K-ブロック  使用        空き 使用% マウント位置
/dev/sda3             38456340  26437676  10065160  73% /
none                   2021060         0   2021060   0% /dev/shm
/dev/loop0             7637400   1928112   5321320  27% /opt/Vine42
/dev/loop1              101086      8908     86959  10% /opt/Vine42/boot

これで Vine Linux 4.2 のディスクを Vine Linux 5.2 からアクセスできるようになりました。

ちなみに、ここでは従来環境のディスクをdd でディスクイメージのファイルに落としていましたが、ディスクをそのまま mount しても同じことは可能です。 この場合は、古いディスクを繋ぐ前にあらかじめ e2label コマンドで "/" や "/boot" といったラベルを消しておきましょう。

# e2label /dev/hda1 ""
# e2label /dev/hda2 ""

そうしなければ、古いVine Linux 4.2のディスクを繋いだときに、そっちが "/" となって起動することがあります。 grubがVine Linux 5.2のカーネルをロードしたのに、"/"としてVine Linux 4.2のディスクが参照され、大量のワーニングを吐きながらVine Linux 4.2が起動する姿を見るとやるせない気分になります。

chrootを使って / のパスを変更

マウントはできましたが、このままではライブラリの依存関係を満たせないので Vine Linux 4.2 のバイナリはほとんどの場合で実行できません。

$ /opt/Vine42/usr/bin/gnome-calculator 
/opt/Vine42/usr/bin/gnome-calculator: error while loading shared libraries: libgnomeui-2.so.0: cannot open shared object file: No such file or directory
$ ldd /opt/Vine42/usr/bin/gnome-calculator
        linux-gate.so.1 =>  (0xffffe000)
        libgnomeui-2.so.0 => not found
        libbonoboui-2.so.0 => not found
        

これは、Vine Linux 4.2のバイナリを実行するのに必要なライブラリ群が/opt/Vine42/usr/lib 以下に配置されている一方、ライブラリは /usr/lib 以下が参照されるため必要なライブラリをリンクできないのが原因です。そこで chroot を使って、/opt/Vine42/usr/lib が /usr/lib として参照できるようパスを変更します。

# chroot /opt/Vine42

これで、/opt/Vine42 が / としてアクセスできるようになります。 試しに、ldd してみるとライブラリの依存関係が満たされていることが確認できるでしょう。

# ldd /usr/bin/gnome-calculator
        linux-gate.so.1 =>  (0xffffe000)
        libgnomeui-2.so.0 => /usr/lib/libgnomeui-2.so.0 (0xf7660000)
        libbonoboui-2.so.0 => /usr/lib/libbonoboui-2.so.0 (0xf7600000)
        

なんちゃって仮想マシンとして使うための初期設定

chrootを使って、Vine Linux 4.2のバイナリが実行できるようになりました。しかし、chrootを実行するにはroot権が必要なほか、何かと不便が多いのであたかも仮想マシンにsshログインするかのように使えるよういくつかの設定をします。

まず、古い mtab が残っていると問題になることがあるので、一旦空っぽにしておきます。

# echo > /etc/mtab 

次に、デバイスファイルがほとんど無いので、必要そうなものをいくつか作ります。

# ls /dev/
console  null
# MAKEDEV /dev/null /dev/random /dev/urandom /dev/ptmx
# mkdir /dev/pts 
# mount /dev/pts
# mount -n -t proc /proc /proc
# mount -n -t sysfs /sys /sys
# mkdir /dev/shm
# mount /dev/shm

先の 3行の手続きは一度だけ必要な初期設定です。後の4回のmountコマンドは、ホストPCを再起動する度に必要となる設定です。

sshd を動かしてログイン

あとはsshdを動かせば、ログインできるようになります。

ただし、22番ポートはホストの Vine Linux 5.2 環境で使われているハズですので、ポート番号を適当に変えた上で sshd を起動します。

# vim /etc/ssh/sshd_config
# diff /etc/ssh/sshd_config.orig /etc/ssh/sshd_config
13c13
< #Port 22
---
> Port 20022
# /etc/init.d/sshd start | nkf -uw
sshdを起動中:                                              [  OK  ]

ちなみに、Vine Linux 4.2 は Vine Linux 5.2とはデフォルトのロケールが異なります。 (5.2はja_JP.UTF-8、4.2はja_JP.eucJP) そのために文字化けすることがあると思います。それぞれ適当に対応してください。

このほか、dbus も動いていないと何かと不便が多いようですので、dbusも起動しておきます。

# /etc/init.d/messagebus start 

これで準備は完了です。chroot したターミナルはもう CTRL+d で閉じて構いません。

それではホストのVine Linuxから chroot上のVine Linux 4.2に一般ユーザーとしてSSHログインしてみましょう。この際、sshのポート番号を変えているので -p オプションで明示的にポート番号を指定することを忘れないで下さい。

$ ssh -p 20022 vine@localhost 
vine@localhost's password: 
Last login: Sun Dec 12 00:21:43 2010 from localhost.localdomain
$ gnome-calculator &

今度は、ちゃんと電卓が起動できたと思います。きっと他の商用アプリケーションも動かせるでしょう。

なお、/etc/resolv.confが空っぽだったり正しくない値が書かれている場合に、DNSによる名前解決が出来ないことがあります。Host環境の設定をそのまま cp するので良いでしょう。

$ sudo cp -a /etc/resolv.conf /opt/Vine42/etc/resolv.conf

事例 2:Debianベースの ARM 開発環境を Vine Linux 5.2 で使う

先の事例では、既存のHDDにインストールしたVine Linux 4.2を Vine Linux 5.2上のchrootで使う事例を紹介しましたが、既存の環境がVMWare上にあった場合でも同様のことが可能です。

ここでは、VMWareのディスクイメージをループバックマウントして、先と同じくchrootのなんちゃって仮想マシンに仕立て上げる方法を紹介します。

VMWareのディスクイメージを入手

ちょっとAndroidをGingerbreadに更新したいなぁなんて思うことは誰でもあると思います。(いや、ないとは思いますが。。) そんなときに、クロス開発環境から作り始めるといろんなこだわりもあったりしてそれだけで半日遊んでしまいます。で、僕は今日いったい何をやっていたんだろう?などと後悔するわけです。閑話休題。

お手軽にARMのクロス開発環境がほしいと思ったときに便利なのが、株式会社アットマークテクノから公開されているATDEです。 VMWareのイメージをダウンロードしてきてそのまま使えますし、お手持ちのdebian の環境にインストールしても使えます。 でも、VMWare Playerは諸般の事情で使えないとか、やっぱりVineをメインで使いたいと思ったときに ATDEの環境をchrootの上で使えると便利です。 ここでは、ATDEのVMWareディスクイメージを例題としてVine Linux 5.2にマウントしてのchrootで使ってみます。

まず初めにATDEのサイトから VMWare イメージ (atde3-20100309.zip [696.69 MB]) をダウンロードし、zipアーカイブを展開します。

$ wget http://armadillo.atmark-techno.com/files/downloads/atde/atde3-20100309.zip
$ sudo unzip atde3-20100309.zip -d /tmp/
Archive:  atde3-20100309.zip
   creating: /tmp/atde3-20100309/
  inflating: /tmp/atde3-20100309/atde3-s005.vmdk
  inflating: /tmp/atde3-20100309/atde3-s007.vmdk
  inflating: /tmp/atde3-20100309/atde3.vmx
  inflating: /tmp/atde3-20100309/atde3-s009.vmdk
  inflating: /tmp/atde3-20100309/atde3.vmxf
  inflating: /tmp/atde3-20100309/atde3.nvram
 extracting: /tmp/atde3-20100309/atde3.vmsd
  inflating: /tmp/atde3-20100309/atde3-s002.vmdk
  inflating: /tmp/atde3-20100309/atde3.vmdk
  inflating: /tmp/atde3-20100309/atde3-s001.vmdk
  inflating: /tmp/atde3-20100309/atde3-s003.vmdk
  inflating: /tmp/atde3-20100309/atde3-s004.vmdk
  inflating: /tmp/atde3-20100309/atde3-s006.vmdk
  inflating: /tmp/atde3-20100309/atde3-s008.vmdk

分割されたvmdkファイルをディスクのイメージに変換

vmdk をループバックマウントできるようにできるように、ディスクのファイル形式をqemu-imgでコマンドで変換します。 もしqemu-imgがインストールされていない場合にはapt-getインストールします。

$ sudo apt-get install qemu
$ cd /tmp/atde3-20100309
$ sudo qemu-img convert atde3-s00[1-9].vmdk -O bin /opt/atde3-20100309.img
(VMDK) image open: flags=0x2 filename=atde3-s001.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s002.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s003.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s004.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s005.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s006.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s007.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s008.vmdk
(VMDK) image open: flags=0x2 filename=atde3-s009.vmdk
$ fdisk -lu /opt/atde3-20100309.img 
設定する必要があります シリンダ数.
あなたは特別機能メニューからこれを行なうことができます

Disk /opt/atde3-20100309.img: 0 MB, 0 bytes
255 heads, 63 sectors/track, 0 cylinders, total 0 sectors
Units = セクタ数 of 1 * 512 = 512 bytes
Disk identifier: 0x0008bb4c

            デバイス Boot      Start         End      Blocks   Id  System
/opt/atde3-20100309.img1   *          63      498014      248976   83  Linux
/opt/atde3-20100309.img2          498015    33543719    16522852+  8e  Linux LVM
領域 2 は異なった物理/論理終点になっています:
     物理=(1023, 254, 63) 論理=(2087, 254, 63)

LVMが使われているディスクイメージをループバックマウント

ディスクは参照できる形になりました。

でも面倒なことにLVMが使われているので、mount offset オプションを使ってマウントできません。(たぶん)

そこでまず、kpartxを使って各パーティションのデバイスマップを作ります。

$ sudo /sbin/kpartx -av /opt/atde3-20100309.img 
add map loop2p1 : 0 497952 linear /dev/loop2 63
add map loop2p2 : 0 33045705 linear /dev/loop2 498015
$ ls /dev/mapper/
control  loop2p1  loop2p2

これでディスクイメージの各物理パーティションに対応したデバイスマップができました。fdiskで見えていたパーティションはそれぞれ、/dev/mapper/loop2p1 /dev/mapper/loop2p2 として参照できるようになっています。

次にLVM のマウントです。各コマンドについては適宜manなどで調べてください。詳細は割愛します。

$ sudo /sbin/pvscan 
  PV /dev/dm-1   VG atde3   lvm2 [15.75 GB / 0    free]
  Total: 1 [15.75 GB] / in use: 1 [15.75 GB] / in no VG: 0 [0   ]
$ sudo /sbin/lvscan 
  inactive          '/dev/atde3/root' [14.70 GB] inherit
  inactive          '/dev/atde3/swap_1' [1.06 GB] inherit
$ sudo /sbin/vgchange -ay
  2 logical volume(s) in volume group "atde3" now active
$ sudo /sbin/lvscan 
  ACTIVE            '/dev/atde3/root' [14.70 GB] inherit
  ACTIVE            '/dev/atde3/swap_1' [1.06 GB] inherit

LVMのパーティションに対応するデバイスファイルができましたので、あとは普通にマウントできます。

$ sudo mkdir /opt/atde3-20100309
$ sudo mount -o loop /dev/atde3/root /opt/atde3-20100309
$ sudo mount -o loop /dev/mapper/loop0p1 /opt/atde3-20100309/boot

マウント後の初期設定

後の手順は事例1 と同様に、sshdを動かしててリモートログインできるように設定します。

後で使うので /etc/resolv.conf を ATDE側にコピーした上でchrootします。

$ sudo cp -a /etc/resolv.conf /opt/atde3-20100309/etc/
$ sudo chroot /opt/atde3-20100309

デバイスファイルはだいたい揃っているので、MAKDEV と mkdir は不要です。

# echo > /etc/mtab
# mount /dev/pts
# mount -n -t proc /proc /proc
# mount -n -t sysfs /sys /sys
# mount /dev/shm

sshdがインストールされていないのを追加すると、あとは事例1 と同様にsshログインできるようになります。

# apt-get install openssh-server
# vim /etc/ssh/sshd_config 
# /etc/init.d/ssh start
# exit
$ ssh -p 20022 atmark@localhost
atmark@localhost's password: atmark
    (初期パスワードはatmark)
$ whoami
atmark

試しにAndroid kernelをビルド

Armadillo-500FX用のビルド方法をまとめられているsolaさんのblog に沿ってATDEのクロスコンパイラを使ってkernelをビルドしてみました。

$ mkdir ~/bin
$ curl http://android.git.kernel.org/repo > ~/bin/repo
$ chmod +x ~/bin/repo
$ export PATH=/home/atmark/bin:$PATH
$ mkdir ~/work
$ cd ~/work
$ git clone git://github.com/sola-dolphin1/OHA-Android-2.3_r1.0.git
Initialized empty Git repository in /home/atmark/work/OHA-Android-2.3_r1.0/.git/
remote: Counting objects: 191730, done.
remote: Compressing objects: 100% (133820/133820), done.
remote: Total 191730 (delta 84118), reused 158374 (delta 50901)
Receiving objects: 100% (191730/191730), 1019.48 MiB | 2956 KiB/s, done.
Resolving deltas: 100% (84118/84118), done.
Checking out files: 100% (249342/249342), done.
$ export ANDROID=/home/atmark/work/OHA-Android-2.3_r1.0
$ cd $ANDROID/kernel/armadillo500fx/
$ make ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- armadillo500fx_dev_android_defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/docproc
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/kxgettext.o
  SHIPPED scripts/kconfig/zconf.tab.c
  SHIPPED scripts/kconfig/lex.zconf.c
  SHIPPED scripts/kconfig/zconf.hash.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#
$ make ARCH=arm CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- -j4
scripts/kconfig/conf -s arch/arm/Kconfig
  CHK     include/linux/version.h
(中略)
  AS      .tmp_kallsyms2.o
  LD      vmlinux
  SYSMAP  System.map
  SYSMAP  .tmp_System.map
  OBJCOPY arch/arm/boot/Image
  Kernel: arch/arm/boot/Image is ready
  AS      arch/arm/boot/compressed/head.o
  GZIP    arch/arm/boot/compressed/piggy.gz
  CC      arch/arm/boot/compressed/misc.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      drivers/scsi/scsi_wait_scan.mod.o
  LD [M]  drivers/scsi/scsi_wait_scan.ko
  AS      arch/arm/boot/compressed/piggy.o
  LD      arch/arm/boot/compressed/vmlinux
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready

このように、ARM用のLinux Kernelをビルド出来る環境が簡単に実現できました。

ここではDebianベースのATDEを例に取りましたが、他のLinuxでも基本的にここで紹介した方法でchroot上で使えるようにできると思います。VMWareのディスクイメージは他にも様々なところで公開されていますので、いろいろ試してみて下さい。

おわりに

ここでは、既存のHDDイメージやVMWareのディスクイメージをループバックマウントして、仮想マシンのように使う事例を紹介しました。

chrootは使い方次第で様々な活用が可能です。今後もchrootを使ってVine Linuxのより幅広い活用事例を紹介していきます。読者の皆様から「こういった内容を詳しく知りたい」や「こんな問題があるけどよい解決方法は?」などのご要望、ご質問があれば前向きにテーマとして取り上げたいと思います。皆様のご意見、ご感想をお待ちしております。