Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題五 - 建立 rootfs 的無磁碟 Linux 系統環境

專題五 - 建立 rootfs 的無磁碟 Linux 系統環境

上次更新日期 2019/07/08

前面幾章主要談的大部分都是虛擬機器的環境,或者是 docker 這類的容器類型的環境,甚至也談到了 ovirt 這種叢集架構的虛擬機器管理機制。 此外,虛擬機器的磁碟檔案系統,可以很輕鬆的透過 copy 或者是 qemu-img 的 backing_file 機制 (我們講的快照碟機制) 來快速建立, 然後硬體配置的 xml 檔案快速修改一下,就能夠啟動另一臺虛擬機器了。那我們能不能在實體機器上面快速的使用這些虛擬機器磁碟檔案? 如果可以的話,那麼實體機器的系統建置就太完美!太快速了!好吧!那我們這個專題時間,就來談一談所謂的『實體機器的無磁碟作業系統』環境吧!

  • 無磁碟環境的概念
  • 以虛擬機器磁碟檔案建立無磁碟環境 rootfs 的檔案系統
  • 建立 iPXE 的網路開機環境
  • 額外的課題
  • 參考資料

無磁碟環境的概念

無硬碟環境可以幫助我們完成非常多的功能!包括裸機復原、偶而需要進行的特殊功能等等,因為他不會破壞系統 (因為是無硬碟), 所以,很多情況下,它真的很有幫助!

  1. 什麼是無磁碟的作業系統?
    1. 現在的作業系統,其實磁碟檔案是很重要的一個項目!因為我們本機端的資料,大部分都是放置在本機的磁碟上的! 當然,未來可能什麼東西都上雲端了~問題是,本機的作業系統檔,總是得要在本機的磁碟上吧?因此,你得要在本機安裝作業系統! 然後啟動作業系統後,才能夠順利的操作你的硬體~這也是我們以前很喜歡畫的四個同心圓,代表:(1)硬體 (2)作業系統 (3)系統呼叫 (4)應用程式!的相關概念。
    2. 大部分大專院校的電腦教室,以及辦公室使用的個人電腦,大部分的情況,都是使用這樣的環境。一部實體機器代表一個實體的作業系統。 所以你可以在離線的情況底下使用該系統,去撰寫你的作業或者是工作上面的辦公室軟體等等。
    3. 在電腦教室中,如果需要進行升級或者是軟體方面的修改,通常得要全部的電腦都進行一次安裝,比較好的,可能就是透過 clonezilla 來進行大批量的復原這樣。因此,數量大的時候,你要進行任何微調,都相當的傷腦筋!
    4. 那麼想一想,我能不能透過網路,直接取得一個作業系統呢?如果可以的話,不就免除了本機的安裝問題?沒錯!這就是無磁碟環境原本的想法。 可以透過中央控制所有的作業系統與磁碟檔案系統,讓用戶端電腦直接透過『網路開機』的行為,就可以取得作業系統核心, 以及根目錄,因為所有的東西都是在網路上,都集中在伺服器,因此,只要伺服器修訂了,所有的用戶端電腦, 就可以使用新的磁碟檔案系統了!相當的方便。
    5. 網路上最有名氣的無磁碟作業系統,大概就屬台灣『國家高速網路中心』所開發的 DRBL 搭配 clonezilla 這兩個軟體最知名! 其中 DRBL 就是透過網路開機的無碟環境系統!
  2. 無磁碟環境是怎麼開機取得作業系統的流程呢?
    1. 先回到開機流程的概念,一般系統開機大概流程都是這樣的:
      1. 按下電源,系統進行 BIOS 讀取,進行系統的自我檢查過程
      2. 根據 CMOS 的紀錄,取得第一個開機裝置
      3. 讀取第一個開機裝置的 MBR 裡面的開機管理程式
      4. 根據開機管理程式的紀錄,取得 kernel 與 initramfs 的檔名,直接從磁碟讀取該檔案
      5. 核心開機,掛載 initramfs ,載入核心驅動,開始驅動硬體與提供作業系統資源
      6. 捨棄 initramfs,重新掛載根目錄,繼續後續的開機流程。
      7. 核心呼叫 systemd ,開始後續所有的應用程式啟用
    2. 在上述的流程中,如果核心與虛擬根目錄 (kernel & initramfs) 主要由網路的 NFS 或 http 或 ftp 提供檔案, 然後,取得根目錄的時候使用 NFS 來掛載 Server 提供的根目錄,不就可以略過本機的檔案系統需求?對了! 這個就是 DRBL 軟體的主要核心架構!
    3. 你如果這樣就相信鳥哥講的話,那應該要打屁股!為啥呢?因為:
      • 那用戶端電腦怎麼透過網路去抓取 kernel 與 initramfs 檔案呢?
      • 而且,Server 端是怎麼告知用戶端 kernel 與 initramfs 放在哪裡呢?
      這個才是重點!其實,一般來說,目前的主機預設都有個『網路開機』的選項,這個選項就是讓用戶端電腦到區域網路裡面去詢問負責提供 dhcp 服務的 server, 並請 dhcp server 提供後續相關的 kernel 與 initramfs 的所在。這整個流程就是所謂的 PXE 環境 (Preboot Execution Environment)
  3. 那麼 PXE 是怎麼取得核心與虛擬根目錄的?整體流程說明:
    1. 詳細的流程也請參考文後的 PXE 說明:
      https://en.wikipedia.org/wiki/Preboot_Execution_Environment
    2. 鳥哥也寫過一篇關於 PXE 的介紹,你可以前往看看:
      http://linux.vbird.org/linux_enterprise/0120installation.php
      該文章裡面比較重要的就是前面那一張簡介的圖示, 基本上,用戶端電腦只要指定使用 PXE 的網路開機,然後用戶端電腦就會主動的透過 Server 的 DHCP 服務取得網路參數, 同時取得 TFTP 的相關 kernel 與 initramfs 設定,之後就可以順利開機!接下來就是看 TFTP 的設定值, 決定使用 NFS 或 http 來取得根目錄囉。
    3. 根據上述的流程說明,Server 就得要啟用 DHCP 服務,以及設定好 TFTP 服務,負責提供核心與 initramfs 的資料。 另外,根據無碟環境的任務,也有可能需要提供 NFS 或 http 或 ftp 的服務!當然,這些服務可以不在同一台 Server 上面, 只要設定正確即可。
  4. 使用無碟環境會有什麼優缺點?
    1. 原本的問題:電腦教室裡面如果有 30 台電腦主機,舊的環境底下,如果你要抽換某些軟體,那就得每一台去安裝處理,無法透過統一的檔案系統提供。
    2. 可能的好處:無碟環境底下,如果有需要安裝新的軟體需求,那麼只要在 Server 端,將軟體安裝到共用的目錄區,每台電腦重新開機後, 就會取得更新後的環境,也就是檔案系統是由 Server 統一管理的,因此在這方面的使用會比較優秀!
    3. 但是支援的情況:而無碟環境也不一定適用所有的作業系統,原生的 DRBL 僅支援 Linux 作業系統而已。
    4. 還有更嚴重的:由於電腦教室所有的電腦主機都是吃來自 Server 的檔案系統,因此 Server 的磁碟效能,以及用戶端到 server 的網路效能, 以及 Server 本身的網卡效能,都會影響整體的教室電腦的 I/O 效能。除非使用 10G 以上的網路, 否則,通常用戶端電腦的 I/O 是個很痛的點。
    5. 另外的問題:如果用戶端電腦正在使用系統時,突然網路有中斷的情況發生時,由於無碟環境用戶端電腦使用的根目錄就是來自於網路, 因此網路中斷之後,整個用戶端電腦將會 crash 而無法使用,這造成相當大的困擾。
    6. 關於開機的網路速度問題:通常 PXE 的環境下,驅動網路卡的 driver 可能不是寫的特別好,所以,很容易只驅動網路卡到 10Mbit/s 的網路速度, 跟原本系統提供的 1000Mbit/s (giga 速度) 差了 100 倍!所以,開機的過程中,只要需要下載比較大容量的檔案時, 就會花費相當多的時間在等待網路傳輸完成上面,這也造成些許困擾。
  5. 另一種無碟環境的設計: rootfs 的情境
    1. 為了解決 PXE 驅動網路卡的問題,因此,網路社群的朋友有開發一個 iPXE 的自由軟體,這個軟體可以透過 http 取得一個可以驅動網卡的韌體, 透過該韌體來驅動本機的網卡。這樣可以確保網路卡速度能達到最佳化 (其實,就是 giga 網路速度而已)。由於設定檔可以透過 http 的文字檔處理, 個人覺得也是相當的方便。此外, iPXE 也支援開機選單功能,也能達到選單移交 (chain loader) 的能力!可以應用於取代傳統的 PXE 環境設定。
    2. 有鑑於無碟環境的: (1)NFS 根目錄來自於網際網路、 (2)目前的硬體記憶體容量都還不小 (幾乎都大於 4G 了),因此, 如果能夠將記憶體模擬成為根目錄,讓整個作業系統 (包含根目錄) 都在記憶體中執行時,不就可以略過網路根目錄的狀態? 這就是 rootfs 的由來!
    3. 回到開機流程的步驟,基本上,你只要將原本透過 NFS 的根目錄,將他變成一個 tar 包裹,並且透過類似 busybox 之類的軟體, 就可以讓整個檔案透過 iPXE 的流程來下載!下載完畢之後,就可以透過虛擬根目錄的方法,直接取得用戶端電腦的根目錄! 這個系統整個功能,就全部在記憶體當中!通常根目錄至少得要花去 1.5G ~ 2.0G 之間,所以,你的系統至少也得要配置 4G 左右的記憶體, 會比較不容易出問題喔!

以虛擬機器磁碟檔案建立無磁碟環境 rootfs 的檔案系統

無碟環境準備中,最重要的當然就是那個根目錄了!根目錄從哪裡來?我們前一章不是建立好了虛擬機器了嘛?能不能透過該系統來取得根目錄呢? 當然可以!來實驗看看:

  1. 根目錄檔案系統資料的抽取方式:
    1. 其實,虛擬機器的根目錄製作方式,也可以從實體機器裡面去複製出來!之前鳥哥寫過一篇建立 Xen 虛擬化的 guest 系統, 就是用這個方式來複製的!簡單的使用 cp 就好了呢!
      建立 Xen 的 guest 系統
    2. 如果想要更乾淨的根目錄系統呢?其實我們有!就是之前安裝虛擬機器時的 demo.img 這個檔案內容了! 只要將這個檔案內容抽取出來,就可以輕鬆愉快的建置好跟目錄了。不過,虛擬機器的磁碟機就是個檔案,這個檔案無法單純使用 mount 去掛載,因此,只好使用額外的 guestfs 軟體功能來抽取根目錄資料囉。
    3. 接下來,我們透過 guest filesystem shell (guestfish) 或 virt-copy-out 將虛擬磁碟機的根目錄給複製出來吧!
      # 1. 預設建立好要複製出來虛擬機器根目錄,就放置到 /vmdisk/rootfs/filesystem/ 目錄下吧!
      # mkdir -p /vmdisk/rootfs/filesystem
      
      # 2. 確認安裝了 libguestfs-tools-c 這個軟體才行!
      # rpm -qi libguestfs-tools-c
      Name        : libguestfs-tools-c
      Epoch       : 1
      Version     : 1.38.2
      Release     : 12.el7_6.2
      Architecture: x86_64
      Install Date: 西元2019年04月23日 (週二) 11時33分46秒
      Group       : Unspecified
      Size        : 20052883
      License     : GPLv2+
      Signature   : RSA/SHA256, 西元2019年03月20日 (週三) 03時52分40秒, Key ID 24c6a8a7f4a80eb5
      Source RPM  : libguestfs-1.38.2-12.el7_6.2.src.rpm
      Build Date  : 西元2019年03月14日 (週四) 18時31分47秒
      Build Host  : x86-01.bsys.centos.org
      Relocations : (not relocatable)
      Packager    : CentOS BuildSystem <http://bugs.centos.org>
      Vendor      : CentOS
      URL         : http://libguestfs.org/
      Summary     : System administration tools for virtual machines
      Description :
      This package contains miscellaneous system administrator command line
      tools for virtual machines.
      
      Note that you should install libguestfs-tools (which pulls in
      this package).  This package is only used directly when you want
      to avoid dependencies on Perl.
      
      # 3. 開始將 demo.img 的資料複製出來:
      # virt-copy-out -a 虛擬磁碟檔 檔內目錄 本機實體目錄
      # virt-copy-out -a /vmdisk/demo.img / /vmdisk/rootfs/filesystem
      
      # 4. 檢查一下複製出來的根目錄是否正常
      # cd /vmdisk/rootfs/filesystem/
      # du -sm *
      0       bin
      100     boot  <==好像大了點
      0       dev
      35      etc
      0       home
      0       lib
      0       lib64
      0       media
      0       mnt
      0       opt
      0       proc
      1       root
      0       run
      0       sbin
      0       srv
      0       sys
      1       tmp
      1068    usr   <==好像大了點
      93      var
      
      這樣就將虛擬機器檔案系統複製出來了!而且看起來內容也沒有什麼大問題!相當的簡單方便啊!不過, 我們從上面的資料也可以發現,傷腦筋的是, /boot 與 /usr 的容量稍大了一些!可能是我們之前有升級, 但是沒有將舊的核心移除!所以導致這個情況的發生。那有辦法在不開啟虛擬機的情況下,處理這個檔案系統嘛?
    4. 使用 rpm --root /chroot/dir/name 的方法來處理這個根目錄的情況:
      # 1. 先檢查 boot/ 底下有什麼東西?
      # ll boot/
      -rw-r--r--. 1 root root   151923  3月 18 23:10 config-3.10.0-957.10.1.el7.x86_64
      drwxr-xr-x. 3 root root       17  3月 12 19:46 efi
      drwxr-xr-x. 2 root root       27  3月 12 19:47 grub
      drwx------. 5 root root       97  4月 23 10:56 grub2
      -rw-------. 1 root root 57153862  3月 12 19:51 initramfs-0-rescue-fcae882ccb0f4ccc9e6b3461dd810057.img
      -rw-------. 1 root root 21318524  3月 19 19:17 initramfs-3.10.0-957.10.1.el7.x86_64.img
      -rw-r--r--. 1 root root   314087  3月 18 23:10 symvers-3.10.0-957.10.1.el7.x86_64.gz
      -rw-------. 1 root root  3544363  3月 18 23:10 System.map-3.10.0-957.10.1.el7.x86_64
      -rwxr-xr-x. 1 root root  6639904  3月 12 19:51 vmlinuz-0-rescue-fcae882ccb0f4ccc9e6b3461dd810057
      -rwxr-xr-x. 1 root root  6643904  3月 18 23:10 vmlinuz-3.10.0-957.10.1.el7.x86_64
      
      # 2. 看起來跟 rescue 有關的那兩個項目,就直接刪除吧!不需要保留了!
      # rm boot/*rescue*
      
      # 3. 因為還需要某些軟體,所以直接來安裝一下這些軟體:
      # yum --installroot /vmdisk/rootfs/filesystem/ install \
      > bash-completion vim-enhanced lsof epel-release net-tools pciutils wget tcpdump
      
      # 3.1 某些軟體是 EPEL 提供的,所以要分兩次來安裝!
      # yum --installroot /vmdisk/rootfs/filesystem/ install \
      > ntfs-utils ntfs-3g ntfsprogs partclone
      
      # 4. 基礎系統的設定處理:
      # vim etc/selinux/config
      SELINUX=disabled
      
      # vim etc/ssh/sshd_config
      UseDNS no
      MaxSessions 100
      
      # rm dev/null
      
      # vim etc/fstab
      這個檔案清空!不需要留下任何有效的資訊!
      
      # vim etc/yum.repos.d/epel.repo
      enabled=0  # 所有的 enable 都設定為 0 喔!
      
      # 5. 開始移除暫時可能用不到的軟體!
      # yum --installroot /vmdisk/rootfs/filesystem/ remove \
         selinux-policy-targeted-3.13.1-229.el7_6.9.noarch \
         libselinux-utils-2.5-14.1.el7.x86_64 \
         container-selinux-2.74-1.el7.noarch  \
         NetworkManager-* \
         firewall*  container* 
      
      # 6. 檢查一下該目錄下所有的軟體名稱:
      # rpm --root /vmdisk/rootfs/filesystem/ -qa | sort
      
      # 7. 通常電腦教室的主機是不需要 wifi 模組的,所以,可以將 iwl??-firmware 的軟體移除
      # yum --installroot /vmdisk/rootfs/filesystem/ remove iwl*-firmware
      
      # 8. 將剛剛使用 yum 所產生的所有暫存資訊都移除
      # yum --installroot /vmdisk/rootfs/filesystem/ --enablerepo=epel clean all
      
      # du -sm
      0       bin
      39      boot
      1       dev
      12      etc
      0       home
      0       lib
      0       lib64
      0       media
      0       mnt
      0       opt
      0       proc
      1       root
      0       run
      0       sbin
      0       srv
      0       sys
      1       tmp
      890     usr
      93      var
      
      這樣處理過後,不但我們需要的軟體都有了,也可以降低一些檔案系統的容量成本,至少未來在用戶端下載我們的檔案系統時,會減少許多容量的存在啊! 加快下載的時間!
  2. 建立開機過程所需要的 initail ram disk (initramfs)
    1. 就像前面提到的開機流程的部份,開機最重要的就是 (1)核心 (2)initramfs (3)根目錄掛載。根目錄的製作,我們上面大概將需要的資料整理好了。 核心檔案基本上就是複製 /boot/vmlinuz-??? 的檔案就好,但是 initramfs 就不能隨便處理!因為我們還得要在 initramfs 裡面, 直接指揮 kernel 進行其他非正規的開機流程!
    2. 一般在 initramfs 內部的開機執行程序,通常是這樣的:
      1. 從網路上面下載 kernel 與 initramfs 檔案
      2. kernel 開始在記憶體內解壓縮,然後執行 kernel 的硬體偵測、資源分配等開機程序
      3. 將 initramfs 的內容解壓縮之後,暫時掛載成為根目錄
      4. 開始執行 initramfs 內的 /init 執行檔 (可以是 script 的格式)
      通常執行 /init 之後,根據一般工作程序,核心會開始掛載開機所需要的模組,進行硬體偵測完畢後,會重新依據預計掛載的根目錄 /etc/fstab 來重新 chroot 到正確的根目錄。我們則是要在 init 裡面偷偷修改成個程序,讓開機掛載根目錄的流程變成這個樣子:
      1. 使用虛擬磁碟 (tmpfs) 的方式,建立磁碟檔案系統所需要的容量
      2. 將前一小節所建立的根目錄,解壓縮到上述的掛載點上面,讓根目錄存在於記憶體中
      3. 執行 swich_root 的指令,讓根目錄移動到上述載點上,並開始載入 systemd 的開機流程。
    3. 好了,根據網路上面的文件,如下的連結,我們由 busybox 這個軟體來作為暫時的 initramfs 的操作環境,作為一個暫時的 Linux 小系統操作程序。
      http://www.espenbraastad.no/posts/centos-7-rootfs-on-tmpfs/
      此外,我們預計進行這個 initramfs 製作時,使用到的目錄規劃是這樣的:
      /vmdisk/rootfs/filesystem/		# 由 VM image 裡面取得的根目錄
      /vmdisk/rootfs/filesystem.d/		# 預計放置由上述目錄壓縮來的根目錄 tar 包
      /vmdisk/rootfs/initramfs/		# 就是 initramfs 的執行流程所需要的各項資料
      
    4. 比較特別的是,busybox 新預先編譯的版本似乎怪怪的,1.28.2 的 x86_64 版本不太正常!編譯過後容易發生怪異的事件,導致無法順利的開機。 因此,這裡我們就來自己編譯一個 busybox 的小型環境!不過,這個 busybox 雖然很好編譯,但是,許多資料我們還是得要處理一番:
      # 1. 先讓我們的系統可以進行編譯,因此先安裝開發工具:
      # yum groupinstall "Development Tools"
      # yum install glibc-static
      
      # 2. 開始下載 busybox,我們在 2019/05/21 下載的最新版本為 1.30.1 這個穩定版:
      # cd /vmdisk/rootfs
      # mkdir busybox
      # cd busybox
      # wget https://busybox.net/downloads/busybox-1.30.1.tar.bz2
      
      # 3. 解壓縮之後就開始來編譯了!
      # tar -jxvf busybox-1.30.1.tar.bz2
      # cd busybox-1.30.1
      # make defconfig
      # LDFLAGS="--static" make -j 4
      ...這會花去一小段時間...
      
      # 4. 觀察編譯完成的檔案內容喔
      # ll busybox
      -rwxr-xr-x. 1 root root 2651704  5月 21 15:27 busybox
      
      # file busybox
      busybox: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, 
        for GNU/Linux 2.6.32, BuildID[sha1]=a8013757b964f2920c0b7e0e63979862bc0c09c9, stripped
      
      上面出現的最重要的特殊字體,就是 statically linked,一定要是靜態函式庫的連結,否則這個程式將無法在 rootfs 的環境下執行。 雖然這樣會導致容量稍微大一點 (網路下載大概是 1M 而已),不過,鳥哥個人覺得,應該還是很值得自己編譯的!
    5. 開始來處理所需要的流程吧:
      # 1. 先建立所需要的目錄:
      # cd /vmdisk/rootfs
      # mkdir filesystem.d initramfs
      # cd initramfs
      
      # 2. 將剛剛編譯完成的 busybox 複製到 bin 底下喔:
      # mkdir bin
      # cp ../busybox/busybox-1.30.1/busybox bin/busybox
      # chmod a+x bin/busybox
      # ll bin
      -rwxr-xr-x. 1 root root 2651704  5月 21 15:28 bin/busybox
      
      # 3. 建立 init 這個執行腳本!
      # vim init
      #!/bin/busybox sh
      
      # 如果有任何執行過程發生錯誤,就丟一個 busybox 的 shell 給管理員 debug 之用!
      error() {
              echo "Jumping into the shell..."
              setsid cttyhack sh
      }
      
      # 開始執行 busybox 內的各項程序
      /bin/busybox --install /bin
      
      # 將核心運作過程中,偵測到的 proc 與 sys 掛載到正確的目錄去
      mkdir -p /proc
      mount -t proc proc /proc
      
      mkdir -p /sys
      mount -t sysfs sysfs /sys
      
      # 將核心偵測到的硬體裝置,一項一項的掛載到正確的目錄去
      mkdir -p /sys/dev
      mkdir -p /var/run
      mkdir -p /dev
      
      mkdir -p /dev/pts
      mount -t devpts devpts /dev/pts
      
      # 處理裝置檔案,加入熱拔插裝置 /dev
      echo /bin/mdev > /proc/sys/kernel/hotplug
      mdev -s
      
      # 建立 tmpfs 的載點,並提供適當的記憶體成為根目錄,1500m 就跟你的記憶體有關!
      mkdir -p /newroot
      mount -t tmpfs -o size=1500m tmpfs /newroot || error
      
      # 開始針對我們建立的根目錄檔案系統解壓縮
      echo "Extracting rootfs... "
      xz -d -c -f rootfs.tar.xz | tar -x -f - -C /newroot || error
      
      # 將核心偵測到的 /sys, /proc, /dev 整個移動到下一個跟目錄環境去
      mount --move /sys /newroot/sys
      mount --move /proc /newroot/proc
      mount --move /dev /newroot/dev
      
      # 最終就是執行未來根目錄中的 systemd
      exec switch_root /newroot /usr/lib/systemd/systemd || error
      
      # chmod a+x init
      
      # 4. 將 init 會運作到的各項資料進行壓縮打包的任務:
      # cd /vmdisk/rootfs
      # vim create.initramfs.sh
      #!/bin/bash
      
      # 1. 先設定好會用到的目錄所在
      basedir=/vmdisk/rootfs
      rootdir=${basedir}/filesystem
      rootfsdir=${basedir}/filesystem.d
      date=$( date +%Y%m%d )
      ramdir=${basedir}/initramfs/
      rootfsfile=${rootfsdir}/rootfs.tar.xz-${date}
      ramfs=${basedir}/initramfs.gz-${date}
      
      if [ ! -d ${rootdir} -o ! -d ${rootfsdir} -o ! -d ${ramdir} ]; then
              echo "Important directory NOT exist"
              exit 1
      fi
      
      # 2. 預防自己忘記加上 yum 的清除,所以這裡再次清理一次 yum 佔存檔
      echo "1. clean yum log file"
      yum --installroot=${rootdir} clean all
      
      # 3. 因為 xz 提供壓縮多執行緒,所以這裡改成這樣,可以加快壓縮的效能!
      echo "2. start to create rootfs"
      cd ${rootdir}
      time tar -c -f - . |  xz -T 0 -9 -c - > ${rootfsfile}
      time cp -a ${rootfsfile} ${ramdir}/rootfs.tar.xz
      
      # 4. 最終,因為核心使用的是 cpio 這個指令來進行檔案的處理,所以就這樣進行即可!
      echo "3. create initramfs"
      cd ${ramdir}
      time find . -print0 | cpio --null -o -H newc --quiet | gzip > ${ramfs}
      
      # chmod a+x create.initramfs.sh
      # ll
      -rwxr-xr-x.  1 root root 1047  5月 20 11:05 create.initramfs.sh
      dr-xr-xr-x. 17 root root  224  4月 23 11:34 filesystem
      drwxr-xr-x.  2 root root    6  5月 20 10:45 filesystem.d
      drwxr-xr-x.  3 root root   29  5月 20 10:57 initramfs
      
      # 5. 最終就是運作他囉! 
      # ./create.initramfs.sh
      
      運作完畢之後,在 /vmdisk/rootfs 底下,應該會有這幾個比較重大的檔案存在:
      # ll /vmdisk/rootfs/initramfs*
      -rw-r--r--. 1 root root 263825132  5月 21 15:51 /vmdisk/rootfs/initramfs.gz-20190521
      
      /vmdisk/rootfs/initramfs:
      總計 256208
      drwxr-xr-x. 2 root root        21  5月 21 14:45 bin
      -rwxr-xr-x. 1 root root      1334  5月 21 10:26 init
      -rw-r--r--. 1 root root 262350264  5月 21 15:51 rootfs.tar.xz
      
      # 記得建立一下 change log 的檔案,告知自己這個目錄的功能為何:
      # vim /vmdisk/rootfs/change_log.txt
      2019/05/20 版本
              1. 基礎系統的安裝,透過 CentOS 7.6 的系統來安裝的!使用最小安裝!同時更新過核心!
              2. 進行系統的各項處理,請參考鳥哥的教學文件
      

建立 iPXE 的網路開機環境

接下來,讓我們開始處理網路開機的動作!需要的包括 dhcp 服務、tftp 服務,以及核心與 initramfs 檔案等等。記得,核心與 initramfs 主要來自於 rootfs 裡面喔!不能亂動!

  1. 使用 iPXE 建立 PXE 所需要的開機管理程式元件:
    1. 如前幾個小節所敘述的,傳統 PXE 元件,驅動硬體可能比較沒有這麼好,因此,我們預計使用 iPXE 來驅動。 iPXE 的官網在底下的連結:
      https://ipxe.org/
      因為 iPXE 提供的功能非常多,另外,我們主要只是想要透過 iPXE 來處理開機選單的製作,因此 iPXE 需要重新編譯才行! 編譯行為的指引也可以參考:
      https://ipxe.org/download
    2. 接下來,讓我們來處理 iPXE 的下載與編譯吧!
      # 1. 先處理想要編譯 iPXE 時,所需要的基礎環境:
      # yum groupinstall "Development Tools"
      # yum install xz-devel
      
      # 2. 使用 git 來下載所需要的 iPXE 原始碼:
      # cd /vmdisk/rootfs/
      # git clone git://git.ipxe.org/ipxe.git
      # cd ipxe
      # ll
      drwxr-xr-x.  6 root root    78  5月 20 11:49 contrib
      -rw-r--r--.  1 root root   558  5月 20 11:49 COPYING
      -rw-r--r--.  1 root root 18092  5月 20 11:49 COPYING.GPLv2
      -rw-r--r--.  1 root root  2931  5月 20 11:49 COPYING.UBDL
      -rw-r--r--.  1 root root   113  5月 20 11:49 README
      drwxr-xr-x. 19 root root  4096  5月 20 11:49 src
      
      # 3. 建立 chainloader 所需要的 iPXE 核心檔案:
      # cd src
      # vim Makefile  # 自己看看,查詢一下 undionly.kpxe 這個關鍵字
      # make bin-x86_64-pcbios/undionly.kpxe
      ... (會花費一小段時間)
      
      # ll bin-x86_64-pcbios/undionly.kpxe
      -rw-r--r--. 1 root root 75468  5月 20 12:00 bin-x86_64-pcbios/undionly.kpxe
      
      快速的將這個重要的檔案編譯完畢!接下來準備玩一玩我們 Server 建立 iPXE 所需要的服務囉!
  2. 處理 tftp 服務:
    1. 從 PXE 的流程當中,我們知道使用者想要從網路取得 kernel + initramfs 時,得要透過 tftp 這個服務! 因此,Server 得要建立這個服務才行!處理的方案相當簡單:
      # 1. 下載安裝 tftp 囉:
      # yum install tftp tftp-server
      
      # 2. 開始啟用 tftp 這個服務。只是要注意,這個服務是 socket 喚醒的!
      # systemctl status tftp.service
      # systemctl status tftp.socket
      # systemctl start tftp.socket
      # systemctl enable tftp.socket
      # netstat -tlunp
      Active Internet connections (only servers)
      Proto Recv-Q Send-Q Local Address   Foreign Address   State    PID/Program name
      tcp        0      0 0.0.0.0:111     0.0.0.0:*         LISTEN   1/systemd
      tcp        0      0 0.0.0.0:80      0.0.0.0:*         LISTEN   4876/httpd
      tcp        0      0 0.0.0.0:22      0.0.0.0:*         LISTEN   4757/sshd
      tcp        0      0 127.0.0.1:25    0.0.0.0:*         LISTEN   5003/master
      tcp6       0      0 :::111          :::*              LISTEN   1/systemd
      tcp6       0      0 :::22           :::*              LISTEN   4757/sshd
      tcp6       0      0 ::1:25          :::*              LISTEN   5003/master
      udp        0      0 0.0.0.0:67      0.0.0.0:*                  5558/dhcpd
      udp        0      0 0.0.0.0:111     0.0.0.0:*                  1/systemd
      udp        0      0 0.0.0.0:602     0.0.0.0:*                  4283/rpcbind
      udp6       0      0 :::69           :::*                       1/systemd
      udp6       0      0 :::111          :::*                       1/systemd
      udp6       0      0 :::602          :::*                       4283/rpcbind
      
      基本上,tftp 是屬於使用才喚醒的一個服務,因此,雖然啟動服務使用的是 tftp.service,但是,事實上它並不是常駐程式, 而是當有任何需求 (UDP port 69 有連線需求時) 才會去喚醒 tftp.service!所以,在這個操作行為上, 我們要啟用的反而是 socket 服務喔!而且該服務負責的 program 就是 systemd!因為這個埠口僅在進行監聽的行為而已!
    2. tftp 伺服器主要的功能是透過 TFTP (port 69) 提供區域網路一個可以下載的環境!既然可以提供下載的環境,那麼自然就得要提供 tftp 的根目錄! 要查詢這個服務的根目錄,可以這樣追蹤:
      # 1. 從 rpm 去找出這個服務的設定檔資訊:
      # rpm -ql tftp-server
      /etc/xinetd.d/tftp                        <==舊版的用這個設定值
      /usr/lib/systemd/system/tftp.service      <==新版的用這個設定值!
      /usr/lib/systemd/system/tftp.socket
      /usr/sbin/in.tftpd
      /usr/share/doc/tftp-server-5.2
      /usr/share/doc/tftp-server-5.2/CHANGES
      /usr/share/doc/tftp-server-5.2/README
      /usr/share/doc/tftp-server-5.2/README.security
      /usr/share/man/man8/in.tftpd.8.gz
      /usr/share/man/man8/tftpd.8.gz
      /var/lib/tftpboot
      
      # 2. 檢查這個設定檔的內容:
      # vim /usr/lib/systemd/system/tftp.service
      [Unit]
      Description=Tftp Server
      Requires=tftp.socket
      Documentation=man:in.tftpd
      
      [Service]
      ExecStart=/usr/sbin/in.tftpd -s /var/lib/tftpboot
      StandardInput=socket
      
      [Install]
      Also=tftp.socket
      
      # 3. 開始查詢 tftp 的根目錄所在:
      # man in.tftpd
      .....
         --secure, -s
                Change  root  directory  on startup.  This means the remote host does not 
                need to pass along the directory as part of the transfer, and may add security.  
                When --secure is specified, exactly one directory should be specified on the 
                command line.  The use of this option is recommended for security as well as 
                compatibility with some boot ROMs which cannot  be  easily  made  to  include  a
                directory name in its request.
      
      很清楚的看到,這個 -s 指的就是 chroot 的根目錄囉!所以,我們就可以知道,原來所有的資料都得要複製到 /var/lib/tftpboot 去才行!
    3. 開始搬移 undinoly.kpxe 檔案到正確的位置:
      # cd /vmdisk/rootfs/ipxe/src/bin-x86_64-pcbios/
      # cp undionly.kpxe /var/lib/tftpboot/
      
      複製到正確的地方就好了!再說明一次,這個 undionly.kpxe 檔案的重點,在進行開機選單的轉移! 讓我們可以透過網頁功能來進行開機選單的製作!方便未來的選單修改行為 (例如可以直接透過 php 的網頁,就可以修改了!)
  3. 開始處理 dhcp 服務的設定:
    1. 一開始得要讓 PXE 的用戶端進行 dhcp 的網路參數取得,然後就得要給予 undionly.kpxe 的檔案所在!因此,這個時候就得要修改 dhcp 了!
      # 1. 開始修改 dhcpd.conf
      # vim /etc/dhcp/dhcpd.conf
      default-lease-time 600;
      max-lease-time 72000;
      ddns-update-style none;
      log-facility local7;
      
      subnet 192.168.19.0 netmask 255.255.255.0 {
        range 192.168.19.1 192.168.19.150;
        option routers 192.168.19.254;
        option domain-name "virtual.dic";
        option domain-name-servers 120.114.100.1,120.114.150.1;
      
        next-server 192.168.19.254;
        if exists user-class and option user-class = "iPXE" {
                filename "http://192.168.19.254/ipxe/menu.php";
        } else {
                filename "undionly.kpxe";
        }
      
        host fantasia {
          hardware ethernet 52:54:00:84:c7:52;
          fixed-address 192.168.19.9;
        }
      }
      
      # 2. 重新啟動 dhcp 吧!
      # systemctl restart dhcpd
      # systemctl status dhcpd
      
      基本上,很單純的加上如上的資料,然後準備開始處理你的 web server 即可啊!
    2. 關於 dhcpd.conf 的內容說明:
      • next-server 192.168.19.254; 說的是,請向後面這個 IP 去找尋開機管理程式的意思。 因此,後面這部主機需要啟用 tftp 這個服務才行!
      • 如果未曾讀取過開機管理程式時:如果是第一次進入本機 tftp 的話,那就讀取檔名為 tftp://undionly.kpxe 這個管理程式。 如前所述,這個檔案其實就是在本機的 /var/lib/tftpboot/ 裡面!
      • 如果是第二次讀取這個檔案,那就直接繼續呼叫 http://192.168.19.254/ipxe/menu.php 這個檔案! 因此,這個時候就是透過 undionly.kpxe 這個管理程式的 chainloader 進行改換成為 menu.php 的處理流程了!
  4. 開始透過 web 提供開機選單的任務:就是 /var/www/html/ipxe/menu.php 的內容!
    1. 先是需要確認安裝了 httpd 以及所需要的 PHP 才行!
      # 1. 安裝軟體
      # yum install httpd php
      # systemctl restart httpd
      
    2. 之後才開始處理 menu.php 的建立!以下為範例檔:
      # 2. 開始設計 menu.php 的內容:
      # cd /var/www/html
      # mkdir ipxe
      # cd ipxe
      # vim menu.php
      #!ipxe
      
      set menu-timeout 60000
      set submenu-timeout ${menu-timeout}
      isset ${menu-default} || set menu-default diskless
      
      :start
      menu iPXE ${version} Boot Menu (ip ${net0/ip}, mac ${net0/mac})
      item --gap -- ------------------------- Select your OS----------------------------------
      item diskless             Go To diskless 
      item local                Go To Local HD booting
      item --gap --
      item --gap -- ------------------------- Advanced Options -------------------------------
      item memtest              Go To Memtest86
      item shell                Drop to iPXE shell
      item instcent             Install CentOS 7 use KSU FTP
      item reboot               Reboot computer
      choose --timeout ${menu-timeout} --default ${menu-default} selected || goto exit
      set menu-timeout 0
      goto ${selected}
      
      :diskless
      echo Boot from rootfs diskless
      kernel vmlinuz devfs=nomount rw panic=60 selinux=0 ip=dhcp biosdevname=0 net.ifnames=0 ipv6.disable=0
      initrd initramfs.gz
      boot || goto failed
      goto start
      
      :local
      sanboot --no-describe --drive 0x80
      
      :memtest
      set base-url http://${net0/gateway}/ipxe
      kernel ${base-url}/memdisk || read void
      initrd ${base-url}/memtest86+-5.01.iso || read void
      imgargs memdisk iso raw || read void
      boot || goto failed
      
      :shell
      echo Type 'exit' to get the back to the menu
      shell
      set menu-timeout 0
      set submenu-timeout 0
      goto start
      
      :instcent
      set base-url http://${net0/gateway}/centos7
      kernel ${base-url}/images/pxeboot/vmlinuz initrd=initrd.img repo=${base-url}
      initrd ${base-url}/images/pxeboot/initrd.img
      boot || goto failed
      
      :reboot
      reboot
      
      :failed
      echo Booting failed, dropping to shell
      goto shell
      
      :exit
      sanboot --no-describe --drive 0x80
      
      詳細的設定可以仔細的參考 iPXE 的官網介紹,這裡只就簡單的部份進行處理!
    3. 上面的 menu.php 我們可以看到重要的 :diskless 的標題中,有個 kernel 以及 initrd 的項目對吧!那個就是核心以及 initramfs 放置的檔名! 因為我們將 menu.php 放在 /var/www/html/ipxe 目錄內,因此,請將核心與 initramfs 移動到正確的位置上!
      # cp /vmdisk/rootfs/filesystem/boot/vmlinuz-3.10.0-957.10.1.el7.x86_64 /var/www/html/ipxe/
      # cp /vmdisk/rootfs/initramfs.gz-20190521 /var/www/html/ipxe/
      # cd /var/www/html/ipxe
      # ln -s initramfs.gz-20190521 initramfs.gz
      # ln -s vmlinuz-3.10.0-957.10.1.el7.x86_64 vmlinuz
      # ll
      lrwxrwxrwx. 1 root root        21  5月 21 14:14 initramfs.gz -> initramfs.gz-20190521
      -rw-r--r--. 1 root root 263825132  5月 21 15:52 initramfs.gz-20190521
      -rw-r--r--. 1 root root      1892  5月 21 14:23 menu.php
      lrwxrwxrwx. 1 root root        34  5月 21 14:15 vmlinuz -> vmlinuz-3.10.0-957.10.1.el7.x86_64
      -rwxr-xr-x. 1 root root   6643904  5月 21 14:09 vmlinuz-3.10.0-957.10.1.el7.x86_64
      
  5. 接下來開始處理 XML 設定檔,嘗試讓 VM 開機透過網路來進行:
    1. 首先,我們還是處理一下 VM 的設定:
      # cd /vmdisk/
      # vim create_vm_settings.sh
      vmnames="rootfs1"
      ...
      
      # sh create_vm_settings.sh
      # vim rootfs1.xml
          <boot dev='network'/>
          <boot dev="cdrom"/>
          <boot dev="hd"/>
      
      # virsh create rootfs1.xml
      
    2. 現在,根據 netstat -tlunp 的情況,進行 spice 的連線喔!連線成功後,請按下第一個光棒來開機! 一定會有很多奇怪的 bug 會產生,這是因為可能有不同的執行檔、不同的檔案系統根目錄的製作方式差異, 因此,請依據最後的錯誤訊息,一個一個去 debug 吧!最終你就可以取得一個完整的 Linux 系統就是了!
    3. 同時再次強調,如果你不是從頭開始看的話,那麼在 busybox 的階段,請不要使用 1.28.2 的預編譯版本! 要就使用 1.26 版,要就自己編譯!千萬不要亂做喔!否則會卡死在某一個 random: fast ... 的環節~找都找不到解決方案! 哈哈!

額外的課題

  1. 利用上面的 menu.php 裡頭的透過 KSU FTP 安裝 CentOS 方式,以崑山計中的 FTP 伺服器 (其實是 http 協定) 內的 CentOS7, 嘗試讓你的開機選單可以透過網路來進行安裝的程序!而無須透過 ISO 映像檔。 (hint: 其實主要就是修改 :instcent 的項目, 並且指定 baseurl 即可!)。如果順利進入該選單的話,基本上,畫面會像這樣:
  2. 透過本地端硬碟來開機!這個就是選擇『 Go To Local HD booting 』的項目!仔細看這個項目的內容, 選單內容是這樣的:
    :local
    sanboot --no-describe --drive 0x80
    
    相關的 iPXE 的 sanboot 可以參考底下的連結: https://ipxe.org/cmd/sanboot
    那個 --drive 0x80 指的就是本地端的第一個磁碟機!按下這個選單,基本上,你就可以開始本機的磁碟開機流程!
  3. 處理 memtest86 事宜:
    1. 一般新的機器到達我們的手裡,為了測試硬體系統的穩定性,擔心會熱當 (不過與磁碟無關),所以,通常我們會進行『燒機』的行為! 所謂的『燒機』,就是讓系統的 CPU 與記憶體在瘋狂的讀寫過程!讓系統產生正常的高熱,並且讀寫數次主記憶體! 若系統可以撐過 24hr 的燒機,通常就是一部不錯的穩定系統。
    2. 經常用來作為燒機的軟體,就是這個 Memtest86 囉!官網在底下:
      https://www.memtest.org/
    3. 通常 Memtest86 會在 Linux 或 Windows 的核心底下執行,因此,我們需要下載 memtest86 的主程式之外,也需要下載 linux 核心! 與 ipxe 相關的說明如下連結:
      http://ipxe.org/appnote/memtest
      可以下載的 memtest86 主程式主要是底下這個:
      http://memtest.org/download/5.01/memtest86+-5.01.iso.gz
      記得要解壓縮!至於提供支援的 Linux 小系統核心,我們使用底下這個: https://www.kernel.org/pub/linux/utils/boot/syslinux/syslinux-5.10.zip
      上述的 syslinux-5.10.zip 解壓縮之後,到裡面的 memdisk/memdisk 這個檔案抓出來!最終兩個檔案放置到 /var/www/html/ipxe 裡面去即可!
      # ll /var/www/html/ipxe/mem*
      -rw-r--r--. 1 root root   25884  5月 21 17:04 /var/www/html/ipxe/memdisk
      -rw-r--r--. 1 root root 1839104  5月 21 17:04 /var/www/html/ipxe/memtest86+-5.01.iso
      
      重點是需要注意檔案的容量!避免抓到錯誤的版本才好!
    4. 重新開機選擇 Memtest 的選單,大概就會出現如下的畫面了! (雖然 VM 的環境下,可能會無法順利執行!)

參考資料題