2015年3月11日 星期三

整個 debian 作業系統養在一個映像檔裡面

既然一個映像檔可以當成硬碟的一個分割來用 (loopback file system)、 既然 linux 作業系統四海為家, 那麼當然可以把整個 linux 塞進一個映像檔裡面。 然後要如何開機呢? 如果能夠再解決一些技術問題, 它將會讓接觸 linux 的門檻降得更低。 不想深究技術細節的讀者也可以直接跳到文末 「第二代救命碟願景」。

假設你已將 debian 7.8 安裝在 /dev/sdx66 這個分割上。 我們將製作一個好幾 G 大的 linux 的映像檔 (叫做 rootfs.img 好了) 並將它放到 /dev/sdz99 這個隨身碟分割上。 請先將 /dev/sdz99 格式化成 ext4 (或 ext3 或 ext2) 並且安裝、 測試 extlinux 開機管理員

在 extlinux.conf 設定檔裡面有一段 initrd=... 跟 root=... 。 一般的 initrd.img 裡面的程式碼會負責把 root 分割掛載起來; 但這次我們想掛載映像檔裡面的 root file system, 需要兩個步驟: 先掛載 /dev/sdz99, 再把裡面的 rootfs.img 掛起來當做真正的 root。 所以我們必須修改 rootfs.img 。 接下來我們將在已裝好系統的 /dev/sdx66 上面修改 etc/initramfs-tools/ 裡面的一些設定檔, 然後產生一個新的 rootfs-loop.img 。

首先請檢查一下 貴哥所提供的這個檔 裡面有沒有惡意程式 :-) 然後

 mount /dev/sdx66 /media/sdx66
 cd /media/sdx66
 tar xzf ..../loopback-rootfs.tgz

這會在 /media/sdx66 的 etc/initramfs-tools/ 底下產生三個文字檔:

 modules.looproot
 scripts/init-top/looproot
 scripts/local-premount/looproot

請參考 modules.looproot 照著修改你原先的 modules 這個檔案, 在最後面加上 loop 跟 ext4 (或 ext3 或 ext2, 或全都加上也可以)。 如果原先並沒有 modules 檔, 就把 modules.looproot 改名為 modules。 這樣開機時才會自動載入這些模組, linux 才能掛載 ext4 分割, 並且掛載映像檔。 第二個檔 init-top/looproot 這個腳本主要是建立一個空的目錄來當做掛載點。 第三個檔 local-premount/looproot 這個腳本就是重點: 它先掛載 /dev/sdz99 再掛載映像檔 rootfs.img -- 當然, 腳本裡面用的都是變數; 這些變數需要在 extlinux.conf 裡面設定。

順便把 /dev/sdx66 整個分割壓縮備份起來, 比方叫做 debian-7.8-a.tgz 好了。

再來, 卸下 /dev/sdx66 並且用它開機 (實體機或虛擬機皆可)。 確認 /etc/initramfs-tools/ 底下的檔案正確無誤, 下指令: mkinitramfs -o ~/initrd-loop.img 。 (如果是虛擬機, 產生完就可以關掉了。)

然後製作 rootfs.img:

 mount /dev/sdz99 /media/sdz99
 fallocate -l 6G rootfs.raw
 mkfs -t ext4 rootfs.img
 mount -o loop rootfs.img /mnt/t1

其中 fallocate 那一句可以快速建立一個 6G (未初始化、 內含亂七八糟資料的) 映像檔。 詳見 這一頁

  1. 把 debian-7.8-a.tgz (先前 /dev/sdx66 分割的備份) 解壓縮到 /mnt/t1 去
  2. 查詢它的 UUID: tune2fs -l rootfs.img | grep UUID 假設是 01234567-89ab-cdef-fedc-ba9876543210。
  3. 編輯 etc/fstab 把 / 那一列最左欄改成正確的 UUID=01234567-89ab-cdef-fedc-ba9876543210 之類的。
  4. 離開並卸載 rootfs.img

修改 (/media/sdz99 底下某處的) extlinux.conf。 因為我把開機時要用到的核心、 initrd、 根目錄映像檔這三個檔案都放在 /debian-7.8/ 底下, 所以我的 extlinux.conf 長這樣:

label debian-7.8
        menu label Debian 7.8 KDE loopback inside /dev/sda1
        kernel /debian-7.8/vmlinuz
        append initrd=/debian-7.8/initrd-loop.img root=UUID=01234567-89ab-cdef-fedc-ba9876543210 loop=/debian-7.8/rootfs.img

卸載 /dev/sdz99, 拿去測試一下吧! 如果失敗, 歡迎留言提問, 請務必貼上錯誤訊息及相關設定檔的內容。

我曾試著想把 /dev/sdx99 格式化成 vfat。 但這會遇到幾個問題:

  1. 採用 fallocate 所產生的大檔, 無法直接複製到 vfat 分割上, 出現這樣的錯誤訊息: cp: cannot lseek `/media/sdb1/debian-7.8/rootfs.img': Invalid argument
  2. vfat 上面的檔案大小不可超過 4G。
  3. 在 extlinux.conf 裡面要指定 root 分割時, 無法用 UUID 的語法。

1 可以採用蠻力解決; 沒力跟 2 與 3 纏鬥了。 如果您採用 vfat 或 ntfs 而且可以解決 2 與 3, 麻煩分享一下。 這樣以後要幫聽眾或學生製作救命碟就簡單多了: 只要花很短的時間幫他製作一顆 finnix 開機碟, 然後請他自行用 windows 瀏覽我的網站、 把 /debian-7.8/ 整個拷貝回隨身碟上即可。 要推出新版時, 也不需要整顆重做, 只要把新版映像檔放上網, 手上有舊版的人都可以自行下載。 也不一定只有我可以推出新版, 其他人也可以分享他所製作的 rootfs 映像檔。 如果隨身碟夠大, 不需要重新分割就可以放好幾個不同版本的 linux。 總之犧牲一點點執行效率, 換來好處多多。 這就是第二代救命碟的願景啊!

本文主要參考資料:

  1. booting linux from a loop file system
  2. Booting Debian from a loopback filesystem
  3. 也請搜尋 『loopback root initramfs』

13 則留言:

  1. 這樣做感覺跟把系統打包成LiveCD/USB幾乎一樣啊,
    只差在LiveCD/USB的映像檔有壓縮過。
    可以參考tux2live進行簡化看看。

    回覆刪除
  2. 我的理解是這樣: tux2live 用來製作 livecd, 採用 squashfs。 所以 tux2live 的優點是有壓縮, 缺點是想存檔的話還要研究一下 persistence 怎麼做。 (若找到請分享連結) 這篇講的 loopback 沒有壓縮, 但是直接就可寫入。 對於製作者(老師)來說手續比較簡單啦,反正要求使用者(學生)準備大一點的隨身碟就好了 :-)

    回覆刪除
  3. 這篇:http://www.vleeuwen.net/2014/01/create-a-persistent-debian-live-usb-flash-drive

    我記得 Ubuntu / Debian 的 Live Persistence 機制已經早有支援。

    不過,個人是覺得取效能與穩定性 (回存)的平衡,Puppy 那種方式反而比較有彈性的。^^

    回覆刪除
  4. 我幾年前用tux2live做出來的live系統就直接有persistence的功能了,
    穩定性沒什麼問題,裝在HP nx6125 筆電 k8 sempron RAM 768M上效能也很好,
    只切一個2G FAT32硬碟分割區含映像檔400多MB用grub2開機。

    回覆刪除
  5. 想要用映像檔開機,應該也可以參考grub的loopback device:
    2. loopback device
    According to Grub manual, "GRUB is able to read from an image (be it one of CD or HDD) stored on any of its accessible storages". The image could be an iso file, a hard disk image with partitions in it, and a disk partition with file system in it. Let us try all these cases. In case grub load the related modules, i.e. partition map module, file system modules, loopback can handle all these cases.

    回覆刪除
  6. 謝謝大家指教! 已用開機參數 live-media-path= 指定客製路徑,把 tux2live 製作出來的 livecd (3G=>0.9G 超清爽 der) 搬到隨身碟上 (只需要 vmlinuzlive initrdlive.img filesystem.squashfs 三個檔加上 extlinux 設定)。 但試不出 persistence。 也搜尋不到文件。 請問要如何指定 persistence 存檔名稱路徑等等啊?

    回覆刪除
  7. tux2live似乎是clonezilla的副產品,
    所以應該可以參考:
    http://clonezilla.org/clonezilla-live/boot-parameters/live-boot.php
    裏面persistence-*的部份
    我是做一個lext2映像檔名叫live-rw放根目錄。

    回覆刪除
  8. 剛剛測試可用persistence-path=來指定live-rw的路徑,這樣就可以不放根目錄了。
    另外因為live-rw是直接覆蓋在live系統根目錄下的,
    所以猜測說不定可以做一個很小只能開機的live系統,
    或者乾脆拿clonezilla來用,
    然後把完整的系統映像檔改名叫live-rw拿來配合使用,
    達到洪老師本來要的效果。

    回覆刪除
  9. 啊 終於試出來了。 兩個要點: (1) 參數是 persistence 而不是 persistent (2) live-rw 不可以跟其他三個檔案放在同一個分割。 http://refracta.freeforums.org/experimental-alternative-usb-installation-method-t103.html#p842 "BTW You can't use a persist file on the same partition
    as the live-media with a standard initrd" 過些時候再來寫完整說明。 謝謝大家 & 謝謝 ceasar 在 e-mail 裡詳細解說!

    回覆刪除
    回覆
    1. 仔細一看,我這裡正常運作的參數卻是persistent-*,用persistence-*不會成功;
      而且live-rw可以放在同一個分割。
      不知道會不會跟linux的版本有關 ,我是用debian wheezy下去做的。
      測試環境:FAT32隨身碟不分割,syslinux開機,
      開機參數 :
      KERNEL /live/vmlinuzlive boot=live username=root persistent persistent-path=/live/ elevatop=noop ipv6.disable=1 video=640x480-8 modeset=1 noprompt quiet splash
      APPEND initrd=/live/initrdlive.img
      含live-rw共4個檔都放在/live/裏面
      使用tux2live-v1.2.0-50製作

      刪除
  10. 而且,如果搭配live-media-path=的話,
    可以在一個隨身碟裡放好幾個live系統,
    建議洪老師可以用我的參數試試看。

    回覆刪除
  11. 洪老師,留言出現被吃掉的狀況,請幫忙看一下。

    回覆刪除
    回覆
    1. 先前三篇相同內容的留言都被 blogger 誤判成垃圾留言 ^_^||| 已貼一篇上去。 奇怪了... 我用的是 debian 7.8 跟 tux2live-v1.2.0-50 ... 用 「persistence "same partition"」 會搜尋到很多人提出「放在同一分割」的需求但上游一直沒有人有空處理,像是這篇: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=715186 至於這一篇 http://www.debianuserforums.org/viewtopic.php?f=7&t=2816#p26388 則提到 refracta2usb 的版本可以使用同一個分割存映像檔跟 live-rw。 暫時沒力氣試了...

      刪除

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。